Exemplo n.º 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);")
Exemplo n.º 2
0
 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')
     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')
Exemplo n.º 3
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)]
            ret = self.type_to_rtype(fdef.type.ret_type)
        else:
            # Handle unannotated functions
            arg_types = [object_rprimitive for arg in fdef.arguments]
            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)
Exemplo n.º 4
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.primitive_op(not_implemented_op, [], line)
    builder.add(Branch(
        builder.binary_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)
Exemplo n.º 5
0
 def test_register(self) -> None:
     self.env.temp_index = 0
     op = LoadInt(5)
     self.block.ops.append(op)
     self.env.add_op(op)
     fn = FuncIR(
         FuncDecl('myfunc', None, 'mod',
                  FuncSignature([self.arg], list_rprimitive)), [self.block],
         self.env)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])))
     generate_native_function(fn, emitter, 'prog.py', 'prog')
     result = emitter.fragments
     assert_string_arrays_equal([
         'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
         '    CPyTagged cpy_r_r0;\n',
         'CPyL0: ;\n',
         '    cpy_r_r0 = 10;\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
Exemplo n.º 6
0
def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value:
    # OK AND NOW THE FUN PART
    base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs
    if base_exprs:
        bases = [builder.accept(x) for x in base_exprs]
        tp_bases = builder.primitive_op(new_tuple_op, bases, cdef.line)
    else:
        tp_bases = builder.add(LoadErrorValue(object_rprimitive, is_borrowed=True))
    modname = builder.load_static_unicode(builder.module_name)
    template = builder.add(LoadStatic(object_rprimitive, cdef.name + "_template",
                                   builder.module_name, NAMESPACE_TYPE))
    # Create the class
    tp = builder.primitive_op(pytype_from_template_op,
                           [template, tp_bases, modname], cdef.line)
    # Immediately fix up the trait vtables, before doing anything with the class.
    ir = builder.mapper.type_to_ir[cdef.info]
    if not ir.is_trait and not ir.builtin_base:
        builder.add(Call(
            FuncDecl(cdef.name + '_trait_vtable_setup',
                     None, builder.module_name,
                     FuncSignature([], bool_rprimitive)), [], -1))
    # Populate a '__mypyc_attrs__' field containing the list of attrs
    builder.primitive_op(py_setattr_op, [
        tp, builder.load_static_unicode('__mypyc_attrs__'),
        create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info], cdef.line)],
        cdef.line)

    # Save the class
    builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE))

    # Add it to the dict
    builder.primitive_op(dict_set_item_op,
                      [
                          builder.load_globals_dict(),
                          builder.load_static_unicode(cdef.name),
                          tp,
                      ], cdef.line)

    return tp
Exemplo n.º 7
0
    def __init__(self,
                 name: str,
                 module_name: str,
                 is_trait: bool = False,
                 is_generated: bool = False,
                 is_abstract: bool = False,
                 is_ext_class: bool = True) -> None:
        self.name = name
        self.module_name = module_name
        self.is_trait = is_trait
        self.is_generated = is_generated
        self.is_abstract = is_abstract
        self.is_ext_class = is_ext_class
        # An augmented class has additional methods separate from what mypyc generates.
        # Right now the only one is dataclasses.
        self.is_augmented = False
        self.inherits_python = False
        self.has_dict = False
        # Do we allow interpreted subclasses? Derived from a mypyc_attr.
        self.allow_interpreted_subclasses = False
        # If this a subclass of some built-in python class, the name
        # of the object for that class. We currently only support this
        # in a few ad-hoc cases.
        self.builtin_base = None  # type: Optional[str]
        # Default empty ctor
        self.ctor = FuncDecl(name, None, module_name,
                             FuncSignature([], RInstance(self)))

        self.attributes = OrderedDict()  # type: OrderedDict[str, RType]
        # We populate method_types with the signatures of every method before
        # we generate methods, and we rely on this information being present.
        self.method_decls = OrderedDict()  # type: OrderedDict[str, FuncDecl]
        # Map of methods that are actually present in an extension class
        self.methods = OrderedDict()  # type: OrderedDict[str, FuncIR]
        # Glue methods for boxing/unboxing when a class changes the type
        # while overriding a method. Maps from (parent class overrided, method)
        # to IR of glue method.
        self.glue_methods = OrderedDict(
        )  # type: Dict[Tuple[ClassIR, str], FuncIR]

        # Properties are accessed like attributes, but have behavior like method calls.
        # They don't belong in the methods dictionary, since we don't want to expose them to
        # Python's method API. But we want to put them into our own vtable as methods, so that
        # they are properly handled and overridden. The property dictionary values are a tuple
        # containing a property getter and an optional property setter.
        self.properties = OrderedDict(
        )  # type: OrderedDict[str, Tuple[FuncIR, Optional[FuncIR]]]
        # We generate these in prepare_class_def so that we have access to them when generating
        # other methods and properties that rely on these types.
        self.property_types = OrderedDict()  # type: OrderedDict[str, RType]

        self.vtable = None  # type: Optional[Dict[str, int]]
        self.vtable_entries = []  # type: VTableEntries
        self.trait_vtables = OrderedDict(
        )  # type: OrderedDict[ClassIR, VTableEntries]
        # N.B: base might not actually quite be the direct base.
        # It is the nearest concrete base, but we allow a trait in between.
        self.base = None  # type: Optional[ClassIR]
        self.traits = []  # type: List[ClassIR]
        # Supply a working mro for most generated classes. Real classes will need to
        # fix it up.
        self.mro = [self]  # type: List[ClassIR]
        # base_mro is the chain of concrete (non-trait) ancestors
        self.base_mro = [self]  # type: List[ClassIR]

        # Direct subclasses of this class (use subclasses() to also incude non-direct ones)
        # None if separate compilation prevents this from working
        self.children = []  # type: Optional[List[ClassIR]]
Exemplo n.º 8
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

    # 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 != '__slots__':
                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
Exemplo n.º 9
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.primitive_op(true_op, [], -1)))

    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