def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generate the '__get__' method for a callable class.""" line = fn_info.fitem.line with builder.enter_method(fn_info.callable_class.ir, '__get__', object_rprimitive, fn_info, self_type=object_rprimitive): instance = builder.add_argument('instance', object_rprimitive) builder.add_argument('owner', object_rprimitive) # If accessed through the class, just return the callable # object. If accessed through an object, create a new bound # instance method object. instance_block, class_block = BasicBlock(), BasicBlock() comparison = builder.translate_is_op(builder.read(instance), builder.none_object(), 'is', line) builder.add_bool_branch(comparison, class_block, instance_block) builder.activate_block(class_block) builder.add(Return(builder.self())) builder.activate_block(instance_block) builder.add( Return( builder.call_c( method_new_op, [builder.self(), builder.read(instance)], line)))
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None: """Generate a "__ne__" method from a "__eq__" method. """ builder.enter_method(cls, '__ne__', object_rprimitive) rhs_arg = builder.add_argument('rhs', object_rprimitive) # If __eq__ returns NotImplemented, then __ne__ should also not_implemented_block, regular_block = BasicBlock(), BasicBlock() eqval = builder.add(MethodCall(builder.self(), '__eq__', [rhs_arg], line)) not_implemented = builder.add(LoadAddress(not_implemented_op.type, not_implemented_op.src, line)) builder.add(Branch( builder.translate_is_op(eqval, not_implemented, 'is', line), not_implemented_block, regular_block, Branch.BOOL)) builder.activate_block(regular_block) retval = builder.coerce( builder.unary_op(eqval, 'not', line), object_rprimitive, line ) builder.add(Return(retval)) builder.activate_block(not_implemented_block) builder.add(Return(not_implemented)) builder.leave_method()
def gen_native_func_call_and_return(fdef: FuncDef) -> None: func_decl = builder.mapper.func_to_decl[fdef] ret_val = builder.builder.call(func_decl, arg_info.args, arg_info.arg_kinds, arg_info.arg_names, line) coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced))
def add_await_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generates the '__await__' method for a generator class.""" builder.enter_method(fn_info.generator_class.ir, '__await__', object_rprimitive, fn_info) builder.add(Return(builder.self())) builder.leave_method()
def generate_attr_defaults_init( builder: IRBuilder, cdef: ClassDef, default_assignments: List[AssignmentStmt]) -> None: """Generate an initialization method for default attr values (from class vars).""" if not default_assignments: return cls = builder.mapper.type_to_ir[cdef.info] if cls.builtin_base: return with builder.enter_method(cls, '__mypyc_defaults_setup', bool_rprimitive): self_var = builder.self() for stmt in default_assignments: lvalue = stmt.lvalues[0] assert isinstance(lvalue, NameExpr) if not stmt.is_final_def and not is_constant(stmt.rvalue): builder.warning('Unsupported default attribute value', stmt.rvalue.line) attr_type = cls.attr_type(lvalue.name) val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line) init = SetAttr(self_var, lvalue.name, val, -1) init.mark_as_initializer() builder.add(init) builder.add(Return(builder.true()))
def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'throw' method for a generator class.""" builder.enter_method(fn_info.generator_class.ir, 'throw', object_rprimitive, fn_info) typ = builder.add_argument('type', object_rprimitive) val = builder.add_argument('value', object_rprimitive, ARG_OPT) tb = builder.add_argument('traceback', object_rprimitive, ARG_OPT) # Because the value and traceback arguments are optional and hence # can be NULL if not passed in, we have to assign them Py_None if # they are not passed in. none_reg = builder.none_object() builder.assign_if_null(val, lambda: none_reg, builder.fn_info.fitem.line) builder.assign_if_null(tb, lambda: none_reg, builder.fn_info.fitem.line) # Call the helper function using the arguments passed in, and return that result. result = builder.add( Call(fn_decl, [ builder.self(), builder.read(typ), builder.read(val), builder.read(tb), none_reg ], fn_info.fitem.line)) builder.add(Return(result)) builder.leave_method()
def add_handler_block(ir: FuncIR) -> BasicBlock: block = BasicBlock() ir.blocks.append(block) op = LoadErrorValue(ir.ret_type) block.ops.append(op) block.ops.append(Return(op)) return block
def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'send' method for a generator class.""" # FIXME: this is basically the same as add_next... builder.enter(fn_info) self_reg = builder.read( add_self_to_env(builder.environment, fn_info.generator_class.ir)) arg = builder.environment.add_local_reg(Var('arg'), object_rprimitive, True) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [self_reg, none_reg, none_reg, none_reg, builder.read(arg)], fn_info.fitem.line)) builder.add(Return(result)) blocks, env, _, fn_info = builder.leave() sig = FuncSignature(( RuntimeArg(SELF_NAME, object_rprimitive), RuntimeArg('arg', object_rprimitive), ), sig.ret_type) next_fn_decl = FuncDecl('send', fn_info.generator_class.ir.name, builder.module_name, sig) next_fn_ir = FuncIR(next_fn_decl, blocks, env) fn_info.generator_class.ir.methods['send'] = next_fn_ir builder.functions.append(next_fn_ir)
def gen_glue_property(builder: IRBuilder, sig: FuncSignature, target: FuncIR, cls: ClassIR, base: ClassIR, line: int, do_pygetattr: bool) -> FuncIR: """Generate glue methods for properties that mediate between different subclass types. Similarly to methods, properties of derived types can be covariantly subtyped. Thus, properties also require glue. However, this only requires the return type to change. Further, instead of a method call, an attribute get is performed. If do_pygetattr is True, then get the attribute using the Python C API instead of a native call. """ builder.enter() rt_arg = RuntimeArg(SELF_NAME, RInstance(cls)) arg = builder.read(add_self_to_env(builder.environment, cls), line) builder.ret_types[-1] = sig.ret_type if do_pygetattr: retval = builder.py_get_attr(arg, target.name, line) else: retval = builder.add(GetAttr(arg, target.name, line)) retbox = builder.coerce(retval, sig.ret_type, line) builder.add(Return(retbox)) blocks, env, return_type, _ = builder.leave() return FuncIR( FuncDecl(target.name + '__' + base.name + '_glue', cls.name, builder.module_name, FuncSignature([rt_arg], return_type)), blocks, env)
def gen_glue_method(builder: IRBuilder, sig: FuncSignature, target: FuncIR, cls: ClassIR, base: ClassIR, line: int, do_pycall: bool, ) -> FuncIR: """Generate glue methods that mediate between different method types in subclasses. For example, if we have: class A: def f(builder: IRBuilder, x: int) -> object: ... then it is totally permissible to have a subclass class B(A): def f(builder: IRBuilder, x: object) -> int: ... since '(object) -> int' is a subtype of '(int) -> object' by the usual contra/co-variant function subtyping rules. The trickiness here is that int and object have different runtime representations in mypyc, so A.f and B.f have different signatures at the native C level. To deal with this, we need to generate glue methods that mediate between the different versions by coercing the arguments and return values. If do_pycall is True, then make the call using the C API instead of a native call. """ builder.enter() builder.ret_types[-1] = sig.ret_type rt_args = list(sig.args) if target.decl.kind == FUNC_NORMAL: rt_args[0] = RuntimeArg(sig.args[0].name, RInstance(cls)) # The environment operates on Vars, so we make some up fake_vars = [(Var(arg.name), arg.type) for arg in rt_args] args = [builder.read(builder.add_local_reg(var, type, is_arg=True), line) for var, type in fake_vars] arg_names = [arg.name if arg.kind in (ARG_NAMED, ARG_NAMED_OPT) else None for arg in rt_args] arg_kinds = [concrete_arg_kind(arg.kind) for arg in rt_args] if do_pycall: retval = builder.builder.py_method_call( args[0], target.name, args[1:], line, arg_kinds[1:], arg_names[1:]) else: retval = builder.builder.call(target.decl, args, arg_kinds, arg_names, line) retval = builder.coerce(retval, sig.ret_type, line) builder.add(Return(retval)) arg_regs, _, blocks, ret_type, _ = builder.leave() return FuncIR( FuncDecl(target.name + '__' + base.name + '_glue', cls.name, builder.module_name, FuncSignature(rt_args, ret_type), target.decl.kind), arg_regs, blocks)
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None: """Generate an initialization method for default attr values (from class vars).""" cls = builder.mapper.type_to_ir[cdef.info] if cls.builtin_base: return # Pull out all assignments in classes in the mro so we can initialize them # TODO: Support nested statements default_assignments = [] for info in reversed(cdef.info.mro): if info not in builder.mapper.type_to_ir: continue for stmt in info.defn.defs.body: if (isinstance(stmt, AssignmentStmt) and isinstance(stmt.lvalues[0], NameExpr) and not is_class_var(stmt.lvalues[0]) and not isinstance(stmt.rvalue, TempNode)): name = stmt.lvalues[0].name if name == '__slots__': continue if name == '__deletable__': check_deletable_declaration(builder, cls, stmt.line) continue # Skip type annotated assignments in dataclasses if is_dataclass(cdef) and stmt.type: continue default_assignments.append(stmt) if not default_assignments: return builder.enter_method(cls, '__mypyc_defaults_setup', bool_rprimitive) self_var = builder.self() for stmt in default_assignments: lvalue = stmt.lvalues[0] assert isinstance(lvalue, NameExpr) if not stmt.is_final_def and not is_constant(stmt.rvalue): builder.warning('Unsupported default attribute value', stmt.rvalue.line) # If the attribute is initialized to None and type isn't optional, # don't initialize it to anything. attr_type = cls.attr_type(lvalue.name) if isinstance(stmt.rvalue, RefExpr) and stmt.rvalue.fullname == 'builtins.None': if (not is_optional_type(attr_type) and not is_object_rprimitive(attr_type) and not is_none_rprimitive(attr_type)): continue val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line) builder.add(SetAttr(self_var, lvalue.name, val, -1)) builder.add(Return(builder.true())) builder.leave_method()
def test_valid_fn(self) -> None: assert_no_errors( FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], blocks=[self.basic_block(ops=[ Return(value=NONE_VALUE), ])], ))
def test_valid_goto(self) -> None: block_1 = self.basic_block([Return(value=NONE_VALUE)]) block_2 = self.basic_block([Goto(label=block_1)]) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], blocks=[block_1, block_2], ) assert_no_errors(fn)
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR: """Generate a "__ne__" method from a "__eq__" method. """ builder.enter() rt_args = (RuntimeArg("self", RInstance(cls)), RuntimeArg("rhs", object_rprimitive)) # The environment operates on Vars, so we make some up fake_vars = [(Var(arg.name), arg.type) for arg in rt_args] args = [ builder.read( builder.environment.add_local_reg( var, type, is_arg=True ), line ) for var, type in fake_vars ] # type: List[Value] builder.ret_types[-1] = object_rprimitive # If __eq__ returns NotImplemented, then __ne__ should also not_implemented_block, regular_block = BasicBlock(), BasicBlock() eqval = builder.add(MethodCall(args[0], '__eq__', [args[1]], line)) not_implemented = builder.add(LoadAddress(not_implemented_op.type, not_implemented_op.src, line)) builder.add(Branch( builder.translate_is_op(eqval, not_implemented, 'is', line), not_implemented_block, regular_block, Branch.BOOL_EXPR)) builder.activate_block(regular_block) retval = builder.coerce( builder.unary_op(eqval, 'not', line), object_rprimitive, line ) builder.add(Return(retval)) builder.activate_block(not_implemented_block) builder.add(Return(not_implemented)) blocks, env, ret_type, _ = builder.leave() return FuncIR( FuncDecl('__ne__', cls.name, builder.module_name, FuncSignature(rt_args, ret_type)), blocks, env)
def gen_calls_to_correct_impl( builder: IRBuilder, impl_to_use: Value, arg_info: ArgInfo, fitem: FuncDef, line: int, ) -> None: current_func_decl = builder.mapper.func_to_decl[fitem] def gen_native_func_call_and_return(fdef: FuncDef) -> None: func_decl = builder.mapper.func_to_decl[fdef] ret_val = builder.builder.call(func_decl, arg_info.args, arg_info.arg_kinds, arg_info.arg_names, line) coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced)) typ, src = builtin_names['builtins.int'] int_type_obj = builder.add(LoadAddress(typ, src, line)) is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line) native_call, non_native_call = BasicBlock(), BasicBlock() builder.add_bool_branch(is_int, native_call, non_native_call) builder.activate_block(native_call) passed_id = builder.add(Unbox(impl_to_use, int_rprimitive, line)) native_ids = get_native_impl_ids(builder, fitem) for impl, i in native_ids.items(): call_impl, next_impl = BasicBlock(), BasicBlock() current_id = builder.load_int(i) builder.builder.compare_tagged_condition( passed_id, current_id, '==', call_impl, next_impl, line, ) # Call the registered implementation builder.activate_block(call_impl) gen_native_func_call_and_return(impl) builder.activate_block(next_impl) # We've already handled all the possible integer IDs, so we should never get here builder.add(Unreachable()) builder.activate_block(non_native_call) ret_val = builder.py_call(impl_to_use, arg_info.args, line, arg_info.arg_kinds, arg_info.arg_names) coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line) builder.add(Return(coerced))
def add_register_method_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: line = -1 with builder.enter_method(fn_info.callable_class.ir, 'register', object_rprimitive): cls_arg = builder.add_argument('cls', object_rprimitive) func_arg = builder.add_argument('func', object_rprimitive, ArgKind.ARG_OPT) ret_val = builder.call_c(register_function, [builder.self(), cls_arg, func_arg], line) builder.add(Return(ret_val, line))
def add_next_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the '__next__' method for a generator class.""" builder.enter_method(fn_info.generator_class.ir, '__next__', object_rprimitive, fn_info) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [builder.self(), none_reg, none_reg, none_reg, none_reg], fn_info.fitem.line)) builder.add(Return(result)) builder.leave_method()
def test_duplicate_op(self) -> None: arg_reg = Register(type=int32_rprimitive, name="r1") assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive)) block = self.basic_block([assign, assign, Return(value=NONE_VALUE)]) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], blocks=[block], ) assert_has_error( fn, FnError(source=assign, desc="Func has a duplicate op"))
def test_invalid_return_type(self) -> None: ret = Return(value=Integer(value=5, rtype=int32_rprimitive)) fn = FuncIR( decl=self.func_decl(name="func_1", ret_type=int64_rprimitive), arg_regs=[], blocks=[self.basic_block([ret])], ) assert_has_error( fn, FnError(source=ret, desc="Cannot coerce source type int32 to dest type int64"), )
def add_get_to_callable_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generate the '__get__' method for a callable class.""" line = fn_info.fitem.line builder.enter(fn_info) vself = builder.read( builder.environment.add_local_reg(Var(SELF_NAME), object_rprimitive, True)) instance = builder.environment.add_local_reg(Var('instance'), object_rprimitive, True) builder.environment.add_local_reg(Var('owner'), object_rprimitive, True) # If accessed through the class, just return the callable # object. If accessed through an object, create a new bound # instance method object. instance_block, class_block = BasicBlock(), BasicBlock() comparison = builder.translate_is_op(builder.read(instance), builder.none_object(), 'is', line) builder.add_bool_branch(comparison, class_block, instance_block) builder.activate_block(class_block) builder.add(Return(vself)) builder.activate_block(instance_block) builder.add( Return( builder.call_c(method_new_op, [vself, builder.read(instance)], line))) blocks, env, _, fn_info = builder.leave() sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), RuntimeArg('instance', object_rprimitive), RuntimeArg('owner', object_rprimitive)), object_rprimitive) get_fn_decl = FuncDecl('__get__', fn_info.callable_class.ir.name, builder.module_name, sig) get_fn_ir = FuncIR(get_fn_decl, blocks, env) fn_info.callable_class.ir.methods['__get__'] = get_fn_ir builder.functions.append(get_fn_ir)
def test_invalid_goto(self) -> None: block_1 = self.basic_block([Return(value=NONE_VALUE)]) goto = Goto(label=block_1) block_2 = self.basic_block([goto]) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], # block_1 omitted blocks=[block_2], ) assert_has_error( fn, FnError(source=goto, desc="Invalid control operation target: 1"))
def add_send_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'send' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, 'send', object_rprimitive, fn_info): arg = builder.add_argument('arg', object_rprimitive) none_reg = builder.none_object() # Call the helper function with error flags set to Py_None, and return that result. result = builder.add( Call(fn_decl, [ builder.self(), none_reg, none_reg, none_reg, builder.read(arg) ], fn_info.fitem.line)) builder.add(Return(result))
def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None: """Create an __init__ that sets registry and dispatch_cache to empty dicts""" line = -1 class_ir = builder.fn_info.callable_class.ir with builder.enter_method(class_ir, '__init__', bool_rprimitive): empty_dict = builder.call_c(dict_new_op, [], line) builder.add(SetAttr(builder.self(), 'registry', empty_dict, line)) cache_dict = builder.call_c(dict_new_op, [], line) dispatch_cache_str = builder.load_str('dispatch_cache') # use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__ builder.call_c(py_setattr_op, [builder.self(), dispatch_cache_str, cache_dict], line) # the generated C code seems to expect that __init__ returns a char, so just return 1 builder.add(Return(Integer(1, bool_rprimitive, line), line))
def test_invalid_register_source(self) -> None: ret = Return(value=Register( type=none_rprimitive, name="r1", )) block = self.basic_block([ret]) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], blocks=[block], ) assert_has_error( fn, FnError(source=ret, desc="Invalid op reference to register r1"))
def add_close_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generates the '__close__' method for a generator class.""" with builder.enter_method(fn_info.generator_class.ir, 'close', object_rprimitive, fn_info): except_block, else_block = BasicBlock(), BasicBlock() builder.builder.push_error_handler(except_block) builder.goto_and_activate(BasicBlock()) generator_exit = builder.load_module_attr_by_fullname( 'builtins.GeneratorExit', fn_info.fitem.line) builder.add( MethodCall( builder.self(), 'throw', [generator_exit, builder.none_object(), builder.none_object()])) builder.goto(else_block) builder.builder.pop_error_handler() builder.activate_block(except_block) old_exc = builder.call_c(error_catch_op, [], fn_info.fitem.line) builder.nonlocal_control.append( ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc)) stop_iteration = builder.load_module_attr_by_fullname( 'builtins.StopIteration', fn_info.fitem.line) exceptions = builder.add( TupleSet([generator_exit, stop_iteration], fn_info.fitem.line)) matches = builder.call_c(exc_matches_op, [exceptions], fn_info.fitem.line) match_block, non_match_block = BasicBlock(), BasicBlock() builder.add(Branch(matches, match_block, non_match_block, Branch.BOOL)) builder.activate_block(match_block) builder.call_c(restore_exc_info_op, [builder.read(old_exc)], fn_info.fitem.line) builder.add(Return(builder.none_object())) builder.activate_block(non_match_block) builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO) builder.add(Unreachable()) builder.nonlocal_control.pop() builder.activate_block(else_block) builder.add( RaiseStandardError(RaiseStandardError.RUNTIME_ERROR, 'generator ignored GeneratorExit', fn_info.fitem.line)) builder.add(Unreachable())
def test_simple(self) -> None: self.block.ops.append(Return(self.reg)) fn = FuncIR(FuncDecl('myfunc', None, 'mod', FuncSignature([self.arg], int_rprimitive)), [self.block], self.env) emitter = Emitter(EmitterContext(NameGenerator([['mod']]))) generate_native_function(fn, emitter, 'prog.py', 'prog', optimize_int=False) result = emitter.fragments assert_string_arrays_equal( [ 'CPyTagged CPyDef_myfunc(CPyTagged cpy_r_arg) {\n', 'CPyL0: ;\n', ' return cpy_r_arg;\n', '}\n', ], result, msg='Generated code invalid')
def test_invalid_assign(self) -> None: arg_reg = Register(type=int64_rprimitive, name="r1") assign = Assign(dest=arg_reg, src=Integer(value=5, rtype=int32_rprimitive)) ret = Return(value=NONE_VALUE) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[arg_reg], blocks=[self.basic_block([assign, ret])], ) assert_has_error( fn, FnError(source=assign, desc="Cannot coerce source type int32 to dest type int64"), )
def add_throw_to_generator_class(builder: IRBuilder, fn_info: FuncInfo, fn_decl: FuncDecl, sig: FuncSignature) -> None: """Generates the 'throw' method for a generator class.""" builder.enter(fn_info) self_reg = builder.read( add_self_to_env(builder.environment, fn_info.generator_class.ir)) # Add the type, value, and traceback variables to the environment. typ = builder.environment.add_local_reg(Var('type'), object_rprimitive, True) val = builder.environment.add_local_reg(Var('value'), object_rprimitive, True) tb = builder.environment.add_local_reg(Var('traceback'), object_rprimitive, True) # Because the value and traceback arguments are optional and hence # can be NULL if not passed in, we have to assign them Py_None if # they are not passed in. none_reg = builder.none_object() builder.assign_if_null(val, lambda: none_reg, builder.fn_info.fitem.line) builder.assign_if_null(tb, lambda: none_reg, builder.fn_info.fitem.line) # Call the helper function using the arguments passed in, and return that result. result = builder.add( Call(fn_decl, [ self_reg, builder.read(typ), builder.read(val), builder.read(tb), none_reg ], fn_info.fitem.line)) builder.add(Return(result)) blocks, env, _, fn_info = builder.leave() # Create the FuncSignature for the throw function. Note that the # value and traceback fields are optional, and are assigned to if # they are not passed in inside the body of the throw function. sig = FuncSignature((RuntimeArg( SELF_NAME, object_rprimitive), RuntimeArg('type', object_rprimitive), RuntimeArg('value', object_rprimitive, ARG_OPT), RuntimeArg('traceback', object_rprimitive, ARG_OPT)), sig.ret_type) throw_fn_decl = FuncDecl('throw', fn_info.generator_class.ir.name, builder.module_name, sig) throw_fn_ir = FuncIR(throw_fn_decl, blocks, env) fn_info.generator_class.ir.methods['throw'] = throw_fn_ir builder.functions.append(throw_fn_ir)
def add_iter_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None: """Generates the '__iter__' method for a generator class.""" builder.enter(fn_info) self_target = add_self_to_env(builder.environment, fn_info.generator_class.ir) builder.add(Return(builder.read(self_target, fn_info.fitem.line))) blocks, env, _, fn_info = builder.leave() # Next, add the actual function as a method of the generator class. sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), ), object_rprimitive) iter_fn_decl = FuncDecl('__iter__', fn_info.generator_class.ir.name, builder.module_name, sig) iter_fn_ir = FuncIR(iter_fn_decl, blocks, env) fn_info.generator_class.ir.methods['__iter__'] = iter_fn_ir builder.functions.append(iter_fn_ir)
def test_invalid_op_source(self) -> None: ret = Return(value=LoadLiteral( value="foo", rtype=str_rprimitive, )) block = self.basic_block([ret]) fn = FuncIR( decl=self.func_decl(name="func_1"), arg_regs=[], blocks=[block], ) assert_has_error( fn, FnError(source=ret, desc="Invalid op reference to op of type LoadLiteral"), )