Example #1
0
 def test_call_two_args(self) -> None:
     decl = FuncDecl('myfn', None, 'mod',
                     FuncSignature([RuntimeArg('m', int_rprimitive),
                                    RuntimeArg('n', int_rprimitive)],
                                   int_rprimitive))
     self.assert_emit(Call(decl, [self.m, self.k], 55),
                      "cpy_r_r0 = CPyDef_myfn(cpy_r_m, cpy_r_k);")
Example #2
0
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)
Example #3
0
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)
Example #4
0
File: mapper.py Project: vemel/mypy
    def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
        if isinstance(fdef.type, CallableType):
            arg_types = [
                self.get_arg_rtype(typ, kind)
                for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds)
            ]
            ret = self.type_to_rtype(fdef.type.ret_type)
        else:
            # Handle unannotated functions
            arg_types = [object_rprimitive for arg in fdef.arguments]
            # We at least know the return type for __init__ methods will be None.
            is_init_method = fdef.name == '__init__' and bool(fdef.info)
            if is_init_method:
                ret = none_rprimitive
            else:
                ret = object_rprimitive

        args = [
            RuntimeArg(arg_name, arg_type,
                       arg_kind) for arg_name, arg_kind, arg_type in zip(
                           fdef.arg_names, fdef.arg_kinds, arg_types)
        ]

        # We force certain dunder methods to return objects to support letting them
        # return NotImplemented. It also avoids some pointless boxing and unboxing,
        # since tp_richcompare needs an object anyways.
        if fdef.name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__',
                         '__ge__'):
            ret = object_rprimitive
        return FuncSignature(args, ret)
Example #5
0
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)
Example #6
0
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)
Example #7
0
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)
Example #8
0
    def add_argument(self, var: Union[str, Var], typ: RType, kind: int = ARG_POS) -> Register:
        """Declare an argument in the current function.

        You should use this instead of directly calling add_local() in new code.
        """
        if isinstance(var, str):
            var = Var(var)
        reg = self.add_local(var, typ, is_arg=True)
        self.runtime_args[-1].append(RuntimeArg(var.name, typ, kind))
        return reg
Example #9
0
def add_helper_to_generator_class(builder: IRBuilder,
                                  arg_regs: List[Register],
                                  blocks: List[BasicBlock],
                                  sig: FuncSignature,
                                  fn_info: FuncInfo) -> FuncDecl:
    """Generates a helper method for a generator class, called by '__next__' and 'throw'."""
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),
                         RuntimeArg('type', object_rprimitive),
                         RuntimeArg('value', object_rprimitive),
                         RuntimeArg('traceback', object_rprimitive),
                         RuntimeArg('arg', object_rprimitive)
                         ), sig.ret_type)
    helper_fn_decl = FuncDecl('__mypyc_generator_helper__', fn_info.generator_class.ir.name,
                              builder.module_name, sig)
    helper_fn_ir = FuncIR(helper_fn_decl, arg_regs, blocks,
                          fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.generator_class.ir.methods['__mypyc_generator_helper__'] = helper_fn_ir
    builder.functions.append(helper_fn_ir)
    return helper_fn_decl
Example #10
0
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)
Example #11
0
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)
Example #12
0
    def fdef_to_sig(self, fdef: FuncDef) -> FuncSignature:
        if isinstance(fdef.type, CallableType):
            arg_types = [
                self.get_arg_rtype(typ, kind)
                for typ, kind in zip(fdef.type.arg_types, fdef.type.arg_kinds)
            ]
            arg_pos_onlys = [name is None for name in fdef.type.arg_names]
            ret = self.type_to_rtype(fdef.type.ret_type)
        else:
            # Handle unannotated functions
            arg_types = [object_rprimitive for arg in fdef.arguments]
            arg_pos_onlys = [arg.pos_only for arg in fdef.arguments]
            # We at least know the return type for __init__ methods will be None.
            is_init_method = fdef.name == '__init__' and bool(fdef.info)
            if is_init_method:
                ret = none_rprimitive
            else:
                ret = object_rprimitive

        # mypyc FuncSignatures (unlike mypy types) want to have a name
        # present even when the argument is position only, since it is
        # the sole way that FuncDecl arguments are tracked. This is
        # generally fine except in some cases (like for computing
        # init_sig) we need to produce FuncSignatures from a
        # deserialized FuncDef that lacks arguments. We won't ever
        # need to use those inside of a FuncIR, so we just make up
        # some crap.
        if hasattr(fdef, 'arguments'):
            arg_names = [arg.variable.name for arg in fdef.arguments]
        else:
            arg_names = [name or '' for name in fdef.arg_names]

        args = [
            RuntimeArg(arg_name, arg_type, arg_kind, arg_pos_only)
            for arg_name, arg_kind, arg_type, arg_pos_only in zip(
                arg_names, fdef.arg_kinds, arg_types, arg_pos_onlys)
        ]

        # We force certain dunder methods to return objects to support letting them
        # return NotImplemented. It also avoids some pointless boxing and unboxing,
        # since tp_richcompare needs an object anyways.
        if fdef.name in ('__eq__', '__ne__', '__lt__', '__gt__', '__le__',
                         '__ge__'):
            ret = object_rprimitive
        return FuncSignature(args, ret)
Example #13
0
def add_call_to_callable_class(builder: IRBuilder,
                               blocks: List[BasicBlock],
                               sig: FuncSignature,
                               env: Environment,
                               fn_info: FuncInfo) -> FuncIR:
    """Generates a '__call__' method for a callable class representing a nested function.

    This takes the blocks, signature, and environment associated with a function definition and
    uses those to build the '__call__' method of a given callable class, used to represent that
    function. Note that a 'self' parameter is added to its list of arguments, as the nested
    function becomes a class method.
    """
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type)
    call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig)
    call_fn_ir = FuncIR(call_fn_decl, blocks, env,
                        fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.callable_class.ir.methods['__call__'] = call_fn_ir
    return call_fn_ir
Example #14
0
def add_call_to_callable_class(builder: IRBuilder,
                               args: List[Register],
                               blocks: List[BasicBlock],
                               sig: FuncSignature,
                               fn_info: FuncInfo) -> FuncIR:
    """Generate a '__call__' method for a callable class representing a nested function.

    This takes the blocks and signature associated with a function
    definition and uses those to build the '__call__' method of a
    given callable class, used to represent that function.
    """
    # Since we create a method, we also add a 'self' parameter.
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive),) + sig.args, sig.ret_type)
    call_fn_decl = FuncDecl('__call__', fn_info.callable_class.ir.name, builder.module_name, sig)
    call_fn_ir = FuncIR(call_fn_decl, args, blocks,
                        fn_info.fitem.line, traceback_name=fn_info.fitem.name)
    fn_info.callable_class.ir.methods['__call__'] = call_fn_ir
    fn_info.callable_class.ir.method_decls['__call__'] = call_fn_decl
    return call_fn_ir
Example #15
0
def transform_lambda_expr(builder: IRBuilder, expr: LambdaExpr) -> Value:
    typ = get_proper_type(builder.types[expr])
    assert isinstance(typ, CallableType)

    runtime_args = []
    for arg, arg_type in zip(expr.arguments, typ.arg_types):
        arg.variable.type = arg_type
        runtime_args.append(
            RuntimeArg(arg.variable.name, builder.type_to_rtype(arg_type), arg.kind))
    ret_type = builder.type_to_rtype(typ.ret_type)

    fsig = FuncSignature(runtime_args, ret_type)

    fname = '{}{}'.format(LAMBDA_NAME, builder.lambda_counter)
    builder.lambda_counter += 1
    func_ir, func_reg = gen_func_item(builder, expr, fname, fsig)
    assert func_reg is not None

    builder.functions.append(func_ir)
    return func_reg
Example #16
0
def add_close_to_generator_class(builder: IRBuilder,
                                 fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    # TODO: Currently this method just triggers a runtime error,
    # we should fill this out eventually.
    builder.enter(fn_info)
    add_self_to_env(builder.environment, fn_info.generator_class.ir)
    builder.add(
        RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                           'close method on generator classes uimplemented',
                           fn_info.fitem.line))
    builder.add(Unreachable())
    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)
    close_fn_decl = FuncDecl('close', fn_info.generator_class.ir.name,
                             builder.module_name, sig)
    close_fn_ir = FuncIR(close_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['close'] = close_fn_ir
    builder.functions.append(close_fn_ir)
Example #17
0
def prepare_class_def(path: str, module_name: str, cdef: ClassDef,
                      errors: Errors, mapper: Mapper) -> None:

    ir = mapper.type_to_ir[cdef.info]
    info = cdef.info

    attrs = get_mypyc_attrs(cdef)
    if attrs.get("allow_interpreted_subclasses") is True:
        ir.allow_interpreted_subclasses = True
    if attrs.get("serializable") is True:
        # Supports copy.copy and pickle (including subclasses)
        ir._serializable = True

    # We sort the table for determinism here on Python 3.5
    for name, node in sorted(info.names.items()):
        # Currently all plugin generated methods are dummies and not included.
        if node.plugin_generated:
            continue

        if isinstance(node.node, Var):
            assert node.node.type, "Class member %s missing type" % name
            if not node.node.is_classvar and name not in ('__slots__', '__deletable__'):
                ir.attributes[name] = mapper.type_to_rtype(node.node.type)
        elif isinstance(node.node, (FuncDef, Decorator)):
            prepare_method_def(ir, module_name, cdef, mapper, node.node)
        elif isinstance(node.node, OverloadedFuncDef):
            # Handle case for property with both a getter and a setter
            if node.node.is_property:
                if is_valid_multipart_property_def(node.node):
                    for item in node.node.items:
                        prepare_method_def(ir, module_name, cdef, mapper, item)
                else:
                    errors.error("Unsupported property decorator semantics", path, cdef.line)

            # Handle case for regular function overload
            else:
                assert node.node.impl
                prepare_method_def(ir, module_name, cdef, mapper, node.node.impl)

    # Check for subclassing from builtin types
    for cls in info.mro:
        # Special case exceptions and dicts
        # XXX: How do we handle *other* things??
        if cls.fullname == 'builtins.BaseException':
            ir.builtin_base = 'PyBaseExceptionObject'
        elif cls.fullname == 'builtins.dict':
            ir.builtin_base = 'PyDictObject'
        elif cls.fullname.startswith('builtins.'):
            if not can_subclass_builtin(cls.fullname):
                # Note that if we try to subclass a C extension class that
                # isn't in builtins, bad things will happen and we won't
                # catch it here! But this should catch a lot of the most
                # common pitfalls.
                errors.error("Inheriting from most builtin types is unimplemented",
                             path, cdef.line)

    if ir.builtin_base:
        ir.attributes.clear()

    # Set up a constructor decl
    init_node = cdef.info['__init__'].node
    if not ir.is_trait and not ir.builtin_base and isinstance(init_node, FuncDef):
        init_sig = mapper.fdef_to_sig(init_node)

        defining_ir = mapper.type_to_ir.get(init_node.info)
        # If there is a nontrivial __init__ that wasn't defined in an
        # extension class, we need to make the constructor take *args,
        # **kwargs so it can call tp_init.
        if ((defining_ir is None or not defining_ir.is_ext_class
             or cdef.info['__init__'].plugin_generated)
                and init_node.info.fullname != 'builtins.object'):
            init_sig = FuncSignature(
                [init_sig.args[0],
                 RuntimeArg("args", tuple_rprimitive, ARG_STAR),
                 RuntimeArg("kwargs", dict_rprimitive, ARG_STAR2)],
                init_sig.ret_type)

        ctor_sig = FuncSignature(init_sig.args[1:], RInstance(ir))
        ir.ctor = FuncDecl(cdef.name, None, module_name, ctor_sig)
        mapper.func_to_decl[cdef.info] = ir.ctor

    # Set up the parent class
    bases = [mapper.type_to_ir[base.type] for base in info.bases
             if base.type in mapper.type_to_ir]
    if not all(c.is_trait for c in bases[1:]):
        errors.error("Non-trait bases must appear first in parent list", path, cdef.line)
    ir.traits = [c for c in bases if c.is_trait]

    mro = []
    base_mro = []
    for cls in info.mro:
        if cls not in mapper.type_to_ir:
            if cls.fullname != 'builtins.object':
                ir.inherits_python = True
            continue
        base_ir = mapper.type_to_ir[cls]
        if not base_ir.is_trait:
            base_mro.append(base_ir)
        mro.append(base_ir)

        if cls.defn.removed_base_type_exprs or not base_ir.is_ext_class:
            ir.inherits_python = True

    base_idx = 1 if not ir.is_trait else 0
    if len(base_mro) > base_idx:
        ir.base = base_mro[base_idx]
    ir.mro = mro
    ir.base_mro = base_mro

    for base in bases:
        if base.children is not None:
            base.children.append(ir)

    if is_dataclass(cdef):
        ir.is_augmented = True
Example #18
0
 def setUp(self) -> None:
     self.arg = RuntimeArg('arg', int_rprimitive)
     self.reg = Register(int_rprimitive, 'arg')
     self.block = BasicBlock(0)
Example #19
0
 def setUp(self) -> None:
     self.var = Var('arg')
     self.arg = RuntimeArg('arg', int_rprimitive)
     self.env = Environment()
     self.reg = self.env.add_local(self.var, int_rprimitive)
     self.block = BasicBlock(0)
Example #20
0
def generate_set_del_item_wrapper(cl: ClassIR, fn: FuncIR, emitter: Emitter) -> str:
    """Generates a wrapper for native __setitem__ method (also works for __delitem__).

    This is used with the mapping protocol slot. Arguments are taken as *PyObjects and we
    return a negative C int on error.

    Create a separate wrapper function for __delitem__ as needed and have the
    __setitem__ wrapper call it if the value is NULL. Return the name
    of the outer (__setitem__) wrapper.
    """
    method_cls = cl.get_method_and_class('__delitem__')
    del_name = None
    if method_cls and method_cls[1] == cl:
        # Generate a separate wrapper for __delitem__
        del_name = generate_del_item_wrapper(cl, method_cls[0], emitter)

    args = fn.args
    if fn.name == '__delitem__':
        # Add an extra argument for value that we expect to be NULL.
        args = list(args) + [RuntimeArg('___value', object_rprimitive, ARG_POS)]

    name = '{}{}{}'.format(DUNDER_PREFIX, '__setitem__', cl.name_prefix(emitter.names))
    input_args = ', '.join('PyObject *obj_{}'.format(arg.name) for arg in args)
    emitter.emit_line('static int {name}({input_args}) {{'.format(
        name=name,
        input_args=input_args,
    ))

    # First check if this is __delitem__
    emitter.emit_line('if (obj_{} == NULL) {{'.format(args[2].name))
    if del_name is not None:
        # We have a native implementation, so call it
        emitter.emit_line('return {}(obj_{}, obj_{});'.format(del_name,
                                                              args[0].name,
                                                              args[1].name))
    else:
        # Try to call superclass method instead
        emitter.emit_line(
            'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name))
        emitter.emit_line('if (super == NULL) return -1;')
        emitter.emit_line(
            'PyObject *result = PyObject_CallMethod(super, "__delitem__", "O", obj_{});'.format(
                args[1].name))
        emitter.emit_line('Py_DECREF(super);')
        emitter.emit_line('Py_XDECREF(result);')
        emitter.emit_line('return result == NULL ? -1 : 0;')
    emitter.emit_line('}')

    method_cls = cl.get_method_and_class('__setitem__')
    if method_cls and method_cls[1] == cl:
        generate_set_del_item_wrapper_inner(fn, emitter, args)
    else:
        emitter.emit_line(
            'PyObject *super = CPy_Super(CPyModule_builtins, obj_{});'.format(args[0].name))
        emitter.emit_line('if (super == NULL) return -1;')
        emitter.emit_line('PyObject *result;')

        if method_cls is None and cl.builtin_base is None:
            msg = "'{}' object does not support item assignment".format(cl.name)
            emitter.emit_line(
                'PyErr_SetString(PyExc_TypeError, "{}");'.format(msg))
            emitter.emit_line('result = NULL;')
        else:
            # A base class may have __setitem__
            emitter.emit_line(
                'result = PyObject_CallMethod(super, "__setitem__", "OO", obj_{}, obj_{});'.format(
                    args[1].name, args[2].name))
        emitter.emit_line('Py_DECREF(super);')
        emitter.emit_line('Py_XDECREF(result);')
        emitter.emit_line('return result == NULL ? -1 : 0;')
        emitter.emit_line('}')
    return name
Example #21
0
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)):
                if stmt.lvalues[0].name == '__slots__':
                    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()
    builder.ret_types[-1] = bool_rprimitive

    rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)), )
    self_var = builder.read(add_self_to_env(builder.environment, cls), -1)

    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()))

    blocks, env, ret_type, _ = builder.leave()
    ir = FuncIR(
        FuncDecl('__mypyc_defaults_setup', cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type)), blocks, env)
    builder.functions.append(ir)
    cls.methods[ir.name] = ir
Example #22
0
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))

    arg_info = get_args(builder, rt_args, line)
    args, arg_kinds, arg_names = arg_info.args, arg_info.arg_kinds, arg_info.arg_names

    # We can do a passthrough *args/**kwargs with a native call, but if the
    # args need to get distributed out to arguments, we just let python handle it
    if (any(kind.is_star() for kind in arg_kinds)
            and any(not arg.kind.is_star() for arg in target.decl.sig.args)):
        do_pycall = True

    if do_pycall:
        if target.decl.kind == FUNC_STATICMETHOD:
            # FIXME: this won't work if we can do interpreted subclasses
            first = builder.builder.get_native_type(cls)
            st = 0
        else:
            first = args[0]
            st = 1
        retval = builder.builder.py_method_call(first, target.name, args[st:],
                                                line, arg_kinds[st:],
                                                arg_names[st:])
    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)