From b796a8d4884ffef869381367bdd801e28a72bd3d Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Mon, 26 Jan 2026 16:36:53 +0100 Subject: [PATCH 1/3] Fix attribute name for nested coroutines --- mypyc/irbuild/builder.py | 2 +- mypyc/irbuild/env_class.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mypyc/irbuild/builder.py b/mypyc/irbuild/builder.py index 3f5a05386e7a7..164765dd8db42 100644 --- a/mypyc/irbuild/builder.py +++ b/mypyc/irbuild/builder.py @@ -657,7 +657,7 @@ def get_assignment_target( # refers to the newly defined variable in that environment class. Add the # target to the table containing class environment variables, as well as the # current environment. - if self.fn_info.is_generator: + if self.fn_info.is_generator or self.fn_info.is_coroutine: return self.add_var_to_env_class( symbol, reg_type, diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index 2a9cc50d9ccf1..1a72b82e9a10a 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -200,7 +200,7 @@ def add_args_to_env( builder.add_local_reg(Var(bitmap_name(i)), bitmap_rprimitive, is_arg=True) else: for arg in args: - if is_free_variable(builder, arg.variable) or fn_info.is_generator: + if is_free_variable(builder, arg.variable) or fn_info.is_generator or fn_info.is_coroutine: 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( @@ -240,7 +240,7 @@ def add_vars_to_env(builder: IRBuilder, prefix: str = "") -> None: # the same name and signature across conditional blocks # will generate different callable classes, so the callable # class that gets instantiated must be generic. - if nested_fn.is_generator: + if nested_fn.is_generator or nested_fn.is_coroutine: prefix = GENERATOR_ATTRIBUTE_PREFIX builder.add_var_to_env_class( nested_fn, object_rprimitive, env_for_func, reassign=False, prefix=prefix From 8bb3a6bb141a3d64de826eabc18886c31cd99c08 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 27 Jan 2026 14:25:27 +0100 Subject: [PATCH 2/3] Add test --- mypyc/test-data/run-async.test | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/mypyc/test-data/run-async.test b/mypyc/test-data/run-async.test index f0320e60ee1a4..3b1bd9def18cf 100644 --- a/mypyc/test-data/run-async.test +++ b/mypyc/test-data/run-async.test @@ -1799,3 +1799,49 @@ for i in range(10): from typing import Any, Generator def run(x: object) -> object: ... + +[case testNestedCoroutineCallsAnotherNestedFunction] +import asyncio +import functools +import inspect +from typing import Any, Callable, TypeVar, cast + +F = TypeVar("F", bound=Callable[..., Any]) + + +def mult(x: int) -> Callable[[F], F]: + def decorate(fn: F) -> F: + def get_multiplier() -> int: + return x + + if inspect.iscoroutinefunction(fn): + @functools.wraps(fn) + async def wrapper_async(*args, **kwargs) -> Any: + return get_multiplier() * await fn(*args, **kwargs) + wrapper = wrapper_async + else: + @functools.wraps(fn) + def wrapper_non_async(*args, **kwargs) -> Any: + return get_multiplier() * fn(*args, **kwargs) + wrapper = wrapper_non_async + + return cast(F, wrapper) + + return decorate + +@mult(3) +def identity(x: int): + return x + +@mult(5) +async def async_identity(x: int): + return x + +def test_nested_coroutine_calls_another_nested_function(): + assert identity(1) == 3 + assert asyncio.run(async_identity(2)) == 10 + +[file asyncio/__init__.pyi] +from typing import Any, Generator + +def run(x: object) -> object: ... From 77a8818b93a7a495f7ef75b69aef7b3ad237fca3 Mon Sep 17 00:00:00 2001 From: Piotr Sawicki Date: Tue, 27 Jan 2026 14:25:53 +0100 Subject: [PATCH 3/3] lint --- mypyc/irbuild/env_class.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mypyc/irbuild/env_class.py b/mypyc/irbuild/env_class.py index 1a72b82e9a10a..a693405178631 100644 --- a/mypyc/irbuild/env_class.py +++ b/mypyc/irbuild/env_class.py @@ -200,7 +200,11 @@ def add_args_to_env( builder.add_local_reg(Var(bitmap_name(i)), bitmap_rprimitive, is_arg=True) else: for arg in args: - if is_free_variable(builder, arg.variable) or fn_info.is_generator or fn_info.is_coroutine: + if ( + is_free_variable(builder, arg.variable) + or fn_info.is_generator + or fn_info.is_coroutine + ): 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(