-
-
Notifications
You must be signed in to change notification settings - Fork 3k
[mypyc] Refactor IR building for generator functions #19008
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 16 commits
fb2d005
9fb14ea
90af722
edbe7ca
476ebe1
756e362
d736b9f
a65ca35
52c8989
368db2d
8f3d0bf
300f2aa
3f1bcc8
4e60d48
3091e55
0c28453
2c495c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,22 +69,14 @@ | |
| instantiate_callable_class, | ||
| setup_callable_class, | ||
| ) | ||
| from mypyc.irbuild.context import FuncInfo, ImplicitClass | ||
| from mypyc.irbuild.context import FuncInfo | ||
| from mypyc.irbuild.env_class import ( | ||
| add_vars_to_env, | ||
| finalize_env_class, | ||
| load_env_registers, | ||
| load_outer_envs, | ||
| setup_env_class, | ||
| setup_func_for_recursive_call, | ||
| ) | ||
| from mypyc.irbuild.generator import ( | ||
| add_methods_to_generator_class, | ||
| add_raise_exception_blocks_to_generator_class, | ||
| create_switch_for_generator_class, | ||
| gen_generator_func, | ||
| populate_switch_for_generator_class, | ||
| setup_env_for_generator_class, | ||
| ) | ||
| from mypyc.irbuild.generator import gen_generator_func, gen_generator_func_body | ||
| from mypyc.irbuild.targets import AssignmentTarget | ||
| from mypyc.irbuild.util import is_constant | ||
| from mypyc.primitives.dict_ops import dict_get_method_with_none, dict_new_op, dict_set_item_op | ||
|
|
@@ -235,123 +227,81 @@ def c() -> None: | |
| func_name = singledispatch_main_func_name(name) | ||
| else: | ||
| func_name = name | ||
| builder.enter( | ||
| FuncInfo( | ||
| fitem=fitem, | ||
| name=func_name, | ||
| class_name=class_name, | ||
| namespace=gen_func_ns(builder), | ||
| is_nested=is_nested, | ||
| contains_nested=contains_nested, | ||
| is_decorated=is_decorated, | ||
| in_non_ext=in_non_ext, | ||
| add_nested_funcs_to_env=add_nested_funcs_to_env, | ||
| ) | ||
|
|
||
| fn_info = FuncInfo( | ||
| fitem=fitem, | ||
| name=func_name, | ||
| class_name=class_name, | ||
| namespace=gen_func_ns(builder), | ||
| is_nested=is_nested, | ||
| contains_nested=contains_nested, | ||
| is_decorated=is_decorated, | ||
| in_non_ext=in_non_ext, | ||
| add_nested_funcs_to_env=add_nested_funcs_to_env, | ||
| ) | ||
| is_generator = fn_info.is_generator | ||
| builder.enter(fn_info, ret_type=sig.ret_type) | ||
|
|
||
| # 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: | ||
| if contains_nested or is_generator: | ||
| setup_env_class(builder) | ||
|
|
||
| if builder.fn_info.is_nested or builder.fn_info.in_non_ext: | ||
| if is_nested or 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) | ||
| args, _, blocks, ret_type, fn_info = builder.leave() | ||
| func_ir, func_reg = gen_func_ir( | ||
| builder, args, blocks, sig, fn_info, cdef, is_singledispatch | ||
| if is_generator: | ||
| # First generate a function that just constructs and returns a generator object. | ||
| func_ir, func_reg = gen_generator_func( | ||
| builder, | ||
| lambda args, blocks, fn_info: gen_func_ir( | ||
| builder, args, blocks, sig, fn_info, cdef, is_singledispatch | ||
| ), | ||
| ) | ||
|
|
||
| # Re-enter the FuncItem and visit the body of the function this time. | ||
| builder.enter(fn_info) | ||
| setup_env_for_generator_class(builder) | ||
| symtable = gen_generator_func_body(builder, fn_info, sig) | ||
|
|
||
| load_outer_envs(builder, builder.fn_info.generator_class) | ||
| top_level = builder.top_level_fn_info() | ||
| if ( | ||
| builder.fn_info.is_nested | ||
| and isinstance(fitem, FuncDef) | ||
| and top_level | ||
| and top_level.add_nested_funcs_to_env | ||
| ): | ||
| 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) | ||
| # Evaluate argument defaults in the surrounding scope, since we | ||
| # calculate them *once* when the function definition is evaluated. | ||
| calculate_arg_defaults(builder, fn_info, func_reg, symtable) | ||
|
||
| else: | ||
| load_env_registers(builder) | ||
| gen_arg_defaults(builder) | ||
| func_ir, func_reg = gen_func_body(builder, sig, cdef, is_singledispatch) | ||
|
|
||
| if builder.fn_info.contains_nested and not builder.fn_info.is_generator: | ||
| finalize_env_class(builder) | ||
| if is_singledispatch: | ||
| # add the generated main singledispatch function | ||
| builder.functions.append(func_ir) | ||
| # create the dispatch function | ||
| assert isinstance(fitem, FuncDef) | ||
| return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig) | ||
|
|
||
| builder.ret_types[-1] = sig.ret_type | ||
| return func_ir, func_reg | ||
|
|
||
| # Add all variables and functions that are declared/defined within this | ||
|
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The following code was moved to |
||
| # 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: FuncInfo | ImplicitClass = builder.fn_info | ||
| 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) | ||
| def gen_func_body( | ||
| builder: IRBuilder, sig: FuncSignature, cdef: ClassDef | None, is_singledispatch: bool | ||
| ) -> tuple[FuncIR, Value | None]: | ||
| load_env_registers(builder) | ||
| gen_arg_defaults(builder) | ||
| if builder.fn_info.contains_nested: | ||
| finalize_env_class(builder) | ||
| add_vars_to_env(builder) | ||
| builder.accept(builder.fn_info.fitem.body) | ||
| builder.maybe_add_implicit_return() | ||
|
|
||
| if builder.fn_info.is_generator: | ||
| populate_switch_for_generator_class(builder) | ||
|
|
||
| # Hang on to the local symbol table for a while, since we use it | ||
| # to calculate argument defaults below. | ||
| symtable = builder.symtables[-1] | ||
|
|
||
| args, _, blocks, ret_type, fn_info = builder.leave() | ||
|
|
||
| if fn_info.is_generator: | ||
| add_methods_to_generator_class(builder, fn_info, sig, args, blocks, fitem.is_coroutine) | ||
| else: | ||
| func_ir, func_reg = gen_func_ir( | ||
| builder, args, blocks, sig, fn_info, cdef, is_singledispatch | ||
| ) | ||
| func_ir, func_reg = gen_func_ir(builder, args, blocks, sig, fn_info, cdef, is_singledispatch) | ||
|
|
||
| # Evaluate argument defaults in the surrounding scope, since we | ||
| # calculate them *once* when the function definition is evaluated. | ||
| calculate_arg_defaults(builder, fn_info, func_reg, symtable) | ||
|
|
||
| if is_singledispatch: | ||
| # add the generated main singledispatch function | ||
| builder.functions.append(func_ir) | ||
| # create the dispatch function | ||
| assert isinstance(fitem, FuncDef) | ||
| return gen_dispatch_func_ir(builder, fitem, fn_info.name, name, sig) | ||
|
|
||
| return func_ir, func_reg | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this code here since this is used for both regular and generator functions.