def visit_call_expr(self, expr: CallExpr) -> Register: if isinstance(expr.callee, MemberExpr): is_module_call = self.is_module_member_expr(expr.callee) if expr.callee.expr in self.types and not is_module_call: target = self.translate_special_method_call(expr.callee, expr) if target: return target # Either its a module call or translating to a special method call failed, so we have # to fallback to a PyCall function = self.accept(expr.callee) return self.py_call(function, expr.args, self.node_type(expr)) assert isinstance(expr.callee, NameExpr) fn = expr.callee.name # TODO: fullname if fn == 'len' and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]: target = self.alloc_target(IntRType()) arg = self.accept(expr.args[0]) expr_rtype = self.node_type(expr.args[0]) if expr_rtype.name == 'list': self.add(PrimitiveOp(target, PrimitiveOp.LIST_LEN, arg)) elif expr_rtype.name == 'sequence_tuple': self.add( PrimitiveOp(target, PrimitiveOp.HOMOGENOUS_TUPLE_LEN, arg)) elif isinstance(expr_rtype, TupleRType): self.add(LoadInt(target, len(expr_rtype.types))) else: assert False, "unsupported use of len" # Handle conversion to sequence tuple elif fn == 'tuple' and len( expr.args) == 1 and expr.arg_kinds == [ARG_POS]: target = self.alloc_target(SequenceTupleRType()) arg = self.accept(expr.args[0]) self.add( PrimitiveOp(target, PrimitiveOp.LIST_TO_HOMOGENOUS_TUPLE, arg)) else: target_type = self.node_type(expr) if not (self.is_native_name_expr(expr.callee)): function = self.accept(expr.callee) return self.py_call(function, expr.args, target_type) target = self.alloc_target(target_type) args = [self.accept(arg) for arg in expr.args] self.add(Call(target, fn, args)) return target
def test_list_get_item(self) -> None: self.assert_emit( PrimitiveOp(self.n, PrimitiveOp.LIST_GET, self.m, self.k), """cpy_r_n = CPyList_GetItem(cpy_r_m, cpy_r_k); if (!cpy_r_n) abort(); """)
def assign_to_target(self, target: AssignmentTarget, rvalue: Expression, rvalue_type: RType, needs_box: bool) -> Register: rvalue_type = rvalue_type or self.node_type(rvalue) if isinstance(target, AssignmentTargetRegister): if needs_box: unboxed = self.accept(rvalue) return self.box(unboxed, rvalue_type, target=target.register) else: return self.accept(rvalue, target=target.register) elif isinstance(target, AssignmentTargetAttr): rvalue_reg = self.accept(rvalue) if needs_box: rvalue_reg = self.box(rvalue_reg, rvalue_type) self.add( SetAttr(target.obj_reg, target.attr, rvalue_reg, target.obj_type)) return INVALID_REGISTER elif isinstance(target, AssignmentTargetIndex): item_reg = self.accept(rvalue) boxed_item_reg = self.box(item_reg, rvalue_type) if isinstance(target.rtype, ListRType): op = PrimitiveOp.LIST_SET elif isinstance(target.rtype, DictRType): op = PrimitiveOp.DICT_SET else: assert False, target.rtype self.add( PrimitiveOp(None, op, target.base_reg, target.index_reg, boxed_item_reg)) return INVALID_REGISTER assert False, 'Unsupported assignment target'
def test_list_len(self) -> None: self.assert_emit( PrimitiveOp(self.n, PrimitiveOp.LIST_LEN, self.l), """long long __tmp1; __tmp1 = PyList_GET_SIZE(cpy_r_l); cpy_r_n = CPyTagged_ShortFromLongLong(__tmp1); """)
def test_new_dict(self) -> None: self.assert_emit( PrimitiveOp(self.d, PrimitiveOp.NEW_DICT), """cpy_r_d = PyDict_New(); if (!cpy_r_d) abort(); """)
def visit_index_expr(self, expr: IndexExpr) -> Register: base_rtype = self.node_type(expr.base) base_reg = self.accept(expr.base) target_type = self.node_type(expr) if isinstance(base_rtype, (ListRType, SequenceTupleRType, DictRType)): index_type = self.node_type(expr.index) if not isinstance(base_rtype, DictRType): assert isinstance( index_type, IntRType), 'Unsupported indexing operation' # TODO if isinstance(base_rtype, ListRType): op = PrimitiveOp.LIST_GET elif isinstance(base_rtype, DictRType): op = PrimitiveOp.DICT_GET else: op = PrimitiveOp.HOMOGENOUS_TUPLE_GET index_reg = self.accept(expr.index) if isinstance(base_rtype, DictRType): index_reg = self.box(index_reg, index_type) tmp = self.alloc_temp(ObjectRType()) self.add(PrimitiveOp(tmp, op, base_reg, index_reg)) target = self.alloc_target(target_type) return self.unbox_or_cast(tmp, target_type, target) elif isinstance(base_rtype, TupleRType): assert isinstance(expr.index, IntExpr) # TODO target = self.alloc_target(target_type) self.add( TupleGet(target, base_reg, expr.index.value, base_rtype.types[expr.index.value])) return target assert False, 'Unsupported indexing operation'
def binary_op(self, ltype: RType, lreg: Register, rtype: RType, rreg: Register, expr_op: str, target: Optional[Register] = None) -> Register: if ltype.name == 'int' and rtype.name == 'int': # Primitive int operation if target is None: target = self.alloc_target(IntRType()) op = self.int_binary_ops[expr_op] elif (ltype.name == 'list' or rtype.name == 'list') and expr_op == '*': if rtype.name == 'list': ltype, rtype = rtype, ltype lreg, rreg = rreg, lreg if rtype.name != 'int': assert False, 'Unsupported binary operation' # TODO: Operator overloading if target is None: target = self.alloc_target(ListRType()) op = PrimitiveOp.LIST_REPEAT elif isinstance(rtype, DictRType): if expr_op == 'in': if target is None: target = self.alloc_target(BoolRType()) lreg = self.box(lreg, ltype) op = PrimitiveOp.DICT_CONTAINS else: assert False, 'Unsupported binary operation' else: assert False, 'Unsupported binary operation' self.add(PrimitiveOp(target, op, lreg, rreg)) return target
def test_dict_get_item(self) -> None: self.assert_emit( PrimitiveOp(self.o, PrimitiveOp.DICT_GET, self.d, self.o2), """cpy_r_o = PyDict_GetItem(cpy_r_d, cpy_r_o2); if (!cpy_r_o) abort(); Py_INCREF(cpy_r_o); """)
def test_dict_get_item(self) -> None: self.assert_emit(PrimitiveOp([self.d, self.o2], dict_get_item_op, 1), """cpy_r_r0 = PyDict_GetItemWithError(cpy_r_d, cpy_r_o2); if (!cpy_r_r0) PyErr_SetObject(PyExc_KeyError, cpy_r_o2); else Py_INCREF(cpy_r_r0); """)
def visit_return_stmt(self, stmt: ReturnStmt) -> Register: if stmt.expr: retval = self.accept(stmt.expr) else: retval = self.environment.add_temp(NoneRType()) self.add(PrimitiveOp(retval, PrimitiveOp.NONE)) self.add(Return(retval)) return INVALID_REGISTER
def visit_tuple_expr(self, expr: TupleExpr) -> Register: tuple_type = self.types[expr] assert isinstance(tuple_type, TupleType) target = self.alloc_target(self.type_to_rtype(tuple_type)) items = [self.accept(i) for i in expr.items] self.add(PrimitiveOp(target, PrimitiveOp.NEW_TUPLE, *items)) return target
def test_dict_contains(self) -> None: self.assert_emit( PrimitiveOp(self.b, PrimitiveOp.DICT_CONTAINS, self.o, self.d), """int __tmp1 = PyDict_Contains(cpy_r_d, cpy_r_o); if (__tmp1 < 0) abort(); cpy_r_b = __tmp1; """)
def test_new_list(self) -> None: self.assert_emit( PrimitiveOp([self.n, self.m], new_list_op, 55), """cpy_r_r0 = PyList_New(2); if (likely(cpy_r_r0 != NULL)) { PyList_SET_ITEM(cpy_r_r0, 0, cpy_r_n); PyList_SET_ITEM(cpy_r_r0, 1, cpy_r_m); } """)
def primitive_op(self, desc: OpDescription, args: List[Value], line: int) -> Value: assert desc.result_type is not None coerced = [] for i, arg in enumerate(args): formal_type = self.op_arg_type(desc, i) arg = self.coerce(arg, formal_type, line) coerced.append(arg) target = self.add(PrimitiveOp(coerced, desc, line)) return target
def test_new_list(self) -> None: self.assert_emit( PrimitiveOp(self.l, PrimitiveOp.NEW_LIST, self.n, self.m), """cpy_r_l = PyList_New(2); Py_INCREF(cpy_r_n); PyList_SET_ITEM(cpy_r_l, 0, cpy_r_n); Py_INCREF(cpy_r_m); PyList_SET_ITEM(cpy_r_l, 1, cpy_r_m); """)
def assert_emit_binary_op(self, op: str, dest: Value, left: Value, right: Value, expected: str) -> None: ops = binary_ops[op] for desc in ops: if (is_subtype(left.type, desc.arg_types[0]) and is_subtype(right.type, desc.arg_types[1])): self.assert_emit(PrimitiveOp([left, right], desc, 55), expected) break else: assert False, 'Could not find matching op'
def test_list_repeat(self) -> None: self.assert_emit( PrimitiveOp(self.ll, PrimitiveOp.LIST_REPEAT, self.l, self.n), """long long __tmp1; __tmp1 = CPyTagged_AsLongLong(cpy_r_n); if (__tmp1 == -1 && PyErr_Occurred()) abort(); cpy_r_ll = PySequence_Repeat(cpy_r_l, __tmp1); if (!cpy_r_ll) abort(); """)
def visit_list_expr(self, expr: ListExpr) -> Register: list_type = self.types[expr] assert isinstance(list_type, Instance) item_type = self.type_to_rtype(list_type.args[0]) target = self.alloc_target(ListRType()) items = [] for item in expr.items: item_reg = self.accept(item) boxed = self.box(item_reg, item_type) items.append(boxed) self.add(PrimitiveOp(target, PrimitiveOp.NEW_LIST, *items)) return target
def translate_special_method_call(self, callee: MemberExpr, expr: CallExpr) -> Register: base_type = self.node_type(callee.expr) result_type = self.node_type(expr) base = self.accept(callee.expr) if callee.name == 'append' and base_type.name == 'list': target = INVALID_REGISTER # TODO: Do we sometimes need to allocate a register? arg = self.box_expr(expr.args[0]) self.add(PrimitiveOp(target, PrimitiveOp.LIST_APPEND, base, arg)) else: assert False, 'Unsupported method call: %s.%s' % (base_type.name, callee.name) return target
def visit_name_expr(self, expr: NameExpr) -> Register: if expr.node.fullname() == 'builtins.None': target = self.alloc_target(NoneRType()) self.add(PrimitiveOp(target, PrimitiveOp.NONE)) return target elif expr.node.fullname() == 'builtins.True': target = self.alloc_target(BoolRType()) self.add(PrimitiveOp(target, PrimitiveOp.TRUE)) return target elif expr.node.fullname() == 'builtins.False': target = self.alloc_target(BoolRType()) self.add(PrimitiveOp(target, PrimitiveOp.FALSE)) return target if not self.is_native_name_expr(expr): return self.load_static_module_attr(expr) # TODO: We assume that this is a Var node, which is very limited assert isinstance(expr.node, Var) reg = self.environment.lookup(expr.node) return self.get_using_binder(reg, expr.node, expr)
def visit_unary_expr(self, expr: UnaryExpr) -> Register: if expr.op != '-': assert False, 'Unsupported unary operation' etype = self.node_type(expr.expr) reg = self.accept(expr.expr) if etype.name != 'int': assert False, 'Unsupported unary operation' target = self.alloc_target(IntRType()) zero = self.accept(IntExpr(0)) self.add(PrimitiveOp(target, PrimitiveOp.INT_SUB, zero, reg)) return target
def visit_name_expr(self, expr: NameExpr) -> Value: assert expr.node, "RefExpr not resolved" fullname = expr.node.fullname if fullname in name_ref_ops: # Use special access op for this particular name. desc = name_ref_ops[fullname] assert desc.result_type is not None return self.builder.add(PrimitiveOp([], desc, expr.line)) if isinstance(expr.node, Var) and expr.node.is_final: value = self.builder.emit_load_final( expr.node, fullname, expr.name, self.builder.is_native_ref_expr(expr), self.builder.types[expr], expr.line, ) if value is not None: return value if isinstance(expr.node, MypyFile) and expr.node.fullname in self.builder.imports: return self.builder.load_module(expr.node.fullname) # If the expression is locally defined, then read the result from the corresponding # assignment target and return it. Otherwise if the expression is a global, load it from # the globals dictionary. # Except for imports, that currently always happens in the global namespace. if expr.kind == LDEF and not (isinstance(expr.node, Var) and expr.node.is_suppressed_import): # Try to detect and error when we hit the irritating mypy bug # where a local variable is cast to None. (#5423) if (isinstance(expr.node, Var) and is_none_rprimitive(self.builder.node_type(expr)) and expr.node.is_inferred): self.builder.error( "Local variable '{}' has inferred type None; add an annotation" .format(expr.node.name), expr.node.line) # TODO: Behavior currently only defined for Var and FuncDef node types. return self.builder.read(self.builder.get_assignment_target(expr), expr.line) return self.builder.load_global(expr)
def transform_del_item(builder: IRBuilder, target: AssignmentTarget, line: int) -> None: if isinstance(target, AssignmentTargetIndex): builder.gen_method_call( target.base, '__delitem__', [target.index], result_type=None, line=line ) elif isinstance(target, AssignmentTargetAttr): key = builder.load_static_unicode(target.attr) builder.add(PrimitiveOp([target.obj, key], py_delattr_op, line)) elif isinstance(target, AssignmentTargetRegister): # Delete a local by assigning an error value to it, which will # prompt the insertion of uninit checks. builder.add(Assign(target.register, builder.add(LoadErrorValue(target.type, undefines=True)))) elif isinstance(target, AssignmentTargetTuple): for subtarget in target.items: transform_del_item(builder, subtarget, line)
def test_load_None(self) -> None: self.assert_emit(PrimitiveOp([], none_object_op, 0), "cpy_r_r0 = Py_None;")
def test_new_dict(self) -> None: self.assert_emit(PrimitiveOp([], new_dict_op, 1), """cpy_r_r0 = PyDict_New();""")
def test_dict_update(self) -> None: self.assert_emit( PrimitiveOp([self.d, self.o], dict_update_op, 1), """cpy_r_r0 = CPyDict_Update(cpy_r_d, cpy_r_o) >= 0;""")
def test_dict_set_item(self) -> None: self.assert_emit( PrimitiveOp([self.d, self.o, self.o2], dict_set_item_op, 1), """cpy_r_r0 = CPyDict_SetItem(cpy_r_d, cpy_r_o, cpy_r_o2) >= 0;""")
def test_list_append(self) -> None: self.assert_emit( PrimitiveOp([self.l, self.o], list_append_op, 1), """cpy_r_r0 = PyList_Append(cpy_r_l, cpy_r_o) >= 0;""")
def test_list_get_item(self) -> None: self.assert_emit(PrimitiveOp([self.m, self.k], list_get_item_op, 55), """cpy_r_r0 = CPyList_GetItem(cpy_r_m, cpy_r_k);""")
def test_list_set_item(self) -> None: self.assert_emit( PrimitiveOp([self.l, self.n, self.o], list_set_item_op, 55), """cpy_r_r0 = CPyList_SetItem(cpy_r_l, cpy_r_n, cpy_r_o);""")