diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 35be60b9d1..85b723794d 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2633,8 +2633,6 @@ class PEP626Tests(unittest.TestCase): f.__code__ = f.__code__.replace(co_linetable=b'\x04\x80\xff\x80') self.lineno_after_raise(f, None) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_lineno_after_raise_in_with_exit(self): class ExitFails: def __enter__(self): diff --git a/bytecode/src/lib.rs b/bytecode/src/lib.rs index e5419308f4..cf011b458f 100644 --- a/bytecode/src/lib.rs +++ b/bytecode/src/lib.rs @@ -695,17 +695,39 @@ impl CodeObject { level: usize, ) -> fmt::Result { let label_targets = self.label_targets(); - + let line_digits = (3).max(self.locations.last().unwrap().row.to_string().len()); + let offset_digits = (4).max(self.instructions.len().to_string().len()); + let mut last_line = u32::MAX; for (offset, instruction) in self.instructions.iter().enumerate() { + // optional line number + let line = self.locations[offset].row; + if line != last_line { + if last_line != u32::MAX { + writeln!(f)?; + } + last_line = line; + write!(f, "{line:line_digits$}")?; + } else { + for _ in 0..line_digits { + write!(f, " ")?; + } + } + write!(f, " ")?; + + // level indent + for _ in 0..level { + write!(f, " ")?; + } + + // arrow and offset let arrow = if label_targets.contains(&Label(offset as u32)) { ">>" } else { " " }; - for _ in 0..level { - write!(f, " ")?; - } - write!(f, "{} {:5} ", arrow, offset)?; + write!(f, "{arrow} {offset:offset_digits$} ")?; + + // instruction instruction.fmt_dis( f, &self.constants, diff --git a/compiler/src/compile.rs b/compiler/src/compile.rs index f183433e89..8c8438aee4 100644 --- a/compiler/src/compile.rs +++ b/compiler/src/compile.rs @@ -1393,54 +1393,69 @@ impl Compiler { body: &[ast::Stmt], is_async: bool, ) -> CompileResult<()> { - let end_blocks = items - .iter() - .map(|item| { - let end_block = self.new_block(); - self.compile_expression(&item.context_expr)?; + let with_location = self.current_source_location; - if is_async { - self.emit(Instruction::BeforeAsyncWith); - self.emit(Instruction::GetAwaitable); - self.emit_constant(ConstantData::None); - self.emit(Instruction::YieldFrom); - self.emit(Instruction::SetupAsyncWith { end: end_block }); - } else { - self.emit(Instruction::SetupWith { end: end_block }); - } + let (item, items) = if let Some(parts) = items.split_first() { + parts + } else { + return Err(self.error(CompileErrorType::EmptyWithItems)); + }; - match &item.optional_vars { - Some(var) => { - self.compile_store(var)?; - } - None => { - self.emit(Instruction::Pop); - } - } - Ok(end_block) - }) - .collect::>>()?; - - self.compile_statements(body)?; - - // sort of "stack up" the layers of with blocks: - // with a, b: body -> start_with(a) start_with(b) body() end_with(b) end_with(a) - for end_block in end_blocks.into_iter().rev() { - self.emit(Instruction::PopBlock); - self.emit(Instruction::EnterFinally); - - self.switch_to_block(end_block); - self.emit(Instruction::WithCleanupStart); + let final_block = { + let final_block = self.new_block(); + self.compile_expression(&item.context_expr)?; + self.set_source_location(with_location); if is_async { + self.emit(Instruction::BeforeAsyncWith); self.emit(Instruction::GetAwaitable); self.emit_constant(ConstantData::None); self.emit(Instruction::YieldFrom); + self.emit(Instruction::SetupAsyncWith { end: final_block }); + } else { + self.emit(Instruction::SetupWith { end: final_block }); } - self.emit(Instruction::WithCleanupFinish); + match &item.optional_vars { + Some(var) => { + self.set_source_location(var.location); + self.compile_store(var)?; + } + None => { + self.emit(Instruction::Pop); + } + } + final_block + }; + + if items.is_empty() { + if body.is_empty() { + return Err(self.error(CompileErrorType::EmptyWithBody)); + } + self.compile_statements(body)?; + } else { + self.set_source_location(with_location); + self.compile_with(items, body, is_async)?; } + // sort of "stack up" the layers of with blocks: + // with a, b: body -> start_with(a) start_with(b) body() end_with(b) end_with(a) + self.set_source_location(with_location); + self.emit(Instruction::PopBlock); + + self.emit(Instruction::EnterFinally); + + self.switch_to_block(final_block); + self.emit(Instruction::WithCleanupStart); + + if is_async { + self.emit(Instruction::GetAwaitable); + self.emit_constant(ConstantData::None); + self.emit(Instruction::YieldFrom); + } + + self.emit(Instruction::WithCleanupFinish); + Ok(()) } diff --git a/compiler/src/error.rs b/compiler/src/error.rs index 38740aa7ba..1d2c1c2fc0 100644 --- a/compiler/src/error.rs +++ b/compiler/src/error.rs @@ -35,6 +35,8 @@ pub enum CompileErrorType { InvalidFutureFeature(String), FunctionImportStar, TooManyStarUnpack, + EmptyWithItems, + EmptyWithBody, } impl fmt::Display for CompileErrorType { @@ -70,6 +72,12 @@ impl fmt::Display for CompileErrorType { CompileErrorType::TooManyStarUnpack => { write!(f, "too many expressions in star-unpacking assignment") } + CompileErrorType::EmptyWithItems => { + write!(f, "empty items on With") + } + CompileErrorType::EmptyWithBody => { + write!(f, "empty body on With") + } } } } diff --git a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap index d3344e3d79..d80f10dfee 100644 --- a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap +++ b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ands.snap @@ -2,12 +2,13 @@ source: compiler/src/compile.rs expression: "compile_exec(\"\\\nif True and False and False:\n pass\n\")" --- - 0 LoadConst (True) - 1 JumpIfFalse (6) - 2 LoadConst (False) - 3 JumpIfFalse (6) - 4 LoadConst (False) - 5 JumpIfFalse (6) - >> 6 LoadConst (None) - 7 ReturnValue + 1 0 LoadConst (True) + 1 JumpIfFalse (6) + 2 LoadConst (False) + 3 JumpIfFalse (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue diff --git a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap index 356067af4b..0a9175bb12 100644 --- a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap +++ b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_mixed.snap @@ -2,14 +2,15 @@ source: compiler/src/compile.rs expression: "compile_exec(\"\\\nif (True and False) or (False and True):\n pass\n\")" --- - 0 LoadConst (True) - 1 JumpIfFalse (4) - 2 LoadConst (False) - 3 JumpIfTrue (8) - >> 4 LoadConst (False) - 5 JumpIfFalse (8) - 6 LoadConst (True) - 7 JumpIfFalse (8) - >> 8 LoadConst (None) - 9 ReturnValue + 1 0 LoadConst (True) + 1 JumpIfFalse (4) + 2 LoadConst (False) + 3 JumpIfTrue (8) + >> 4 LoadConst (False) + 5 JumpIfFalse (8) + 6 LoadConst (True) + 7 JumpIfFalse (8) + + 2 >> 8 LoadConst (None) + 9 ReturnValue diff --git a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap index 4463c3bd05..4b812639b2 100644 --- a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap +++ b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__if_ors.snap @@ -2,12 +2,13 @@ source: compiler/src/compile.rs expression: "compile_exec(\"\\\nif True or False or False:\n pass\n\")" --- - 0 LoadConst (True) - 1 JumpIfTrue (6) - 2 LoadConst (False) - 3 JumpIfTrue (6) - 4 LoadConst (False) - 5 JumpIfFalse (6) - >> 6 LoadConst (None) - 7 ReturnValue + 1 0 LoadConst (True) + 1 JumpIfTrue (6) + 2 LoadConst (False) + 3 JumpIfTrue (6) + 4 LoadConst (False) + 5 JumpIfFalse (6) + + 2 >> 6 LoadConst (None) + 7 ReturnValue diff --git a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap index adc63bdfa5..79a1a86ad1 100644 --- a/compiler/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap +++ b/compiler/src/snapshots/rustpython_compiler_core__compile__tests__nested_double_async_with.snap @@ -1,77 +1,87 @@ --- source: compiler/src/compile.rs expression: "compile_exec(\"\\\nfor stop_exc in (StopIteration('spam'), StopAsyncIteration('ham')):\n with self.subTest(type=type(stop_exc)):\n try:\n async with woohoo():\n raise stop_exc\n except Exception as ex:\n self.assertIs(ex, stop_exc)\n else:\n self.fail(f'{stop_exc} was suppressed')\n\")" - --- - 0 SetupLoop (69) - 1 LoadNameAny (0, StopIteration) - 2 LoadConst ("spam") - 3 CallFunctionPositional (1) - 4 LoadNameAny (1, StopAsyncIteration) - 5 LoadConst ("ham") - 6 CallFunctionPositional (1) - 7 BuildTuple (2, false) - 8 GetIter - >> 9 ForIter (68) - 10 StoreLocal (2, stop_exc) - 11 LoadNameAny (3, self) - 12 LoadMethod (subTest) - 13 LoadNameAny (5, type) - 14 LoadNameAny (2, stop_exc) - 15 CallFunctionPositional (1) - 16 LoadConst (("type")) - 17 CallMethodKeyword (1) - 18 SetupWith (65) - 19 Pop - 20 SetupExcept (40) - 21 LoadNameAny (6, woohoo) - 22 CallFunctionPositional (0) - 23 BeforeAsyncWith - 24 GetAwaitable - 25 LoadConst (None) - 26 YieldFrom - 27 SetupAsyncWith (33) - 28 Pop - 29 LoadNameAny (2, stop_exc) - 30 Raise (Raise) - 31 PopBlock - 32 EnterFinally - >> 33 WithCleanupStart - 34 GetAwaitable - 35 LoadConst (None) - 36 YieldFrom - 37 WithCleanupFinish - 38 PopBlock - 39 Jump (54) - >> 40 Duplicate - 41 LoadNameAny (7, Exception) - 42 TestOperation (ExceptionMatch) - 43 JumpIfFalse (53) - 44 StoreLocal (8, ex) - 45 LoadNameAny (3, self) - 46 LoadMethod (assertIs) - 47 LoadNameAny (8, ex) - 48 LoadNameAny (2, stop_exc) - 49 CallMethodPositional (2) - 50 Pop - 51 PopException - 52 Jump (63) - >> 53 Raise (Reraise) - >> 54 LoadNameAny (3, self) - 55 LoadMethod (fail) - 56 LoadConst ("") - 57 LoadNameAny (2, stop_exc) - 58 FormatValue (None) - 59 LoadConst (" was suppressed") - 60 BuildString (2) - 61 CallMethodPositional (1) - 62 Pop - >> 63 PopBlock - 64 EnterFinally - >> 65 WithCleanupStart - 66 WithCleanupFinish - 67 Jump (9) - >> 68 PopBlock - >> 69 LoadConst (None) - 70 ReturnValue + 1 0 SetupLoop (69) + 1 LoadNameAny (0, StopIteration) + 2 LoadConst ("spam") + 3 CallFunctionPositional (1) + 4 LoadNameAny (1, StopAsyncIteration) + 5 LoadConst ("ham") + 6 CallFunctionPositional (1) + 7 BuildTuple (2, false) + 8 GetIter + >> 9 ForIter (68) + 10 StoreLocal (2, stop_exc) + + 2 11 LoadNameAny (3, self) + 12 LoadMethod (subTest) + 13 LoadNameAny (5, type) + 14 LoadNameAny (2, stop_exc) + 15 CallFunctionPositional (1) + 16 LoadConst (("type")) + 17 CallMethodKeyword (1) + 18 SetupWith (65) + 19 Pop + + 3 20 SetupExcept (40) + + 4 21 LoadNameAny (6, woohoo) + 22 CallFunctionPositional (0) + 23 BeforeAsyncWith + 24 GetAwaitable + 25 LoadConst (None) + 26 YieldFrom + 27 SetupAsyncWith (33) + 28 Pop + + 5 29 LoadNameAny (2, stop_exc) + 30 Raise (Raise) + + 4 31 PopBlock + 32 EnterFinally + >> 33 WithCleanupStart + 34 GetAwaitable + 35 LoadConst (None) + 36 YieldFrom + 37 WithCleanupFinish + 38 PopBlock + 39 Jump (54) + >> 40 Duplicate + + 6 41 LoadNameAny (7, Exception) + 42 TestOperation (ExceptionMatch) + 43 JumpIfFalse (53) + 44 StoreLocal (8, ex) + + 7 45 LoadNameAny (3, self) + 46 LoadMethod (assertIs) + 47 LoadNameAny (8, ex) + 48 LoadNameAny (2, stop_exc) + 49 CallMethodPositional (2) + 50 Pop + 51 PopException + 52 Jump (63) + >> 53 Raise (Reraise) + + 9 >> 54 LoadNameAny (3, self) + 55 LoadMethod (fail) + 56 LoadConst ("") + + 1 57 LoadNameAny (2, stop_exc) + 58 FormatValue (None) + + 9 59 LoadConst (" was suppressed") + 60 BuildString (2) + 61 CallMethodPositional (1) + 62 Pop + + 2 >> 63 PopBlock + 64 EnterFinally + >> 65 WithCleanupStart + 66 WithCleanupFinish + 67 Jump (9) + >> 68 PopBlock + >> 69 LoadConst (None) + 70 ReturnValue