예제 #1
0
def check_instance_attribute_access_through_class(builder: IRBuilder,
                                                  expr: MemberExpr,
                                                  typ: Optional[ProperType]) -> None:
    """Report error if accessing an instance attribute through class object."""
    if isinstance(expr.expr, RefExpr):
        node = expr.expr.node
        if isinstance(typ, TypeType) and isinstance(typ.item, Instance):
            # TODO: Handle other item types
            node = typ.item.type
        if isinstance(node, TypeInfo):
            class_ir = builder.mapper.type_to_ir.get(node)
            if class_ir is not None and class_ir.is_ext_class:
                sym = node.get(expr.name)
                if (sym is not None
                        and isinstance(sym.node, Var)
                        and not sym.node.is_classvar
                        and not sym.node.is_final):
                    builder.error(
                        'Cannot access instance attribute "{}" through class object'.format(
                            expr.name),
                        expr.line
                    )
                    builder.note(
                        '(Hint: Use "x: Final = ..." or "x: ClassVar = ..." to define '
                        'a class attribute)',
                        expr.line
                    )
예제 #2
0
def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value:
    if any(o.is_async):
        builder.error('async comprehensions are unimplemented', o.line)

    builder.warning('Treating generator comprehension as list', o.line)
    return builder.call_c(iter_op, [translate_list_comprehension(builder, o)],
                          o.line)
예제 #3
0
파일: statement.py 프로젝트: sthagen/mypy
def transform_del_item(builder: IRBuilder, target: AssignmentTarget,
                       line: int) -> None:
    if isinstance(target, AssignmentTargetIndex):
        builder.gen_method_call(target.base,
                                '__delitem__', [target.index],
                                result_type=None,
                                line=line)
    elif isinstance(target, AssignmentTargetAttr):
        if isinstance(target.obj_type, RInstance):
            cl = target.obj_type.class_ir
            if not cl.is_deletable(target.attr):
                builder.error('"{}" cannot be deleted'.format(target.attr),
                              line)
                builder.note(
                    'Using "__deletable__ = ' +
                    '[\'<attr>\']" in the class body enables "del obj.<attr>"',
                    line)
        key = builder.load_str(target.attr)
        builder.call_c(py_delattr_op, [target.obj, key], line)
    elif isinstance(target, AssignmentTargetRegister):
        # Delete a local by assigning an error value to it, which will
        # prompt the insertion of uninit checks.
        builder.add(
            Assign(target.register,
                   builder.add(LoadErrorValue(target.type, undefines=True))))
    elif isinstance(target, AssignmentTargetTuple):
        for subtarget in target.items:
            transform_del_item(builder, subtarget, line)
예제 #4
0
def transform_yield_expr(builder: IRBuilder, expr: YieldExpr) -> Value:
    if builder.fn_info.is_coroutine:
        builder.error('async generators are unimplemented', expr.line)

    if expr.expr:
        retval = builder.accept(expr.expr)
    else:
        retval = builder.builder.none()
    return emit_yield(builder, retval, expr.line)
예제 #5
0
파일: expression.py 프로젝트: wolpa/mypy
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 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)
예제 #6
0
def transform_with_stmt(builder: IRBuilder, o: WithStmt) -> None:
    if o.is_async:
        builder.error('async with is unimplemented', o.line)

    # Generate separate logic for each expr in it, left to right
    def generate(i: int) -> None:
        if i >= len(o.expr):
            builder.accept(o.body)
        else:
            transform_with(builder, o.expr[i], o.target[i], lambda: generate(i + 1), o.line)

    generate(0)
예제 #7
0
파일: statement.py 프로젝트: sthagen/mypy
def transform_for_stmt(builder: IRBuilder, s: ForStmt) -> None:
    if s.is_async:
        builder.error('async for is unimplemented', s.line)

    def body() -> None:
        builder.accept(s.body)

    def else_block() -> None:
        assert s.else_body is not None
        builder.accept(s.else_body)

    for_loop_helper(builder, s.index, s.expr, body,
                    else_block if s.else_body else None, s.line)
예제 #8
0
def transform_dictionary_comprehension(builder: IRBuilder, o: DictionaryComprehension) -> Value:
    if any(o.is_async):
        builder.error('async comprehensions are unimplemented', o.line)

    d = builder.call_c(dict_new_op, [], o.line)
    loop_params = list(zip(o.indices, o.sequences, o.condlists))

    def gen_inner_stmts() -> None:
        k = builder.accept(o.key)
        v = builder.accept(o.value)
        builder.call_c(dict_set_item_op, [d, k, v], o.line)

    comprehension_helper(builder, loop_params, gen_inner_stmts, o.line)
    return d
예제 #9
0
파일: classdef.py 프로젝트: srittau/mypy
def check_deletable_declaration(builder: IRBuilder, cl: ClassIR, line: int) -> None:
    for attr in cl.deletable:
        if attr not in cl.attributes:
            if not cl.has_attr(attr):
                builder.error('Attribute "{}" not defined'.format(attr), line)
                continue
            for base in cl.mro:
                if attr in base.property_types:
                    builder.error('Cannot make property "{}" deletable'.format(attr), line)
                    break
            else:
                _, base = cl.attr_details(attr)
                builder.error(('Attribute "{}" not defined in "{}" ' +
                               '(defined in "{}")').format(attr, cl.name, base.name), line)
예제 #10
0
파일: for_helpers.py 프로젝트: xw0329/mypy
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 (expr.callee.fullname == 'builtins.range'
                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 = builder.add(LoadInt(0))
                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(rtyp)):
            # 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
예제 #11
0
def transform_set_comprehension(builder: IRBuilder, o: SetComprehension) -> Value:
    if any(o.generator.is_async):
        builder.error('async comprehensions are unimplemented', o.line)
    return translate_set_comprehension(builder, o.generator)
예제 #12
0
def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value:
    if expr.node is None:
        builder.add(RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                                       "mypyc internal error: should be unreachable",
                                       expr.line))
        return builder.none()
    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_str(expr.node.fullname)],
                                 expr.line)
            return obj
        else:
            return builder.read(builder.get_assignment_target(expr), expr.line)

    return builder.load_global(expr)
예제 #13
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[Tuple[Lvalue, RType]]

    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.call_c(
                py_setattr_op, [typ, builder.load_str(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_str(cdef.name),
                           non_ext_class
                       ], cdef.line)

        # Cache any cachable class attributes
        cache_class_attrs(builder, attrs_to_cache, cdef)
예제 #14
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)