Skip to content

[mypyc] Add prefix to attributes of generator classes #19535

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion mypyc/irbuild/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1325,10 +1325,11 @@ def add_var_to_env_class(
base: FuncInfo | ImplicitClass,
reassign: bool = False,
always_defined: bool = False,
prefix: str = "",
) -> AssignmentTarget:
# First, define the variable name as an attribute of the environment class, and then
# construct a target for that attribute.
name = remangle_redefinition_name(var.name)
name = prefix + remangle_redefinition_name(var.name)
self.fn_info.env_class.attributes[name] = rtype
if always_defined:
self.fn_info.env_class.attrs_with_defaults.add(name)
Expand Down
28 changes: 18 additions & 10 deletions mypyc/irbuild/env_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class is generated, the function environment has not yet been
return env_class


def finalize_env_class(builder: IRBuilder) -> None:
def finalize_env_class(builder: IRBuilder, prefix: str = "") -> None:
"""Generate, instantiate, and set up the environment of an environment class."""
if not builder.fn_info.can_merge_generator_and_env_classes():
instantiate_env_class(builder)
Expand All @@ -69,9 +69,9 @@ def finalize_env_class(builder: IRBuilder) -> None:
# that were previously added to the environment with references to the function's
# environment class.
if builder.fn_info.is_nested:
add_args_to_env(builder, local=False, base=builder.fn_info.callable_class)
add_args_to_env(builder, local=False, base=builder.fn_info.callable_class, prefix=prefix)
else:
add_args_to_env(builder, local=False, base=builder.fn_info)
add_args_to_env(builder, local=False, base=builder.fn_info, prefix=prefix)


def instantiate_env_class(builder: IRBuilder) -> Value:
Expand All @@ -96,15 +96,15 @@ def instantiate_env_class(builder: IRBuilder) -> Value:
return curr_env_reg


def load_env_registers(builder: IRBuilder) -> None:
def load_env_registers(builder: IRBuilder, prefix: str = "") -> None:
"""Load the registers for the current FuncItem being visited.

Adds the arguments of the FuncItem to the environment. If the
FuncItem is nested inside of another function, then this also
loads all of the outer environments of the FuncItem into registers
so that they can be used when accessing free variables.
"""
add_args_to_env(builder, local=True)
add_args_to_env(builder, local=True, prefix=prefix)

fn_info = builder.fn_info
fitem = fn_info.fitem
Expand Down Expand Up @@ -134,8 +134,11 @@ def load_outer_env(
assert isinstance(env.type, RInstance), f"{env} must be of type RInstance"

for symbol, target in outer_env.items():
env.type.class_ir.attributes[symbol.name] = target.type
symbol_target = AssignmentTargetAttr(env, symbol.name)
attr_name = symbol.name
if isinstance(target, AssignmentTargetAttr):
attr_name = target.attr
env.type.class_ir.attributes[attr_name] = target.type
symbol_target = AssignmentTargetAttr(env, attr_name)
builder.add_target(symbol, symbol_target)

return env
Expand Down Expand Up @@ -178,6 +181,7 @@ def add_args_to_env(
local: bool = True,
base: FuncInfo | ImplicitClass | None = None,
reassign: bool = True,
prefix: str = "",
) -> None:
fn_info = builder.fn_info
args = fn_info.fitem.arguments
Expand All @@ -193,10 +197,12 @@ def add_args_to_env(
if is_free_variable(builder, arg.variable) or fn_info.is_generator:
rtype = builder.type_to_rtype(arg.variable.type)
assert base is not None, "base cannot be None for adding nonlocal args"
builder.add_var_to_env_class(arg.variable, rtype, base, reassign=reassign)
builder.add_var_to_env_class(
arg.variable, rtype, base, reassign=reassign, prefix=prefix
)


def add_vars_to_env(builder: IRBuilder) -> None:
def add_vars_to_env(builder: IRBuilder, prefix: str = "") -> None:
"""Add relevant local variables and nested functions to the environment class.

Add all variables and functions that are declared/defined within current
Expand All @@ -216,7 +222,9 @@ def add_vars_to_env(builder: IRBuilder) -> None:
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)
builder.add_var_to_env_class(
var, rtype, env_for_func, reassign=False, prefix=prefix
)

if builder.fn_info.fitem in builder.encapsulating_funcs:
for nested_fn in builder.encapsulating_funcs[builder.fn_info.fitem]:
Expand Down
14 changes: 9 additions & 5 deletions mypyc/irbuild/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
restore_exc_info_op,
)

GENERATOR_ATTRIBUTE_PREFIX = "__mypyc_generator_attribute__"


def gen_generator_func(
builder: IRBuilder,
Expand All @@ -68,14 +70,14 @@ def gen_generator_func(
) -> tuple[FuncIR, Value | None]:
"""Generate IR for generator function that returns generator object."""
setup_generator_class(builder)
load_env_registers(builder)
load_env_registers(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
gen_arg_defaults(builder)
if builder.fn_info.can_merge_generator_and_env_classes():
gen = instantiate_generator_class(builder)
builder.fn_info._curr_env_reg = gen
finalize_env_class(builder)
finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
else:
finalize_env_class(builder)
finalize_env_class(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)
gen = instantiate_generator_class(builder)
builder.add(Return(gen))

Expand Down Expand Up @@ -108,7 +110,7 @@ class that implements the function (each function gets a separate class).
create_switch_for_generator_class(builder)
add_raise_exception_blocks_to_generator_class(builder, fitem.line)

add_vars_to_env(builder)
add_vars_to_env(builder, prefix=GENERATOR_ATTRIBUTE_PREFIX)

builder.accept(fitem.body)
builder.maybe_add_implicit_return()
Expand Down Expand Up @@ -429,7 +431,9 @@ def setup_env_for_generator_class(builder: IRBuilder) -> None:

# Add arguments from the original generator function to the
# environment of the generator class.
add_args_to_env(builder, local=False, base=cls, reassign=False)
add_args_to_env(
builder, local=False, base=cls, reassign=False, prefix=GENERATOR_ATTRIBUTE_PREFIX
)

# Set the next label register for the generator class.
cls.next_label_reg = builder.read(cls.next_label_target, fitem.line)
28 changes: 27 additions & 1 deletion mypyc/test-data/run-async.test
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,7 @@ async def async_def_contains_normal(x: int) -> int:
return a

def test_async_def_contains_normal() -> None:
assert normal_contains_async_def(2) == (2 + 2 + 4 + 5)
assert asyncio.run(async_def_contains_normal(2)) == (2 + 2 + 4 + 5)

async def async_def_contains_async_def(x: int) -> int:
async def f(y: int) -> int:
Expand Down Expand Up @@ -1248,3 +1248,29 @@ def test_async_context_manager_exception_handling() -> None:
[file asyncio/__init__.pyi]
async def sleep(t: float) -> None: ...
def run(x: object) -> object: ...

[case testCallableArgWithSameNameAsHelperMethod]
import asyncio
from typing import Awaitable, Callable


MyCallable = Callable[[int, int], Awaitable[int]]

async def add(a: int, b: int) -> int:
return a + b

async def await_send(send: MyCallable) -> int:
return await send(1, 2)

async def await_throw(throw: MyCallable) -> int:
return await throw(3, 4)

async def tests() -> None:
assert await await_send(add) == 3
assert await await_throw(add) == 7

def test_callable_arg_same_name_as_helper() -> None:
asyncio.run(tests())

[file asyncio/__init__.pyi]
def run(x: object) -> object: ...
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy