Example #1
0
def translate_super_method_call(builder: IRBuilder, expr: CallExpr,
                                callee: SuperExpr) -> Value:
    if callee.info is None or callee.call.args:
        return translate_call(builder, expr, callee)
    ir = builder.mapper.type_to_ir[callee.info]
    # Search for the method in the mro, skipping ourselves.
    for base in ir.mro[1:]:
        if callee.name in base.method_decls:
            break
    else:
        return translate_call(builder, expr, callee)

    decl = base.method_decl(callee.name)
    arg_values = [builder.accept(arg) for arg in expr.args]
    arg_kinds, arg_names = expr.arg_kinds[:], expr.arg_names[:]

    if decl.kind != FUNC_STATICMETHOD:
        vself = next(iter(builder.environment.indexes))  # grab first argument
        if decl.kind == FUNC_CLASSMETHOD:
            vself = builder.primitive_op(type_op, [vself], expr.line)
        elif builder.fn_info.is_generator:
            # For generator classes, the self target is the 6th value
            # in the symbol table (which is an ordered dict). This is sort
            # of ugly, but we can't search by name since the 'self' parameter
            # could be named anything, and it doesn't get added to the
            # environment indexes.
            self_targ = list(builder.environment.symtable.values())[6]
            vself = builder.read(self_targ, builder.fn_info.fitem.line)
        arg_values.insert(0, vself)
        arg_kinds.insert(0, ARG_POS)
        arg_names.insert(0, None)

    return builder.builder.call(decl, arg_values, arg_kinds, arg_names,
                                expr.line)
Example #2
0
def transform_operator_assignment_stmt(builder: IRBuilder, stmt: OperatorAssignmentStmt) -> None:
    """Operator assignment statement such as x += 1"""
    builder.disallow_class_assignments([stmt.lvalue], stmt.line)
    target = builder.get_assignment_target(stmt.lvalue)
    target_value = builder.read(target, stmt.line)
    rreg = builder.accept(stmt.rvalue)
    # the Python parser strips the '=' from operator assignment statements, so re-add it
    op = stmt.op + '='
    res = builder.binary_op(target_value, rreg, op, stmt.line)
    # usually operator assignments are done in-place
    # but when target doesn't support that we need to manually assign
    builder.assign(target, res, res.line)
Example #3
0
def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
    assert expr.node, "RefExpr not resolved"
    fullname = expr.node.fullname
    if fullname in name_ref_ops:
        # Use special access op for this particular name.
        desc = name_ref_ops[fullname]
        assert desc.result_type is not None
        return builder.add(PrimitiveOp([], desc, expr.line))

    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 and FuncDef node types.
        return builder.read(builder.get_assignment_target(expr), expr.line)

    return builder.load_global(expr)
Example #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)
Example #5
0
def transform_super_expr(builder: IRBuilder, o: SuperExpr) -> Value:
    # warning(builder, 'can not optimize super() expression', o.line)
    sup_val = builder.load_module_attr_by_fullname('builtins.super', o.line)
    if o.call.args:
        args = [builder.accept(arg) for arg in o.call.args]
    else:
        assert o.info is not None
        typ = builder.load_native_type_object(o.info.fullname)
        ir = builder.mapper.type_to_ir[o.info]
        iter_env = iter(builder.environment.indexes)
        vself = next(iter_env)  # grab first argument
        if builder.fn_info.is_generator:
            # grab sixth argument (see comment in translate_super_method_call)
            self_targ = list(builder.environment.symtable.values())[6]
            vself = builder.read(self_targ, builder.fn_info.fitem.line)
        elif not ir.is_ext_class:
            vself = next(
                iter_env)  # second argument is self if non_extension class
        args = [typ, vself]
    res = builder.py_call(sup_val, args, o.line)
    return builder.py_get_attr(res, o.name, o.line)
Example #6
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
Example #7
0
def transform_try_except(builder: IRBuilder,
                         body: GenFunc,
                         handlers: Sequence[
                             Tuple[Optional[Expression], Optional[Expression], GenFunc]],
                         else_body: Optional[GenFunc],
                         line: int) -> None:
    """Generalized try/except/else handling that takes functions to gen the bodies.

    The point of this is to also be able to support with."""
    assert handlers, "try needs except"

    except_entry, exit_block, cleanup_block = BasicBlock(), BasicBlock(), BasicBlock()
    double_except_block = BasicBlock()
    # If there is an else block, jump there after the try, otherwise just leave
    else_block = BasicBlock() if else_body else exit_block

    # Compile the try block with an error handler
    builder.builder.push_error_handler(except_entry)
    builder.goto_and_activate(BasicBlock())
    body()
    builder.goto(else_block)
    builder.builder.pop_error_handler()

    # The error handler catches the error and then checks it
    # against the except clauses. We compile the error handler
    # itself with an error handler so that it can properly restore
    # the *old* exc_info if an exception occurs.
    # The exception chaining will be done automatically when the
    # exception is raised, based on the exception in exc_info.
    builder.builder.push_error_handler(double_except_block)
    builder.activate_block(except_entry)
    old_exc = builder.maybe_spill(builder.primitive_op(error_catch_op, [], line))
    # Compile the except blocks with the nonlocal control flow overridden to clear exc_info
    builder.nonlocal_control.append(
        ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc))

    # Process the bodies
    for type, var, handler_body in handlers:
        next_block = None
        if type:
            next_block, body_block = BasicBlock(), BasicBlock()
            matches = builder.primitive_op(
                exc_matches_op, [builder.accept(type)], type.line
            )
            builder.add(Branch(matches, body_block, next_block, Branch.BOOL_EXPR))
            builder.activate_block(body_block)
        if var:
            target = builder.get_assignment_target(var)
            builder.assign(
                target,
                builder.primitive_op(get_exc_value_op, [], var.line),
                var.line
            )
        handler_body()
        builder.goto(cleanup_block)
        if next_block:
            builder.activate_block(next_block)

    # Reraise the exception if needed
    if next_block:
        builder.primitive_op(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())

    builder.nonlocal_control.pop()
    builder.builder.pop_error_handler()

    # Cleanup for if we leave except through normal control flow:
    # restore the saved exc_info information and continue propagating
    # the exception if it exists.
    builder.activate_block(cleanup_block)
    builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line)
    builder.goto(exit_block)

    # Cleanup for if we leave except through a raised exception:
    # restore the saved exc_info information and continue propagating
    # the exception.
    builder.activate_block(double_except_block)
    builder.primitive_op(restore_exc_info_op, [builder.read(old_exc)], line)
    builder.primitive_op(keep_propagating_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())

    # If present, compile the else body in the obvious way
    if else_body:
        builder.activate_block(else_block)
        else_body()
        builder.goto(exit_block)

    builder.activate_block(exit_block)