Skip to content

Commit 41860a9

Browse files
committed
Fix async comprehension exception handler range and SEND stack depth
- Move PopBlock before compile_store in async comprehension so STORE_SUBSCR is not covered by the END_ASYNC_FOR exception handler - Add SEND jump target depth = depth - 1 in stackdepth calculation - Remove expectedFailure from three now-passing test_coroutines tests
1 parent ed903aa commit 41860a9

File tree

4 files changed

+19
-15
lines changed

4 files changed

+19
-15
lines changed

Lib/test/test_coroutines.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,7 +1364,6 @@ async def foo():
13641364
self.fail('invalid asynchronous context manager did not fail')
13651365

13661366

1367-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: "'async with' received an object from __aexit__ that does not implement __await__: int" does not match "'range_iterator' object is not callable"
13681367
def test_with_8(self):
13691368
CNT = 0
13701369

@@ -2170,7 +2169,6 @@ async def func(): pass
21702169
f"coroutine {coro_repr}")
21712170
self.assertIn("was never awaited", str(cm.unraisable.exc_value))
21722171

2173-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: StopAsyncIteration not raised
21742172
def test_for_assign_raising_stop_async_iteration(self):
21752173
class BadTarget:
21762174
def __setitem__(self, key, value):
@@ -2204,7 +2202,6 @@ async def run_gen():
22042202
return 'end'
22052203
self.assertEqual(run_async(run_gen()), ([], 'end'))
22062204

2207-
@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: StopAsyncIteration not raised
22082205
def test_for_assign_raising_stop_async_iteration_2(self):
22092206
class BadIterable:
22102207
def __iter__(self):

crates/codegen/src/compile.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7414,9 +7414,11 @@ impl Compiler {
74147414
)?;
74157415
self.emit_load_const(ConstantData::None);
74167416
self.compile_yield_from_sequence(true)?;
7417-
self.compile_store(&generator.target)?;
7417+
// POP_BLOCK before store: only __anext__/yield_from are
7418+
// protected by SetupFinally targeting END_ASYNC_FOR.
74187419
emit!(self, PseudoInstruction::PopBlock);
74197420
self.pop_fblock(FBlockType::AsyncComprehensionGenerator);
7421+
self.compile_store(&generator.target)?;
74207422
} else {
74217423
emit!(
74227424
self,

crates/codegen/src/ir.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,15 @@ impl CodeInfo {
837837
}
838838
stackdepth_push(&mut stack, &mut start_depths, ins.target, handler_depth);
839839
} else {
840-
let target_depth = depth.checked_add_signed(effect).ok_or({
841-
if effect < 0 {
840+
// SEND's jump path has different stack effect than
841+
// normal path: jump replaces receiver (depth - 1),
842+
// normal pushes above receiver (depth + 0).
843+
let jump_effect = match instr.real() {
844+
Some(Instruction::Send { .. }) => -1i32,
845+
_ => effect,
846+
};
847+
let target_depth = depth.checked_add_signed(jump_effect).ok_or({
848+
if jump_effect < 0 {
842849
InternalError::StackUnderflow
843850
} else {
844851
InternalError::StackOverflow

crates/vm/src/frame.rs

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -937,9 +937,9 @@ impl ExecutingFrame<'_> {
937937
Ok(None)
938938
}
939939
Instruction::EndAsyncFor => {
940-
// END_ASYNC_FOR pops (awaitable, exc) from stack
941-
// Stack: [awaitable, exc] -> []
942-
// exception_unwind pushes exception to stack before jumping to handler
940+
// Pops (awaitable, exc) from stack.
941+
// If exc is StopAsyncIteration, clears it (normal loop end).
942+
// Otherwise re-raises.
943943
let exc = self.pop_value();
944944
let _awaitable = self.pop_value();
945945

@@ -1979,22 +1979,20 @@ impl ExecutingFrame<'_> {
19791979
Ok(Some(ExecutionResult::Yield(value)))
19801980
}
19811981
Instruction::Send { target } => {
1982-
// Stack: (receiver, value) -> (receiver, retval)
1983-
// On StopIteration: replace value with stop value and jump to target
1982+
// (receiver, v -- receiver, retval)
1983+
// Pops v, sends it to receiver. On yield, pushes retval
1984+
// (so stack = [..., receiver, retval]). On return/StopIteration,
1985+
// also pushes retval and jumps to END_SEND which will pop receiver.
19841986
let exit_label = target.get(arg);
19851987
let val = self.pop_value();
19861988
let receiver = self.top_value();
19871989

19881990
match self._send(receiver, val, vm)? {
19891991
PyIterReturn::Return(value) => {
1990-
// Value yielded, push it back for YIELD_VALUE
1991-
// Stack: (receiver, retval)
19921992
self.push_value(value);
19931993
Ok(None)
19941994
}
19951995
PyIterReturn::StopIteration(value) => {
1996-
// StopIteration: replace top with stop value, jump to exit
1997-
// Stack: (receiver, value) - receiver stays, v replaced
19981996
let value = vm.unwrap_or_none(value);
19991997
self.push_value(value);
20001998
self.jump(exit_label);

0 commit comments

Comments
 (0)