Skip to content

Commit 3c21bbf

Browse files
yoffCopilot
andcommitted
Python: test dead bindings under no-raise CFG abstraction
Adds 'dead_under_no_raise.py' to the bindings test suite, capturing the three CPython patterns where bindings legitimately have no CFG node because the surrounding code is unreachable under the 'no expressions raise' abstraction: 1. Statements after a 'try: return X; except: pass' block. 2. The 'else:' clause of a try whose body always raises. 3. Cache-lookup pattern 'try: return cache[k]; except: pass' followed by computation and store. These bindings intentionally carry no 'cfgdefines=' annotations. If raise modelling is later added to the CFG, the BindingsTest will surface the new CFG nodes as unexpected results and this file will need to be revisited. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 01c6b2b commit 3c21bbf

1 file changed

Lines changed: 52 additions & 0 deletions

File tree

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Dead bindings under the "no expressions raise" CFG abstraction.
2+
#
3+
# The new CFG does not currently model raise edges from arbitrary
4+
# expressions. As a consequence, code that is only reachable through
5+
# exception flow is (correctly) classified as dead and has no CFG node.
6+
# Variable bindings in dead code do not need CFG nodes - SSA / dataflow
7+
# over dead code is moot.
8+
#
9+
# These tests act as a regression guard: the bindings below intentionally
10+
# have no `cfgdefines=` annotations. If raise modelling is later added,
11+
# the BindingsTest infrastructure will surface the new CFG nodes as
12+
# unexpected results, and this file will need to be revisited.
13+
14+
15+
def f(obj): # $ cfgdefines=f cfgdefines=obj
16+
try:
17+
return len(obj)
18+
except TypeError:
19+
pass
20+
21+
# The first try's body always returns; its except handler does not
22+
# raise or otherwise transfer control, so under "no expressions
23+
# raise" the only paths out of the try-statement are dead. Everything
24+
# below is unreachable.
25+
try:
26+
hint = type(obj).__length_hint__
27+
except AttributeError:
28+
return None
29+
return hint
30+
31+
32+
def g(): # $ cfgdefines=g
33+
try:
34+
raise Exception("inner")
35+
except:
36+
raise Exception("outer")
37+
else:
38+
# Unreachable: the inner try body always raises, so the `else:`
39+
# clause never runs.
40+
hit_inner_else = True
41+
42+
43+
def h(cache, key): # $ cfgdefines=h cfgdefines=cache cfgdefines=key
44+
try:
45+
return cache[key]
46+
except KeyError:
47+
pass
48+
49+
# Same pattern as `f`: dead under "no expressions raise".
50+
value = compute(key)
51+
cache[key] = value
52+
return value

0 commit comments

Comments
 (0)