Skip to content

Commit b25e850

Browse files
committed
Exclude async/await comprehensions from PEP 709 inlining in symboltable
Async comprehensions and comprehensions with await in the element expression need their own coroutine scope and cannot be inlined. The symboltable builder was not checking these conditions, causing incorrect symbol scope resolution when an async comprehension was nested inside an inlined comprehension (e.g. [[x async for x in g] for j in items]).
1 parent c18b464 commit b25e850

1 file changed

Lines changed: 24 additions & 1 deletion

File tree

crates/codegen/src/symboltable.rs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,26 @@ fn drop_class_free(symbol_table: &mut SymbolTable, newfree: &mut IndexSet<String
299299
}
300300
}
301301

302+
/// Check if an expression contains an `await` node (shallow, not into nested scopes).
303+
fn expr_contains_await(expr: &ast::Expr) -> bool {
304+
use ast::visitor::Visitor;
305+
struct AwaitFinder(bool);
306+
impl ast::visitor::Visitor<'_> for AwaitFinder {
307+
fn visit_expr(&mut self, expr: &ast::Expr) {
308+
if !self.0 {
309+
if matches!(expr, ast::Expr::Await(_)) {
310+
self.0 = true;
311+
} else {
312+
ast::visitor::walk_expr(self, expr);
313+
}
314+
}
315+
}
316+
}
317+
let mut finder = AwaitFinder(false);
318+
finder.visit_expr(expr);
319+
finder.0
320+
}
321+
302322
/// PEP 709: Merge symbols from an inlined comprehension into the parent scope.
303323
/// Matches symtable.c inline_comprehension().
304324
fn inline_comprehension(
@@ -2102,9 +2122,12 @@ impl SymbolTableBuilder {
21022122
// but only inside function-like scopes (fastlocals).
21032123
// Module/class scope uses STORE_NAME which is incompatible
21042124
// with LOAD_FAST_AND_CLEAR / STORE_FAST save/restore.
2125+
// Async comprehensions cannot be inlined because they need
2126+
// their own coroutine scope.
21052127
// Note: tables.last() is the comprehension scope we just pushed,
21062128
// so we check the second-to-last for the parent scope.
2107-
if !is_generator {
2129+
let element_has_await = expr_contains_await(elt1) || elt2.is_some_and(expr_contains_await);
2130+
if !is_generator && !has_async_gen && !element_has_await {
21082131
let parent = self.tables.iter().rev().nth(1);
21092132
let parent_is_func = parent.is_some_and(|t| {
21102133
matches!(

0 commit comments

Comments
 (0)