Skip to content

Commit 408255b

Browse files
committed
Exclude async comprehensions from PEP 709 inlining
Async comprehensions require SetupFinally/EndAsyncFor exception handling for StopAsyncIteration which has complex stack interactions when inlined. Fall back to the non-inlined code object path for comprehensions with async generators or await expressions.
1 parent e863384 commit 408255b

File tree

1 file changed

+13
-42
lines changed

1 file changed

+13
-42
lines changed

crates/codegen/src/compile.rs

Lines changed: 13 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7656,15 +7656,15 @@ impl Compiler {
76567656
// We must have at least one generator:
76577657
assert!(!generators.is_empty());
76587658

7659-
if is_inlined {
7659+
if is_inlined && !has_an_async_gen && !element_contains_await {
76607660
// PEP 709: Inlined comprehension - compile inline without new scope
7661+
// Async comprehensions are not inlined due to complex exception handling
76617662
let was_in_inlined_comp = self.current_code_info().in_inlined_comp;
76627663
self.current_code_info().in_inlined_comp = true;
76637664
let result = self.compile_inlined_comprehension(
76647665
init_collection,
76657666
generators,
76667667
compile_element,
7667-
has_an_async_gen,
76687668
);
76697669
self.current_code_info().in_inlined_comp = was_in_inlined_comp;
76707670
return result;
@@ -7854,7 +7854,6 @@ impl Compiler {
78547854
init_collection: Option<AnyInstruction>,
78557855
generators: &[ast::Comprehension],
78567856
compile_element: &dyn Fn(&mut Self) -> CompileResult<()>,
7857-
_has_an_async_gen: bool,
78587857
) -> CompileResult<()> {
78597858
// PEP 709: Consume the comprehension's sub_table (but we won't use it as a separate scope).
78607859
// The symbols are already merged into parent scope by analyze_symbol_table.
@@ -7882,13 +7881,9 @@ impl Compiler {
78827881
}
78837882

78847883
// Step 1: Compile the outermost iterator
7884+
// Async comprehensions are never inlined, so only sync iteration here
78857885
self.compile_expression(&generators[0].iter)?;
7886-
// Use is_async from the first generator, not has_an_async_gen which covers ALL generators
7887-
if generators[0].is_async {
7888-
emit!(self, Instruction::GetAIter);
7889-
} else {
7890-
emit!(self, Instruction::GetIter);
7891-
}
7886+
emit!(self, Instruction::GetIter);
78927887

78937888
// Step 2: Save local variables that will be shadowed by the comprehension
78947889
for name in &pushed_locals {
@@ -7938,32 +7933,15 @@ impl Compiler {
79387933
if i > 0 {
79397934
// For nested loops, compile the iterator expression
79407935
self.compile_expression(&generator.iter)?;
7941-
if generator.is_async {
7942-
emit!(self, Instruction::GetAIter);
7943-
} else {
7944-
emit!(self, Instruction::GetIter);
7945-
}
7936+
emit!(self, Instruction::GetIter);
79467937
}
79477938

79487939
self.switch_to_block(loop_block);
7949-
let mut end_async_for_target = BlockIdx::NULL;
79507940

7951-
if generator.is_async {
7952-
emit!(self, Instruction::GetANext);
7953-
self.emit_load_const(ConstantData::None);
7954-
end_async_for_target = self.compile_yield_from_sequence(true)?;
7955-
self.compile_store(&generator.target)?;
7956-
} else {
7957-
emit!(self, Instruction::ForIter { delta: after_block });
7958-
self.compile_store(&generator.target)?;
7959-
}
7960-
loop_labels.push((
7961-
loop_block,
7962-
if_cleanup_block,
7963-
after_block,
7964-
generator.is_async,
7965-
end_async_for_target,
7966-
));
7941+
emit!(self, Instruction::ForIter { delta: after_block });
7942+
self.compile_store(&generator.target)?;
7943+
7944+
loop_labels.push((loop_block, if_cleanup_block, after_block));
79677945

79687946
// Evaluate the if conditions
79697947
for if_condition in &generator.ifs {
@@ -7974,8 +7952,8 @@ impl Compiler {
79747952
// Step 6: Compile the element expression and append to collection
79757953
compile_element(self)?;
79767954

7977-
// Step 7: Close all loops
7978-
for (loop_block, if_cleanup_block, after_block, is_async, end_async_for_target) in
7955+
// Step 7: Close all loops (sync only - async comprehensions are never inlined)
7956+
for (loop_block, if_cleanup_block, after_block) in
79797957
loop_labels.iter().rev().copied()
79807958
{
79817959
emit!(self, PseudoInstruction::Jump { delta: loop_block });
@@ -7984,15 +7962,8 @@ impl Compiler {
79847962
emit!(self, PseudoInstruction::Jump { delta: loop_block });
79857963

79867964
self.switch_to_block(after_block);
7987-
if is_async {
7988-
self.emit_end_async_for(end_async_for_target);
7989-
// Pop the iterator
7990-
emit!(self, Instruction::PopTop);
7991-
} else {
7992-
// END_FOR + POP_ITER pattern (CPython 3.14)
7993-
emit!(self, Instruction::EndFor);
7994-
emit!(self, Instruction::PopIter);
7995-
}
7965+
emit!(self, Instruction::EndFor);
7966+
emit!(self, Instruction::PopIter);
79967967
}
79977968

79987969
// Step 8: Clean up - restore saved locals

0 commit comments

Comments
 (0)