def test_int_op(self) -> None:
     n1 = Integer(2)
     n2 = Integer(4)
     op1 = IntOp(int_rprimitive, n1, n2, IntOp.ADD)
     op2 = IntOp(int_rprimitive, op1, n2, IntOp.ADD)
     block = make_block([op1, op2, Unreachable()])
     assert generate_names_for_ir([], [block]) == {op1: 'r0', op2: 'r1'}
 def init(self, expr_reg: Value, target_type: RType, reverse: bool) -> None:
     builder = self.builder
     self.reverse = reverse
     # Define target to contain the expression, along with the index that will be used
     # for the for-loop. If we are inside of a generator function, spill these into the
     # environment class.
     self.expr_target = builder.maybe_spill(expr_reg)
     if not reverse:
         index_reg = Integer(0)  # type: Value
     else:
         index_reg = builder.binary_op(self.load_len(self.expr_target),
                                       Integer(1), '-', self.line)
     self.index_target = builder.maybe_spill_assignable(index_reg)
     self.target_type = target_type
Ejemplo n.º 3
0
    def gen_return(self, builder: 'IRBuilder', value: Value,
                   line: int) -> None:
        # Assign an invalid next label number so that the next time
        # __next__ is called, we jump to the case in which
        # StopIteration is raised.
        builder.assign(builder.fn_info.generator_class.next_label_target,
                       Integer(-1), line)

        # Raise a StopIteration containing a field for the value that
        # should be returned. Before doing so, create a new block
        # without an error handler set so that the implicitly thrown
        # StopIteration isn't caught by except blocks inside of the
        # generator function.
        builder.builder.push_error_handler(None)
        builder.goto_and_activate(BasicBlock())

        # Skip creating a traceback frame when we raise here, because
        # we don't care about the traceback frame and it is kind of
        # expensive since raising StopIteration is an extremely common
        # case.  Also we call a special internal function to set
        # StopIteration instead of using RaiseStandardError because
        # the obvious thing doesn't work if the value is a tuple
        # (???).
        builder.call_c(set_stop_iteration_value, [value], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())
        builder.builder.pop_error_handler()
 def test_assign(self) -> None:
     reg = register('foo')
     n = Integer(2)
     op1 = Assign(reg, n)
     op2 = Assign(reg, n)
     block = make_block([op1, op2])
     assert generate_names_for_ir([reg], [block]) == {reg: 'foo'}
def split_blocks_at_errors(blocks: List[BasicBlock],
                           default_error_handler: BasicBlock,
                           func_name: Optional[str]) -> List[BasicBlock]:
    new_blocks = []  # type: List[BasicBlock]

    # First split blocks on ops that may raise.
    for block in blocks:
        ops = block.ops
        block.ops = []
        cur_block = block
        new_blocks.append(cur_block)

        # If the block has an error handler specified, use it. Otherwise
        # fall back to the default.
        error_label = block.error_handler or default_error_handler
        block.error_handler = None

        for op in ops:
            target = op  # type: Value
            cur_block.ops.append(op)
            if isinstance(op, RegisterOp) and op.error_kind != ERR_NEVER:
                # Split
                new_block = BasicBlock()
                new_blocks.append(new_block)

                if op.error_kind == ERR_MAGIC:
                    # Op returns an error value on error that depends on result RType.
                    variant = Branch.IS_ERROR
                    negated = False
                elif op.error_kind == ERR_FALSE:
                    # Op returns a C false value on error.
                    variant = Branch.BOOL
                    negated = True
                elif op.error_kind == ERR_ALWAYS:
                    variant = Branch.BOOL
                    negated = True
                    # this is a hack to represent the always fail
                    # semantics, using a temporary bool with value false
                    target = Integer(0, bool_rprimitive)
                else:
                    assert False, 'unknown error kind %d' % op.error_kind

                # Void ops can't generate errors since error is always
                # indicated by a special value stored in a register.
                if op.error_kind != ERR_ALWAYS:
                    assert not op.is_void, "void op generating errors?"

                branch = Branch(target,
                                true_label=error_label,
                                false_label=new_block,
                                op=variant,
                                line=op.line)
                branch.negated = negated
                if op.line != NO_TRACEBACK_LINE_NO and func_name is not None:
                    branch.traceback_entry = (func_name, op.line)
                cur_block.ops.append(branch)
                cur_block = new_block

    return new_blocks
    def gen_step(self) -> None:
        builder = self.builder
        line = self.line

        # Increment index register. If the range is known to fit in short ints, use
        # short ints.
        if (is_short_int_rprimitive(self.start_reg.type)
                and is_short_int_rprimitive(self.end_reg.type)):
            new_val = builder.int_op(short_int_rprimitive,
                                     builder.read(self.index_reg, line),
                                     Integer(self.step), IntOp.ADD, line)

        else:
            new_val = builder.binary_op(
                builder.read(self.index_reg, line), Integer(self.step), '+', line)
        builder.assign(self.index_reg, new_val, line)
        builder.assign(self.index_target, new_val, line)
 def gen_step(self) -> None:
     # Step to the next item.
     builder = self.builder
     line = self.line
     step = 1 if not self.reverse else -1
     add = builder.int_op(short_int_rprimitive,
                          builder.read(self.index_target, line),
                          Integer(step), IntOp.ADD, line)
     builder.assign(self.index_target, add, line)
 def init(self) -> None:
     builder = self.builder
     # Create a register to store the state of the loop index and
     # initialize this register along with the loop index to 0.
     zero = Integer(0)
     self.index_reg = builder.maybe_spill_assignable(zero)
     self.index_target = builder.get_assignment_target(
         self.index)  # type: Union[Register, AssignmentTarget]
     builder.assign(self.index_target, zero, self.line)
 def gen_step(self) -> None:
     builder = self.builder
     line = self.line
     # We can safely assume that the integer is short, since we are not going to wrap
     # around a 63-bit integer.
     # NOTE: This would be questionable if short ints could be 32 bits.
     new_val = builder.int_op(short_int_rprimitive,
                              builder.read(self.index_reg, line),
                              Integer(1), IntOp.ADD, line)
     builder.assign(self.index_reg, new_val, line)
     builder.assign(self.index_target, new_val, line)
Ejemplo n.º 10
0
 def test_invalid_return_type(self) -> None:
     ret = Return(value=Integer(value=5, rtype=int32_rprimitive))
     fn = FuncIR(
         decl=self.func_decl(name="func_1", ret_type=int64_rprimitive),
         arg_regs=[],
         blocks=[self.basic_block([ret])],
     )
     assert_has_error(
         fn,
         FnError(source=ret,
                 desc="Cannot coerce source type int32 to dest type int64"),
     )
Ejemplo n.º 11
0
def join_formatted_bytes(builder: IRBuilder, literals: List[str],
                         substitutions: List[Value], line: int) -> Value:
    """Merge the list of literals and the list of substitutions
    alternatively using 'bytes_build_op'."""
    result_list: List[Value] = [Integer(0, c_pyssize_t_rprimitive)]

    for a, b in zip(literals, substitutions):
        if a:
            result_list.append(builder.load_bytes_from_str_literal(a))
        result_list.append(b)
    if literals[-1]:
        result_list.append(builder.load_bytes_from_str_literal(literals[-1]))

    # Special case for empty bytes and literal
    if len(result_list) == 1:
        return builder.load_bytes_from_str_literal('')
    if not substitutions and len(result_list) == 2:
        return result_list[1]

    result_list[0] = Integer(len(result_list) - 1, c_pyssize_t_rprimitive)
    return builder.call_c(bytes_build_op, result_list, line)
Ejemplo n.º 12
0
 def test_duplicate_op(self) -> None:
     arg_reg = Register(type=int32_rprimitive, name="r1")
     assign = Assign(dest=arg_reg,
                     src=Integer(value=5, rtype=int32_rprimitive))
     block = self.basic_block([assign, assign, Return(value=NONE_VALUE)])
     fn = FuncIR(
         decl=self.func_decl(name="func_1"),
         arg_regs=[],
         blocks=[block],
     )
     assert_has_error(
         fn, FnError(source=assign, desc="Func has a duplicate op"))
Ejemplo n.º 13
0
def join_formatted_strings(builder: IRBuilder, literals: Optional[List[str]],
                           substitutions: List[Value], line: int) -> Value:
    """Merge the list of literals and the list of substitutions
    alternatively using 'str_build_op'.

    `substitutions` is the result value of formatting conversions.

    If the `literals` is set to None, we simply join the substitutions;
    Otherwise, the `literals` is the literal substrings of the original
    format string and its length should be exactly one more than
    substitutions.

    For example:
    (1)    'This is a %s and the value is %d'
        -> literals: ['This is a ', ' and the value is', '']
    (2)    '{} and the value is {}'
        -> literals: ['', ' and the value is', '']
    """
    # The first parameter for str_build_op is the total size of
    # the following PyObject*
    result_list: List[Value] = [Integer(0, c_pyssize_t_rprimitive)]

    if literals is not None:
        for a, b in zip(literals, substitutions):
            if a:
                result_list.append(builder.load_str(a))
            result_list.append(b)
        if literals[-1]:
            result_list.append(builder.load_str(literals[-1]))
    else:
        result_list.extend(substitutions)

    # Special case for empty string and literal string
    if len(result_list) == 1:
        return builder.load_str("")
    if not substitutions and len(result_list) == 2:
        return result_list[1]

    result_list[0] = Integer(len(result_list) - 1, c_pyssize_t_rprimitive)
    return builder.call_c(str_build_op, result_list, line)
Ejemplo n.º 14
0
def translate_len(builder: IRBuilder, expr: CallExpr,
                  callee: RefExpr) -> Optional[Value]:
    if (len(expr.args) == 1 and expr.arg_kinds == [ARG_POS]):
        expr_rtype = builder.node_type(expr.args[0])
        if isinstance(expr_rtype, RTuple):
            # len() of fixed-length tuple can be trivially determined
            # statically, though we still need to evaluate it.
            builder.accept(expr.args[0])
            return Integer(len(expr_rtype.types))
        else:
            obj = builder.accept(expr.args[0])
            return builder.builtin_len(obj, expr.line)
    return None
Ejemplo n.º 15
0
def translate_dict_setdefault(
        builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]:
    """Special case for 'dict.setdefault' which would only construct
    default empty collection when needed.

    The dict_setdefault_spec_init_op checks whether the dict contains
    the key and would construct the empty collection only once.

    For example, this specializer works for the following cases:
         d.setdefault(key, set()).add(value)
         d.setdefault(key, []).append(value)
         d.setdefault(key, {})[inner_key] = inner_val
    """
    if (len(expr.args) == 2
            and expr.arg_kinds == [ARG_POS, ARG_POS]
            and isinstance(callee, MemberExpr)):
        arg = expr.args[1]
        if isinstance(arg, ListExpr):
            if len(arg.items):
                return None
            data_type = Integer(1, c_int_rprimitive, expr.line)
        elif isinstance(arg, DictExpr):
            if len(arg.items):
                return None
            data_type = Integer(2, c_int_rprimitive, expr.line)
        elif (isinstance(arg, CallExpr) and isinstance(arg.callee, NameExpr)
              and arg.callee.fullname == 'builtins.set'):
            if len(arg.args):
                return None
            data_type = Integer(3, c_int_rprimitive, expr.line)
        else:
            return None

        callee_dict = builder.accept(callee.expr)
        key_val = builder.accept(expr.args[0])
        return builder.call_c(dict_setdefault_spec_init_op,
                              [callee_dict, key_val, data_type],
                              expr.line)
    return None
    def init(self, expr_reg: Value, target_type: RType) -> None:
        builder = self.builder
        self.target_type = target_type

        # We add some variables to environment class, so they can be read across yield.
        self.expr_target = builder.maybe_spill(expr_reg)
        offset = Integer(0)
        self.offset_target = builder.maybe_spill_assignable(offset)
        self.size = builder.maybe_spill(self.load_len(self.expr_target))

        # For dict class (not a subclass) this is the dictionary itself.
        iter_reg = builder.call_c(self.dict_iter_op, [expr_reg], self.line)
        self.iter_target = builder.maybe_spill(iter_reg)
Ejemplo n.º 17
0
def generate_singledispatch_callable_class_ctor(builder: IRBuilder) -> None:
    """Create an __init__ that sets registry and dispatch_cache to empty dicts"""
    line = -1
    class_ir = builder.fn_info.callable_class.ir
    with builder.enter_method(class_ir, '__init__', bool_rprimitive):
        empty_dict = builder.call_c(dict_new_op, [], line)
        builder.add(SetAttr(builder.self(), 'registry', empty_dict, line))
        cache_dict = builder.call_c(dict_new_op, [], line)
        dispatch_cache_str = builder.load_str('dispatch_cache')
        # use the py_setattr_op instead of SetAttr so that it also gets added to our __dict__
        builder.call_c(py_setattr_op,
                       [builder.self(), dispatch_cache_str, cache_dict], line)
        # the generated C code seems to expect that __init__ returns a char, so just return 1
        builder.add(Return(Integer(1, bool_rprimitive, line), line))
Ejemplo n.º 18
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, Integer(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())
Ejemplo n.º 19
0
 def test_invalid_assign(self) -> None:
     arg_reg = Register(type=int64_rprimitive, name="r1")
     assign = Assign(dest=arg_reg,
                     src=Integer(value=5, rtype=int32_rprimitive))
     ret = Return(value=NONE_VALUE)
     fn = FuncIR(
         decl=self.func_decl(name="func_1"),
         arg_regs=[arg_reg],
         blocks=[self.basic_block([assign, ret])],
     )
     assert_has_error(
         fn,
         FnError(source=assign,
                 desc="Cannot coerce source type int32 to dest type int64"),
     )
Ejemplo n.º 20
0
def emit_yield(builder: IRBuilder, val: Value, line: int) -> Value:
    retval = builder.coerce(val, builder.ret_types[-1], line)

    cls = builder.fn_info.generator_class
    # Create a new block for the instructions immediately following the yield expression, and
    # set the next label so that the next time '__next__' is called on the generator object,
    # the function continues at the new block.
    next_block = BasicBlock()
    next_label = len(cls.continuation_blocks)
    cls.continuation_blocks.append(next_block)
    builder.assign(cls.next_label_target, Integer(next_label), line)
    builder.add(Return(retval))
    builder.activate_block(next_block)

    add_raise_exception_blocks_to_generator_class(builder, line)

    assert cls.send_arg_reg is not None
    return cls.send_arg_reg
 def gen_condition(self) -> None:
     builder = self.builder
     line = self.line
     # TODO: Don't reload the length each time when iterating an immutable sequence?
     if self.reverse:
         # If we are iterating in reverse order, we obviously need
         # to check that the index is still positive. Somewhat less
         # obviously we still need to check against the length,
         # since it could shrink out from under us.
         comparison = builder.binary_op(builder.read(self.index_target, line),
                                        Integer(0), '>=', line)
         second_check = BasicBlock()
         builder.add_bool_branch(comparison, second_check, self.loop_exit)
         builder.activate_block(second_check)
     # For compatibility with python semantics we recalculate the length
     # at every iteration.
     len_reg = self.load_len(self.expr_target)
     comparison = builder.binary_op(builder.read(self.index_target, line), len_reg, '<', line)
     builder.add_bool_branch(comparison, self.body_block, self.loop_exit)
Ejemplo n.º 22
0
def instantiate_generator_class(builder: IRBuilder) -> Value:
    fitem = builder.fn_info.fitem
    generator_reg = builder.add(Call(builder.fn_info.generator_class.ir.ctor, [], fitem.line))

    # Get the current environment register. If the current function is nested, then the
    # generator class gets instantiated from the callable class' '__call__' method, and hence
    # we use the callable class' environment register. Otherwise, we use the original
    # function's environment register.
    if builder.fn_info.is_nested:
        curr_env_reg = builder.fn_info.callable_class.curr_env_reg
    else:
        curr_env_reg = builder.fn_info.curr_env_reg

    # Set the generator class' environment attribute to point at the environment class
    # defined in the current scope.
    builder.add(SetAttr(generator_reg, ENV_ATTR_NAME, curr_env_reg, fitem.line))

    # Set the generator class' environment class' NEXT_LABEL_ATTR_NAME attribute to 0.
    zero = Integer(0)
    builder.add(SetAttr(curr_env_reg, NEXT_LABEL_ATTR_NAME, zero, fitem.line))
    return generator_reg
Ejemplo n.º 23
0
 def test_register(self) -> None:
     reg = Register(int_rprimitive)
     op = Assign(reg, Integer(5))
     self.block.ops.append(op)
     fn = FuncIR(
         FuncDecl('myfunc', None, 'mod',
                  FuncSignature([self.arg], list_rprimitive)), [self.reg],
         [self.block])
     value_names = generate_names_for_ir(fn.arg_regs, fn.blocks)
     emitter = Emitter(EmitterContext(NameGenerator([['mod']])),
                       value_names)
     generate_native_function(fn, emitter, 'prog.py', 'prog')
     result = emitter.fragments
     assert_string_arrays_equal([
         'PyObject *CPyDef_myfunc(CPyTagged cpy_r_arg) {\n',
         '    CPyTagged cpy_r_r0;\n',
         'CPyL0: ;\n',
         '    cpy_r_r0 = 10;\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
Ejemplo n.º 24
0
    def process_sequence_assignment(self,
                                    target: AssignmentTargetTuple,
                                    rvalue: Value,
                                    line: int) -> None:
        """Process assignment like 'x, y = s', where s is a variable-length list or tuple."""
        # Check the length of sequence.
        expected_len = Integer(len(target.items), c_pyssize_t_rprimitive)
        self.builder.call_c(check_unpack_count_op, [rvalue, expected_len], line)

        # Read sequence items.
        values = []
        for i in range(len(target.items)):
            item = target.items[i]
            index = self.builder.load_int(i)
            if is_list_rprimitive(rvalue.type):
                item_value = self.call_c(list_get_item_unsafe_op, [rvalue, index], line)
            else:
                item_value = self.builder.gen_method_call(
                    rvalue, '__getitem__', [index], item.type, line)
            values.append(item_value)

        # Assign sequence items to the target lvalues.
        for lvalue, value in zip(target.items, values):
            self.assign(lvalue, value, line)
Ejemplo n.º 25
0
    def process_iterator_tuple_assignment(self,
                                          target: AssignmentTargetTuple,
                                          rvalue_reg: Value,
                                          line: int) -> None:

        iterator = self.call_c(iter_op, [rvalue_reg], line)

        # This may be the whole lvalue list if there is no starred value
        split_idx = target.star_idx if target.star_idx is not None else len(target.items)

        # Assign values before the first starred value
        for litem in target.items[:split_idx]:
            ritem = self.call_c(next_op, [iterator], line)
            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(ritem, error_block, ok_block, Branch.IS_ERROR))

            self.activate_block(error_block)
            self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                        'not enough values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)

            self.assign(litem, ritem, line)

        # Assign the starred value and all values after it
        if target.star_idx is not None:
            post_star_vals = target.items[split_idx + 1:]
            iter_list = self.call_c(to_list, [iterator], line)
            iter_list_len = self.builtin_len(iter_list, line)
            post_star_len = Integer(len(post_star_vals))
            condition = self.binary_op(post_star_len, iter_list_len, '<=', line)

            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(condition, ok_block, error_block, Branch.BOOL))

            self.activate_block(error_block)
            self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                        'not enough values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)

            for litem in reversed(post_star_vals):
                ritem = self.call_c(list_pop_last, [iter_list], line)
                self.assign(litem, ritem, line)

            # Assign the starred value
            self.assign(target.items[target.star_idx], iter_list, line)

        # There is no starred value, so check if there are extra values in rhs that
        # have not been assigned.
        else:
            extra = self.call_c(next_op, [iterator], line)
            error_block, ok_block = BasicBlock(), BasicBlock()
            self.add(Branch(extra, ok_block, error_block, Branch.IS_ERROR))

            self.activate_block(error_block)
            self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                        'too many values to unpack', line))
            self.add(Unreachable())

            self.activate_block(ok_block)
Ejemplo n.º 26
0
 def test_long_unsigned(self) -> None:
     a = Register(int64_rprimitive, 'a')
     self.assert_emit(Assign(a, Integer(1 << 31, int64_rprimitive)),
                      """cpy_r_a = 2147483648U;""")
     self.assert_emit(Assign(a, Integer((1 << 31) - 1, int64_rprimitive)),
                      """cpy_r_a = 2147483647;""")
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 = 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
Ejemplo n.º 28
0
from mypyc.ir.ops import (BasicBlock, Op, Return, Integer, Goto, Register,
                          LoadLiteral, Assign)
from mypyc.ir.func_ir import FuncIR, FuncDecl, FuncSignature
from mypyc.ir.pprint import format_func


def assert_has_error(fn: FuncIR, error: FnError) -> None:
    errors = check_func_ir(fn)
    assert errors == [error]


def assert_no_errors(fn: FuncIR) -> None:
    assert not check_func_ir(fn)


NONE_VALUE = Integer(0, rtype=none_rprimitive)


class TestIrcheck(unittest.TestCase):
    def setUp(self) -> None:
        self.label = 0

    def basic_block(self, ops: List[Op]) -> BasicBlock:
        self.label += 1
        block = BasicBlock(self.label)
        block.ops = ops
        return block

    def func_decl(self,
                  name: str,
                  ret_type: Optional[RType] = None) -> FuncDecl:
Ejemplo n.º 29
0
 def test_integer(self) -> None:
     self.assert_emit(Assign(self.n, Integer(5)), "cpy_r_n = 10;")
     self.assert_emit(Assign(self.i32, Integer(5, c_int_rprimitive)),
                      "cpy_r_i32 = 5;")
Ejemplo n.º 30
0
 def test_long_signed(self) -> None:
     a = Register(int64_rprimitive, 'a')
     self.assert_emit(Assign(a, Integer(-(1 << 31) + 1, int64_rprimitive)),
                      """cpy_r_a = -2147483647;""")
     self.assert_emit(Assign(a, Integer(-(1 << 31), int64_rprimitive)),
                      """cpy_r_a = -2147483648LL;""")