1"""Types used in the intermediate representation. 2 3These are runtime types (RTypes), as opposed to mypy Type objects. 4The latter are only used during type checking and not directly used at 5runtime. Runtime types are derived from mypy types, but there's no 6simple one-to-one correspondence. (Here 'runtime' means 'runtime 7checked'.) 8 9The generated IR ensures some runtime type safety properties based on 10RTypes. Compiled code can assume that the runtime value matches the 11static RType of a value. If the RType of a register is 'builtins.str' 12(str_rprimitive), for example, the generated IR will ensure that the 13register will have a 'str' object. 14 15RTypes are simpler and less expressive than mypy (or PEP 484) 16types. For example, all mypy types of form 'list[T]' (for arbitrary T) 17are erased to the single RType 'builtins.list' (list_rprimitive). 18 19mypyc.irbuild.mapper.Mapper.type_to_rtype converts mypy Types to mypyc 20RTypes. 21""" 22 23from abc import abstractmethod 24from typing import Optional, Union, List, Dict, Generic, TypeVar, Tuple 25 26from typing_extensions import Final, ClassVar, TYPE_CHECKING 27 28from mypyc.common import JsonDict, short_name, IS_32_BIT_PLATFORM, PLATFORM_SIZE 29from mypyc.namegen import NameGenerator 30 31if TYPE_CHECKING: 32 from mypyc.ir.ops import DeserMaps 33 from mypyc.ir.class_ir import ClassIR 34 35T = TypeVar('T') 36 37 38class RType: 39 """Abstract base class for runtime types (erased, only concrete; no generics).""" 40 41 name = None # type: str 42 # If True, the type has a special unboxed representation. If False, the 43 # type is represented as PyObject *. Even if True, the representation 44 # may contain pointers. 45 is_unboxed = False 46 # This is the C undefined value for this type. It's used for initialization 47 # if there's no value yet, and for function return value on error/exception. 48 c_undefined = None # type: str 49 # If unboxed: does the unboxed version use reference counting? 50 is_refcounted = True 51 # C type; use Emitter.ctype() to access 52 _ctype = None # type: str 53 54 @abstractmethod 55 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 56 raise NotImplementedError 57 58 def short_name(self) -> str: 59 return short_name(self.name) 60 61 def __str__(self) -> str: 62 return short_name(self.name) 63 64 def __repr__(self) -> str: 65 return '<%s>' % self.__class__.__name__ 66 67 def serialize(self) -> Union[JsonDict, str]: 68 raise NotImplementedError('Cannot serialize {} instance'.format(self.__class__.__name__)) 69 70 71def deserialize_type(data: Union[JsonDict, str], ctx: 'DeserMaps') -> 'RType': 72 """Deserialize a JSON-serialized RType. 73 74 Arguments: 75 data: The decoded JSON of the serialized type 76 ctx: The deserialization maps to use 77 """ 78 # Since there are so few types, we just case on them directly. If 79 # more get added we should switch to a system like mypy.types 80 # uses. 81 if isinstance(data, str): 82 if data in ctx.classes: 83 return RInstance(ctx.classes[data]) 84 elif data in RPrimitive.primitive_map: 85 return RPrimitive.primitive_map[data] 86 elif data == "void": 87 return RVoid() 88 else: 89 assert False, "Can't find class {}".format(data) 90 elif data['.class'] == 'RTuple': 91 return RTuple.deserialize(data, ctx) 92 elif data['.class'] == 'RUnion': 93 return RUnion.deserialize(data, ctx) 94 raise NotImplementedError('unexpected .class {}'.format(data['.class'])) 95 96 97class RTypeVisitor(Generic[T]): 98 """Generic visitor over RTypes (uses the visitor design pattern).""" 99 100 @abstractmethod 101 def visit_rprimitive(self, typ: 'RPrimitive') -> T: 102 raise NotImplementedError 103 104 @abstractmethod 105 def visit_rinstance(self, typ: 'RInstance') -> T: 106 raise NotImplementedError 107 108 @abstractmethod 109 def visit_runion(self, typ: 'RUnion') -> T: 110 raise NotImplementedError 111 112 @abstractmethod 113 def visit_rtuple(self, typ: 'RTuple') -> T: 114 raise NotImplementedError 115 116 @abstractmethod 117 def visit_rstruct(self, typ: 'RStruct') -> T: 118 raise NotImplementedError 119 120 @abstractmethod 121 def visit_rarray(self, typ: 'RArray') -> T: 122 raise NotImplementedError 123 124 @abstractmethod 125 def visit_rvoid(self, typ: 'RVoid') -> T: 126 raise NotImplementedError 127 128 129class RVoid(RType): 130 """The void type (no value). 131 132 This is a singleton -- use void_rtype (below) to refer to this instead of 133 constructing a new instance. 134 """ 135 136 is_unboxed = False 137 name = 'void' 138 ctype = 'void' 139 140 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 141 return visitor.visit_rvoid(self) 142 143 def serialize(self) -> str: 144 return 'void' 145 146 def __eq__(self, other: object) -> bool: 147 return isinstance(other, RVoid) 148 149 def __hash__(self) -> int: 150 return hash(RVoid) 151 152 153# Singleton instance of RVoid 154void_rtype = RVoid() # type: Final 155 156 157class RPrimitive(RType): 158 """Primitive type such as 'object' or 'int'. 159 160 These often have custom ops associated with them. The 'object' 161 primitive type can be used to hold arbitrary Python objects. 162 163 Different primitive types have different representations, and 164 primitives may be unboxed or boxed. Primitive types don't need to 165 directly correspond to Python types, but most do. 166 167 NOTE: All supported primitive types are defined below 168 (e.g. object_rprimitive). 169 """ 170 171 # Map from primitive names to primitive types and is used by deserialization 172 primitive_map = {} # type: ClassVar[Dict[str, RPrimitive]] 173 174 def __init__(self, 175 name: str, 176 is_unboxed: bool, 177 is_refcounted: bool, 178 ctype: str = 'PyObject *', 179 size: int = PLATFORM_SIZE) -> None: 180 RPrimitive.primitive_map[name] = self 181 182 self.name = name 183 self.is_unboxed = is_unboxed 184 self._ctype = ctype 185 self.is_refcounted = is_refcounted 186 self.size = size 187 # TODO: For low-level integers, they actually don't have undefined values 188 # we need to figure out some way to represent here. 189 if ctype == 'CPyTagged': 190 self.c_undefined = 'CPY_INT_TAG' 191 elif ctype in ('int32_t', 'int64_t', 'CPyPtr', 'uint32_t', 'uint64_t'): 192 self.c_undefined = '0' 193 elif ctype == 'PyObject *': 194 # Boxed types use the null pointer as the error value. 195 self.c_undefined = 'NULL' 196 elif ctype == 'char': 197 self.c_undefined = '2' 198 elif ctype == 'PyObject **': 199 self.c_undefined = 'NULL' 200 else: 201 assert False, 'Unrecognized ctype: %r' % ctype 202 203 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 204 return visitor.visit_rprimitive(self) 205 206 def serialize(self) -> str: 207 return self.name 208 209 def __repr__(self) -> str: 210 return '<RPrimitive %s>' % self.name 211 212 def __eq__(self, other: object) -> bool: 213 return isinstance(other, RPrimitive) and other.name == self.name 214 215 def __hash__(self) -> int: 216 return hash(self.name) 217 218 219# NOTE: All the supported instances of RPrimitive are defined 220# below. Use these instead of creating new instances. 221 222# Used to represent arbitrary objects and dynamically typed (Any) 223# values. There are various ops that let you perform generic, runtime 224# checked operations on these (that match Python semantics). See the 225# ops in mypyc.primitives.misc_ops, including py_getattr_op, 226# py_call_op, and many others. 227# 228# If there is no more specific RType available for some value, we fall 229# back to using this type. 230# 231# NOTE: Even though this is very flexible, this type should be used as 232# little as possible, as generic ops are typically slow. Other types, 233# including other primitive types and RInstance, are usually much 234# faster. 235object_rprimitive = RPrimitive('builtins.object', is_unboxed=False, 236 is_refcounted=True) # type: Final 237 238# represents a low level pointer of an object 239object_pointer_rprimitive = RPrimitive('object_ptr', is_unboxed=False, 240 is_refcounted=False, ctype='PyObject **') # type: Final 241 242# Arbitrary-precision integer (corresponds to Python 'int'). Small 243# enough values are stored unboxed, while large integers are 244# represented as a tagged pointer to a Python 'int' PyObject. The 245# lowest bit is used as the tag to decide whether it is a signed 246# unboxed value (shifted left by one) or a PyObject * pointing to an 247# 'int' object. Pointers have the least significant bit set. 248# 249# The undefined/error value is the null pointer (1 -- only the least 250# significant bit is set)). 251# 252# This cannot represent a subclass of int. An instance of a subclass 253# of int is coerced to the corresponding 'int' value. 254int_rprimitive = RPrimitive('builtins.int', is_unboxed=True, is_refcounted=True, 255 ctype='CPyTagged') # type: Final 256 257# An unboxed integer. The representation is the same as for unboxed 258# int_rprimitive (shifted left by one). These can be used when an 259# integer is known to be small enough to fit size_t (CPyTagged). 260short_int_rprimitive = RPrimitive('short_int', is_unboxed=True, is_refcounted=False, 261 ctype='CPyTagged') # type: Final 262 263# Low level integer types (correspond to C integer types) 264 265int32_rprimitive = RPrimitive('int32', is_unboxed=True, is_refcounted=False, 266 ctype='int32_t', size=4) # type: Final 267int64_rprimitive = RPrimitive('int64', is_unboxed=True, is_refcounted=False, 268 ctype='int64_t', size=8) # type: Final 269uint32_rprimitive = RPrimitive('uint32', is_unboxed=True, is_refcounted=False, 270 ctype='uint32_t', size=4) # type: Final 271uint64_rprimitive = RPrimitive('uint64', is_unboxed=True, is_refcounted=False, 272 ctype='uint64_t', size=8) # type: Final 273 274# The C 'int' type 275c_int_rprimitive = int32_rprimitive 276 277if IS_32_BIT_PLATFORM: 278 c_size_t_rprimitive = uint32_rprimitive 279 c_pyssize_t_rprimitive = RPrimitive('native_int', is_unboxed=True, is_refcounted=False, 280 ctype='int32_t', size=4) 281else: 282 c_size_t_rprimitive = uint64_rprimitive 283 c_pyssize_t_rprimitive = RPrimitive('native_int', is_unboxed=True, is_refcounted=False, 284 ctype='int64_t', size=8) 285 286# Low level pointer, represented as integer in C backends 287pointer_rprimitive = RPrimitive('ptr', is_unboxed=True, is_refcounted=False, 288 ctype='CPyPtr') # type: Final 289 290# Floats are represent as 'float' PyObject * values. (In the future 291# we'll likely switch to a more efficient, unboxed representation.) 292float_rprimitive = RPrimitive('builtins.float', is_unboxed=False, 293 is_refcounted=True) # type: Final 294 295# An unboxed Python bool value. This actually has three possible values 296# (0 -> False, 1 -> True, 2 -> error). If you only need True/False, use 297# bit_rprimitive instead. 298bool_rprimitive = RPrimitive('builtins.bool', is_unboxed=True, is_refcounted=False, 299 ctype='char', size=1) # type: Final 300 301# A low-level boolean value with two possible values: 0 and 1. Any 302# other value results in undefined behavior. Undefined or error values 303# are not supported. 304bit_rprimitive = RPrimitive('bit', is_unboxed=True, is_refcounted=False, 305 ctype='char', size=1) # type: Final 306 307# The 'None' value. The possible values are 0 -> None and 2 -> error. 308none_rprimitive = RPrimitive('builtins.None', is_unboxed=True, is_refcounted=False, 309 ctype='char', size=1) # type: Final 310 311# Python list object (or an instance of a subclass of list). 312list_rprimitive = RPrimitive('builtins.list', is_unboxed=False, is_refcounted=True) # type: Final 313 314# Python dict object (or an instance of a subclass of dict). 315dict_rprimitive = RPrimitive('builtins.dict', is_unboxed=False, is_refcounted=True) # type: Final 316 317# Python set object (or an instance of a subclass of set). 318set_rprimitive = RPrimitive('builtins.set', is_unboxed=False, is_refcounted=True) # type: Final 319 320# Python str object. At the C layer, str is referred to as unicode 321# (PyUnicode). 322str_rprimitive = RPrimitive('builtins.str', is_unboxed=False, is_refcounted=True) # type: Final 323 324# Tuple of an arbitrary length (corresponds to Tuple[t, ...], with 325# explicit '...'). 326tuple_rprimitive = RPrimitive('builtins.tuple', is_unboxed=False, 327 is_refcounted=True) # type: Final 328 329 330def is_tagged(rtype: RType) -> bool: 331 return rtype is int_rprimitive or rtype is short_int_rprimitive 332 333 334def is_int_rprimitive(rtype: RType) -> bool: 335 return rtype is int_rprimitive 336 337 338def is_short_int_rprimitive(rtype: RType) -> bool: 339 return rtype is short_int_rprimitive 340 341 342def is_int32_rprimitive(rtype: RType) -> bool: 343 return (rtype is int32_rprimitive or 344 (rtype is c_pyssize_t_rprimitive and rtype._ctype == 'int32_t')) 345 346 347def is_int64_rprimitive(rtype: RType) -> bool: 348 return (rtype is int64_rprimitive or 349 (rtype is c_pyssize_t_rprimitive and rtype._ctype == 'int64_t')) 350 351 352def is_uint32_rprimitive(rtype: RType) -> bool: 353 return rtype is uint32_rprimitive 354 355 356def is_uint64_rprimitive(rtype: RType) -> bool: 357 return rtype is uint64_rprimitive 358 359 360def is_c_py_ssize_t_rprimitive(rtype: RType) -> bool: 361 return rtype is c_pyssize_t_rprimitive 362 363 364def is_pointer_rprimitive(rtype: RType) -> bool: 365 return rtype is pointer_rprimitive 366 367 368def is_float_rprimitive(rtype: RType) -> bool: 369 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.float' 370 371 372def is_bool_rprimitive(rtype: RType) -> bool: 373 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.bool' 374 375 376def is_bit_rprimitive(rtype: RType) -> bool: 377 return isinstance(rtype, RPrimitive) and rtype.name == 'bit' 378 379 380def is_object_rprimitive(rtype: RType) -> bool: 381 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.object' 382 383 384def is_none_rprimitive(rtype: RType) -> bool: 385 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.None' 386 387 388def is_list_rprimitive(rtype: RType) -> bool: 389 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.list' 390 391 392def is_dict_rprimitive(rtype: RType) -> bool: 393 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.dict' 394 395 396def is_set_rprimitive(rtype: RType) -> bool: 397 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.set' 398 399 400def is_str_rprimitive(rtype: RType) -> bool: 401 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.str' 402 403 404def is_tuple_rprimitive(rtype: RType) -> bool: 405 return isinstance(rtype, RPrimitive) and rtype.name == 'builtins.tuple' 406 407 408def is_sequence_rprimitive(rtype: RType) -> bool: 409 return isinstance(rtype, RPrimitive) and ( 410 is_list_rprimitive(rtype) or is_tuple_rprimitive(rtype) or is_str_rprimitive(rtype) 411 ) 412 413 414class TupleNameVisitor(RTypeVisitor[str]): 415 """Produce a tuple name based on the concrete representations of types.""" 416 417 def visit_rinstance(self, t: 'RInstance') -> str: 418 return "O" 419 420 def visit_runion(self, t: 'RUnion') -> str: 421 return "O" 422 423 def visit_rprimitive(self, t: 'RPrimitive') -> str: 424 if t._ctype == 'CPyTagged': 425 return 'I' 426 elif t._ctype == 'char': 427 return 'C' 428 assert not t.is_unboxed, "{} unexpected unboxed type".format(t) 429 return 'O' 430 431 def visit_rtuple(self, t: 'RTuple') -> str: 432 parts = [elem.accept(self) for elem in t.types] 433 return 'T{}{}'.format(len(parts), ''.join(parts)) 434 435 def visit_rstruct(self, t: 'RStruct') -> str: 436 assert False, 'RStruct not supported in tuple' 437 438 def visit_rarray(self, t: 'RArray') -> str: 439 assert False, 'RArray not supported in tuple' 440 441 def visit_rvoid(self, t: 'RVoid') -> str: 442 assert False, "rvoid in tuple?" 443 444 445class RTuple(RType): 446 """Fixed-length unboxed tuple (represented as a C struct). 447 448 These are used to represent mypy TupleType values (fixed-length 449 Python tuples). Since this is unboxed, the identity of a tuple 450 object is not preserved within compiled code. If the identity of a 451 tuple is important, or there is a need to have multiple references 452 to a single tuple object, a variable-length tuple should be used 453 (tuple_rprimitive or Tuple[T, ...] with explicit '...'), as they 454 are boxed. 455 456 These aren't immutable. However, user code won't be able to mutate 457 individual tuple items. 458 """ 459 460 is_unboxed = True 461 462 def __init__(self, types: List[RType]) -> None: 463 self.name = 'tuple' 464 self.types = tuple(types) 465 self.is_refcounted = any(t.is_refcounted for t in self.types) 466 # Generate a unique id which is used in naming corresponding C identifiers. 467 # This is necessary since C does not have anonymous structural type equivalence 468 # in the same way python can just assign a Tuple[int, bool] to a Tuple[int, bool]. 469 self.unique_id = self.accept(TupleNameVisitor()) 470 # Nominally the max c length is 31 chars, but I'm not honestly worried about this. 471 self.struct_name = 'tuple_{}'.format(self.unique_id) 472 self._ctype = '{}'.format(self.struct_name) 473 474 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 475 return visitor.visit_rtuple(self) 476 477 def __str__(self) -> str: 478 return 'tuple[%s]' % ', '.join(str(typ) for typ in self.types) 479 480 def __repr__(self) -> str: 481 return '<RTuple %s>' % ', '.join(repr(typ) for typ in self.types) 482 483 def __eq__(self, other: object) -> bool: 484 return isinstance(other, RTuple) and self.types == other.types 485 486 def __hash__(self) -> int: 487 return hash((self.name, self.types)) 488 489 def serialize(self) -> JsonDict: 490 types = [x.serialize() for x in self.types] 491 return {'.class': 'RTuple', 'types': types} 492 493 @classmethod 494 def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RTuple': 495 types = [deserialize_type(t, ctx) for t in data['types']] 496 return RTuple(types) 497 498 499# Exception tuple: (exception class, exception instance, traceback object) 500exc_rtuple = RTuple([object_rprimitive, object_rprimitive, object_rprimitive]) 501 502# Dictionary iterator tuple: (should continue, internal offset, key, value) 503# See mypyc.irbuild.for_helpers.ForDictionaryCommon for more details. 504dict_next_rtuple_pair = RTuple( 505 [bool_rprimitive, int_rprimitive, object_rprimitive, object_rprimitive] 506) 507# Same as above but just for key or value. 508dict_next_rtuple_single = RTuple( 509 [bool_rprimitive, int_rprimitive, object_rprimitive] 510) 511 512 513def compute_rtype_alignment(typ: RType) -> int: 514 """Compute alignment of a given type based on platform alignment rule""" 515 platform_alignment = PLATFORM_SIZE 516 if isinstance(typ, RPrimitive): 517 return typ.size 518 elif isinstance(typ, RInstance): 519 return platform_alignment 520 elif isinstance(typ, RUnion): 521 return platform_alignment 522 elif isinstance(typ, RArray): 523 return compute_rtype_alignment(typ.item_type) 524 else: 525 if isinstance(typ, RTuple): 526 items = list(typ.types) 527 elif isinstance(typ, RStruct): 528 items = typ.types 529 else: 530 assert False, "invalid rtype for computing alignment" 531 max_alignment = max([compute_rtype_alignment(item) for item in items]) 532 return max_alignment 533 534 535def compute_rtype_size(typ: RType) -> int: 536 """Compute unaligned size of rtype""" 537 if isinstance(typ, RPrimitive): 538 return typ.size 539 elif isinstance(typ, RTuple): 540 return compute_aligned_offsets_and_size(list(typ.types))[1] 541 elif isinstance(typ, RUnion): 542 return PLATFORM_SIZE 543 elif isinstance(typ, RStruct): 544 return compute_aligned_offsets_and_size(typ.types)[1] 545 elif isinstance(typ, RInstance): 546 return PLATFORM_SIZE 547 elif isinstance(typ, RArray): 548 alignment = compute_rtype_alignment(typ) 549 aligned_size = (compute_rtype_size(typ.item_type) + (alignment - 1)) & ~(alignment - 1) 550 return aligned_size * typ.length 551 else: 552 assert False, "invalid rtype for computing size" 553 554 555def compute_aligned_offsets_and_size(types: List[RType]) -> Tuple[List[int], int]: 556 """Compute offsets and total size of a list of types after alignment 557 558 Note that the types argument are types of values that are stored 559 sequentially with platform default alignment. 560 """ 561 unaligned_sizes = [compute_rtype_size(typ) for typ in types] 562 alignments = [compute_rtype_alignment(typ) for typ in types] 563 564 current_offset = 0 565 offsets = [] 566 final_size = 0 567 for i in range(len(unaligned_sizes)): 568 offsets.append(current_offset) 569 if i + 1 < len(unaligned_sizes): 570 cur_size = unaligned_sizes[i] 571 current_offset += cur_size 572 next_alignment = alignments[i + 1] 573 # compute aligned offset, 574 # check https://en.wikipedia.org/wiki/Data_structure_alignment for more information 575 current_offset = (current_offset + (next_alignment - 1)) & -next_alignment 576 else: 577 struct_alignment = max(alignments) 578 final_size = current_offset + unaligned_sizes[i] 579 final_size = (final_size + (struct_alignment - 1)) & -struct_alignment 580 return offsets, final_size 581 582 583class RStruct(RType): 584 """C struct type""" 585 586 def __init__(self, 587 name: str, 588 names: List[str], 589 types: List[RType]) -> None: 590 self.name = name 591 self.names = names 592 self.types = types 593 # generate dummy names 594 if len(self.names) < len(self.types): 595 for i in range(len(self.types) - len(self.names)): 596 self.names.append('_item' + str(i)) 597 self.offsets, self.size = compute_aligned_offsets_and_size(types) 598 self._ctype = name 599 600 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 601 return visitor.visit_rstruct(self) 602 603 def __str__(self) -> str: 604 # if not tuple(unamed structs) 605 return '%s{%s}' % (self.name, ', '.join(name + ":" + str(typ) 606 for name, typ in zip(self.names, self.types))) 607 608 def __repr__(self) -> str: 609 return '<RStruct %s{%s}>' % (self.name, ', '.join(name + ":" + repr(typ) for name, typ 610 in zip(self.names, self.types))) 611 612 def __eq__(self, other: object) -> bool: 613 return (isinstance(other, RStruct) and self.name == other.name 614 and self.names == other.names and self.types == other.types) 615 616 def __hash__(self) -> int: 617 return hash((self.name, tuple(self.names), tuple(self.types))) 618 619 def serialize(self) -> JsonDict: 620 assert False 621 622 @classmethod 623 def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RStruct': 624 assert False 625 626 627class RInstance(RType): 628 """Instance of user-defined class (compiled to C extension class). 629 630 The runtime representation is 'PyObject *', and these are always 631 boxed and thus reference-counted. 632 633 These support fast method calls and fast attribute access using 634 vtables, and they usually use a dict-free, struct-based 635 representation of attributes. Method calls and attribute access 636 can skip the vtable if we know that there is no overriding. 637 638 These are also sometimes called 'native' types, since these have 639 the most efficient representation and ops (along with certain 640 RPrimitive types and RTuple). 641 """ 642 643 is_unboxed = False 644 645 def __init__(self, class_ir: 'ClassIR') -> None: 646 # name is used for formatting the name in messages and debug output 647 # so we want the fullname for precision. 648 self.name = class_ir.fullname 649 self.class_ir = class_ir 650 self._ctype = 'PyObject *' 651 652 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 653 return visitor.visit_rinstance(self) 654 655 def struct_name(self, names: NameGenerator) -> str: 656 return self.class_ir.struct_name(names) 657 658 def getter_index(self, name: str) -> int: 659 return self.class_ir.vtable_entry(name) 660 661 def setter_index(self, name: str) -> int: 662 return self.getter_index(name) + 1 663 664 def method_index(self, name: str) -> int: 665 return self.class_ir.vtable_entry(name) 666 667 def attr_type(self, name: str) -> RType: 668 return self.class_ir.attr_type(name) 669 670 def __repr__(self) -> str: 671 return '<RInstance %s>' % self.name 672 673 def __eq__(self, other: object) -> bool: 674 return isinstance(other, RInstance) and other.name == self.name 675 676 def __hash__(self) -> int: 677 return hash(self.name) 678 679 def serialize(self) -> str: 680 return self.name 681 682 683class RUnion(RType): 684 """union[x, ..., y]""" 685 686 is_unboxed = False 687 688 def __init__(self, items: List[RType]) -> None: 689 self.name = 'union' 690 self.items = items 691 self.items_set = frozenset(items) 692 self._ctype = 'PyObject *' 693 694 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 695 return visitor.visit_runion(self) 696 697 def __repr__(self) -> str: 698 return '<RUnion %s>' % ', '.join(str(item) for item in self.items) 699 700 def __str__(self) -> str: 701 return 'union[%s]' % ', '.join(str(item) for item in self.items) 702 703 # We compare based on the set because order in a union doesn't matter 704 def __eq__(self, other: object) -> bool: 705 return isinstance(other, RUnion) and self.items_set == other.items_set 706 707 def __hash__(self) -> int: 708 return hash(('union', self.items_set)) 709 710 def serialize(self) -> JsonDict: 711 types = [x.serialize() for x in self.items] 712 return {'.class': 'RUnion', 'types': types} 713 714 @classmethod 715 def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RUnion': 716 types = [deserialize_type(t, ctx) for t in data['types']] 717 return RUnion(types) 718 719 720def optional_value_type(rtype: RType) -> Optional[RType]: 721 """If rtype is the union of none_rprimitive and another type X, return X. 722 723 Otherwise return None. 724 """ 725 if isinstance(rtype, RUnion) and len(rtype.items) == 2: 726 if rtype.items[0] == none_rprimitive: 727 return rtype.items[1] 728 elif rtype.items[1] == none_rprimitive: 729 return rtype.items[0] 730 return None 731 732 733def is_optional_type(rtype: RType) -> bool: 734 """Is rtype an optional type with exactly two union items?""" 735 return optional_value_type(rtype) is not None 736 737 738class RArray(RType): 739 """Fixed-length C array type (for example, int[5]). 740 741 Note that the implementation is a bit limited, and these can basically 742 be only used for local variables that are initialized in one location. 743 """ 744 745 def __init__(self, 746 item_type: RType, 747 length: int) -> None: 748 self.item_type = item_type 749 # Number of items 750 self.length = length 751 self.is_refcounted = False 752 753 def accept(self, visitor: 'RTypeVisitor[T]') -> T: 754 return visitor.visit_rarray(self) 755 756 def __str__(self) -> str: 757 return '%s[%s]' % (self.item_type, self.length) 758 759 def __repr__(self) -> str: 760 return '<RArray %r[%s]>' % (self.item_type, self.length) 761 762 def __eq__(self, other: object) -> bool: 763 return (isinstance(other, RArray) and self.item_type == other.item_type 764 and self.length == other.length) 765 766 def __hash__(self) -> int: 767 return hash((self.item_type, self.length)) 768 769 def serialize(self) -> JsonDict: 770 assert False 771 772 @classmethod 773 def deserialize(cls, data: JsonDict, ctx: 'DeserMaps') -> 'RArray': 774 assert False 775 776 777PyObject = RStruct( 778 name='PyObject', 779 names=['ob_refcnt', 'ob_type'], 780 types=[c_pyssize_t_rprimitive, pointer_rprimitive]) 781 782PyVarObject = RStruct( 783 name='PyVarObject', 784 names=['ob_base', 'ob_size'], 785 types=[PyObject, c_pyssize_t_rprimitive]) 786 787setentry = RStruct( 788 name='setentry', 789 names=['key', 'hash'], 790 types=[pointer_rprimitive, c_pyssize_t_rprimitive]) 791 792smalltable = RStruct( 793 name='smalltable', 794 names=[], 795 types=[setentry] * 8) 796 797PySetObject = RStruct( 798 name='PySetObject', 799 names=['ob_base', 'fill', 'used', 'mask', 'table', 'hash', 'finger', 800 'smalltable', 'weakreflist'], 801 types=[PyObject, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, 802 pointer_rprimitive, c_pyssize_t_rprimitive, c_pyssize_t_rprimitive, smalltable, 803 pointer_rprimitive]) 804 805PyListObject = RStruct( 806 name='PyListObject', 807 names=['ob_base', 'ob_item', 'allocated'], 808 types=[PyObject, pointer_rprimitive, c_pyssize_t_rprimitive] 809) 810