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