def transform_assignment_stmt(builder: IRBuilder, stmt: AssignmentStmt) -> None: assert len(stmt.lvalues) >= 1 builder.disallow_class_assignments(stmt.lvalues, stmt.line) lvalue = stmt.lvalues[0] if stmt.type and isinstance(stmt.rvalue, TempNode): # This is actually a variable annotation without initializer. Don't generate # an assignment but we need to call get_assignment_target since it adds a # name binding as a side effect. builder.get_assignment_target(lvalue, stmt.line) return # multiple assignment if (isinstance(lvalue, TupleExpr) and isinstance(stmt.rvalue, TupleExpr) and len(lvalue.items) == len(stmt.rvalue.items)): temps = [] for right in stmt.rvalue.items: rvalue_reg = builder.accept(right) temp = builder.alloc_temp(rvalue_reg.type) builder.assign(temp, rvalue_reg, stmt.line) temps.append(temp) for (left, temp) in zip(lvalue.items, temps): assignment_target = builder.get_assignment_target(left) builder.assign(assignment_target, temp, stmt.line) return line = stmt.rvalue.line rvalue_reg = builder.accept(stmt.rvalue) if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(lvalue, rvalue_reg) for lvalue in stmt.lvalues: target = builder.get_assignment_target(lvalue) builder.assign(target, rvalue_reg, line)
def try_finally_entry_blocks(builder: IRBuilder, err_handler: BasicBlock, return_entry: BasicBlock, main_entry: BasicBlock, finally_block: BasicBlock, ret_reg: Optional[Register]) -> Value: old_exc = builder.alloc_temp(exc_rtuple) # Entry block for non-exceptional flow builder.activate_block(main_entry) if ret_reg: builder.add( Assign(ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])))) builder.goto(return_entry) builder.activate_block(return_entry) builder.add(Assign(old_exc, builder.add(LoadErrorValue(exc_rtuple)))) builder.goto(finally_block) # Entry block for errors builder.activate_block(err_handler) if ret_reg: builder.add( Assign(ret_reg, builder.add(LoadErrorValue(builder.ret_types[-1])))) builder.add(Assign(old_exc, builder.call_c(error_catch_op, [], -1))) builder.goto(finally_block) return old_exc
def setup_non_ext_dict(builder: IRBuilder, cdef: ClassDef, metaclass: Value, bases: Value) -> Value: """Initialize the class dictionary for a non-extension class. This class dictionary is passed to the metaclass constructor. """ # Check if the metaclass defines a __prepare__ method, and if so, call it. has_prepare = builder.call_c( py_hasattr_op, [metaclass, builder.load_static_unicode('__prepare__')], cdef.line) non_ext_dict = builder.alloc_temp(dict_rprimitive) true_block, false_block, exit_block, = BasicBlock(), BasicBlock( ), BasicBlock() builder.add_bool_branch(has_prepare, true_block, false_block) builder.activate_block(true_block) cls_name = builder.load_static_unicode(cdef.name) prepare_meth = builder.py_get_attr(metaclass, '__prepare__', cdef.line) prepare_dict = builder.py_call(prepare_meth, [cls_name, bases], cdef.line) builder.assign(non_ext_dict, prepare_dict, cdef.line) builder.goto(exit_block) builder.activate_block(false_block) builder.assign(non_ext_dict, builder.call_c(dict_new_op, [], cdef.line), cdef.line) builder.goto(exit_block) builder.activate_block(exit_block) return non_ext_dict
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 = builder.alloc_temp(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
def any_all_helper(builder: IRBuilder, gen: GeneratorExpr, initial_value_op: OpDescription, modify: Callable[[Value], Value], new_value_op: OpDescription) -> Value: retval = builder.alloc_temp(bool_rprimitive) builder.assign(retval, builder.primitive_op(initial_value_op, [], -1), -1) loop_params = list(zip(gen.indices, gen.sequences, gen.condlists)) true_block, false_block, exit_block = BasicBlock(), BasicBlock( ), BasicBlock() def gen_inner_stmts() -> None: comparison = modify(builder.accept(gen.left_expr)) builder.add_bool_branch(comparison, true_block, false_block) builder.activate_block(true_block) builder.assign(retval, builder.primitive_op(new_value_op, [], -1), -1) builder.goto(exit_block) builder.activate_block(false_block) comprehension_helper(builder, loop_params, gen_inner_stmts, gen.line) builder.goto_and_activate(exit_block) return retval
def transform_conditional_expr(builder: IRBuilder, expr: ConditionalExpr) -> Value: if_body, else_body, next = BasicBlock(), BasicBlock(), BasicBlock() builder.process_conditional(expr.cond, if_body, else_body) expr_type = builder.node_type(expr) # Having actual Phi nodes would be really nice here! target = builder.alloc_temp(expr_type) builder.activate_block(if_body) true_value = builder.accept(expr.if_expr) true_value = builder.coerce(true_value, expr_type, expr.line) builder.add(Assign(target, true_value)) builder.goto(next) builder.activate_block(else_body) false_value = builder.accept(expr.else_expr) false_value = builder.coerce(false_value, expr_type, expr.line) builder.add(Assign(target, false_value)) builder.goto(next) builder.activate_block(next) return target
def handle_yield_from_and_await(builder: IRBuilder, o: Union[YieldFromExpr, AwaitExpr]) -> Value: # This is basically an implementation of the code in PEP 380. # TODO: do we want to use the right types here? result = builder.alloc_temp(object_rprimitive) to_yield_reg = builder.alloc_temp(object_rprimitive) received_reg = builder.alloc_temp(object_rprimitive) if isinstance(o, YieldFromExpr): iter_val = builder.primitive_op(iter_op, [builder.accept(o.expr)], o.line) else: iter_val = builder.primitive_op(coro_op, [builder.accept(o.expr)], o.line) iter_reg = builder.maybe_spill_assignable(iter_val) stop_block, main_block, done_block = BasicBlock(), BasicBlock(), BasicBlock() _y_init = builder.primitive_op(next_raw_op, [builder.read(iter_reg)], o.line) builder.add(Branch(_y_init, stop_block, main_block, Branch.IS_ERROR)) # Try extracting a return value from a StopIteration and return it. # If it wasn't, this reraises the exception. builder.activate_block(stop_block) builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line) builder.goto(done_block) builder.activate_block(main_block) builder.assign(to_yield_reg, _y_init, o.line) # OK Now the main loop! loop_block = BasicBlock() builder.goto_and_activate(loop_block) def try_body() -> None: builder.assign( received_reg, emit_yield(builder, builder.read(to_yield_reg), o.line), o.line ) def except_body() -> None: # The body of the except is all implemented in a C function to # reduce how much code we need to generate. It returns a value # indicating whether to break or yield (or raise an exception). res = builder.primitive_op(yield_from_except_op, [builder.read(iter_reg)], o.line) to_stop = builder.add(TupleGet(res, 0, o.line)) val = builder.add(TupleGet(res, 1, o.line)) ok, stop = BasicBlock(), BasicBlock() builder.add(Branch(to_stop, stop, ok, Branch.BOOL_EXPR)) # The exception got swallowed. Continue, yielding the returned value builder.activate_block(ok) builder.assign(to_yield_reg, val, o.line) builder.nonlocal_control[-1].gen_continue(builder, o.line) # The exception was a StopIteration. Stop iterating. builder.activate_block(stop) builder.assign(result, val, o.line) builder.nonlocal_control[-1].gen_break(builder, o.line) def else_body() -> None: # Do a next() or a .send(). It will return NULL on exception # but it won't automatically propagate. _y = builder.primitive_op( send_op, [builder.read(iter_reg), builder.read(received_reg)], o.line ) ok, stop = BasicBlock(), BasicBlock() builder.add(Branch(_y, stop, ok, Branch.IS_ERROR)) # Everything's fine. Yield it. builder.activate_block(ok) builder.assign(to_yield_reg, _y, o.line) builder.nonlocal_control[-1].gen_continue(builder, o.line) # Try extracting a return value from a StopIteration and return it. # If it wasn't, this rereaises the exception. builder.activate_block(stop) builder.assign(result, builder.primitive_op(check_stop_op, [], o.line), o.line) builder.nonlocal_control[-1].gen_break(builder, o.line) builder.push_loop_stack(loop_block, done_block) transform_try_except( builder, try_body, [(None, None, except_body)], else_body, o.line ) builder.pop_loop_stack() builder.goto_and_activate(done_block) return builder.read(result)