mapper.py 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. """Maintain a mapping from mypy concepts to IR/compiled concepts."""
  2. from __future__ import annotations
  3. from mypy.nodes import ARG_STAR, ARG_STAR2, GDEF, ArgKind, FuncDef, RefExpr, SymbolNode, TypeInfo
  4. from mypy.types import (
  5. AnyType,
  6. CallableType,
  7. Instance,
  8. LiteralType,
  9. NoneTyp,
  10. Overloaded,
  11. PartialType,
  12. TupleType,
  13. Type,
  14. TypedDictType,
  15. TypeType,
  16. TypeVarType,
  17. UnboundType,
  18. UninhabitedType,
  19. UnionType,
  20. get_proper_type,
  21. )
  22. from mypyc.ir.class_ir import ClassIR
  23. from mypyc.ir.func_ir import FuncDecl, FuncSignature, RuntimeArg
  24. from mypyc.ir.rtypes import (
  25. RInstance,
  26. RTuple,
  27. RType,
  28. RUnion,
  29. bool_rprimitive,
  30. bytes_rprimitive,
  31. dict_rprimitive,
  32. float_rprimitive,
  33. int16_rprimitive,
  34. int32_rprimitive,
  35. int64_rprimitive,
  36. int_rprimitive,
  37. list_rprimitive,
  38. none_rprimitive,
  39. object_rprimitive,
  40. range_rprimitive,
  41. set_rprimitive,
  42. str_rprimitive,
  43. tuple_rprimitive,
  44. uint8_rprimitive,
  45. )
  46. class Mapper:
  47. """Keep track of mappings from mypy concepts to IR concepts.
  48. For example, we keep track of how the mypy TypeInfos of compiled
  49. classes map to class IR objects.
  50. This state is shared across all modules being compiled in all
  51. compilation groups.
  52. """
  53. def __init__(self, group_map: dict[str, str | None]) -> None:
  54. self.group_map = group_map
  55. self.type_to_ir: dict[TypeInfo, ClassIR] = {}
  56. self.func_to_decl: dict[SymbolNode, FuncDecl] = {}
  57. def type_to_rtype(self, typ: Type | None) -> RType:
  58. if typ is None:
  59. return object_rprimitive
  60. typ = get_proper_type(typ)
  61. if isinstance(typ, Instance):
  62. if typ.type.fullname == "builtins.int":
  63. return int_rprimitive
  64. elif typ.type.fullname == "builtins.float":
  65. return float_rprimitive
  66. elif typ.type.fullname == "builtins.bool":
  67. return bool_rprimitive
  68. elif typ.type.fullname == "builtins.str":
  69. return str_rprimitive
  70. elif typ.type.fullname == "builtins.bytes":
  71. return bytes_rprimitive
  72. elif typ.type.fullname == "builtins.list":
  73. return list_rprimitive
  74. # Dict subclasses are at least somewhat common and we
  75. # specifically support them, so make sure that dict operations
  76. # get optimized on them.
  77. elif any(cls.fullname == "builtins.dict" for cls in typ.type.mro):
  78. return dict_rprimitive
  79. elif typ.type.fullname == "builtins.set":
  80. return set_rprimitive
  81. elif typ.type.fullname == "builtins.tuple":
  82. return tuple_rprimitive # Varying-length tuple
  83. elif typ.type.fullname == "builtins.range":
  84. return range_rprimitive
  85. elif typ.type in self.type_to_ir:
  86. inst = RInstance(self.type_to_ir[typ.type])
  87. # Treat protocols as Union[protocol, object], so that we can do fast
  88. # method calls in the cases where the protocol is explicitly inherited from
  89. # and fall back to generic operations when it isn't.
  90. if typ.type.is_protocol:
  91. return RUnion([inst, object_rprimitive])
  92. else:
  93. return inst
  94. elif typ.type.fullname == "mypy_extensions.i64":
  95. return int64_rprimitive
  96. elif typ.type.fullname == "mypy_extensions.i32":
  97. return int32_rprimitive
  98. elif typ.type.fullname == "mypy_extensions.i16":
  99. return int16_rprimitive
  100. elif typ.type.fullname == "mypy_extensions.u8":
  101. return uint8_rprimitive
  102. else:
  103. return object_rprimitive
  104. elif isinstance(typ, TupleType):
  105. # Use our unboxed tuples for raw tuples but fall back to
  106. # being boxed for NamedTuple.
  107. if typ.partial_fallback.type.fullname == "builtins.tuple":
  108. return RTuple([self.type_to_rtype(t) for t in typ.items])
  109. else:
  110. return tuple_rprimitive
  111. elif isinstance(typ, CallableType):
  112. return object_rprimitive
  113. elif isinstance(typ, NoneTyp):
  114. return none_rprimitive
  115. elif isinstance(typ, UnionType):
  116. return RUnion.make_simplified_union([self.type_to_rtype(item) for item in typ.items])
  117. elif isinstance(typ, AnyType):
  118. return object_rprimitive
  119. elif isinstance(typ, TypeType):
  120. return object_rprimitive
  121. elif isinstance(typ, TypeVarType):
  122. # Erase type variable to upper bound.
  123. # TODO: Erase to union if object has value restriction?
  124. return self.type_to_rtype(typ.upper_bound)
  125. elif isinstance(typ, PartialType):
  126. assert typ.var.type is not None
  127. return self.type_to_rtype(typ.var.type)
  128. elif isinstance(typ, Overloaded):
  129. return object_rprimitive
  130. elif isinstance(typ, TypedDictType):
  131. return dict_rprimitive
  132. elif isinstance(typ, LiteralType):
  133. return self.type_to_rtype(typ.fallback)
  134. elif isinstance(typ, (UninhabitedType, UnboundType)):
  135. # Sure, whatever!
  136. return object_rprimitive
  137. # I think we've covered everything that is supposed to
  138. # actually show up, so anything else is a bug somewhere.
  139. assert False, "unexpected type %s" % type(typ)
  140. def get_arg_rtype(self, typ: Type, kind: ArgKind) -> RType:
  141. if kind == ARG_STAR:
  142. return tuple_rprimitive
  143. elif kind == ARG_STAR2:
  144. return dict_rprimitive
  145. else:
  146. return self.type_to_rtype(typ)
  147. def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
  148. if isinstance(fdef.type, CallableType):
  149. arg_types = [
  150. self.get_arg_rtype(typ, kind)
  151. for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds)
  152. ]
  153. arg_pos_onlys = [name is None for name in fdef.type.arg_names]
  154. ret = self.type_to_rtype(fdef.type.ret_type)
  155. else:
  156. # Handle unannotated functions
  157. arg_types = [object_rprimitive for _ in fdef.arguments]
  158. arg_pos_onlys = [arg.pos_only for arg in fdef.arguments]
  159. # We at least know the return type for __init__ methods will be None.
  160. is_init_method = fdef.name == "__init__" and bool(fdef.info)
  161. if is_init_method:
  162. ret = none_rprimitive
  163. else:
  164. ret = object_rprimitive
  165. # mypyc FuncSignatures (unlike mypy types) want to have a name
  166. # present even when the argument is position only, since it is
  167. # the sole way that FuncDecl arguments are tracked. This is
  168. # generally fine except in some cases (like for computing
  169. # init_sig) we need to produce FuncSignatures from a
  170. # deserialized FuncDef that lacks arguments. We won't ever
  171. # need to use those inside of a FuncIR, so we just make up
  172. # some crap.
  173. if hasattr(fdef, "arguments"):
  174. arg_names = [arg.variable.name for arg in fdef.arguments]
  175. else:
  176. arg_names = [name or "" for name in fdef.arg_names]
  177. args = [
  178. RuntimeArg(arg_name, arg_type, arg_kind, arg_pos_only)
  179. for arg_name, arg_kind, arg_type, arg_pos_only in zip(
  180. arg_names, fdef.arg_kinds, arg_types, arg_pos_onlys
  181. )
  182. ]
  183. # We force certain dunder methods to return objects to support letting them
  184. # return NotImplemented. It also avoids some pointless boxing and unboxing,
  185. # since tp_richcompare needs an object anyways.
  186. if fdef.name in ("__eq__", "__ne__", "__lt__", "__gt__", "__le__", "__ge__"):
  187. ret = object_rprimitive
  188. return FuncSignature(args, ret)
  189. def is_native_module(self, module: str) -> bool:
  190. """Is the given module one compiled by mypyc?"""
  191. return module in self.group_map
  192. def is_native_ref_expr(self, expr: RefExpr) -> bool:
  193. if expr.node is None:
  194. return False
  195. if "." in expr.node.fullname:
  196. return self.is_native_module(expr.node.fullname.rpartition(".")[0])
  197. return True
  198. def is_native_module_ref_expr(self, expr: RefExpr) -> bool:
  199. return self.is_native_ref_expr(expr) and expr.kind == GDEF