PY-48760 Implement CFG for PEP-634 match statements

I introduced a new type of CFG instructions, similar to ConditionalInstruction,
called RefutablePatternInstruction. The idea is that every pattern that can
possibly fail to match is surrounded with a pair of such instructions, helping
to describe how the control flow moves in each case. The PEP calls the opposite
type of patterns that always match "irrefutable", hence the name. We need these
synthetic instructions because otherwise some refutable patterns such as literal
ones (e.g. "42") don't leave any nodes in the graph. Incorporating the information
about irrefutable patterns right into the graph allows catching cases such
as "wildcard/name capture makes remaining patterns unreachable", both in OR
patterns and independent case clauses.

GitOrigin-RevId: beebe1890a6a824b188e6954a2c92f7ec52079e0
This commit is contained in:
Mikhail Golubev
2021-05-07 14:59:36 +03:00
committed by intellij-monorepo-bot
parent 79999b52e4
commit cb08d4de98
96 changed files with 1203 additions and 9 deletions

View File

@@ -0,0 +1,6 @@
while x:
match x:
case 42:
break
y
z

View File

@@ -0,0 +1,14 @@
0(1) element: null
1(2) element: PyWhileStatement
2(3,11) READ ACCESS: x
3(4) element: PyStatementList. Condition: x:true
4(5) element: PyMatchStatement
5(6) READ ACCESS: x
6(7,9) refutable pattern: 42
7(8) matched pattern: 42
8(11) element: PyBreakStatement
9(10) element: PyExpressionStatement
10(1) READ ACCESS: y
11(12) element: PyExpressionStatement
12(13) READ ACCESS: z
13() element: null

View File

@@ -0,0 +1,6 @@
while x:
match x:
case 42:
continue
y
z

View File

@@ -0,0 +1,14 @@
0(1) element: null
1(2) element: PyWhileStatement
2(3,11) READ ACCESS: x
3(4) element: PyStatementList. Condition: x:true
4(5) element: PyMatchStatement
5(6) READ ACCESS: x
6(7,9) refutable pattern: 42
7(8) matched pattern: 42
8(1) element: PyContinueStatement
9(10) element: PyExpressionStatement
10(1) READ ACCESS: y
11(12) element: PyExpressionStatement
12(13) READ ACCESS: z
13() element: null

View File

@@ -0,0 +1,5 @@
def func(x):
match x:
case 42:
return
y

View File

@@ -0,0 +1,10 @@
0(1) element: null
1(2) WRITE ACCESS: x
2(3) element: PyMatchStatement
3(4) READ ACCESS: x
4(5,7) refutable pattern: 42
5(6) matched pattern: 42
6(9) element: PyReturnStatement
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9() element: null

View File

@@ -0,0 +1,9 @@
match 1:
case 1:
match 11:
case 11:
y11
y1
case 2:
y2
z

View File

@@ -0,0 +1,18 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,11) refutable pattern: 1
3(4) matched pattern: 1
4(5) element: PyMatchStatement
5(6,9) refutable pattern: 11
6(7) matched pattern: 11
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y11
9(10) element: PyExpressionStatement
10(15) READ ACCESS: y1
11(12,15) refutable pattern: 2
12(13) matched pattern: 2
13(14) element: PyExpressionStatement
14(15) READ ACCESS: y2
15(16) element: PyExpressionStatement
16(17) READ ACCESS: z
17() element: null

View File

@@ -0,0 +1,8 @@
match 1:
case 1:
match 11:
case 11:
y11
case 2:
y2
z

View File

@@ -0,0 +1,16 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,9) refutable pattern: 1
3(4) matched pattern: 1
4(5) element: PyMatchStatement
5(6,13) refutable pattern: 11
6(7) matched pattern: 11
7(8) element: PyExpressionStatement
8(13) READ ACCESS: y11
9(10,13) refutable pattern: 2
10(11) matched pattern: 2
11(12) element: PyExpressionStatement
12(13) READ ACCESS: y2
13(14) element: PyExpressionStatement
14(15) READ ACCESS: z
15() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [42] | foo.bar as x:
y
z

View File

@@ -0,0 +1,19 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,16) refutable pattern: [42] | foo.bar as x
3(4,16) refutable pattern: [42] | foo.bar
4(5,8) refutable pattern: [42]
5(6,8) refutable pattern: 42
6(7) matched pattern: 42
7(12) matched pattern: [42]
8(9,16) refutable pattern: foo.bar
9(10) READ ACCESS: foo
10(11) matched pattern: foo.bar
11(12) matched pattern: [42] | foo.bar
12(13) WRITE ACCESS: x
13(14) matched pattern: [42] | foo.bar as x
14(15) element: PyExpressionStatement
15(16) READ ACCESS: y
16(17) element: PyExpressionStatement
17(18) READ ACCESS: z
18() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [x]:
y
z

View File

@@ -0,0 +1,10 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,7) refutable pattern: [x]
3(4) WRITE ACCESS: x
4(5) matched pattern: [x]
5(6) element: PyExpressionStatement
6(7) READ ACCESS: y
7(8) element: PyExpressionStatement
8(9) READ ACCESS: z
9() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case x:
y
z

View File

@@ -0,0 +1,8 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyExpressionStatement
4(5) READ ACCESS: y
5(6) element: PyExpressionStatement
6(7) READ ACCESS: z
7() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case Class(1, attr=foo.bar):
x
y

View File

@@ -0,0 +1,17 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,14) refutable pattern: Class(1, attr=foo.bar)
3(4) READ ACCESS: Class
4(5,14) refutable pattern: 1
5(6) matched pattern: 1
6(7,14) refutable pattern: attr=foo.bar
7(8,14) refutable pattern: foo.bar
8(9) READ ACCESS: foo
9(10) matched pattern: foo.bar
10(11) matched pattern: attr=foo.bar
11(12) matched pattern: Class(1, attr=foo.bar)
12(13) element: PyExpressionStatement
13(14) READ ACCESS: x
14(15) element: PyExpressionStatement
15(16) READ ACCESS: y
16() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case x if x > 0 and x < 10:
y
z

View File

@@ -0,0 +1,16 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyBinaryExpression
4(5,6) READ ACCESS: x
5(13) element: null. Condition: x > 0:false
6(7) element: null. Condition: x > 0:true
7(8,9) READ ACCESS: x
8(13) element: null. Condition: x < 10:false
9(10) element: null. Condition: x < 10:true
10(11) element: PyStatementList. Condition: x > 0 and x < 10:true
11(12) element: PyExpressionStatement
12(13) READ ACCESS: y
13(14) element: PyExpressionStatement
14(15) READ ACCESS: z
15() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case x if x % 4 == 0 and (x % 400 == 0 or x % 100 != 0):
y
z

View File

@@ -0,0 +1,22 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyBinaryExpression
4(5,6) READ ACCESS: x
5(19) element: null. Condition: x % 4 == 0:false
6(7) element: null. Condition: x % 4 == 0:true
7(8) element: PyBinaryExpression
8(9,10) READ ACCESS: x
9(16) element: null. Condition: x % 400 == 0:true
10(11) element: null. Condition: x % 400 == 0:false
11(12,13) READ ACCESS: x
12(19) element: null. Condition: x % 100 != 0:false
13(14,15) element: null. Condition: x % 100 != 0:true
14(19) element: null. Condition: (x % 400 == 0 or x % 100 != 0):false
15(16) element: null. Condition: (x % 400 == 0 or x % 100 != 0):true
16(17) element: PyStatementList. Condition: x % 4 == 0 and (x % 400 == 0 or x % 100 != 0):true
17(18) element: PyExpressionStatement
18(19) READ ACCESS: y
19(20) element: PyExpressionStatement
20(21) READ ACCESS: z
21() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case x if x > 0 or x < 0:
y
z

View File

@@ -0,0 +1,16 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyBinaryExpression
4(5,6) READ ACCESS: x
5(10) element: null. Condition: x > 0:true
6(7) element: null. Condition: x > 0:false
7(8,9) READ ACCESS: x
8(13) element: null. Condition: x < 0:false
9(10) element: null. Condition: x < 0:true
10(11) element: PyStatementList. Condition: x > 0 or x < 0:true
11(12) element: PyExpressionStatement
12(13) READ ACCESS: y
13(14) element: PyExpressionStatement
14(15) READ ACCESS: z
15() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case {"foo": 1, **x}:
y
z

View File

@@ -0,0 +1,16 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,13) refutable pattern: {"foo": 1, **x}
3(4,13) refutable pattern: "foo": 1
4(5,13) refutable pattern: "foo"
5(6) matched pattern: "foo"
6(7,13) refutable pattern: 1
7(8) matched pattern: 1
8(9) matched pattern: "foo": 1
9(10) WRITE ACCESS: x
10(11) matched pattern: {"foo": 1, **x}
11(12) element: PyExpressionStatement
12(13) READ ACCESS: y
13(14) element: PyExpressionStatement
14(15) READ ACCESS: z
15() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [x1, x2, x3] if (x1 or x2) > x3:
y
z

View File

@@ -0,0 +1,21 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,18) refutable pattern: [x1, x2, x3]
3(4) WRITE ACCESS: x1
4(5) WRITE ACCESS: x2
5(6) WRITE ACCESS: x3
6(7) matched pattern: [x1, x2, x3]
7(8) element: PyBinaryExpression
8(9,10) READ ACCESS: x1
9(14) element: null. Condition: x1:true
10(11) element: null. Condition: x1:false
11(12,13) READ ACCESS: x2
12(14) element: null. Condition: x2:false
13(14) element: null. Condition: x2:true
14(15,18) READ ACCESS: x3
15(16) element: PyStatementList. Condition: (x1 or x2) > x3:true
16(17) element: PyExpressionStatement
17(18) READ ACCESS: y
18(19) element: PyExpressionStatement
19(20) READ ACCESS: z
20() element: null

View File

@@ -0,0 +1,11 @@
0(1) element: null
1(2) element: PyMatchStatement
2(6) WRITE ACCESS: x
3(4,8) refutable pattern: [x]
4(5) WRITE ACCESS: x
5(6) matched pattern: [x]
6(7) element: PyExpressionStatement
7(8) READ ACCESS: y
8(9) element: PyExpressionStatement
9(10) READ ACCESS: z
10() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [x] | x :
y
z

View File

@@ -0,0 +1,11 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,5) refutable pattern: [x]
3(4) WRITE ACCESS: x
4(6) matched pattern: [x]
5(6) WRITE ACCESS: x
6(7) element: PyExpressionStatement
7(8) READ ACCESS: y
8(9) element: PyExpressionStatement
9(10) READ ACCESS: z
10() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case 42:
y
z

View File

@@ -0,0 +1,9 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,6) refutable pattern: 42
3(4) matched pattern: 42
4(5) element: PyExpressionStatement
5(6) READ ACCESS: y
6(7) element: PyExpressionStatement
7(8) READ ACCESS: z
8() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case {'foo': 1, 'bar': foo.bar}:
x
y

View File

@@ -0,0 +1,22 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,19) refutable pattern: {'foo': 1, 'bar': foo.bar}
3(4,19) refutable pattern: 'foo': 1
4(5,19) refutable pattern: 'foo'
5(6) matched pattern: 'foo'
6(7,19) refutable pattern: 1
7(8) matched pattern: 1
8(9) matched pattern: 'foo': 1
9(10,19) refutable pattern: 'bar': foo.bar
10(11,19) refutable pattern: 'bar'
11(12) matched pattern: 'bar'
12(13,19) refutable pattern: foo.bar
13(14) READ ACCESS: foo
14(15) matched pattern: foo.bar
15(16) matched pattern: 'bar': foo.bar
16(17) matched pattern: {'foo': 1, 'bar': foo.bar}
17(18) element: PyExpressionStatement
18(19) READ ACCESS: x
19(20) element: PyExpressionStatement
20(21) READ ACCESS: y
21() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [1, *x]:
y
z

View File

@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,9) refutable pattern: [1, *x]
3(4,9) refutable pattern: 1
4(5) matched pattern: 1
5(6) WRITE ACCESS: x
6(7) matched pattern: [1, *x]
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9(10) element: PyExpressionStatement
10(11) READ ACCESS: z
11() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case 1 | (2 | 3):
x
y

View File

@@ -0,0 +1,19 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,16) refutable pattern: 1 | (2 | 3)
3(4,5) refutable pattern: 1
4(14) matched pattern: 1
5(6,16) refutable pattern: (2 | 3)
6(7,16) refutable pattern: 2 | 3
7(8,9) refutable pattern: 2
8(14) matched pattern: 2
9(10,16) refutable pattern: 3
10(11) matched pattern: 3
11(12) matched pattern: 2 | 3
12(13) matched pattern: (2 | 3)
13(14) matched pattern: 1 | (2 | 3)
14(15) element: PyExpressionStatement
15(16) READ ACCESS: x
16(17) element: PyExpressionStatement
17(18) READ ACCESS: y
18() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case ((x)):
y
z

View File

@@ -0,0 +1,8 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyExpressionStatement
4(5) READ ACCESS: y
5(6) element: PyExpressionStatement
6(7) READ ACCESS: z
7() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [x] | (foo.bar as x):
y
z

View File

@@ -0,0 +1,20 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,17) refutable pattern: [x] | (foo.bar as x)
3(4,6) refutable pattern: [x]
4(5) WRITE ACCESS: x
5(15) matched pattern: [x]
6(7,17) refutable pattern: (foo.bar as x)
7(8,17) refutable pattern: foo.bar as x
8(9,17) refutable pattern: foo.bar
9(10) READ ACCESS: foo
10(11) matched pattern: foo.bar
11(12) WRITE ACCESS: x
12(13) matched pattern: foo.bar as x
13(14) matched pattern: (foo.bar as x)
14(15) matched pattern: [x] | (foo.bar as x)
15(16) element: PyExpressionStatement
16(17) READ ACCESS: y
17(18) element: PyExpressionStatement
18(19) READ ACCESS: z
19() element: null

View File

@@ -0,0 +1,13 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,10) refutable pattern: [] | 42
3(4,5) refutable pattern: []
4(8) matched pattern: []
5(6,10) refutable pattern: 42
6(7) matched pattern: 42
7(8) matched pattern: [] | 42
8(9) element: PyExpressionStatement
9(10) READ ACCESS: y
10(11) element: PyExpressionStatement
11(12) READ ACCESS: z
12() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case _ | 42:
y
z

View File

@@ -0,0 +1,10 @@
0(1) element: null
1(2) element: PyMatchStatement
2(5) element: PyWildcardPattern
3(4,7) refutable pattern: 42
4(5) matched pattern: 42
5(6) element: PyExpressionStatement
6(7) READ ACCESS: y
7(8) element: PyExpressionStatement
8(9) READ ACCESS: z
9() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [x] if x > 0 and x % 2 == 0:
y
z

View File

@@ -0,0 +1,18 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,15) refutable pattern: [x]
3(4) WRITE ACCESS: x
4(5) matched pattern: [x]
5(6) element: PyBinaryExpression
6(7,8) READ ACCESS: x
7(15) element: null. Condition: x > 0:false
8(9) element: null. Condition: x > 0:true
9(10,11) READ ACCESS: x
10(15) element: null. Condition: x % 2 == 0:false
11(12) element: null. Condition: x % 2 == 0:true
12(13) element: PyStatementList. Condition: x > 0 and x % 2 == 0:true
13(14) element: PyExpressionStatement
14(15) READ ACCESS: y
15(16) element: PyExpressionStatement
16(17) READ ACCESS: z
17() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [1, foo.bar]:
x
y

View File

@@ -0,0 +1,14 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,11) refutable pattern: [1, foo.bar]
3(4,11) refutable pattern: 1
4(5) matched pattern: 1
5(6,11) refutable pattern: foo.bar
6(7) READ ACCESS: foo
7(8) matched pattern: foo.bar
8(9) matched pattern: [1, foo.bar]
9(10) element: PyExpressionStatement
10(11) READ ACCESS: x
11(12) element: PyExpressionStatement
12(13) READ ACCESS: y
13() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case [1 | x]:
y
z

View File

@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,9) refutable pattern: [1 | x]
3(4,5) refutable pattern: 1
4(7) matched pattern: 1
5(6) WRITE ACCESS: x
6(7) matched pattern: [1 | x]
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9(10) element: PyExpressionStatement
10(11) READ ACCESS: z
11() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case x if x > 0:
y
z

View File

@@ -0,0 +1,10 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4,7) READ ACCESS: x
4(5) element: PyStatementList. Condition: x > 0:true
5(6) element: PyExpressionStatement
6(7) READ ACCESS: y
7(8) element: PyExpressionStatement
8(9) READ ACCESS: z
9() element: null

View File

@@ -0,0 +1,4 @@
match 42:
case _:
y
z

View File

@@ -0,0 +1,8 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) element: PyWildcardPattern
3(4) element: PyExpressionStatement
4(5) READ ACCESS: y
5(6) element: PyExpressionStatement
6(7) READ ACCESS: z
7() element: null

View File

@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,9) refutable pattern: [1, *_]
3(4,9) refutable pattern: 1
4(5) matched pattern: 1
5(6) element: PyWildcardPattern
6(7) matched pattern: [1, *_]
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9(10) element: PyExpressionStatement
10(11) READ ACCESS: z
11() element: null

View File

@@ -0,0 +1,6 @@
match 42:
case x:
y
case 42:
y
z

View File

@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3) WRITE ACCESS: x
3(4) element: PyExpressionStatement
4(9) READ ACCESS: y
5(6,9) refutable pattern: 42
6(7) matched pattern: 42
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9(10) element: PyExpressionStatement
10(11) READ ACCESS: z
11() element: null

View File

@@ -0,0 +1,6 @@
match 42:
case 42:
y
case x:
y
z

View File

@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyMatchStatement
2(3,6) refutable pattern: 42
3(4) matched pattern: 42
4(5) element: PyExpressionStatement
5(9) READ ACCESS: y
6(7) WRITE ACCESS: x
7(8) element: PyExpressionStatement
8(9) READ ACCESS: y
9(10) element: PyExpressionStatement
10(11) READ ACCESS: z
11() element: null