Ejemplo n.º 1
0
 def matching_primitive_op(self,
                           candidates: List[OpDescription],
                           args: List[Value],
                           line: int,
                           result_type: Optional[RType] = None) -> Optional[Value]:
     # Find the highest-priority primitive op that matches.
     matching = None  # type: Optional[OpDescription]
     for desc in candidates:
         if len(desc.arg_types) != len(args):
             continue
         if all(is_subtype(actual.type, formal)
                for actual, formal in zip(args, desc.arg_types)):
             if matching:
                 assert matching.priority != desc.priority, 'Ambiguous:\n1) %s\n2) %s' % (
                     matching, desc)
                 if desc.priority > matching.priority:
                     matching = desc
             else:
                 matching = desc
     if matching:
         target = self.primitive_op(matching, args, line)
         if result_type and not is_runtime_subtype(target.type, result_type):
             if is_none_rprimitive(result_type):
                 # Special case None return. The actual result may actually be a bool
                 # and so we can't just coerce it.
                 target = self.none()
             else:
                 target = self.coerce(target, result_type, line)
         return target
     return None
Ejemplo n.º 2
0
 def __init__(self, src: Value, line: int = -1) -> None:
     super().__init__(line)
     self.src = src
     self.type = object_rprimitive
     # When we box None and bool values, we produce a borrowed result
     if is_none_rprimitive(self.src.type) or is_bool_rprimitive(self.src.type):
         self.is_borrowed = True
Ejemplo n.º 3
0
 def call_c(self,
            desc: CFunctionDescription,
            args: List[Value],
            line: int,
            result_type: Optional[RType] = None) -> Value:
     # handle void function via singleton RVoid instance
     coerced = []
     # coerce fixed number arguments
     for i in range(min(len(args), len(desc.arg_types))):
         formal_type = desc.arg_types[i]
         arg = args[i]
         arg = self.coerce(arg, formal_type, line)
         coerced.append(arg)
     # coerce any var_arg
     var_arg_idx = -1
     if desc.var_arg_type is not None:
         var_arg_idx = len(desc.arg_types)
         for i in range(len(desc.arg_types), len(args)):
             arg = args[i]
             arg = self.coerce(arg, desc.var_arg_type, line)
             coerced.append(arg)
     target = self.add(
         CallC(desc.c_function_name, coerced, desc.return_type, desc.steals,
               desc.error_kind, line, var_arg_idx))
     if result_type and not is_runtime_subtype(target.type, result_type):
         if is_none_rprimitive(result_type):
             # Special case None return. The actual result may actually be a bool
             # and so we can't just coerce it.
             target = self.none()
         else:
             target = self.coerce(target, result_type, line)
     return target
Ejemplo n.º 4
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)):
                name = stmt.lvalues[0].name
                if name == '__slots__':
                    continue

                if name == '__deletable__':
                    check_deletable_declaration(builder, cls, stmt.line)
                    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_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)

        # If the attribute is initialized to None and type isn't optional,
        # don't initialize it to anything.
        attr_type = cls.attr_type(lvalue.name)
        if isinstance(stmt.rvalue,
                      RefExpr) and stmt.rvalue.fullname == 'builtins.None':
            if (not is_optional_type(attr_type)
                    and not is_object_rprimitive(attr_type)
                    and not is_none_rprimitive(attr_type)):
                continue
        val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line)
        builder.add(SetAttr(self_var, lvalue.name, val, -1))

    builder.add(Return(builder.true()))

    builder.leave_method()
Ejemplo n.º 5
0
    def emit_box(self,
                 src: str,
                 dest: str,
                 typ: RType,
                 declare_dest: bool = False,
                 can_borrow: bool = False) -> None:
        """Emit code for boxing a value of given type.

        Generate a simple assignment if no boxing is needed.

        The source reference count is stolen for the result (no need to decref afterwards).
        """
        # TODO: Always generate a new reference (if a reference type)
        if declare_dest:
            declaration = 'PyObject *'
        else:
            declaration = ''
        if is_int_rprimitive(typ) or is_short_int_rprimitive(typ):
            # Steal the existing reference if it exists.
            self.emit_line('{}{} = CPyTagged_StealAsObject({});'.format(
                declaration, dest, src))
        elif is_bool_rprimitive(typ):
            # N.B: bool is special cased to produce a borrowed value
            # after boxing, so we don't need to increment the refcount
            # when this comes directly from a Box op.
            self.emit_lines('{}{} = {} ? Py_True : Py_False;'.format(
                declaration, dest, src))
            if not can_borrow:
                self.emit_inc_ref(dest, object_rprimitive)
        elif is_none_rprimitive(typ):
            # N.B: None is special cased to produce a borrowed value
            # after boxing, so we don't need to increment the refcount
            # when this comes directly from a Box op.
            self.emit_lines('{}{} = Py_None;'.format(declaration, dest))
            if not can_borrow:
                self.emit_inc_ref(dest, object_rprimitive)
        elif isinstance(typ, RTuple):
            self.declare_tuple_struct(typ)
            self.emit_line('{}{} = PyTuple_New({});'.format(
                declaration, dest, len(typ.types)))
            self.emit_line('if (unlikely({} == NULL))'.format(dest))
            self.emit_line('    CPyError_OutOfMemory();')
            # TODO: Fail if dest is None
            for i in range(0, len(typ.types)):
                if not typ.is_unboxed:
                    self.emit_line('PyTuple_SET_ITEM({}, {}, {}.f{}'.format(
                        dest, i, src, i))
                else:
                    inner_name = self.temp_name()
                    self.emit_box('{}.f{}'.format(src, i),
                                  inner_name,
                                  typ.types[i],
                                  declare_dest=True)
                    self.emit_line('PyTuple_SET_ITEM({}, {}, {});'.format(
                        dest, i, inner_name))
        else:
            assert not typ.is_unboxed
            # Type is boxed -- trivially just assign.
            self.emit_line('{}{} = {};'.format(declaration, dest, src))
Ejemplo n.º 6
0
def find_attr_initializers(
    builder: IRBuilder,
    cdef: ClassDef,
    skip: Optional[Callable[[str, AssignmentStmt], bool]] = None,
) -> Tuple[Set[str], List[AssignmentStmt]]:
    """Find initializers of attributes in a class body.

    If provided, the skip arg should be a callable which will return whether
    to skip generating a default for an attribute.  It will be passed the name of
    the attribute and the corresponding AssignmentStmt.
    """
    cls = builder.mapper.type_to_ir[cdef.info]
    if cls.builtin_base:
        return set(), []

    attrs_with_defaults = set()

    # 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)):
                name = stmt.lvalues[0].name
                if name == '__slots__':
                    continue

                if name == '__deletable__':
                    check_deletable_declaration(builder, cls, stmt.line)
                    continue

                if skip is not None and skip(name, stmt):
                    continue

                attr_type = cls.attr_type(name)

                # If the attribute is initialized to None and type isn't optional,
                # doesn't initialize it to anything (special case for "# type:" comments).
                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

                attrs_with_defaults.add(name)
                default_assignments.append(stmt)

    return attrs_with_defaults, default_assignments
Ejemplo n.º 7
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 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)
Ejemplo n.º 8
0
 def call_c(self,
            desc: CFunctionDescription,
            args: List[Value],
            line: int,
            result_type: Optional[RType] = None) -> Value:
     # handle void function via singleton RVoid instance
     coerced = []
     # coerce fixed number arguments
     for i in range(min(len(args), len(desc.arg_types))):
         formal_type = desc.arg_types[i]
         arg = args[i]
         arg = self.coerce(arg, formal_type, line)
         coerced.append(arg)
     # reorder args if necessary
     if desc.ordering is not None:
         assert desc.var_arg_type is None
         coerced = [coerced[i] for i in desc.ordering]
     # coerce any var_arg
     var_arg_idx = -1
     if desc.var_arg_type is not None:
         var_arg_idx = len(desc.arg_types)
         for i in range(len(desc.arg_types), len(args)):
             arg = args[i]
             arg = self.coerce(arg, desc.var_arg_type, line)
             coerced.append(arg)
     # add extra integer constant if any
     for item in desc.extra_int_constants:
         val, typ = item
         extra_int_constant = self.add(LoadInt(val, line, rtype=typ))
         coerced.append(extra_int_constant)
     target = self.add(
         CallC(desc.c_function_name, coerced, desc.return_type, desc.steals,
               desc.is_borrowed, desc.error_kind, line, var_arg_idx))
     if desc.truncated_type is None:
         result = target
     else:
         truncate = self.add(
             Truncate(target, desc.return_type, desc.truncated_type))
         result = truncate
     if result_type and not is_runtime_subtype(result.type, result_type):
         if is_none_rprimitive(result_type):
             # Special case None return. The actual result may actually be a bool
             # and so we can't just coerce it.
             result = self.none()
         else:
             result = self.coerce(target, result_type, line)
     return result
Ejemplo n.º 9
0
 def call_c(self,
            desc: CFunctionDescription,
            args: List[Value],
            line: int,
            result_type: Optional[RType] = None) -> Value:
     # handle void function via singleton RVoid instance
     coerced = []
     for i, arg in enumerate(args):
         formal_type = desc.arg_types[i]
         arg = self.coerce(arg, formal_type, line)
         coerced.append(arg)
     target = self.add(CallC(desc.c_function_name, coerced, desc.return_type, desc.steals,
                             desc.error_kind, line))
     if result_type and not is_runtime_subtype(target.type, result_type):
         if is_none_rprimitive(result_type):
             # Special case None return. The actual result may actually be a bool
             # and so we can't just coerce it.
             target = self.none()
         else:
             target = self.coerce(target, result_type, line)
     return target
Ejemplo n.º 10
0
    def emit_unbox(self,
                   src: str,
                   dest: str,
                   typ: RType,
                   custom_failure: Optional[str] = None,
                   declare_dest: bool = False,
                   borrow: bool = False,
                   optional: bool = False) -> None:
        """Emit code for unboxing a value of given type (from PyObject *).

        Evaluate C code in 'failure' if the value has an incompatible type.

        Always generate a new reference.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            failure: What happens on error
            declare_dest: If True, also declare the variable 'dest'
            borrow: If True, create a borrowed reference
        """
        # TODO: Verify refcount handling.
        raise_exc = 'CPy_TypeError("{}", {});'.format(self.pretty_name(typ),
                                                      src)
        if custom_failure is not None:
            failure = [raise_exc, custom_failure]
        else:
            failure = [raise_exc, '%s = %s;' % (dest, self.c_error_value(typ))]
        if is_int_rprimitive(typ) or is_short_int_rprimitive(typ):
            if declare_dest:
                self.emit_line('CPyTagged {};'.format(dest))
            self.emit_arg_check(src, dest, typ,
                                '(likely(PyLong_Check({})))'.format(src),
                                optional)
            if borrow:
                self.emit_line(
                    '    {} = CPyTagged_BorrowFromObject({});'.format(
                        dest, src))
            else:
                self.emit_line('    {} = CPyTagged_FromObject({});'.format(
                    dest, src))
            self.emit_line('else {')
            self.emit_lines(*failure)
            self.emit_line('}')
        elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
            # Whether we are borrowing or not makes no difference.
            if declare_dest:
                self.emit_line('char {};'.format(dest))
            self.emit_arg_check(src, dest, typ,
                                '(unlikely(!PyBool_Check({}))) {{'.format(src),
                                optional)
            self.emit_lines(*failure)
            self.emit_line('} else')
            conversion = '{} == Py_True'.format(src)
            self.emit_line('    {} = {};'.format(dest, conversion))
        elif is_none_rprimitive(typ):
            # Whether we are borrowing or not makes no difference.
            if declare_dest:
                self.emit_line('char {};'.format(dest))
            self.emit_arg_check(src, dest, typ,
                                '(unlikely({} != Py_None)) {{'.format(src),
                                optional)
            self.emit_lines(*failure)
            self.emit_line('} else')
            self.emit_line('    {} = 1;'.format(dest))
        elif isinstance(typ, RTuple):
            self.declare_tuple_struct(typ)
            if declare_dest:
                self.emit_line('{} {};'.format(self.ctype(typ), dest))
            # HACK: The error handling for unboxing tuples is busted
            # and instead of fixing it I am just wrapping it in the
            # cast code which I think is right. This is not good.
            if optional:
                self.emit_line('if ({} == NULL) {{'.format(src))
                self.emit_line('{} = {};'.format(dest,
                                                 self.c_error_value(typ)))
                self.emit_line('} else {')

            cast_temp = self.temp_name()
            self.emit_tuple_cast(src,
                                 cast_temp,
                                 typ,
                                 declare_dest=True,
                                 err='',
                                 src_type=None)
            self.emit_line('if (unlikely({} == NULL)) {{'.format(cast_temp))

            # self.emit_arg_check(src, dest, typ,
            #     '(!PyTuple_Check({}) || PyTuple_Size({}) != {}) {{'.format(
            #         src, src, len(typ.types)), optional)
            self.emit_lines(*failure)  # TODO: Decrease refcount?
            self.emit_line('} else {')
            if not typ.types:
                self.emit_line('{}.empty_struct_error_flag = 0;'.format(dest))
            for i, item_type in enumerate(typ.types):
                temp = self.temp_name()
                # emit_tuple_cast above checks the size, so this should not fail
                self.emit_line(
                    'PyObject *{} = PyTuple_GET_ITEM({}, {});'.format(
                        temp, src, i))
                temp2 = self.temp_name()
                # Unbox or check the item.
                if item_type.is_unboxed:
                    self.emit_unbox(temp,
                                    temp2,
                                    item_type,
                                    custom_failure,
                                    declare_dest=True,
                                    borrow=borrow)
                else:
                    if not borrow:
                        self.emit_inc_ref(temp, object_rprimitive)
                    self.emit_cast(temp, temp2, item_type, declare_dest=True)
                self.emit_line('{}.f{} = {};'.format(dest, i, temp2))
            self.emit_line('}')
            if optional:
                self.emit_line('}')

        else:
            assert False, 'Unboxing not implemented: %s' % typ
Ejemplo n.º 11
0
    def emit_cast(self,
                  src: str,
                  dest: str,
                  typ: RType,
                  declare_dest: bool = False,
                  custom_message: Optional[str] = None,
                  optional: bool = False,
                  src_type: Optional[RType] = None,
                  likely: bool = True) -> None:
        """Emit code for casting a value of given type.

        Somewhat strangely, this supports unboxed types but only
        operates on boxed versions.  This is necessary to properly
        handle types such as Optional[int] in compatibility glue.

        Assign NULL (error value) to dest if the value has an incompatible type.

        Always copy/steal the reference in src.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'
            likely: If the cast is likely to succeed (can be False for unions)
        """
        if custom_message is not None:
            err = custom_message
        else:
            err = 'CPy_TypeError("{}", {});'.format(self.pretty_name(typ), src)

        # Special case casting *from* optional
        if src_type and is_optional_type(
                src_type) and not is_object_rprimitive(typ):
            value_type = optional_value_type(src_type)
            assert value_type is not None
            if is_same_type(value_type, typ):
                if declare_dest:
                    self.emit_line('PyObject *{};'.format(dest))
                check = '({} != Py_None)'
                if likely:
                    check = '(likely{})'.format(check)
                self.emit_arg_check(src, dest, typ, check.format(src),
                                    optional)
                self.emit_lines('    {} = {};'.format(dest, src), 'else {',
                                err, '{} = NULL;'.format(dest), '}')
                return

        # TODO: Verify refcount handling.
        if (is_list_rprimitive(typ) or is_dict_rprimitive(typ)
                or is_set_rprimitive(typ) or is_float_rprimitive(typ)
                or is_str_rprimitive(typ) or is_int_rprimitive(typ)
                or is_bool_rprimitive(typ)):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            if is_list_rprimitive(typ):
                prefix = 'PyList'
            elif is_dict_rprimitive(typ):
                prefix = 'PyDict'
            elif is_set_rprimitive(typ):
                prefix = 'PySet'
            elif is_float_rprimitive(typ):
                prefix = 'CPyFloat'
            elif is_str_rprimitive(typ):
                prefix = 'PyUnicode'
            elif is_int_rprimitive(typ):
                prefix = 'PyLong'
            elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
                prefix = 'PyBool'
            else:
                assert False, 'unexpected primitive type'
            check = '({}_Check({}))'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(prefix, src),
                                optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_tuple_rprimitive(typ):
            if declare_dest:
                self.emit_line('{} {};'.format(self.ctype(typ), dest))
            check = '(PyTuple_Check({}))'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif isinstance(typ, RInstance):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            concrete = all_concrete_classes(typ.class_ir)
            # If there are too many concrete subclasses or we can't find any
            # (meaning the code ought to be dead or we aren't doing global opts),
            # fall back to a normal typecheck.
            # Otherwise check all the subclasses.
            if not concrete or len(
                    concrete) > FAST_ISINSTANCE_MAX_SUBCLASSES + 1:
                check = '(PyObject_TypeCheck({}, {}))'.format(
                    src, self.type_struct_name(typ.class_ir))
            else:
                full_str = '(Py_TYPE({src}) == {targets[0]})'
                for i in range(1, len(concrete)):
                    full_str += ' || (Py_TYPE({src}) == {targets[%d]})' % i
                if len(concrete) > 1:
                    full_str = '(%s)' % full_str
                check = full_str.format(
                    src=src,
                    targets=[self.type_struct_name(ir) for ir in concrete])
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check, optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_none_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            check = '({} == Py_None)'
            if likely:
                check = '(likely{})'.format(check)
            self.emit_arg_check(src, dest, typ, check.format(src), optional)
            self.emit_lines('    {} = {};'.format(dest, src), 'else {', err,
                            '{} = NULL;'.format(dest), '}')
        elif is_object_rprimitive(typ):
            if declare_dest:
                self.emit_line('PyObject *{};'.format(dest))
            self.emit_arg_check(src, dest, typ, '', optional)
            self.emit_line('{} = {};'.format(dest, src))
            if optional:
                self.emit_line('}')
        elif isinstance(typ, RUnion):
            self.emit_union_cast(src, dest, typ, declare_dest, err, optional,
                                 src_type)
        elif isinstance(typ, RTuple):
            assert not optional
            self.emit_tuple_cast(src, dest, typ, declare_dest, err, src_type)
        else:
            assert False, 'Cast not implemented: %s' % typ
Ejemplo n.º 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)
Ejemplo n.º 13
0
 def maybe_add_implicit_return(self) -> None:
     if is_none_rprimitive(self.ret_types[-1]) or is_object_rprimitive(
             self.ret_types[-1]):
         self.add_implicit_return()
     else:
         self.add_implicit_unreachable()
Ejemplo n.º 14
0
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None:
    """Generate an initialization method for default attr values (from class vars)."""
    cls = builder.mapper.type_to_ir[cdef.info]
    if cls.builtin_base:
        return

    # Pull out all assignments in classes in the mro so we can initialize them
    # TODO: Support nested statements
    default_assignments = []
    for info in reversed(cdef.info.mro):
        if info not in builder.mapper.type_to_ir:
            continue
        for stmt in info.defn.defs.body:
            if (isinstance(stmt, AssignmentStmt)
                    and isinstance(stmt.lvalues[0], NameExpr)
                    and not is_class_var(stmt.lvalues[0])
                    and not isinstance(stmt.rvalue, TempNode)):
                if stmt.lvalues[0].name == '__slots__':
                    continue

                # Skip type annotated assignments in dataclasses
                if is_dataclass(cdef) and stmt.type:
                    continue

                default_assignments.append(stmt)

    if not default_assignments:
        return

    builder.enter()
    builder.ret_types[-1] = bool_rprimitive

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

    for stmt in default_assignments:
        lvalue = stmt.lvalues[0]
        assert isinstance(lvalue, NameExpr)
        if not stmt.is_final_def and not is_constant(stmt.rvalue):
            builder.warning('Unsupported default attribute value',
                            stmt.rvalue.line)

        # If the attribute is initialized to None and type isn't optional,
        # don't initialize it to anything.
        attr_type = cls.attr_type(lvalue.name)
        if isinstance(stmt.rvalue,
                      RefExpr) and stmt.rvalue.fullname == 'builtins.None':
            if (not is_optional_type(attr_type)
                    and not is_object_rprimitive(attr_type)
                    and not is_none_rprimitive(attr_type)):
                continue
        val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line)
        builder.add(SetAttr(self_var, lvalue.name, val, -1))

    builder.add(Return(builder.true()))

    blocks, env, ret_type, _ = builder.leave()
    ir = FuncIR(
        FuncDecl('__mypyc_defaults_setup', cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type)), blocks, env)
    builder.functions.append(ir)
    cls.methods[ir.name] = ir
Ejemplo n.º 15
0
    def emit_unbox(self,
                   src: str,
                   dest: str,
                   typ: RType,
                   *,
                   declare_dest: bool = False,
                   error: Optional[ErrorHandler] = None,
                   raise_exception: bool = True,
                   optional: bool = False,
                   borrow: bool = False) -> None:
        """Emit code for unboxing a value of given type (from PyObject *).

        By default, assign error value to dest if the value has an
        incompatible type and raise TypeError. These can be customized
        using 'error' and 'raise_exception'.

        Generate a new reference unless 'borrow' is True.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'
            error: What happens on error
            raise_exception: If True, also raise TypeError on failure
            borrow: If True, create a borrowed reference

        """
        error = error or AssignHandler()
        # TODO: Verify refcount handling.
        if isinstance(error, AssignHandler):
            failure = f'{dest} = {self.c_error_value(typ)};'
        elif isinstance(error, GotoHandler):
            failure = 'goto %s;' % error.label
        else:
            assert isinstance(error, ReturnHandler)
            failure = 'return %s;' % error.value
        if raise_exception:
            raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); '
            failure = raise_exc + failure
        if is_int_rprimitive(typ) or is_short_int_rprimitive(typ):
            if declare_dest:
                self.emit_line(f'CPyTagged {dest};')
            self.emit_arg_check(src, dest, typ, f'(likely(PyLong_Check({src})))',
                                optional)
            if borrow:
                self.emit_line(f'    {dest} = CPyTagged_BorrowFromObject({src});')
            else:
                self.emit_line(f'    {dest} = CPyTagged_FromObject({src});')
            self.emit_line('else {')
            self.emit_line(failure)
            self.emit_line('}')
        elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
            # Whether we are borrowing or not makes no difference.
            if declare_dest:
                self.emit_line(f'char {dest};')
            self.emit_arg_check(src, dest, typ, f'(unlikely(!PyBool_Check({src}))) {{',
                                optional)
            self.emit_line(failure)
            self.emit_line('} else')
            conversion = f'{src} == Py_True'
            self.emit_line(f'    {dest} = {conversion};')
        elif is_none_rprimitive(typ):
            # Whether we are borrowing or not makes no difference.
            if declare_dest:
                self.emit_line(f'char {dest};')
            self.emit_arg_check(src, dest, typ, f'(unlikely({src} != Py_None)) {{',
                                optional)
            self.emit_line(failure)
            self.emit_line('} else')
            self.emit_line(f'    {dest} = 1;')
        elif isinstance(typ, RTuple):
            self.declare_tuple_struct(typ)
            if declare_dest:
                self.emit_line(f'{self.ctype(typ)} {dest};')
            # HACK: The error handling for unboxing tuples is busted
            # and instead of fixing it I am just wrapping it in the
            # cast code which I think is right. This is not good.
            if optional:
                self.emit_line(f'if ({src} == NULL) {{')
                self.emit_line(f'{dest} = {self.c_error_value(typ)};')
                self.emit_line('} else {')

            cast_temp = self.temp_name()
            self.emit_tuple_cast(src, cast_temp, typ, declare_dest=True, err='', src_type=None)
            self.emit_line(f'if (unlikely({cast_temp} == NULL)) {{')

            # self.emit_arg_check(src, dest, typ,
            #     '(!PyTuple_Check({}) || PyTuple_Size({}) != {}) {{'.format(
            #         src, src, len(typ.types)), optional)
            self.emit_line(failure)  # TODO: Decrease refcount?
            self.emit_line('} else {')
            if not typ.types:
                self.emit_line(f'{dest}.empty_struct_error_flag = 0;')
            for i, item_type in enumerate(typ.types):
                temp = self.temp_name()
                # emit_tuple_cast above checks the size, so this should not fail
                self.emit_line(f'PyObject *{temp} = PyTuple_GET_ITEM({src}, {i});')
                temp2 = self.temp_name()
                # Unbox or check the item.
                if item_type.is_unboxed:
                    self.emit_unbox(temp,
                                    temp2,
                                    item_type,
                                    raise_exception=raise_exception,
                                    error=error,
                                    declare_dest=True,
                                    borrow=borrow)
                else:
                    if not borrow:
                        self.emit_inc_ref(temp, object_rprimitive)
                    self.emit_cast(temp, temp2, item_type, declare_dest=True)
                self.emit_line(f'{dest}.f{i} = {temp2};')
            self.emit_line('}')
            if optional:
                self.emit_line('}')

        else:
            assert False, 'Unboxing not implemented: %s' % typ
Ejemplo n.º 16
0
    def emit_cast(self,
                  src: str,
                  dest: str,
                  typ: RType,
                  *,
                  declare_dest: bool = False,
                  error: Optional[ErrorHandler] = None,
                  raise_exception: bool = True,
                  optional: bool = False,
                  src_type: Optional[RType] = None,
                  likely: bool = True) -> None:
        """Emit code for casting a value of given type.

        Somewhat strangely, this supports unboxed types but only
        operates on boxed versions.  This is necessary to properly
        handle types such as Optional[int] in compatibility glue.

        By default, assign NULL (error value) to dest if the value has
        an incompatible type and raise TypeError. These can be customized
        using 'error' and 'raise_exception'.

        Always copy/steal the reference in 'src'.

        Args:
            src: Name of source C variable
            dest: Name of target C variable
            typ: Type of value
            declare_dest: If True, also declare the variable 'dest'
            error: What happens on error
            raise_exception: If True, also raise TypeError on failure
            likely: If the cast is likely to succeed (can be False for unions)
        """
        error = error or AssignHandler()
        if isinstance(error, AssignHandler):
            handle_error = '%s = NULL;' % dest
        elif isinstance(error, GotoHandler):
            handle_error = 'goto %s;' % error.label
        else:
            assert isinstance(error, ReturnHandler)
            handle_error = 'return %s;' % error.value
        if raise_exception:
            raise_exc = f'CPy_TypeError("{self.pretty_name(typ)}", {src}); '
            err = raise_exc + handle_error
        else:
            err = handle_error

        # Special case casting *from* optional
        if src_type and is_optional_type(src_type) and not is_object_rprimitive(typ):
            value_type = optional_value_type(src_type)
            assert value_type is not None
            if is_same_type(value_type, typ):
                if declare_dest:
                    self.emit_line(f'PyObject *{dest};')
                check = '({} != Py_None)'
                if likely:
                    check = f'(likely{check})'
                self.emit_arg_check(src, dest, typ, check.format(src), optional)
                self.emit_lines(
                    f'    {dest} = {src};',
                    'else {',
                    err,
                    '}')
                return

        # TODO: Verify refcount handling.
        if (is_list_rprimitive(typ) or is_dict_rprimitive(typ) or is_set_rprimitive(typ)
                or is_str_rprimitive(typ) or is_range_rprimitive(typ) or is_float_rprimitive(typ)
                or is_int_rprimitive(typ) or is_bool_rprimitive(typ) or is_bit_rprimitive(typ)):
            if declare_dest:
                self.emit_line(f'PyObject *{dest};')
            if is_list_rprimitive(typ):
                prefix = 'PyList'
            elif is_dict_rprimitive(typ):
                prefix = 'PyDict'
            elif is_set_rprimitive(typ):
                prefix = 'PySet'
            elif is_str_rprimitive(typ):
                prefix = 'PyUnicode'
            elif is_range_rprimitive(typ):
                prefix = 'PyRange'
            elif is_float_rprimitive(typ):
                prefix = 'CPyFloat'
            elif is_int_rprimitive(typ):
                prefix = 'PyLong'
            elif is_bool_rprimitive(typ) or is_bit_rprimitive(typ):
                prefix = 'PyBool'
            else:
                assert False, 'unexpected primitive type'
            check = '({}_Check({}))'
            if likely:
                check = f'(likely{check})'
            self.emit_arg_check(src, dest, typ, check.format(prefix, src), optional)
            self.emit_lines(
                f'    {dest} = {src};',
                'else {',
                err,
                '}')
        elif is_bytes_rprimitive(typ):
            if declare_dest:
                self.emit_line(f'PyObject *{dest};')
            check = '(PyBytes_Check({}) || PyByteArray_Check({}))'
            if likely:
                check = f'(likely{check})'
            self.emit_arg_check(src, dest, typ, check.format(src, src), optional)
            self.emit_lines(
                f'    {dest} = {src};',
                'else {',
                err,
                '}')
        elif is_tuple_rprimitive(typ):
            if declare_dest:
                self.emit_line(f'{self.ctype(typ)} {dest};')
            check = '(PyTuple_Check({}))'
            if likely:
                check = f'(likely{check})'
            self.emit_arg_check(src, dest, typ,
                                check.format(src), optional)
            self.emit_lines(
                f'    {dest} = {src};',
                'else {',
                err,
                '}')
        elif isinstance(typ, RInstance):
            if declare_dest:
                self.emit_line(f'PyObject *{dest};')
            concrete = all_concrete_classes(typ.class_ir)
            # If there are too many concrete subclasses or we can't find any
            # (meaning the code ought to be dead or we aren't doing global opts),
            # fall back to a normal typecheck.
            # Otherwise check all the subclasses.
            if not concrete or len(concrete) > FAST_ISINSTANCE_MAX_SUBCLASSES + 1:
                check = '(PyObject_TypeCheck({}, {}))'.format(
                    src, self.type_struct_name(typ.class_ir))
            else:
                full_str = '(Py_TYPE({src}) == {targets[0]})'
                for i in range(1, len(concrete)):
                    full_str += ' || (Py_TYPE({src}) == {targets[%d]})' % i
                if len(concrete) > 1:
                    full_str = '(%s)' % full_str
                check = full_str.format(
                    src=src, targets=[self.type_struct_name(ir) for ir in concrete])
            if likely:
                check = f'(likely{check})'
            self.emit_arg_check(src, dest, typ, check, optional)
            self.emit_lines(
                f'    {dest} = {src};',
                'else {',
                err,
                '}')
        elif is_none_rprimitive(typ):
            if declare_dest:
                self.emit_line(f'PyObject *{dest};')
            check = '({} == Py_None)'
            if likely:
                check = f'(likely{check})'
            self.emit_arg_check(src, dest, typ,
                                check.format(src), optional)
            self.emit_lines(
                f'    {dest} = {src};',
                'else {',
                err,
                '}')
        elif is_object_rprimitive(typ):
            if declare_dest:
                self.emit_line(f'PyObject *{dest};')
            self.emit_arg_check(src, dest, typ, '', optional)
            self.emit_line(f'{dest} = {src};')
            if optional:
                self.emit_line('}')
        elif isinstance(typ, RUnion):
            self.emit_union_cast(src, dest, typ, declare_dest, err, optional, src_type)
        elif isinstance(typ, RTuple):
            assert not optional
            self.emit_tuple_cast(src, dest, typ, declare_dest, err, src_type)
        else:
            assert False, 'Cast not implemented: %s' % typ