1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
|
From: CF Bolz-Tereick <cfbolz@gmx.de>
Date: Mon, 25 Aug 2025 16:35:49 +0200
Subject: gh-5328: be more careful in duplicate_exits_without_lineno
there's still a case where I didn't manage to find a unit test yet,
trying to see whether the buildbot shows one
Bug-Debian: https://bugs.debian.org/1119266
Bug-Upstream: https://github.com/pypy/pypy/issues/5328
Origin: upstream, 0b47638bbde129c521ce486f0357c74e5e1deed3
---
pypy/interpreter/astcompiler/assemble.py | 20 +++++++++++++++++---
pypy/interpreter/astcompiler/test/test_compiler.py | 19 ++++++++++++++++++-
2 files changed, 35 insertions(+), 4 deletions(-)
diff --git a/pypy/interpreter/astcompiler/assemble.py b/pypy/interpreter/astcompiler/assemble.py
index 2e7596f..42f4513 100644
--- a/pypy/interpreter/astcompiler/assemble.py
+++ b/pypy/interpreter/astcompiler/assemble.py
@@ -829,7 +829,9 @@ class PythonCodeMaker(ast.ASTVisitor):
# if it's an unconditional jump, duplicate it
if op.opcode in (ops.JUMP_FORWARD, ops.JUMP_ABSOLUTE):
+ assert block.cant_add_instructions
block.instructions.pop()
+ block.cant_add_instructions = False
j -= 1
target.marked -= 2 # one fewer incoming links
for instr in target.instructions:
@@ -840,18 +842,30 @@ class PythonCodeMaker(ast.ASTVisitor):
if copy.jump:
copy.jump.marked += 2
block.emit_instr(copy)
+ if target.next_block and not block.cant_add_instructions:
+ print "missing test! please report a bug about the astcompiler", self.name
+ assert 0
+ instr = Instruction(ops.JUMP_ABSOLUTE, position_info=self.position_info)
+ instr.jump = target.next_block
+ block.emit_instr(instr)
+ target.next_block.marked += 2
+
elif (target.marked >> 1) > 1:
# copy the block, it has more than one predecessor
target.marked -= 2 # one fewer incoming links for old target
newtarget = target.copy()
newtarget.marked = 1 << 1 # new target has one incoming link
newtarget.instructions[0].position_info = op.position_info
- # maintain marked correctly:
- if newtarget.next_block:
- newtarget.next_block.marked += 2
for copied_op in newtarget.instructions:
if copied_op.jump:
copied_op.jump.marked += 2
+ # deal with fall-through of copied target block - rare, but
+ # see test_if_call_or_call_bug
+ if target.next_block and not newtarget.cant_add_instructions:
+ instr = Instruction(ops.JUMP_ABSOLUTE, position_info=self.position_info)
+ instr.jump = target.next_block
+ target.next_block.marked += 2
+ newtarget.emit_instr(instr)
op.jump = newtarget
blocks.append(newtarget)
diff --git a/pypy/interpreter/astcompiler/test/test_compiler.py b/pypy/interpreter/astcompiler/test/test_compiler.py
index 7843c31..17efbe1 100644
--- a/pypy/interpreter/astcompiler/test/test_compiler.py
+++ b/pypy/interpreter/astcompiler/test/test_compiler.py
@@ -2401,7 +2401,16 @@ match x:
@pytest.mark.xfail
def test_if_call_or_call_bug(self):
# used to crash
- compile_with_astcompiler("def func_if_call_or_call():\n if a: f1() or g1()\n", 'exec', self.space)
+ compile_with_astcompiler("""
+def func_if_call_or_call():
+ if a:
+ (f1() or
+ g1())""", 'exec', self.space)
+ compile_with_astcompiler("""
+def func_if_call_and_call():
+ if a:
+ (f1() and
+ g1())""", 'exec', self.space)
class TestLinenoChanges310(object):
def get_line_numbers(self, source, expected, function=False):
@@ -2627,6 +2636,14 @@ def withreturn():
)
""", [3, 0, 1])
+ def test_or_with_implicit_return(self):
+ code = self.get_line_numbers("""
+def or_with_implicit_return():
+ if a:
+ (g
+ or
+ h)""", [1, 2, 4, 1, 2], function=True)
+
class TestErrorPositions(BaseTestCompiler):
def test_import_star_in_function_position(self):
src = "def f(): from _ import *"
|