Esempio n. 1
0
def create_switch_for_generator_class(builder: IRBuilder) -> None:
    builder.add(Goto(builder.fn_info.generator_class.switch_block))
    block = BasicBlock()
    builder.fn_info.generator_class.continuation_blocks.append(block)
    builder.activate_block(block)
Esempio n. 2
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
Esempio n. 3
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)
Esempio n. 4
0
def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value:
    builder.warning('Treating generator comprehension as list', o.line)
    return builder.call_c(iter_op, [translate_list_comprehension(builder, o)],
                          o.line)
Esempio n. 5
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.call_c(dict_set_item_op,
                [
                    builder.load_globals_dict(),
                    builder.load_static_unicode(cdef.name),
                    tp,
                ], cdef.line)

    return tp
Esempio n. 6
0
def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value:
    # x in (...)/[...]
    # x not in (...)/[...]
    if (e.operators[0] in ['in', 'not in'] and len(e.operators) == 1
            and isinstance(e.operands[1], (TupleExpr, ListExpr))):
        items = e.operands[1].items
        n_items = len(items)
        # x in y -> x == y[0] or ... or x == y[n]
        # x not in y -> x != y[0] and ... and x != y[n]
        # 16 is arbitrarily chosen to limit code size
        if 1 < n_items < 16:
            if e.operators[0] == 'in':
                bin_op = 'or'
                cmp_op = '=='
            else:
                bin_op = 'and'
                cmp_op = '!='
            lhs = e.operands[0]
            mypy_file = builder.graph['builtins'].tree
            assert mypy_file is not None
            bool_type = Instance(cast(TypeInfo, mypy_file.names['bool'].node),
                                 [])
            exprs = []
            for item in items:
                expr = ComparisonExpr([cmp_op], [lhs, item])
                builder.types[expr] = bool_type
                exprs.append(expr)

            or_expr = exprs.pop(0)  # type: Expression
            for expr in exprs:
                or_expr = OpExpr(bin_op, or_expr, expr)
                builder.types[or_expr] = bool_type
            return builder.accept(or_expr)
        # x in [y]/(y) -> x == y
        # x not in [y]/(y) -> x != y
        elif n_items == 1:
            if e.operators[0] == 'in':
                cmp_op = '=='
            else:
                cmp_op = '!='
            e.operators = [cmp_op]
            e.operands[1] = items[0]
        # x in []/() -> False
        # x not in []/() -> True
        elif n_items == 0:
            if e.operators[0] == 'in':
                return builder.false()
            else:
                return builder.true()

    # TODO: Don't produce an expression when used in conditional context
    # All of the trickiness here is due to support for chained conditionals
    # (`e1 < e2 > e3`, etc). `e1 < e2 > e3` is approximately equivalent to
    # `e1 < e2 and e2 > e3` except that `e2` is only evaluated once.
    expr_type = builder.node_type(e)

    # go(i, prev) generates code for `ei opi e{i+1} op{i+1} ... en`,
    # assuming that prev contains the value of `ei`.
    def go(i: int, prev: Value) -> Value:
        if i == len(e.operators) - 1:
            return transform_basic_comparison(
                builder, e.operators[i], prev,
                builder.accept(e.operands[i + 1]), e.line)

        next = builder.accept(e.operands[i + 1])
        return builder.builder.shortcircuit_helper(
            'and', expr_type, lambda: transform_basic_comparison(
                builder, e.operators[i], prev, next, e.line),
            lambda: go(i + 1, next), e.line)

    return go(0, builder.accept(e.operands[0]))
Esempio n. 7
0
def transform_ellipsis(builder: IRBuilder, o: EllipsisExpr) -> Value:
    return builder.add(LoadAddress(ellipsis_op.type, ellipsis_op.src, o.line))
Esempio n. 8
0
def translate_next_call(builder: IRBuilder, expr: CallExpr,
                        callee: RefExpr) -> Optional[Value]:
    # Special case for calling next() on a generator expression, an
    # idiom that shows up some in mypy.
    #
    # For example, next(x for x in l if x.id == 12, None) will
    # generate code that searches l for an element where x.id == 12
    # and produce the first such object, or None if no such element
    # exists.
    if not (expr.arg_kinds in ([ARG_POS], [ARG_POS, ARG_POS])
            and isinstance(expr.args[0], GeneratorExpr)):
        return None

    gen = expr.args[0]

    retval = builder.alloc_temp(builder.node_type(expr))
    default_val = None
    if len(expr.args) > 1:
        default_val = builder.accept(expr.args[1])

    exit_block = BasicBlock()

    def gen_inner_stmts() -> None:
        # next takes the first element of the generator, so if
        # something gets produced, we are done.
        builder.assign(retval, builder.accept(gen.left_expr),
                       gen.left_expr.line)
        builder.goto(exit_block)

    loop_params = list(zip(gen.indices, gen.sequences, gen.condlists))
    comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line)

    # Now we need the case for when nothing got hit. If there was
    # a default value, we produce it, and otherwise we raise
    # StopIteration.
    if default_val:
        builder.assign(retval, default_val, gen.left_expr.line)
        builder.goto(exit_block)
    else:
        builder.add(
            RaiseStandardError(RaiseStandardError.STOP_ITERATION, None,
                               expr.line))
        builder.add(Unreachable())

    builder.activate_block(exit_block)
    return retval
Esempio n. 9
0
def translate_globals(builder: IRBuilder, expr: CallExpr,
                      callee: RefExpr) -> Optional[Value]:
    # Special case builtins.globals
    if len(expr.args) == 0:
        return builder.load_globals_dict()
    return None
Esempio n. 10
0
def for_loop_helper(builder: IRBuilder, index: Lvalue, expr: Expression,
                    body_insts: GenFunc, else_insts: Optional[GenFunc],
                    line: int) -> None:
    """Generate IR for a loop.

    Args:
        index: the loop index Lvalue
        expr: the expression to iterate over
        body_insts: a function that generates the body of the loop
        else_insts: a function that generates the else block instructions
    """
    # Body of the loop
    body_block = BasicBlock()
    # Block that steps to the next item
    step_block = BasicBlock()
    # Block for the else clause, if we need it
    else_block = BasicBlock()
    # Block executed after the loop
    exit_block = BasicBlock()

    # Determine where we want to exit, if our condition check fails.
    normal_loop_exit = else_block if else_insts is not None else exit_block

    for_gen = make_for_loop_generator(builder, index, expr, body_block,
                                      normal_loop_exit, line)

    builder.push_loop_stack(step_block, exit_block)
    condition_block = BasicBlock()
    builder.goto_and_activate(condition_block)

    # Add loop condition check.
    for_gen.gen_condition()

    # Generate loop body.
    builder.activate_block(body_block)
    for_gen.begin_body()
    body_insts()

    # We generate a separate step block (which might be empty).
    builder.goto_and_activate(step_block)
    for_gen.gen_step()
    # Go back to loop condition.
    builder.goto(condition_block)

    for_gen.add_cleanup(normal_loop_exit)
    builder.pop_loop_stack()

    if else_insts is not None:
        builder.activate_block(else_block)
        else_insts()
        builder.goto(exit_block)

    builder.activate_block(exit_block)
Esempio n. 11
0
def for_loop_helper_with_index(builder: IRBuilder, index: Lvalue,
                               expr: Expression, expr_reg: Value,
                               body_insts: Callable[[Value],
                                                    None], line: int) -> None:
    """Generate IR for a sequence iteration.

    This function only works for sequence type. Compared to for_loop_helper,
    it would feed iteration index to body_insts.

    Args:
        index: the loop index Lvalue
        expr: the expression to iterate over
        body_insts: a function that generates the body of the loop.
                    It needs a index as parameter.
    """
    assert is_sequence_rprimitive(expr_reg.type)
    target_type = builder.get_sequence_type(expr)

    body_block = BasicBlock()
    step_block = BasicBlock()
    exit_block = BasicBlock()
    condition_block = BasicBlock()

    for_gen = ForSequence(builder, index, body_block, exit_block, line, False)
    for_gen.init(expr_reg, target_type, reverse=False)

    builder.push_loop_stack(step_block, exit_block)

    builder.goto_and_activate(condition_block)
    for_gen.gen_condition()

    builder.activate_block(body_block)
    for_gen.begin_body()
    body_insts(builder.read(for_gen.index_target))

    builder.goto_and_activate(step_block)
    for_gen.gen_step()
    builder.goto(condition_block)

    for_gen.add_cleanup(exit_block)
    builder.pop_loop_stack()

    builder.activate_block(exit_block)
Esempio n. 12
0
def make_for_loop_generator(builder: IRBuilder,
                            index: Lvalue,
                            expr: Expression,
                            body_block: BasicBlock,
                            loop_exit: BasicBlock,
                            line: int,
                            nested: bool = False) -> 'ForGenerator':
    """Return helper object for generating a for loop over an iterable.

    If "nested" is True, this is a nested iterator such as "e" in "enumerate(e)".
    """

    rtyp = builder.node_type(expr)
    if is_sequence_rprimitive(rtyp):
        # Special case "for x in <list>".
        expr_reg = builder.accept(expr)
        target_type = builder.get_sequence_type(expr)

        for_list = ForSequence(builder, index, body_block, loop_exit, line,
                               nested)
        for_list.init(expr_reg, target_type, reverse=False)
        return for_list

    if is_dict_rprimitive(rtyp):
        # Special case "for k in <dict>".
        expr_reg = builder.accept(expr)
        target_type = builder.get_dict_key_type(expr)

        for_dict = ForDictionaryKeys(builder, index, body_block, loop_exit,
                                     line, nested)
        for_dict.init(expr_reg, target_type)
        return for_dict

    if (isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr)):
        if (is_range_ref(expr.callee)
                and (len(expr.args) <= 2 or
                     (len(expr.args) == 3
                      and builder.extract_int(expr.args[2]) is not None))
                and set(expr.arg_kinds) == {ARG_POS}):
            # Special case "for x in range(...)".
            # We support the 3 arg form but only for int literals, since it doesn't
            # seem worth the hassle of supporting dynamically determining which
            # direction of comparison to do.
            if len(expr.args) == 1:
                start_reg = Integer(0)  # type: Value
                end_reg = builder.accept(expr.args[0])
            else:
                start_reg = builder.accept(expr.args[0])
                end_reg = builder.accept(expr.args[1])
            if len(expr.args) == 3:
                step = builder.extract_int(expr.args[2])
                assert step is not None
                if step == 0:
                    builder.error("range() step can't be zero",
                                  expr.args[2].line)
            else:
                step = 1

            for_range = ForRange(builder, index, body_block, loop_exit, line,
                                 nested)
            for_range.init(start_reg, end_reg, step)
            return for_range

        elif (expr.callee.fullname == 'builtins.enumerate'
              and len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]
              and isinstance(index, TupleExpr) and len(index.items) == 2):
            # Special case "for i, x in enumerate(y)".
            lvalue1 = index.items[0]
            lvalue2 = index.items[1]
            for_enumerate = ForEnumerate(builder, index, body_block, loop_exit,
                                         line, nested)
            for_enumerate.init(lvalue1, lvalue2, expr.args[0])
            return for_enumerate

        elif (expr.callee.fullname == 'builtins.zip' and len(expr.args) >= 2
              and set(expr.arg_kinds) == {ARG_POS}
              and isinstance(index, TupleExpr)
              and len(index.items) == len(expr.args)):
            # Special case "for x, y in zip(a, b)".
            for_zip = ForZip(builder, index, body_block, loop_exit, line,
                             nested)
            for_zip.init(index.items, expr.args)
            return for_zip

        if (expr.callee.fullname == 'builtins.reversed' and len(expr.args) == 1
                and expr.arg_kinds == [ARG_POS]
                and is_sequence_rprimitive(builder.node_type(expr.args[0]))):
            # Special case "for x in reversed(<list>)".
            expr_reg = builder.accept(expr.args[0])
            target_type = builder.get_sequence_type(expr)

            for_list = ForSequence(builder, index, body_block, loop_exit, line,
                                   nested)
            for_list.init(expr_reg, target_type, reverse=True)
            return for_list
    if (isinstance(expr, CallExpr) and isinstance(expr.callee, MemberExpr)
            and not expr.args):
        # Special cases for dictionary iterator methods, like dict.items().
        rtype = builder.node_type(expr.callee.expr)
        if (is_dict_rprimitive(rtype)
                and expr.callee.name in ('keys', 'values', 'items')):
            expr_reg = builder.accept(expr.callee.expr)
            for_dict_type = None  # type: Optional[Type[ForGenerator]]
            if expr.callee.name == 'keys':
                target_type = builder.get_dict_key_type(expr.callee.expr)
                for_dict_type = ForDictionaryKeys
            elif expr.callee.name == 'values':
                target_type = builder.get_dict_value_type(expr.callee.expr)
                for_dict_type = ForDictionaryValues
            else:
                target_type = builder.get_dict_item_type(expr.callee.expr)
                for_dict_type = ForDictionaryItems
            for_dict_gen = for_dict_type(builder, index, body_block, loop_exit,
                                         line, nested)
            for_dict_gen.init(expr_reg, target_type)
            return for_dict_gen

    # Default to a generic for loop.
    expr_reg = builder.accept(expr)
    for_obj = ForIterable(builder, index, body_block, loop_exit, line, nested)
    item_type = builder._analyze_iterable_item_type(expr)
    item_rtype = builder.type_to_rtype(item_type)
    for_obj.init(expr_reg, item_rtype)
    return for_obj
Esempio n. 13
0
def add_raise_exception_blocks_to_generator_class(builder: IRBuilder,
                                                  line: int) -> None:
    """
    Generates blocks to check if error flags are set while calling the helper method for
    generator functions, and raises an exception if those flags are set.
    """
    cls = builder.fn_info.generator_class
    assert cls.exc_regs is not None
    exc_type, exc_val, exc_tb = cls.exc_regs

    # Check to see if an exception was raised.
    error_block = BasicBlock()
    ok_block = BasicBlock()
    comparison = builder.binary_op(exc_type, builder.none_object(), 'is not',
                                   line)
    builder.add_bool_branch(comparison, error_block, ok_block)

    builder.activate_block(error_block)
    builder.primitive_op(raise_exception_with_tb_op,
                         [exc_type, exc_val, exc_tb], line)
    builder.add(Unreachable())
    builder.goto_and_activate(ok_block)
Esempio n. 14
0
def populate_switch_for_generator_class(builder: IRBuilder) -> None:
    cls = builder.fn_info.generator_class
    line = builder.fn_info.fitem.line

    builder.activate_block(cls.switch_block)
    for label, true_block in enumerate(cls.continuation_blocks):
        false_block = BasicBlock()
        comparison = builder.binary_op(cls.next_label_reg,
                                       builder.add(LoadInt(label)), '==', line)
        builder.add_bool_branch(comparison, true_block, false_block)
        builder.activate_block(false_block)

    builder.add(
        RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, line))
    builder.add(Unreachable())
Esempio n. 15
0
def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
    assert expr.node, "RefExpr not resolved"
    fullname = expr.node.fullname
    if fullname in builtin_names:
        typ, src = builtin_names[fullname]
        return builder.add(LoadAddress(typ, src, expr.line))
    # special cases
    if fullname == 'builtins.None':
        return builder.none()
    if fullname == 'builtins.True':
        return builder.true()
    if fullname == 'builtins.False':
        return builder.false()

    if isinstance(expr.node, Var) and expr.node.is_final:
        value = builder.emit_load_final(
            expr.node,
            fullname,
            expr.name,
            builder.is_native_ref_expr(expr),
            builder.types[expr],
            expr.line,
        )
        if value is not None:
            return value

    if isinstance(expr.node,
                  MypyFile) and expr.node.fullname in builder.imports:
        return 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(builder.node_type(expr))
                and expr.node.is_inferred):
            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, FuncDef and MypyFile node types.
        if isinstance(expr.node, MypyFile):
            # Load reference to a module imported inside function from
            # the modules dictionary. It would be closer to Python
            # semantics to access modules imported inside functions
            # via local variables, but this is tricky since the mypy
            # AST doesn't include a Var node for the module. We
            # instead load the module separately on each access.
            mod_dict = builder.call_c(get_module_dict_op, [], expr.line)
            obj = builder.call_c(
                dict_get_item_op,
                [mod_dict,
                 builder.load_static_unicode(expr.node.fullname)], expr.line)
            return obj
        else:
            return builder.read(builder.get_assignment_target(expr), expr.line)

    return builder.load_global(expr)
Esempio n. 16
0
File: main.py Progetto: sthagen/mypy
def transform_mypy_file(builder: IRBuilder, mypyfile: MypyFile) -> None:
    """Generate IR for a single module."""

    if mypyfile.fullname in ('typing', 'abc'):
        # These module are special; their contents are currently all
        # built-in primitives.
        return

    builder.set_module(mypyfile.fullname, mypyfile.path)

    classes = [node for node in mypyfile.defs if isinstance(node, ClassDef)]

    # Collect all classes.
    for cls in classes:
        ir = builder.mapper.type_to_ir[cls.info]
        builder.classes.append(ir)

    builder.enter('<top level>')

    # Make sure we have a builtins import
    builder.gen_import('builtins', -1)

    # Generate ops.
    for node in mypyfile.defs:
        builder.accept(node)

    builder.maybe_add_implicit_return()

    # Generate special function representing module top level.
    args, _, blocks, ret_type, _ = builder.leave()
    sig = FuncSignature([], none_rprimitive)
    func_ir = FuncIR(FuncDecl(TOP_LEVEL_NAME, None, builder.module_name, sig),
                     args,
                     blocks,
                     traceback_name="<module>")
    builder.functions.append(func_ir)
Esempio n. 17
0
def transform_conditional_expr(builder: IRBuilder,
                               expr: ConditionalExpr) -> Value:
    if_body, else_body, next = BasicBlock(), BasicBlock(), BasicBlock()

    builder.process_conditional(expr.cond, if_body, else_body)
    expr_type = builder.node_type(expr)
    # Having actual Phi nodes would be really nice here!
    target = builder.alloc_temp(expr_type)

    builder.activate_block(if_body)
    true_value = builder.accept(expr.if_expr)
    true_value = builder.coerce(true_value, expr_type, expr.line)
    builder.add(Assign(target, true_value))
    builder.goto(next)

    builder.activate_block(else_body)
    false_value = builder.accept(expr.else_expr)
    false_value = builder.coerce(false_value, expr_type, expr.line)
    builder.add(Assign(target, false_value))
    builder.goto(next)

    builder.activate_block(next)

    return target
Esempio n. 18
0
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
    """Create IR for a class definition.

    This can generate both extension (native) and non-extension
    classes.  These are generated in very different ways. In the
    latter case we construct a Python type object at runtime by doing
    the equivalent of "type(name, bases, dict)" in IR. Extension
    classes are defined via C structs that are generated later in
    mypyc.codegen.emitclass.

    This is the main entry point to this module.
    """
    ir = builder.mapper.type_to_ir[cdef.info]

    # We do this check here because the base field of parent
    # classes aren't necessarily populated yet at
    # prepare_class_def time.
    if any(ir.base_mro[i].base != ir.base_mro[i + 1]
           for i in range(len(ir.base_mro) - 1)):
        builder.error("Non-trait MRO must be linear", cdef.line)

    if ir.allow_interpreted_subclasses:
        for parent in ir.mro:
            if not parent.allow_interpreted_subclasses:
                builder.error(
                    'Base class "{}" does not allow interpreted subclasses'.
                    format(parent.fullname), cdef.line)

    # Currently, we only create non-extension classes for classes that are
    # decorated or inherit from Enum. Classes decorated with @trait do not
    # apply here, and are handled in a different way.
    if ir.is_ext_class:
        cls_type = dataclass_type(cdef)
        if cls_type is None:
            cls_builder: ClassBuilder = ExtClassBuilder(builder, cdef)
        elif cls_type in ['dataclasses', 'attr-auto']:
            cls_builder = DataClassBuilder(builder, cdef)
        elif cls_type == 'attr':
            cls_builder = AttrsClassBuilder(builder, cdef)
        else:
            raise ValueError(cls_type)
    else:
        cls_builder = NonExtClassBuilder(builder, cdef)

    for stmt in cdef.defs.body:
        if isinstance(stmt, OverloadedFuncDef) and stmt.is_property:
            if isinstance(cls_builder, NonExtClassBuilder):
                # properties with both getters and setters in non_extension
                # classes not supported
                builder.error(
                    "Property setters not supported in non-extension classes",
                    stmt.line)
            for item in stmt.items:
                with builder.catch_errors(stmt.line):
                    cls_builder.add_method(get_func_def(item))
        elif isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)):
            # Ignore plugin generated methods (since they have no
            # bodies to compile and will need to have the bodies
            # provided by some other mechanism.)
            if cdef.info.names[stmt.name].plugin_generated:
                continue
            with builder.catch_errors(stmt.line):
                cls_builder.add_method(get_func_def(stmt))
        elif isinstance(stmt, PassStmt):
            continue
        elif isinstance(stmt, AssignmentStmt):
            if len(stmt.lvalues) != 1:
                builder.error(
                    "Multiple assignment in class bodies not supported",
                    stmt.line)
                continue
            lvalue = stmt.lvalues[0]
            if not isinstance(lvalue, NameExpr):
                builder.error(
                    "Only assignment to variables is supported in class bodies",
                    stmt.line)
                continue
            # We want to collect class variables in a dictionary for both real
            # non-extension classes and fake dataclass ones.
            cls_builder.add_attr(lvalue, stmt)

        elif isinstance(stmt, ExpressionStmt) and isinstance(
                stmt.expr, StrExpr):
            # Docstring. Ignore
            pass
        else:
            builder.error("Unsupported statement in class body", stmt.line)

    cls_builder.finalize(ir)
Esempio n. 19
0
def transform_str_expr(builder: IRBuilder, expr: StrExpr) -> Value:
    return builder.load_static_unicode(expr.value)
Esempio n. 20
0
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()))
Esempio n. 21
0
def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value:
    """Create a list, then turn it into a tuple."""
    val_as_list = _visit_list_display(builder, expr.items, expr.line)
    return builder.call_c(list_tuple_op, [val_as_list], expr.line)
Esempio n. 22
0
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None:
    """Generate a "__ne__" method from a "__eq__" method. """
    with 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))
Esempio n. 23
0
def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value:
    value = builder.accept(o.value)
    target = builder.get_assignment_target(o.target)
    builder.assign(target, value, o.line)
    return value
Esempio n. 24
0
def translate_cast_expr(builder: IRBuilder, expr: CastExpr) -> Value:
    src = builder.accept(expr.expr)
    target_type = builder.type_to_rtype(expr.type)
    return builder.coerce(src, target_type, expr.line)
Esempio n. 25
0
def setup_non_ext_dict(builder: IRBuilder,
                       cdef: ClassDef,
                       metaclass: Value,
                       bases: Value) -> Value:
    """Initialize the class dictionary for a non-extension class.

    This class dictionary is passed to the metaclass constructor.
    """
    # Check if the metaclass defines a __prepare__ method, and if so, call it.
    has_prepare = builder.primitive_op(py_hasattr_op,
                                    [metaclass,
                                    builder.load_static_unicode('__prepare__')], cdef.line)

    non_ext_dict = builder.alloc_temp(dict_rprimitive)

    true_block, false_block, exit_block, = BasicBlock(), BasicBlock(), BasicBlock()
    builder.add_bool_branch(has_prepare, true_block, false_block)

    builder.activate_block(true_block)
    cls_name = builder.load_static_unicode(cdef.name)
    prepare_meth = builder.py_get_attr(metaclass, '__prepare__', cdef.line)
    prepare_dict = builder.py_call(prepare_meth, [cls_name, bases], cdef.line)
    builder.assign(non_ext_dict, prepare_dict, cdef.line)
    builder.goto(exit_block)

    builder.activate_block(false_block)
    builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line)
    builder.goto(exit_block)
    builder.activate_block(exit_block)

    return non_ext_dict
Esempio n. 26
0
def transform_unary_expr(builder: IRBuilder, expr: UnaryExpr) -> Value:
    return builder.unary_op(builder.accept(expr.expr), expr.op, expr.line)
Esempio n. 27
0
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None:
    """Create IR for a class definition.

    This can generate both extension (native) and non-extension
    classes.  These are generated in very different ways. In the
    latter case we construct a Python type object at runtime by doing
    the equivalent of "type(name, bases, dict)" in IR. Extension
    classes are defined via C structs that are generated later in
    mypyc.codegen.emitclass.

    This is the main entry point to this module.
    """
    ir = builder.mapper.type_to_ir[cdef.info]

    # We do this check here because the base field of parent
    # classes aren't necessarily populated yet at
    # prepare_class_def time.
    if any(ir.base_mro[i].base != ir. base_mro[i + 1] for i in range(len(ir.base_mro) - 1)):
        builder.error("Non-trait MRO must be linear", cdef.line)

    if ir.allow_interpreted_subclasses:
        for parent in ir.mro:
            if not parent.allow_interpreted_subclasses:
                builder.error(
                    'Base class "{}" does not allow interpreted subclasses'.format(
                        parent.fullname), cdef.line)

    # Currently, we only create non-extension classes for classes that are
    # decorated or inherit from Enum. Classes decorated with @trait do not
    # apply here, and are handled in a different way.
    if ir.is_ext_class:
        # If the class is not decorated, generate an extension class for it.
        type_obj = allocate_class(builder, cdef)  # type: Optional[Value]
        non_ext = None  # type: Optional[NonExtClassInfo]
        dataclass_non_ext = dataclass_non_ext_info(builder, cdef)
    else:
        non_ext_bases = populate_non_ext_bases(builder, cdef)
        non_ext_metaclass = find_non_ext_metaclass(builder, cdef, non_ext_bases)
        non_ext_dict = setup_non_ext_dict(builder, cdef, non_ext_metaclass, non_ext_bases)
        # We populate __annotations__ for non-extension classes
        # because dataclasses uses it to determine which attributes to compute on.
        # TODO: Maybe generate more precise types for annotations
        non_ext_anns = builder.call_c(dict_new_op, [], cdef.line)
        non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass)
        dataclass_non_ext = None
        type_obj = None

    attrs_to_cache = []  # type: List[Lvalue]

    for stmt in cdef.defs.body:
        if isinstance(stmt, OverloadedFuncDef) and stmt.is_property:
            if not ir.is_ext_class:
                # properties with both getters and setters in non_extension
                # classes not supported
                builder.error("Property setters not supported in non-extension classes",
                           stmt.line)
            for item in stmt.items:
                with builder.catch_errors(stmt.line):
                    transform_method(builder, cdef, non_ext, get_func_def(item))
        elif isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)):
            # Ignore plugin generated methods (since they have no
            # bodies to compile and will need to have the bodies
            # provided by some other mechanism.)
            if cdef.info.names[stmt.name].plugin_generated:
                continue
            with builder.catch_errors(stmt.line):
                transform_method(builder, cdef, non_ext, get_func_def(stmt))
        elif isinstance(stmt, PassStmt):
            continue
        elif isinstance(stmt, AssignmentStmt):
            if len(stmt.lvalues) != 1:
                builder.error("Multiple assignment in class bodies not supported", stmt.line)
                continue
            lvalue = stmt.lvalues[0]
            if not isinstance(lvalue, NameExpr):
                builder.error("Only assignment to variables is supported in class bodies",
                           stmt.line)
                continue
            # We want to collect class variables in a dictionary for both real
            # non-extension classes and fake dataclass ones.
            var_non_ext = non_ext or dataclass_non_ext
            if var_non_ext:
                add_non_ext_class_attr(builder, var_non_ext, lvalue, stmt, cdef, attrs_to_cache)
                if non_ext:
                    continue
            # Variable declaration with no body
            if isinstance(stmt.rvalue, TempNode):
                continue
            # Only treat marked class variables as class variables.
            if not (is_class_var(lvalue) or stmt.is_final_def):
                continue
            typ = builder.load_native_type_object(cdef.fullname)
            value = builder.accept(stmt.rvalue)
            builder.primitive_op(
                py_setattr_op, [typ, builder.load_static_unicode(lvalue.name), value], stmt.line)
            if builder.non_function_scope() and stmt.is_final_def:
                builder.init_final_static(lvalue, value, cdef.name)
        elif isinstance(stmt, ExpressionStmt) and isinstance(stmt.expr, StrExpr):
            # Docstring. Ignore
            pass
        else:
            builder.error("Unsupported statement in class body", stmt.line)

    if not non_ext:  # That is, an extension class
        generate_attr_defaults(builder, cdef)
        create_ne_from_eq(builder, cdef)
        if dataclass_non_ext:
            assert type_obj
            dataclass_finalize(builder, cdef, dataclass_non_ext, type_obj)
    else:
        # Dynamically create the class via the type constructor
        non_ext_class = load_non_ext_class(builder, ir, non_ext, cdef.line)
        non_ext_class = load_decorated_class(builder, cdef, non_ext_class)

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

        # Add the non-extension class to the dict
        builder.call_c(dict_set_item_op,
                       [
                           builder.load_globals_dict(),
                           builder.load_static_unicode(cdef.name),
                           non_ext_class
                       ], cdef.line)

        # Cache any cachable class attributes
        cache_class_attrs(builder, attrs_to_cache, cdef)
Esempio n. 28
0
def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value:
    if expr.op in ('and', 'or'):
        return builder.shortcircuit_expr(expr)
    return builder.binary_op(builder.accept(expr.left),
                             builder.accept(expr.right), expr.op, expr.line)
Esempio n. 29
0
def translate_method_call(builder: IRBuilder, expr: CallExpr,
                          callee: MemberExpr) -> Value:
    """Generate IR for an arbitrary call of form e.m(...).

    This can also deal with calls to module-level functions.
    """
    if builder.is_native_ref_expr(callee):
        # Call to module-level native function or such
        return translate_call(builder, expr, callee)
    elif (isinstance(callee.expr, RefExpr)
          and isinstance(callee.expr.node, TypeInfo)
          and callee.expr.node in builder.mapper.type_to_ir and
          builder.mapper.type_to_ir[callee.expr.node].has_method(callee.name)):
        # Call a method via the *class*
        assert isinstance(callee.expr.node, TypeInfo)
        ir = builder.mapper.type_to_ir[callee.expr.node]
        decl = ir.method_decl(callee.name)
        args = []
        arg_kinds, arg_names = expr.arg_kinds[:], expr.arg_names[:]
        # Add the class argument for class methods in extension classes
        if decl.kind == FUNC_CLASSMETHOD and ir.is_ext_class:
            args.append(
                builder.load_native_type_object(callee.expr.node.fullname))
            arg_kinds.insert(0, ARG_POS)
            arg_names.insert(0, None)
        args += [builder.accept(arg) for arg in expr.args]

        if ir.is_ext_class:
            return builder.builder.call(decl, args, arg_kinds, arg_names,
                                        expr.line)
        else:
            obj = builder.accept(callee.expr)
            return builder.gen_method_call(obj, callee.name, args,
                                           builder.node_type(expr), expr.line,
                                           expr.arg_kinds, expr.arg_names)

    elif builder.is_module_member_expr(callee):
        # Fall back to a PyCall for non-native module calls
        function = builder.accept(callee)
        args = [builder.accept(arg) for arg in expr.args]
        return builder.py_call(function,
                               args,
                               expr.line,
                               arg_kinds=expr.arg_kinds,
                               arg_names=expr.arg_names)
    else:
        receiver_typ = builder.node_type(callee.expr)

        # If there is a specializer for this method name/type, try calling it.
        # We would return the first successful one.
        if (callee.name, receiver_typ) in specializers:
            for specializer in specializers[callee.name, receiver_typ]:
                val = specializer(builder, expr, callee)
                if val is not None:
                    return val

        obj = builder.accept(callee.expr)
        args = [builder.accept(arg) for arg in expr.args]
        return builder.gen_method_call(obj, callee.name, args,
                                       builder.node_type(expr), expr.line,
                                       expr.arg_kinds, expr.arg_names)
Esempio n. 30
0
def gen_generator_func(builder: IRBuilder) -> None:
    setup_generator_class(builder)
    load_env_registers(builder)
    gen_arg_defaults(builder)
    finalize_env_class(builder)
    builder.add(Return(instantiate_generator_class(builder)))