Ejemplo n.º 1
0
def transform_raise_stmt(builder: IRBuilder, s: RaiseStmt) -> None:
    if s.expr is None:
        builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())
        return

    exc = builder.accept(s.expr)
    builder.call_c(raise_exception_op, [exc], s.line)
    builder.add(Unreachable())
Ejemplo n.º 2
0
def add_close_to_generator_class(builder: IRBuilder,
                                 fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    with builder.enter_method(fn_info.generator_class.ir, 'close',
                              object_rprimitive, fn_info):
        except_block, else_block = BasicBlock(), BasicBlock()
        builder.builder.push_error_handler(except_block)
        builder.goto_and_activate(BasicBlock())
        generator_exit = builder.load_module_attr_by_fullname(
            'builtins.GeneratorExit', fn_info.fitem.line)
        builder.add(
            MethodCall(
                builder.self(), 'throw',
                [generator_exit,
                 builder.none_object(),
                 builder.none_object()]))
        builder.goto(else_block)
        builder.builder.pop_error_handler()

        builder.activate_block(except_block)
        old_exc = builder.call_c(error_catch_op, [], fn_info.fitem.line)
        builder.nonlocal_control.append(
            ExceptNonlocalControl(builder.nonlocal_control[-1], old_exc))
        stop_iteration = builder.load_module_attr_by_fullname(
            'builtins.StopIteration', fn_info.fitem.line)
        exceptions = builder.add(
            TupleSet([generator_exit, stop_iteration], fn_info.fitem.line))
        matches = builder.call_c(exc_matches_op, [exceptions],
                                 fn_info.fitem.line)

        match_block, non_match_block = BasicBlock(), BasicBlock()
        builder.add(Branch(matches, match_block, non_match_block, Branch.BOOL))

        builder.activate_block(match_block)
        builder.call_c(restore_exc_info_op, [builder.read(old_exc)],
                       fn_info.fitem.line)
        builder.add(Return(builder.none_object()))

        builder.activate_block(non_match_block)
        builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
        builder.add(Unreachable())

        builder.nonlocal_control.pop()

        builder.activate_block(else_block)
        builder.add(
            RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                               'generator ignored GeneratorExit',
                               fn_info.fitem.line))
        builder.add(Unreachable())
 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'}
Ejemplo n.º 4
0
def transform_assert_stmt(builder: IRBuilder, a: AssertStmt) -> None:
    if builder.options.strip_asserts:
        return
    cond = builder.accept(a.expr)
    ok_block, error_block = BasicBlock(), BasicBlock()
    builder.add_bool_branch(cond, ok_block, error_block)
    builder.activate_block(error_block)
    if a.msg is None:
        # Special case (for simpler generated code)
        builder.add(
            RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, None,
                               a.line))
    elif isinstance(a.msg, StrExpr):
        # Another special case
        builder.add(
            RaiseStandardError(RaiseStandardError.ASSERTION_ERROR, a.msg.value,
                               a.line))
    else:
        # The general case -- explicitly construct an exception instance
        message = builder.accept(a.msg)
        exc_type = builder.load_module_attr_by_fullname(
            'builtins.AssertionError', a.line)
        exc = builder.py_call(exc_type, [message], a.line)
        builder.call_c(raise_exception_op, [exc], a.line)
    builder.add(Unreachable())
    builder.activate_block(ok_block)
Ejemplo n.º 5
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()
Ejemplo n.º 6
0
def split_blocks_at_uninits(blocks: List[BasicBlock],
                            pre_must_defined: 'AnalysisDict[Value]') -> List[BasicBlock]:
    new_blocks: List[BasicBlock] = []

    init_registers = []
    init_registers_set = set()

    # 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)

        for i, op in enumerate(ops):
            defined = pre_must_defined[block, i]
            for src in op.unique_sources():
                # If a register operand is not guaranteed to be
                # initialized is an operand to something other than a
                # check that it is defined, insert a check.

                # Note that for register operand in a LoadAddress op,
                # we should be able to use it without initialization
                # as we may need to use its address to update itself
                if (isinstance(src, Register) and src not in defined
                        and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
                        and not isinstance(op, LoadAddress)):
                    new_block, error_block = BasicBlock(), BasicBlock()
                    new_block.error_handler = error_block.error_handler = cur_block.error_handler
                    new_blocks += [error_block, new_block]

                    if src not in init_registers_set:
                        init_registers.append(src)
                        init_registers_set.add(src)

                    cur_block.ops.append(Branch(src,
                                                true_label=error_block,
                                                false_label=new_block,
                                                op=Branch.IS_ERROR,
                                                line=op.line))
                    raise_std = RaiseStandardError(
                        RaiseStandardError.UNBOUND_LOCAL_ERROR,
                        'local variable "{}" referenced before assignment'.format(src.name),
                        op.line)
                    error_block.ops.append(raise_std)
                    error_block.ops.append(Unreachable())
                    cur_block = new_block
            cur_block.ops.append(op)

    if init_registers:
        new_ops: List[Op] = []
        for reg in init_registers:
            err = LoadErrorValue(reg.type, undefines=True)
            new_ops.append(err)
            new_ops.append(Assign(reg, err))
        new_blocks[0].ops[0:0] = new_ops

    return new_blocks
Ejemplo n.º 7
0
def add_close_to_generator_class(builder: IRBuilder, fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    # TODO: Currently this method just triggers a runtime error.
    #       We should fill this out (https://github.com/mypyc/mypyc/issues/790).
    with builder.enter_method(fn_info.generator_class.ir, 'close', object_rprimitive, fn_info):
        builder.add(RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                                       'close method on generator classes unimplemented',
                                       fn_info.fitem.line))
        builder.add(Unreachable())
Ejemplo n.º 8
0
def gen_calls_to_correct_impl(
    builder: IRBuilder,
    impl_to_use: Value,
    arg_info: ArgInfo,
    fitem: FuncDef,
    line: int,
) -> None:
    current_func_decl = builder.mapper.func_to_decl[fitem]

    def gen_native_func_call_and_return(fdef: FuncDef) -> None:
        func_decl = builder.mapper.func_to_decl[fdef]
        ret_val = builder.builder.call(func_decl, arg_info.args,
                                       arg_info.arg_kinds, arg_info.arg_names,
                                       line)
        coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line)
        builder.add(Return(coerced))

    typ, src = builtin_names['builtins.int']
    int_type_obj = builder.add(LoadAddress(typ, src, line))
    is_int = builder.builder.type_is_op(impl_to_use, int_type_obj, line)

    native_call, non_native_call = BasicBlock(), BasicBlock()
    builder.add_bool_branch(is_int, native_call, non_native_call)
    builder.activate_block(native_call)

    passed_id = builder.add(Unbox(impl_to_use, int_rprimitive, line))

    native_ids = get_native_impl_ids(builder, fitem)
    for impl, i in native_ids.items():
        call_impl, next_impl = BasicBlock(), BasicBlock()

        current_id = builder.load_int(i)
        builder.builder.compare_tagged_condition(
            passed_id,
            current_id,
            '==',
            call_impl,
            next_impl,
            line,
        )

        # Call the registered implementation
        builder.activate_block(call_impl)

        gen_native_func_call_and_return(impl)
        builder.activate_block(next_impl)

    # We've already handled all the possible integer IDs, so we should never get here
    builder.add(Unreachable())

    builder.activate_block(non_native_call)
    ret_val = builder.py_call(impl_to_use, arg_info.args, line,
                              arg_info.arg_kinds, arg_info.arg_names)
    coerced = builder.coerce(ret_val, current_func_decl.sig.ret_type, line)
    builder.add(Return(coerced))
Ejemplo n.º 9
0
 def except_body() -> None:
     builder.assign(exc, builder.false(), line)
     out_block, reraise_block = BasicBlock(), BasicBlock()
     builder.add_bool_branch(
         builder.py_call(builder.read(exit_),
                         [builder.read(mgr)] + get_sys_exc_info(builder),
                         line), out_block, reraise_block)
     builder.activate_block(reraise_block)
     builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
     builder.add(Unreachable())
     builder.activate_block(out_block)
Ejemplo n.º 10
0
def transform_block(builder: IRBuilder, block: Block) -> None:
    if not block.is_unreachable:
        for stmt in block.body:
            builder.accept(stmt)
    # Raise a RuntimeError if we hit a non-empty unreachable block.
    # Don't complain about empty unreachable blocks, since mypy inserts
    # those after `if MYPY`.
    elif block.body:
        builder.add(RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                                       'Reached allegedly unreachable code!',
                                       block.line))
        builder.add(Unreachable())
Ejemplo n.º 11
0
def try_finally_resolve_control(builder: IRBuilder,
                                cleanup_block: BasicBlock,
                                finally_control: FinallyNonlocalControl,
                                old_exc: Value,
                                ret_reg: Optional[Value]) -> BasicBlock:
    """Resolve the control flow out of a finally block.

    This means returning if there was a return, propagating
    exceptions, break/continue (soon), or just continuing on.
    """
    reraise, rest = BasicBlock(), BasicBlock()
    builder.add(Branch(old_exc, rest, reraise, Branch.IS_ERROR))

    # Reraise the exception if there was one
    builder.activate_block(reraise)
    builder.call_c(reraise_exception_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())
    builder.builder.pop_error_handler()

    # If there was a return, keep returning
    if ret_reg:
        builder.activate_block(rest)
        return_block, rest = BasicBlock(), BasicBlock()
        builder.add(Branch(ret_reg, rest, return_block, Branch.IS_ERROR))

        builder.activate_block(return_block)
        builder.nonlocal_control[-1].gen_return(builder, ret_reg, -1)

    # TODO: handle break/continue
    builder.activate_block(rest)
    out_block = BasicBlock()
    builder.goto(out_block)

    # If there was an exception, restore again
    builder.activate_block(cleanup_block)
    finally_control.gen_cleanup(builder, -1)
    builder.call_c(keep_propagating_op, [], NO_TRACEBACK_LINE_NO)
    builder.add(Unreachable())

    return out_block
Ejemplo n.º 12
0
    def process_iterator_tuple_assignment_helper(self,
                                                 litem: AssignmentTarget,
                                                 ritem: Value, line: int) -> None:
        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)
Ejemplo n.º 13
0
def populate_switch_for_generator_class(builder: IRBuilder) -> None:
    cls = builder.fn_info.generator_class
    line = builder.fn_info.fitem.line

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

    builder.add(
        RaiseStandardError(RaiseStandardError.STOP_ITERATION, None, line))
    builder.add(Unreachable())
Ejemplo n.º 14
0
 def load_final_static(self, fullname: str, typ: RType, line: int,
                       error_name: Optional[str] = None) -> Value:
     if error_name is None:
         error_name = fullname
     ok_block, error_block = BasicBlock(), BasicBlock()
     split_name = split_target(self.graph, fullname)
     assert split_name is not None
     value = self.add(LoadStatic(typ, split_name[1], split_name[0], line=line))
     self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True))
     self.activate_block(error_block)
     self.add(RaiseStandardError(RaiseStandardError.VALUE_ERROR,
                                 'value for final name "{}" was not set'.format(error_name),
                                 line))
     self.add(Unreachable())
     self.activate_block(ok_block)
     return value
Ejemplo n.º 15
0
 def load_static_checked(self, typ: RType, identifier: str, module_name: Optional[str] = None,
                         namespace: str = NAMESPACE_STATIC,
                         line: int = -1,
                         error_msg: Optional[str] = None) -> Value:
     if error_msg is None:
         error_msg = "name '{}' is not defined".format(identifier)
     ok_block, error_block = BasicBlock(), BasicBlock()
     value = self.add(LoadStatic(typ, identifier, module_name, namespace, line=line))
     self.add(Branch(value, error_block, ok_block, Branch.IS_ERROR, rare=True))
     self.activate_block(error_block)
     self.add(RaiseStandardError(RaiseStandardError.NAME_ERROR,
                                 error_msg,
                                 line))
     self.add(Unreachable())
     self.activate_block(ok_block)
     return value
Ejemplo n.º 16
0
def split_blocks_at_uninits(env: Environment,
                            blocks: List[BasicBlock],
                            pre_must_defined: 'AnalysisDict[Value]') -> 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)

        for i, op in enumerate(ops):
            defined = pre_must_defined[block, i]
            for src in op.unique_sources():
                # If a register operand is not guaranteed to be
                # initialized is an operand to something other than a
                # check that it is defined, insert a check.

                # Note that for register operand in a LoadAddress op,
                # we should be able to use it without initialization
                # as we may need to use its address to update itself
                if (isinstance(src, Register) and src not in defined
                        and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
                        and not isinstance(op, LoadAddress)):
                    new_block, error_block = BasicBlock(), BasicBlock()
                    new_block.error_handler = error_block.error_handler = cur_block.error_handler
                    new_blocks += [error_block, new_block]

                    env.vars_needing_init.add(src)

                    cur_block.ops.append(Branch(src,
                                                true_label=error_block,
                                                false_label=new_block,
                                                op=Branch.IS_ERROR,
                                                line=op.line))
                    raise_std = RaiseStandardError(
                        RaiseStandardError.UNBOUND_LOCAL_ERROR,
                        "local variable '{}' referenced before assignment".format(src.name),
                        op.line)
                    env.add_op(raise_std)
                    error_block.ops.append(raise_std)
                    error_block.ops.append(Unreachable())
                    cur_block = new_block
            cur_block.ops.append(op)

    return new_blocks
Ejemplo n.º 17
0
def translate_next_call(builder: IRBuilder, expr: CallExpr,
                        callee: RefExpr) -> Optional[Value]:
    """Special case for calling next() on a generator expression, an
    idiom that shows up some in mypy.

    For example, next(x for x in l if x.id == 12, None) will
    generate code that searches l for an element where x.id == 12
    and produce the first such object, or None if no such element
    exists.
    """
    if not (expr.arg_kinds in ([ARG_POS], [ARG_POS, ARG_POS])
            and isinstance(expr.args[0], GeneratorExpr)):
        return None

    gen = expr.args[0]

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

    exit_block = BasicBlock()

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

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

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

    builder.activate_block(exit_block)
    return retval
Ejemplo n.º 18
0
def add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int) -> None:
    """Add error handling blocks to a generator class.

    Generates blocks to check if error flags are set while calling the
    helper method for generator functions, and raises an exception if
    those flags are set.
    """
    cls = builder.fn_info.generator_class
    assert cls.exc_regs is not None
    exc_type, exc_val, exc_tb = cls.exc_regs

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

    builder.activate_block(error_block)
    builder.call_c(raise_exception_with_tb_op, [exc_type, exc_val, exc_tb], line)
    builder.add(Unreachable())
    builder.goto_and_activate(ok_block)
Ejemplo n.º 19
0
def add_close_to_generator_class(builder: IRBuilder,
                                 fn_info: FuncInfo) -> None:
    """Generates the '__close__' method for a generator class."""
    # TODO: Currently this method just triggers a runtime error,
    # we should fill this out eventually.
    builder.enter(fn_info)
    add_self_to_env(builder.environment, fn_info.generator_class.ir)
    builder.add(
        RaiseStandardError(RaiseStandardError.RUNTIME_ERROR,
                           'close method on generator classes uimplemented',
                           fn_info.fitem.line))
    builder.add(Unreachable())
    blocks, env, _, fn_info = builder.leave()

    # Next, add the actual function as a method of the generator class.
    sig = FuncSignature((RuntimeArg(SELF_NAME, object_rprimitive), ),
                        object_rprimitive)
    close_fn_decl = FuncDecl('close', fn_info.generator_class.ir.name,
                             builder.module_name, sig)
    close_fn_ir = FuncIR(close_fn_decl, blocks, env)
    fn_info.generator_class.ir.methods['close'] = close_fn_ir
    builder.functions.append(close_fn_ir)
Ejemplo n.º 20
0
 def test_register(self) -> None:
     reg = Register(int_rprimitive)
     op = Assign(reg, Integer(5))
     self.block.ops.append(op)
     self.block.ops.append(Unreachable())
     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',
         '    CPy_Unreachable();\n',
         '}\n',
     ],
                                result,
                                msg='Generated code invalid')
Ejemplo n.º 21
0
def transform_try_except(builder: IRBuilder, body: GenFunc,
                         handlers: Sequence[Tuple[Optional[Expression],
                                                  Optional[Expression],
                                                  GenFunc]],
                         else_body: Optional[GenFunc], line: int) -> None:
    """Generalized try/except/else handling that takes functions to gen the bodies.

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

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

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

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

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

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

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

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

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

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

    builder.activate_block(exit_block)
Ejemplo n.º 22
0
 def test_int_op(self) -> None:
     op1 = LoadInt(2)
     op2 = LoadInt(4)
     op3 = IntOp(int_rprimitive, op1, op2, IntOp.ADD)
     block = make_block([op1, op2, op3, Unreachable()])
     assert generate_names_for_ir([], [block]) == {op1: 'i0', op2: 'i1', op3: 'r0'}
Ejemplo n.º 23
0
    def process_iterator_tuple_assignment(self, target: AssignmentTargetTuple,
                                          rvalue_reg: Value,
                                          line: int) -> None:

        iterator = self.primitive_op(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.primitive_op(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.primitive_op(to_list, [iterator], line)
            iter_list_len = self.primitive_op(list_len_op, [iter_list], line)
            post_star_len = self.add(LoadInt(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_EXPR))

            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.primitive_op(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.primitive_op(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.º 24
0
 def add_implicit_unreachable(self) -> None:
     block = self.builder.blocks[-1]
     if not block.terminated:
         self.add(Unreachable())