def create_switch_for_generator_class(builder: IRBuilder) -> None: builder.add(Goto(builder.fn_info.generator_class.switch_block)) block = BasicBlock() builder.fn_info.generator_class.continuation_blocks.append(block) builder.activate_block(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 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.primitive_op(not_implemented_op, [], line) builder.add(Branch( builder.binary_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)
def transform_generator_expr(builder: IRBuilder, o: GeneratorExpr) -> Value: builder.warning('Treating generator comprehension as list', o.line) return builder.call_c(iter_op, [translate_list_comprehension(builder, o)], o.line)
def allocate_class(builder: IRBuilder, cdef: ClassDef) -> Value: # OK AND NOW THE FUN PART base_exprs = cdef.base_type_exprs + cdef.removed_base_type_exprs if base_exprs: bases = [builder.accept(x) for x in base_exprs] tp_bases = builder.primitive_op(new_tuple_op, bases, cdef.line) else: tp_bases = builder.add(LoadErrorValue(object_rprimitive, is_borrowed=True)) modname = builder.load_static_unicode(builder.module_name) template = builder.add(LoadStatic(object_rprimitive, cdef.name + "_template", builder.module_name, NAMESPACE_TYPE)) # Create the class tp = builder.primitive_op(pytype_from_template_op, [template, tp_bases, modname], cdef.line) # Immediately fix up the trait vtables, before doing anything with the class. ir = builder.mapper.type_to_ir[cdef.info] if not ir.is_trait and not ir.builtin_base: builder.add(Call( FuncDecl(cdef.name + '_trait_vtable_setup', None, builder.module_name, FuncSignature([], bool_rprimitive)), [], -1)) # Populate a '__mypyc_attrs__' field containing the list of attrs builder.primitive_op(py_setattr_op, [ tp, builder.load_static_unicode('__mypyc_attrs__'), create_mypyc_attrs_tuple(builder, builder.mapper.type_to_ir[cdef.info], cdef.line)], cdef.line) # Save the class builder.add(InitStatic(tp, cdef.name, builder.module_name, NAMESPACE_TYPE)) # Add it to the dict builder.call_c(dict_set_item_op, [ builder.load_globals_dict(), builder.load_static_unicode(cdef.name), tp, ], cdef.line) return tp
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 transform_ellipsis(builder: IRBuilder, o: EllipsisExpr) -> Value: return builder.add(LoadAddress(ellipsis_op.type, ellipsis_op.src, o.line))
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 translate_globals(builder: IRBuilder, expr: CallExpr, callee: RefExpr) -> Optional[Value]: # Special case builtins.globals if len(expr.args) == 0: return builder.load_globals_dict() return None
def for_loop_helper(builder: IRBuilder, index: Lvalue, expr: Expression, body_insts: GenFunc, else_insts: Optional[GenFunc], line: int) -> None: """Generate IR for a loop. Args: index: the loop index Lvalue expr: the expression to iterate over body_insts: a function that generates the body of the loop else_insts: a function that generates the else block instructions """ # Body of the loop body_block = BasicBlock() # Block that steps to the next item step_block = BasicBlock() # Block for the else clause, if we need it else_block = BasicBlock() # Block executed after the loop exit_block = BasicBlock() # Determine where we want to exit, if our condition check fails. normal_loop_exit = else_block if else_insts is not None else exit_block for_gen = make_for_loop_generator(builder, index, expr, body_block, normal_loop_exit, line) builder.push_loop_stack(step_block, exit_block) condition_block = BasicBlock() builder.goto_and_activate(condition_block) # Add loop condition check. for_gen.gen_condition() # Generate loop body. builder.activate_block(body_block) for_gen.begin_body() body_insts() # We generate a separate step block (which might be empty). builder.goto_and_activate(step_block) for_gen.gen_step() # Go back to loop condition. builder.goto(condition_block) for_gen.add_cleanup(normal_loop_exit) builder.pop_loop_stack() if else_insts is not None: builder.activate_block(else_block) else_insts() builder.goto(exit_block) builder.activate_block(exit_block)
def for_loop_helper_with_index(builder: IRBuilder, index: Lvalue, expr: Expression, expr_reg: Value, body_insts: Callable[[Value], None], line: int) -> None: """Generate IR for a sequence iteration. This function only works for sequence type. Compared to for_loop_helper, it would feed iteration index to body_insts. Args: index: the loop index Lvalue expr: the expression to iterate over body_insts: a function that generates the body of the loop. It needs a index as parameter. """ assert is_sequence_rprimitive(expr_reg.type) target_type = builder.get_sequence_type(expr) body_block = BasicBlock() step_block = BasicBlock() exit_block = BasicBlock() condition_block = BasicBlock() for_gen = ForSequence(builder, index, body_block, exit_block, line, False) for_gen.init(expr_reg, target_type, reverse=False) builder.push_loop_stack(step_block, exit_block) builder.goto_and_activate(condition_block) for_gen.gen_condition() builder.activate_block(body_block) for_gen.begin_body() body_insts(builder.read(for_gen.index_target)) builder.goto_and_activate(step_block) for_gen.gen_step() builder.goto(condition_block) for_gen.add_cleanup(exit_block) builder.pop_loop_stack() builder.activate_block(exit_block)
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 add_raise_exception_blocks_to_generator_class(builder: IRBuilder, line: int) -> None: """ 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.binary_op(exc_type, builder.none_object(), 'is not', line) builder.add_bool_branch(comparison, error_block, ok_block) builder.activate_block(error_block) builder.primitive_op(raise_exception_with_tb_op, [exc_type, exc_val, exc_tb], line) builder.add(Unreachable()) builder.goto_and_activate(ok_block)
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())
def transform_name_expr(builder: IRBuilder, expr: NameExpr) -> Value: assert expr.node, "RefExpr not resolved" fullname = expr.node.fullname if fullname in builtin_names: typ, src = builtin_names[fullname] return builder.add(LoadAddress(typ, src, expr.line)) # special cases if fullname == 'builtins.None': return builder.none() if fullname == 'builtins.True': return builder.true() if fullname == 'builtins.False': return builder.false() if isinstance(expr.node, Var) and expr.node.is_final: value = builder.emit_load_final( expr.node, fullname, expr.name, builder.is_native_ref_expr(expr), builder.types[expr], expr.line, ) if value is not None: return value if isinstance(expr.node, MypyFile) and expr.node.fullname in builder.imports: return builder.load_module(expr.node.fullname) # If the expression is locally defined, then read the result from the corresponding # assignment target and return it. Otherwise if the expression is a global, load it from # the globals dictionary. # Except for imports, that currently always happens in the global namespace. if expr.kind == LDEF and not (isinstance(expr.node, Var) and expr.node.is_suppressed_import): # Try to detect and error when we hit the irritating mypy bug # where a local variable is cast to None. (#5423) if (isinstance(expr.node, Var) and is_none_rprimitive(builder.node_type(expr)) and expr.node.is_inferred): builder.error( "Local variable '{}' has inferred type None; add an annotation" .format(expr.node.name), expr.node.line) # TODO: Behavior currently only defined for Var, FuncDef and MypyFile node types. if isinstance(expr.node, MypyFile): # Load reference to a module imported inside function from # the modules dictionary. It would be closer to Python # semantics to access modules imported inside functions # via local variables, but this is tricky since the mypy # AST doesn't include a Var node for the module. We # instead load the module separately on each access. mod_dict = builder.call_c(get_module_dict_op, [], expr.line) obj = builder.call_c( dict_get_item_op, [mod_dict, builder.load_static_unicode(expr.node.fullname)], expr.line) return obj else: return builder.read(builder.get_assignment_target(expr), expr.line) return builder.load_global(expr)
def transform_mypy_file(builder: IRBuilder, mypyfile: MypyFile) -> None: """Generate IR for a single module.""" if mypyfile.fullname in ('typing', 'abc'): # These module are special; their contents are currently all # built-in primitives. return builder.set_module(mypyfile.fullname, mypyfile.path) classes = [node for node in mypyfile.defs if isinstance(node, ClassDef)] # Collect all classes. for cls in classes: ir = builder.mapper.type_to_ir[cls.info] builder.classes.append(ir) builder.enter('<top level>') # Make sure we have a builtins import builder.gen_import('builtins', -1) # Generate ops. for node in mypyfile.defs: builder.accept(node) builder.maybe_add_implicit_return() # Generate special function representing module top level. args, _, blocks, ret_type, _ = builder.leave() sig = FuncSignature([], none_rprimitive) func_ir = FuncIR(FuncDecl(TOP_LEVEL_NAME, None, builder.module_name, sig), args, blocks, traceback_name="<module>") builder.functions.append(func_ir)
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 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: cls_type = dataclass_type(cdef) if cls_type is None: cls_builder: ClassBuilder = ExtClassBuilder(builder, cdef) elif cls_type in ['dataclasses', 'attr-auto']: cls_builder = DataClassBuilder(builder, cdef) elif cls_type == 'attr': cls_builder = AttrsClassBuilder(builder, cdef) else: raise ValueError(cls_type) else: cls_builder = NonExtClassBuilder(builder, cdef) for stmt in cdef.defs.body: if isinstance(stmt, OverloadedFuncDef) and stmt.is_property: if isinstance(cls_builder, NonExtClassBuilder): # 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): cls_builder.add_method(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): cls_builder.add_method(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. cls_builder.add_attr(lvalue, stmt) elif isinstance(stmt, ExpressionStmt) and isinstance( stmt.expr, StrExpr): # Docstring. Ignore pass else: builder.error("Unsupported statement in class body", stmt.line) cls_builder.finalize(ir)
def transform_str_expr(builder: IRBuilder, expr: StrExpr) -> Value: return builder.load_static_unicode(expr.value)
def generate_attr_defaults_init( builder: IRBuilder, cdef: ClassDef, default_assignments: List[AssignmentStmt]) -> None: """Generate an initialization method for default attr values (from class vars).""" if not default_assignments: return cls = builder.mapper.type_to_ir[cdef.info] if cls.builtin_base: return with builder.enter_method(cls, '__mypyc_defaults_setup', bool_rprimitive): self_var = builder.self() 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) attr_type = cls.attr_type(lvalue.name) val = builder.coerce(builder.accept(stmt.rvalue), attr_type, stmt.line) init = SetAttr(self_var, lvalue.name, val, -1) init.mark_as_initializer() builder.add(init) builder.add(Return(builder.true()))
def _visit_tuple_display(builder: IRBuilder, expr: TupleExpr) -> Value: """Create a list, then turn it into a tuple.""" val_as_list = _visit_list_display(builder, expr.items, expr.line) return builder.call_c(list_tuple_op, [val_as_list], expr.line)
def gen_glue_ne_method(builder: IRBuilder, cls: ClassIR, line: int) -> None: """Generate a "__ne__" method from a "__eq__" method. """ with 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))
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 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.primitive_op(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 transform_unary_expr(builder: IRBuilder, expr: UnaryExpr) -> Value: return builder.unary_op(builder.accept(expr.expr), expr.op, expr.line)
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 = allocate_class(builder, cdef) # type: Optional[Value] non_ext = None # type: Optional[NonExtClassInfo] 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 type_obj = None attrs_to_cache = [] # type: List[Lvalue] 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.primitive_op( py_setattr_op, [typ, builder.load_static_unicode(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_static_unicode(cdef.name), non_ext_class ], cdef.line) # Cache any cachable class attributes cache_class_attrs(builder, attrs_to_cache, cdef)
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 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 gen_generator_func(builder: IRBuilder) -> None: setup_generator_class(builder) load_env_registers(builder) gen_arg_defaults(builder) finalize_env_class(builder) builder.add(Return(instantiate_generator_class(builder)))