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
Beispiel #2
0
 def test_branch_is_error_next_block(self) -> None:
     next_block = BasicBlock(8)
     b = Branch(self.b, next_block, BasicBlock(9), Branch.IS_ERROR)
     self.assert_emit(b,
                      """if (cpy_r_b != 2) goto CPyL9;""",
                      next_block=next_block)
     b = Branch(self.b, next_block, BasicBlock(9), Branch.IS_ERROR)
     b.negated = True
     self.assert_emit(b,
                      """if (cpy_r_b == 2) goto CPyL9;""",
                      next_block=next_block)
Beispiel #3
0
 def test_branch_no_else_negated(self) -> None:
     next_block = BasicBlock(1)
     b = Branch(self.b, next_block, BasicBlock(2), Branch.BOOL)
     self.assert_emit(b,
                      """if (!cpy_r_b) goto CPyL2;""",
                      next_block=next_block)
     next_block = BasicBlock(1)
     b = Branch(self.b, next_block, BasicBlock(2), Branch.BOOL)
     b.negated = True
     self.assert_emit(b,
                      """if (cpy_r_b) goto CPyL2;""",
                      next_block=next_block)
Beispiel #4
0
    def compare_tuples(self,
                       lhs: Value,
                       rhs: Value,
                       op: str,
                       line: int = -1) -> Value:
        """Compare two tuples item by item"""
        # type cast to pass mypy check
        assert isinstance(lhs.type, RTuple) and isinstance(rhs.type, RTuple)
        equal = True if op == '==' else False
        result = self.alloc_temp(bool_rprimitive)
        # empty tuples
        if len(lhs.type.types) == 0 and len(rhs.type.types) == 0:
            self.add(
                Assign(result,
                       self.true() if equal else self.false(), line))
            return result
        length = len(lhs.type.types)
        false_assign, true_assign, out = BasicBlock(), BasicBlock(
        ), BasicBlock()
        check_blocks = [BasicBlock() for i in range(length)]
        lhs_items = [self.add(TupleGet(lhs, i, line)) for i in range(length)]
        rhs_items = [self.add(TupleGet(rhs, i, line)) for i in range(length)]

        if equal:
            early_stop, final = false_assign, true_assign
        else:
            early_stop, final = true_assign, false_assign

        for i in range(len(lhs.type.types)):
            if i != 0:
                self.activate_block(check_blocks[i])
            lhs_item = lhs_items[i]
            rhs_item = rhs_items[i]
            compare = self.binary_op(lhs_item, rhs_item, op, line)
            # Cast to bool if necessary since most types uses comparison returning a object type
            # See generic_ops.py for more information
            if not is_bool_rprimitive(compare.type):
                compare = self.call_c(bool_op, [compare], line)
            if i < len(lhs.type.types) - 1:
                branch = Branch(compare, early_stop, check_blocks[i + 1],
                                Branch.BOOL_EXPR)
            else:
                branch = Branch(compare, early_stop, final, Branch.BOOL_EXPR)
            # if op is ==, we branch on false, else branch on true
            branch.negated = equal
            self.add(branch)
        self.activate_block(false_assign)
        self.add(Assign(result, self.false(), line))
        self.goto(out)
        self.activate_block(true_assign)
        self.add(Assign(result, self.true(), line))
        self.goto_and_activate(out)
        return result
Beispiel #5
0
    def gen_cleanup(self, builder: 'IRBuilder', line: int) -> None:
        # Do an error branch on the return value register, which
        # may be undefined. This will allow it to be properly
        # decrefed if it is not null. This is kind of a hack.
        if self.ret_reg:
            target = BasicBlock()
            builder.add(Branch(self.ret_reg, target, target, Branch.IS_ERROR))
            builder.activate_block(target)

        # Restore the old exc_info
        target, cleanup = BasicBlock(), BasicBlock()
        builder.add(Branch(self.saved, target, cleanup, Branch.IS_ERROR))
        builder.activate_block(cleanup)
        builder.call_c(restore_exc_info_op, [self.saved], line)
        builder.goto_and_activate(target)
Beispiel #6
0
 def test_branch(self) -> None:
     self.assert_emit(Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL_EXPR),
                      """if (cpy_r_b) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
     b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.BOOL_EXPR)
     b.negated = True
     self.assert_emit(b,
                      """if (!cpy_r_b) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
Beispiel #7
0
 def test_get_attr_merged(self) -> None:
     op = GetAttr(self.r, 'y', 1)
     branch = Branch(op, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
     branch.traceback_entry = ('foobar', 123)
     self.assert_emit(op,
                      """\
         cpy_r_r0 = ((mod___AObject *)cpy_r_r)->_y;
         if (unlikely(cpy_r_r0 == CPY_INT_TAG)) {
             CPy_AttributeError("prog.py", "foobar", "A", "y", 123, CPyStatic_prog___globals);
             goto CPyL8;
         }
         CPyTagged_INCREF(cpy_r_r0);
         goto CPyL9;
         """,
                      next_branch=branch)
Beispiel #8
0
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None:
    """Generate a "__ne__" method from a "__eq__" method. """
    builder.enter_method(cls, '__ne__', object_rprimitive)
    rhs_arg = builder.add_argument('rhs', object_rprimitive)

    # If __eq__ returns NotImplemented, then __ne__ should also
    not_implemented_block, regular_block = BasicBlock(), BasicBlock()
    eqval = builder.add(MethodCall(builder.self(), '__eq__', [rhs_arg], line))
    not_implemented = builder.add(LoadAddress(not_implemented_op.type,
                                              not_implemented_op.src, line))
    builder.add(Branch(
        builder.translate_is_op(eqval, not_implemented, 'is', line),
        not_implemented_block,
        regular_block,
        Branch.BOOL))

    builder.activate_block(regular_block)
    retval = builder.coerce(
        builder.unary_op(eqval, 'not', line), object_rprimitive, line
    )
    builder.add(Return(retval))

    builder.activate_block(not_implemented_block)
    builder.add(Return(not_implemented))

    builder.leave_method()
Beispiel #9
0
 def gen_cleanup(self, builder: 'IRBuilder', line: int) -> None:
     # Restore the old exc_info
     target, cleanup = BasicBlock(), BasicBlock()
     builder.add(Branch(self.saved, target, cleanup, Branch.IS_ERROR))
     builder.activate_block(cleanup)
     builder.call_c(restore_exc_info_op, [self.saved], line)
     builder.goto_and_activate(target)
Beispiel #10
0
 def test_branch_is_error(self) -> None:
     b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
     self.assert_emit(
         b, """if (cpy_r_b == 2) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
     b = Branch(self.b, BasicBlock(8), BasicBlock(9), Branch.IS_ERROR)
     b.negated = True
     self.assert_emit(
         b, """if (cpy_r_b != 2) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
Beispiel #11
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
Beispiel #12
0
 def test_branch_rare(self) -> None:
     self.assert_emit(
         Branch(self.b,
                BasicBlock(8),
                BasicBlock(9),
                Branch.BOOL,
                rare=True), """if (unlikely(cpy_r_b)) {
                             goto CPyL8;
                         } else
                             goto CPyL9;
                      """)
     next_block = BasicBlock(9)
     self.assert_emit(Branch(self.b,
                             BasicBlock(8),
                             next_block,
                             Branch.BOOL,
                             rare=True),
                      """if (unlikely(cpy_r_b)) goto CPyL8;""",
                      next_block=next_block)
     next_block = BasicBlock(8)
     b = Branch(self.b, next_block, BasicBlock(9), Branch.BOOL, rare=True)
     self.assert_emit(b,
                      """if (likely(!cpy_r_b)) goto CPyL9;""",
                      next_block=next_block)
     next_block = BasicBlock(8)
     b = Branch(self.b, next_block, BasicBlock(9), Branch.BOOL, rare=True)
     b.negated = True
     self.assert_emit(b,
                      """if (likely(cpy_r_b)) goto CPyL9;""",
                      next_block=next_block)
Beispiel #13
0
 def assign_if_null(self, target: AssignmentTargetRegister,
                    get_val: Callable[[], Value], line: int) -> None:
     """Generate blocks for registers that NULL values."""
     error_block, body_block = BasicBlock(), BasicBlock()
     self.add(Branch(target.register, error_block, body_block, Branch.IS_ERROR))
     self.activate_block(error_block)
     self.add(Assign(target.register, self.coerce(get_val(), target.register.type, line)))
     self.goto(body_block)
     self.activate_block(body_block)
Beispiel #14
0
 def compare_tagged(self, lhs: Value, rhs: Value, op: str, line: int) -> Value:
     """Compare two tagged integers using given op"""
     op_type, c_func_desc = int_logical_op_mapping[op]
     result = self.alloc_temp(bool_rprimitive)
     short_int_block, int_block, out = BasicBlock(), BasicBlock(), BasicBlock()
     check = self.check_tagged_short_int(lhs, line)
     branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(short_int_block)
     eq = self.binary_int_op(bool_rprimitive, lhs, rhs, op_type, line)
     self.add(Assign(result, eq, line))
     self.goto(out)
     self.activate_block(int_block)
     call = self.call_c(c_func_desc, [lhs, rhs], line)
     self.add(Assign(result, call, line))
     self.goto_and_activate(out)
     return result
Beispiel #15
0
 def assign_if_null(self, target: Register,
                    get_val: Callable[[], Value], line: int) -> None:
     """If target is NULL, assign value produced by get_val to it."""
     error_block, body_block = BasicBlock(), BasicBlock()
     self.add(Branch(target, error_block, body_block, Branch.IS_ERROR))
     self.activate_block(error_block)
     self.add(Assign(target, self.coerce(get_val(), target.type, line)))
     self.goto(body_block)
     self.activate_block(body_block)
Beispiel #16
0
 def test_cast_and_branch_no_merge_3(self) -> None:
     op = Cast(self.r, dict_rprimitive, 1)
     next_block = BasicBlock(9)
     branch = Branch(op, BasicBlock(8), next_block, Branch.BOOL)
     branch.traceback_entry = ('foobar', 123)
     self.assert_emit(
         op,
         """\
         if (likely(PyDict_Check(cpy_r_r)))
             cpy_r_r0 = cpy_r_r;
         else {
             CPy_TypeError("dict", cpy_r_r);
             cpy_r_r0 = NULL;
         }
         """,
         next_block=next_block,
         next_branch=branch,
     )
Beispiel #17
0
 def finally_body() -> None:
     out_block, exit_block = BasicBlock(), BasicBlock()
     builder.add(
         Branch(builder.read(exc), exit_block, out_block, Branch.BOOL))
     builder.activate_block(exit_block)
     none = builder.none_object()
     builder.py_call(builder.read(exit_),
                     [builder.read(mgr), none, none, none], line)
     builder.goto_and_activate(out_block)
Beispiel #18
0
 def gen_condition(self) -> None:
     # We call __next__ on the iterator and check to see if the return value
     # is NULL, which signals either the end of the Iterable being traversed
     # or an exception being raised. Note that Branch.IS_ERROR checks only
     # for NULL (an exception does not necessarily have to be raised).
     builder = self.builder
     line = self.line
     self.next_reg = builder.call_c(next_op, [builder.read(self.iter_target, line)], line)
     builder.add(Branch(self.next_reg, self.loop_exit, self.body_block, Branch.IS_ERROR))
Beispiel #19
0
    def test_cast_and_branch_merge(self) -> None:
        op = Cast(self.r, dict_rprimitive, 1)
        next_block = BasicBlock(9)
        branch = Branch(op, BasicBlock(8), next_block, Branch.IS_ERROR)
        branch.traceback_entry = ('foobar', 123)
        self.assert_emit(
            op,
            """\
if (likely(PyDict_Check(cpy_r_r)))
    cpy_r_r0 = cpy_r_r;
else {
    CPy_TypeErrorTraceback("prog.py", "foobar", 123, CPyStatic_prog___globals, "dict", cpy_r_r);
    goto CPyL8;
}
""",
            next_block=next_block,
            next_branch=branch,
            skip_next=True,
        )
Beispiel #20
0
    def add_bool_branch(self, value: Value, true: BasicBlock,
                        false: BasicBlock) -> None:
        if is_runtime_subtype(value.type, int_rprimitive):
            zero = self.add(LoadInt(0))
            value = self.binary_op(value, zero, '!=', value.line)
        elif is_same_type(value.type, list_rprimitive):
            length = self.primitive_op(list_len_op, [value], value.line)
            zero = self.add(LoadInt(0))
            value = self.binary_op(length, zero, '!=', value.line)
        elif (isinstance(value.type, RInstance)
              and value.type.class_ir.is_ext_class
              and value.type.class_ir.has_method('__bool__')):
            # Directly call the __bool__ method on classes that have it.
            value = self.gen_method_call(value, '__bool__', [],
                                         bool_rprimitive, value.line)
        else:
            value_type = optional_value_type(value.type)
            if value_type is not None:
                is_none = self.binary_op(value, self.none_object(), 'is not',
                                         value.line)
                branch = Branch(is_none, true, false, Branch.BOOL_EXPR)
                self.add(branch)
                always_truthy = False
                if isinstance(value_type, RInstance):
                    # check whether X.__bool__ is always just the default (object.__bool__)
                    if (not value_type.class_ir.has_method('__bool__') and
                            value_type.class_ir.is_method_final('__bool__')):
                        always_truthy = True

                if not always_truthy:
                    # Optional[X] where X may be falsey and requires a check
                    branch.true = BasicBlock()
                    self.activate_block(branch.true)
                    # unbox_or_cast instead of coerce because we want the
                    # type to change even if it is a subtype.
                    remaining = self.unbox_or_cast(value, value_type,
                                                   value.line)
                    self.add_bool_branch(remaining, true, false)
                return
            elif not is_same_type(value.type, bool_rprimitive):
                value = self.primitive_op(bool_op, [value], value.line)
        self.add(Branch(value, true, false, Branch.BOOL_EXPR))
Beispiel #21
0
 def compare_tagged(self, lhs: Value, rhs: Value, op: str,
                    line: int) -> Value:
     """Compare two tagged integers using given op"""
     # generate fast binary logic ops on short ints
     if is_short_int_rprimitive(lhs.type) and is_short_int_rprimitive(
             rhs.type):
         return self.comparison_op(lhs, rhs,
                                   int_comparison_op_mapping[op][0], line)
     op_type, c_func_desc, negate_result, swap_op = int_comparison_op_mapping[
         op]
     result = self.alloc_temp(bool_rprimitive)
     short_int_block, int_block, out = BasicBlock(), BasicBlock(
     ), BasicBlock()
     check_lhs = self.check_tagged_short_int(lhs, line)
     if op in ("==", "!="):
         check = check_lhs
     else:
         # for non-equal logical ops(less than, greater than, etc.), need to check both side
         check_rhs = self.check_tagged_short_int(rhs, line)
         check = self.binary_int_op(bool_rprimitive, check_lhs, check_rhs,
                                    BinaryIntOp.AND, line)
     branch = Branch(check, short_int_block, int_block, Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(short_int_block)
     eq = self.comparison_op(lhs, rhs, op_type, line)
     self.add(Assign(result, eq, line))
     self.goto(out)
     self.activate_block(int_block)
     if swap_op:
         args = [rhs, lhs]
     else:
         args = [lhs, rhs]
     call = self.call_c(c_func_desc, args, line)
     if negate_result:
         # TODO: introduce UnaryIntOp?
         call_result = self.unary_op(call, "not", line)
     else:
         call_result = call
     self.add(Assign(result, call_result, line))
     self.goto_and_activate(out)
     return result
Beispiel #22
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
Beispiel #23
0
 def compare_strings(self, lhs: Value, rhs: Value, op: str,
                     line: int) -> Value:
     """Compare two strings"""
     compare_result = self.call_c(unicode_compare, [lhs, rhs], line)
     error_constant = self.add(LoadInt(-1, line, c_int_rprimitive))
     compare_error_check = self.add(
         ComparisonOp(compare_result, error_constant, ComparisonOp.EQ,
                      line))
     exception_check, propagate, final_compare = BasicBlock(), BasicBlock(
     ), BasicBlock()
     branch = Branch(compare_error_check, exception_check, final_compare,
                     Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(exception_check)
     check_error_result = self.call_c(err_occurred_op, [], line)
     null = self.add(LoadInt(0, line, pointer_rprimitive))
     compare_error_check = self.add(
         ComparisonOp(check_error_result, null, ComparisonOp.NEQ, line))
     branch = Branch(compare_error_check, propagate, final_compare,
                     Branch.BOOL_EXPR)
     branch.negated = False
     self.add(branch)
     self.activate_block(propagate)
     self.call_c(keep_propagating_op, [], line)
     self.goto(final_compare)
     self.activate_block(final_compare)
     op_type = ComparisonOp.EQ if op == '==' else ComparisonOp.NEQ
     return self.add(
         ComparisonOp(compare_result,
                      self.add(LoadInt(0, line, c_int_rprimitive)), op_type,
                      line))
Beispiel #24
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)
Beispiel #25
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())
Beispiel #26
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
Beispiel #27
0
    def gen_condition(self) -> None:
        """Get next key/value pair, set new offset, and check if we should continue."""
        builder = self.builder
        line = self.line
        self.next_tuple = self.builder.call_c(
            self.dict_next_op, [builder.read(self.iter_target, line),
                                builder.read(self.offset_target, line)], line)

        # Do this here instead of in gen_step() to minimize variables in environment.
        new_offset = builder.add(TupleGet(self.next_tuple, 1, line))
        builder.assign(self.offset_target, new_offset, line)

        should_continue = builder.add(TupleGet(self.next_tuple, 0, line))
        builder.add(
            Branch(should_continue, self.body_block, self.loop_exit, Branch.BOOL_EXPR)
        )
Beispiel #28
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
Beispiel #29
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
Beispiel #30
0
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> FuncIR:
    """Generate a "__ne__" method from a "__eq__" method. """
    builder.enter()

    rt_args = (RuntimeArg("self", RInstance(cls)), RuntimeArg("rhs", object_rprimitive))

    # The environment operates on Vars, so we make some up
    fake_vars = [(Var(arg.name), arg.type) for arg in rt_args]
    args = [
        builder.read(
            builder.environment.add_local_reg(
                var, type, is_arg=True
            ),
            line
        )
        for var, type in fake_vars
    ]  # type: List[Value]
    builder.ret_types[-1] = object_rprimitive

    # If __eq__ returns NotImplemented, then __ne__ should also
    not_implemented_block, regular_block = BasicBlock(), BasicBlock()
    eqval = builder.add(MethodCall(args[0], '__eq__', [args[1]], line))
    not_implemented = builder.add(LoadAddress(not_implemented_op.type,
                                              not_implemented_op.src, line))
    builder.add(Branch(
        builder.translate_is_op(eqval, not_implemented, 'is', line),
        not_implemented_block,
        regular_block,
        Branch.BOOL_EXPR))

    builder.activate_block(regular_block)
    retval = builder.coerce(
        builder.unary_op(eqval, 'not', line), object_rprimitive, line
    )
    builder.add(Return(retval))

    builder.activate_block(not_implemented_block)
    builder.add(Return(not_implemented))

    blocks, env, ret_type, _ = builder.leave()
    return FuncIR(
        FuncDecl('__ne__', cls.name, builder.module_name,
                 FuncSignature(rt_args, ret_type)),
        blocks, env)