def setup_func_for_recursive_call(builder: IRBuilder, fdef: FuncDef, base: ImplicitClass) -> None: """Enable calling a nested function (with a callable class) recursively. Adds the instance of the callable class representing the given FuncDef to a register in the environment so that the function can be called recursively. Note that this needs to be done only for nested functions. """ # First, set the attribute of the environment class so that GetAttr can be called on it. prev_env = builder.fn_infos[-2].env_class prev_env.attributes[fdef.name] = builder.type_to_rtype(fdef.type) if isinstance(base, GeneratorClass): # If we are dealing with a generator class, then we need to first get the register # holding the current environment class, and load the previous environment class from # there. prev_env_reg = builder.add( GetAttr(base.curr_env_reg, ENV_ATTR_NAME, -1)) else: prev_env_reg = base.prev_env_reg # Obtain the instance of the callable class representing the FuncDef, and add it to the # current environment. val = builder.add(GetAttr(prev_env_reg, fdef.name, -1)) target = builder.add_local_reg(fdef, object_rprimitive) builder.assign(target, val, -1)
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 add_args_to_env(builder: IRBuilder, local: bool = True, base: Optional[Union[FuncInfo, ImplicitClass]] = None, reassign: bool = True) -> None: fn_info = builder.fn_info if local: for arg in fn_info.fitem.arguments: rtype = builder.type_to_rtype(arg.variable.type) builder.add_local_reg(arg.variable, rtype, is_arg=True) else: for arg in fn_info.fitem.arguments: if is_free_variable(builder, arg.variable) or fn_info.is_generator: rtype = builder.type_to_rtype(arg.variable.type) assert base is not None, 'base cannot be None for adding nonlocal args' builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign)
def get_args(builder: IRBuilder, rt_args: Sequence[RuntimeArg], line: int) -> ArgInfo: # 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.is_named() or (arg.kind.is_optional() and not arg.pos_only) else None for arg in rt_args] arg_kinds = [arg.kind for arg in rt_args] return ArgInfo(args, arg_names, arg_kinds)
def get_func_target(builder: IRBuilder, fdef: FuncDef) -> AssignmentTarget: """Given a FuncDef, return the target for the instance of its callable class. If the function was not already defined somewhere, then define it and add it to the current environment. """ if fdef.original_def: # Get the target associated with the previously defined FuncDef. return builder.lookup(fdef.original_def) if builder.fn_info.is_generator or builder.fn_info.contains_nested: return builder.lookup(fdef) return builder.add_local_reg(fdef, object_rprimitive)