"""'Runtime subtype' check for RTypes. A type S is a runtime subtype of T if a value of type S can be used at runtime when a value of type T is expected without requiring any runtime conversions. For boxed types, runtime subtyping is the same as regular subtyping. Unboxed subtypes, on the other hand, are not runtime subtypes of object (since they require boxing to be used as an object), but short ints are runtime subtypes of int. Subtyping is used to determine whether an object can be in a particular place and runtime subtyping is used to determine whether a coercion is necessary first. """ from __future__ import annotations from mypyc.ir.rtypes import ( RArray, RInstance, RPrimitive, RStruct, RTuple, RType, RTypeVisitor, RUnion, RVec, RVoid, is_bit_rprimitive, is_bool_rprimitive, is_int_rprimitive, is_short_int_rprimitive, ) from mypyc.subtype import is_subtype def is_runtime_subtype(left: RType, right: RType) -> bool: if isinstance(right, RUnion) and not isinstance(left, RUnion): return any(not item.is_unboxed and is_runtime_subtype(left, item) for item in right.items) return left.accept(RTSubtypeVisitor(right)) class RTSubtypeVisitor(RTypeVisitor[bool]): """Is left a runtime subtype of right? A few special cases such as right being 'object' are handled in is_runtime_subtype and don't need to be covered here. """ def __init__(self, right: RType) -> None: self.right = right def visit_rinstance(self, left: RInstance) -> bool: return is_subtype(left, self.right) def visit_rvec(self, left: RVec) -> bool: # TODO: Better implementation return left == self.right def visit_runion(self, left: RUnion) -> bool: return not self.right.is_unboxed and is_subtype(left, self.right) def visit_rprimitive(self, left: RPrimitive) -> bool: if is_short_int_rprimitive(left) and is_int_rprimitive(self.right): return True if is_bit_rprimitive(left) and is_bool_rprimitive(self.right): return True return left is self.right def visit_rtuple(self, left: RTuple) -> bool: if isinstance(self.right, RTuple): return len(self.right.types) == len(left.types) and all( is_runtime_subtype(t1, t2) for t1, t2 in zip(left.types, self.right.types) ) return False def visit_rstruct(self, left: RStruct) -> bool: return isinstance(self.right, RStruct) and self.right.name == left.name def visit_rarray(self, left: RArray) -> bool: return left == self.right def visit_rvoid(self, left: RVoid) -> bool: return isinstance(self.right, RVoid)