From 4ab3f0f736df4bda33464b883e36b577fe186021 Mon Sep 17 00:00:00 2001 From: Jukka Lehtosalo Date: Wed, 4 Jun 2025 11:29:39 +0100 Subject: [PATCH] [mypyc] Free generator after await encounters StopIteration Previously the awaited generator could stay alive until the generator that performed the await was freed, delaying object reclamation. The refcount analysis doesn't understand registers spilled to the environment, so we need to manually clear the value. Consider code like this: ``` async def foo() -> None: await bar() await zar() ``` Previously, the `bar()` generator was only freed at end of `foo()`. Now we release it before `await zar()`, as expected. --- mypyc/irbuild/statement.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mypyc/irbuild/statement.py b/mypyc/irbuild/statement.py index b109d925558b..16a0483a8729 100644 --- a/mypyc/irbuild/statement.py +++ b/mypyc/irbuild/statement.py @@ -940,6 +940,10 @@ def emit_yield_from_or_await( # If it wasn't, this reraises the exception. builder.activate_block(stop_block) builder.assign(result, builder.call_c(check_stop_op, [], line), line) + # Clear the spilled iterator/coroutine so that it will be freed. + # Otherwise, the freeing of the spilled register would likely be delayed. + err = builder.add(LoadErrorValue(object_rprimitive)) + builder.assign(iter_reg, err, line) builder.goto(done_block) builder.activate_block(main_block)