def transform_unary_expr(builder: IRBuilder, expr: UnaryExpr) -> Value: return builder.unary_op(builder.accept(expr.expr), expr.op, expr.line)
def transform_assignment_expr(builder: IRBuilder, o: AssignmentExpr) -> Value: value = builder.accept(o.value) target = builder.get_assignment_target(o.target) builder.assign(target, value, o.line) return value
def translate_cast_expr(builder: IRBuilder, expr: CastExpr) -> Value: src = builder.accept(expr.expr) target_type = builder.type_to_rtype(expr.type) return builder.coerce(src, target_type, expr.line)
def transform_op_expr(builder: IRBuilder, expr: OpExpr) -> Value: if expr.op in ('and', 'or'): return builder.shortcircuit_expr(expr) return builder.binary_op(builder.accept(expr.left), builder.accept(expr.right), expr.op, expr.line)
def transform_comparison_expr(builder: IRBuilder, e: ComparisonExpr) -> Value: # x in (...)/[...] # x not in (...)/[...] if (e.operators[0] in ['in', 'not in'] and len(e.operators) == 1 and isinstance(e.operands[1], (TupleExpr, ListExpr))): items = e.operands[1].items n_items = len(items) # x in y -> x == y[0] or ... or x == y[n] # x not in y -> x != y[0] and ... and x != y[n] # 16 is arbitrarily chosen to limit code size if 1 < n_items < 16: if e.operators[0] == 'in': bin_op = 'or' cmp_op = '==' else: bin_op = 'and' cmp_op = '!=' lhs = e.operands[0] mypy_file = builder.graph['builtins'].tree assert mypy_file is not None bool_type = Instance(cast(TypeInfo, mypy_file.names['bool'].node), []) exprs = [] for item in items: expr = ComparisonExpr([cmp_op], [lhs, item]) builder.types[expr] = bool_type exprs.append(expr) or_expr = exprs.pop(0) # type: Expression for expr in exprs: or_expr = OpExpr(bin_op, or_expr, expr) builder.types[or_expr] = bool_type return builder.accept(or_expr) # x in [y]/(y) -> x == y # x not in [y]/(y) -> x != y elif n_items == 1: if e.operators[0] == 'in': cmp_op = '==' else: cmp_op = '!=' e.operators = [cmp_op] e.operands[1] = items[0] # x in []/() -> False # x not in []/() -> True elif n_items == 0: if e.operators[0] == 'in': return builder.false() else: return builder.true() # TODO: Don't produce an expression when used in conditional context # All of the trickiness here is due to support for chained conditionals # (`e1 < e2 > e3`, etc). `e1 < e2 > e3` is approximately equivalent to # `e1 < e2 and e2 > e3` except that `e2` is only evaluated once. expr_type = builder.node_type(e) # go(i, prev) generates code for `ei opi e{i+1} op{i+1} ... en`, # assuming that prev contains the value of `ei`. def go(i: int, prev: Value) -> Value: if i == len(e.operators) - 1: return transform_basic_comparison( builder, e.operators[i], prev, builder.accept(e.operands[i + 1]), e.line) next = builder.accept(e.operands[i + 1]) return builder.builder.shortcircuit_helper( 'and', expr_type, lambda: transform_basic_comparison( builder, e.operators[i], prev, next, e.line), lambda: go(i + 1, next), e.line) return go(0, builder.accept(e.operands[0]))
def translate_method_call(builder: IRBuilder, expr: CallExpr, callee: MemberExpr) -> Value: """Generate IR for an arbitrary call of form e.m(...). This can also deal with calls to module-level functions. """ if builder.is_native_ref_expr(callee): # Call to module-level native function or such return translate_call(builder, expr, callee) elif (isinstance(callee.expr, RefExpr) and isinstance(callee.expr.node, TypeInfo) and callee.expr.node in builder.mapper.type_to_ir and builder.mapper.type_to_ir[callee.expr.node].has_method(callee.name)): # Call a method via the *class* assert isinstance(callee.expr.node, TypeInfo) ir = builder.mapper.type_to_ir[callee.expr.node] decl = ir.method_decl(callee.name) args = [] arg_kinds, arg_names = expr.arg_kinds[:], expr.arg_names[:] # Add the class argument for class methods in extension classes if decl.kind == FUNC_CLASSMETHOD and ir.is_ext_class: args.append( builder.load_native_type_object(callee.expr.node.fullname)) arg_kinds.insert(0, ARG_POS) arg_names.insert(0, None) args += [builder.accept(arg) for arg in expr.args] if ir.is_ext_class: return builder.builder.call(decl, args, arg_kinds, arg_names, expr.line) else: obj = builder.accept(callee.expr) return builder.gen_method_call(obj, callee.name, args, builder.node_type(expr), expr.line, expr.arg_kinds, expr.arg_names) elif builder.is_module_member_expr(callee): # Fall back to a PyCall for non-native module calls function = builder.accept(callee) args = [builder.accept(arg) for arg in expr.args] return builder.py_call(function, args, expr.line, arg_kinds=expr.arg_kinds, arg_names=expr.arg_names) else: receiver_typ = builder.node_type(callee.expr) # If there is a specializer for this method name/type, try calling it. # We would return the first successful one. if (callee.name, receiver_typ) in specializers: for specializer in specializers[callee.name, receiver_typ]: val = specializer(builder, expr, callee) if val is not None: return val obj = builder.accept(callee.expr) args = [builder.accept(arg) for arg in expr.args] return builder.gen_method_call(obj, callee.name, args, builder.node_type(expr), expr.line, expr.arg_kinds, expr.arg_names)
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 (is_range_ref(expr.callee) 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
def transform_overloaded_func_def(builder: IRBuilder, o: OverloadedFuncDef) -> None: # Handle regular overload case assert o.impl builder.accept(o.impl)
def transform_class_def(builder: IRBuilder, cdef: ClassDef) -> None: """Create IR for a class definition. This can generate both extension (native) and non-extension classes. These are generated in very different ways. In the latter case we construct a Python type object at runtime by doing the equivalent of "type(name, bases, dict)" in IR. Extension classes are defined via C structs that are generated later in mypyc.codegen.emitclass. This is the main entry point to this module. """ ir = builder.mapper.type_to_ir[cdef.info] # We do this check here because the base field of parent # classes aren't necessarily populated yet at # prepare_class_def time. if any(ir.base_mro[i].base != ir.base_mro[i + 1] for i in range(len(ir.base_mro) - 1)): builder.error("Non-trait MRO must be linear", cdef.line) if ir.allow_interpreted_subclasses: for parent in ir.mro: if not parent.allow_interpreted_subclasses: builder.error( 'Base class "{}" does not allow interpreted subclasses'. format(parent.fullname), cdef.line) # Currently, we only create non-extension classes for classes that are # decorated or inherit from Enum. Classes decorated with @trait do not # apply here, and are handled in a different way. if ir.is_ext_class: # If the class is not decorated, generate an extension class for it. type_obj: Optional[Value] = allocate_class(builder, cdef) non_ext: Optional[NonExtClassInfo] = None dataclass_non_ext = dataclass_non_ext_info(builder, cdef) else: non_ext_bases = populate_non_ext_bases(builder, cdef) non_ext_metaclass = find_non_ext_metaclass(builder, cdef, non_ext_bases) non_ext_dict = setup_non_ext_dict(builder, cdef, non_ext_metaclass, non_ext_bases) # We populate __annotations__ for non-extension classes # because dataclasses uses it to determine which attributes to compute on. # TODO: Maybe generate more precise types for annotations non_ext_anns = builder.call_c(dict_new_op, [], cdef.line) non_ext = NonExtClassInfo(non_ext_dict, non_ext_bases, non_ext_anns, non_ext_metaclass) dataclass_non_ext = None attrs_to_cache: List[Tuple[Lvalue, RType]] = [] for stmt in cdef.defs.body: if isinstance(stmt, OverloadedFuncDef) and stmt.is_property: if not ir.is_ext_class: # properties with both getters and setters in non_extension # classes not supported builder.error( "Property setters not supported in non-extension classes", stmt.line) for item in stmt.items: with builder.catch_errors(stmt.line): transform_method(builder, cdef, non_ext, get_func_def(item)) elif isinstance(stmt, (FuncDef, Decorator, OverloadedFuncDef)): # Ignore plugin generated methods (since they have no # bodies to compile and will need to have the bodies # provided by some other mechanism.) if cdef.info.names[stmt.name].plugin_generated: continue with builder.catch_errors(stmt.line): transform_method(builder, cdef, non_ext, get_func_def(stmt)) elif isinstance(stmt, PassStmt): continue elif isinstance(stmt, AssignmentStmt): if len(stmt.lvalues) != 1: builder.error( "Multiple assignment in class bodies not supported", stmt.line) continue lvalue = stmt.lvalues[0] if not isinstance(lvalue, NameExpr): builder.error( "Only assignment to variables is supported in class bodies", stmt.line) continue # We want to collect class variables in a dictionary for both real # non-extension classes and fake dataclass ones. var_non_ext = non_ext or dataclass_non_ext if var_non_ext: add_non_ext_class_attr(builder, var_non_ext, lvalue, stmt, cdef, attrs_to_cache) if non_ext: continue # Variable declaration with no body if isinstance(stmt.rvalue, TempNode): continue # Only treat marked class variables as class variables. if not (is_class_var(lvalue) or stmt.is_final_def): continue typ = builder.load_native_type_object(cdef.fullname) value = builder.accept(stmt.rvalue) builder.call_c(py_setattr_op, [typ, builder.load_str(lvalue.name), value], stmt.line) if builder.non_function_scope() and stmt.is_final_def: builder.init_final_static(lvalue, value, cdef.name) elif isinstance(stmt, ExpressionStmt) and isinstance( stmt.expr, StrExpr): # Docstring. Ignore pass else: builder.error("Unsupported statement in class body", stmt.line) if not non_ext: # That is, an extension class generate_attr_defaults(builder, cdef) create_ne_from_eq(builder, cdef) if dataclass_non_ext: assert type_obj dataclass_finalize(builder, cdef, dataclass_non_ext, type_obj) else: # Dynamically create the class via the type constructor non_ext_class = load_non_ext_class(builder, ir, non_ext, cdef.line) non_ext_class = load_decorated_class(builder, cdef, non_ext_class) # Save the decorated class builder.add( InitStatic(non_ext_class, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add the non-extension class to the dict builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_str(cdef.name), non_ext_class ], cdef.line) # Cache any cacheable class attributes cache_class_attrs(builder, attrs_to_cache, cdef)
def gen_func_item( builder: IRBuilder, fitem: FuncItem, name: str, sig: FuncSignature, cdef: Optional[ClassDef] = None, ) -> Tuple[FuncIR, Optional[Value]]: """Generate and return the FuncIR for a given FuncDef. If the given FuncItem is a nested function, then we generate a callable class representing the function and use that instead of the actual function. if the given FuncItem contains a nested function, then we generate an environment class so that inner nested functions can access the environment of the given FuncDef. Consider the following nested function: def a() -> None: def b() -> None: def c() -> None: return None return None return None The classes generated would look something like the following. has pointer to +-------+ +--------------------------> | a_env | | +-------+ | ^ | | has pointer to +-------+ associated with +-------+ | b_obj | -------------------> | b_env | +-------+ +-------+ ^ | +-------+ has pointer to | | c_obj | --------------------------+ +-------+ """ # TODO: do something about abstract methods. func_reg = None # type: Optional[Value] # We treat lambdas as always being nested because we always generate # a class for lambdas, no matter where they are. (It would probably also # work to special case toplevel lambdas and generate a non-class function.) is_nested = fitem in builder.nested_fitems or isinstance(fitem, LambdaExpr) contains_nested = fitem in builder.encapsulating_funcs.keys() is_decorated = fitem in builder.fdefs_to_decorators in_non_ext = False class_name = None if cdef: ir = builder.mapper.type_to_ir[cdef.info] in_non_ext = not ir.is_ext_class class_name = cdef.name builder.enter( FuncInfo(fitem, name, class_name, gen_func_ns(builder), is_nested, contains_nested, is_decorated, in_non_ext)) # Functions that contain nested functions need an environment class to store variables that # are free in their nested functions. Generator functions need an environment class to # store a variable denoting the next instruction to be executed when the __next__ function # is called, along with all the variables inside the function itself. if builder.fn_info.contains_nested or builder.fn_info.is_generator: setup_env_class(builder) if builder.fn_info.is_nested or builder.fn_info.in_non_ext: setup_callable_class(builder) if builder.fn_info.is_generator: # Do a first-pass and generate a function that just returns a generator object. gen_generator_func(builder) blocks, env, ret_type, fn_info = builder.leave() func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) # Re-enter the FuncItem and visit the body of the function this time. builder.enter(fn_info) setup_env_for_generator_class(builder) load_outer_envs(builder, builder.fn_info.generator_class) if builder.fn_info.is_nested and isinstance(fitem, FuncDef): setup_func_for_recursive_call(builder, fitem, builder.fn_info.generator_class) create_switch_for_generator_class(builder) add_raise_exception_blocks_to_generator_class(builder, fitem.line) else: load_env_registers(builder) gen_arg_defaults(builder) if builder.fn_info.contains_nested and not builder.fn_info.is_generator: finalize_env_class(builder) builder.ret_types[-1] = sig.ret_type # Add all variables and functions that are declared/defined within this # function and are referenced in functions nested within this one to this # function's environment class so the nested functions can reference # them even if they are declared after the nested function's definition. # Note that this is done before visiting the body of this function. env_for_func = builder.fn_info # type: Union[FuncInfo, ImplicitClass] if builder.fn_info.is_generator: env_for_func = builder.fn_info.generator_class elif builder.fn_info.is_nested or builder.fn_info.in_non_ext: env_for_func = builder.fn_info.callable_class if builder.fn_info.fitem in builder.free_variables: # Sort the variables to keep things deterministic for var in sorted(builder.free_variables[builder.fn_info.fitem], key=lambda x: x.name): if isinstance(var, Var): rtype = builder.type_to_rtype(var.type) builder.add_var_to_env_class(var, rtype, env_for_func, reassign=False) if builder.fn_info.fitem in builder.encapsulating_funcs: for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]: if isinstance(nested_fn, FuncDef): # The return type is 'object' instead of an RInstance of the # callable class because differently defined functions with # the same name and signature across conditional blocks # will generate different callable classes, so the callable # class that gets instantiated must be generic. builder.add_var_to_env_class(nested_fn, object_rprimitive, env_for_func, reassign=False) builder.accept(fitem.body) builder.maybe_add_implicit_return() if builder.fn_info.is_generator: populate_switch_for_generator_class(builder) blocks, env, ret_type, fn_info = builder.leave() if fn_info.is_generator: add_methods_to_generator_class(builder, fn_info, sig, env, blocks, fitem.is_coroutine) else: func_ir, func_reg = gen_func_ir(builder, blocks, sig, env, fn_info, cdef) calculate_arg_defaults(builder, fn_info, env, func_reg) return (func_ir, func_reg)
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)
def transform_yield_expr(builder: IRBuilder, expr: YieldExpr) -> Value: if expr.expr: retval = builder.accept(expr.expr) else: retval = builder.builder.none() return emit_yield(builder, retval, expr.line)
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_EXPR)) 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)
def generate_attr_defaults(builder: IRBuilder, cdef: ClassDef) -> None: """Generate an initialization method for default attr values (from class vars).""" cls = builder.mapper.type_to_ir[cdef.info] if cls.builtin_base: return # Pull out all assignments in classes in the mro so we can initialize them # TODO: Support nested statements default_assignments = [] for info in reversed(cdef.info.mro): if info not in builder.mapper.type_to_ir: continue for stmt in info.defn.defs.body: if (isinstance(stmt, AssignmentStmt) and isinstance(stmt.lvalues[0], NameExpr) and not is_class_var(stmt.lvalues[0]) and not isinstance(stmt.rvalue, TempNode)): if stmt.lvalues[0].name == '__slots__': continue # Skip type annotated assignments in dataclasses if is_dataclass(cdef) and stmt.type: continue default_assignments.append(stmt) if not default_assignments: return builder.enter() builder.ret_types[-1] = bool_rprimitive rt_args = (RuntimeArg(SELF_NAME, RInstance(cls)),) self_var = builder.read(add_self_to_env(builder.environment, cls), -1) for stmt in default_assignments: lvalue = stmt.lvalues[0] assert isinstance(lvalue, NameExpr) if not stmt.is_final_def and not is_constant(stmt.rvalue): builder.warning('Unsupported default attribute value', stmt.rvalue.line) # If the attribute is initialized to None and type isn't optional, # don't initialize it to anything. attr_type = cls.attr_type(lvalue.name) if isinstance(stmt.rvalue, RefExpr) and stmt.rvalue.fullname == 'builtins.None': if (not is_optional_type(attr_type) and not is_object_rprimitive(attr_type) and not is_none_rprimitive(attr_type)): continue val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line) builder.add(SetAttr(self_var, lvalue.name, val, -1)) builder.add(Return(builder.primitive_op(true_op, [], -1))) blocks, env, ret_type, _ = builder.leave() ir = FuncIR( FuncDecl('__mypyc_defaults_setup', cls.name, builder.module_name, FuncSignature(rt_args, ret_type)), blocks, env) builder.functions.append(ir) cls.methods[ir.name] = ir
def translate_is_none(builder: IRBuilder, expr: Expression, negated: bool) -> Value: v = builder.accept(expr, can_borrow=True) return builder.binary_op(v, builder.none_object(), 'is not' if negated else 'is', expr.line)
def transform_unary_expr(builder: IRBuilder, expr: UnaryExpr) -> Value: folded = try_constant_fold(builder, expr) if folded: return folded return builder.unary_op(builder.accept(expr.expr), expr.op, expr.line)