Skip to content

Commit 140e69c

Browse files
authored
gh-126835: Move const folding of lists & sets from ast_opt.c to flowgraph.c (#130032)
1 parent c7a9d06 commit 140e69c

File tree

5 files changed

+231
-120
lines changed

5 files changed

+231
-120
lines changed

Lib/test/test_ast/test_ast.py

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3239,46 +3239,6 @@ def test_folding_tuple(self):
32393239

32403240
self.assert_ast(code, non_optimized_target, optimized_target)
32413241

3242-
def test_folding_comparator(self):
3243-
code = "1 %s %s1%s"
3244-
operators = [("in", ast.In()), ("not in", ast.NotIn())]
3245-
braces = [
3246-
("[", "]", ast.List, (1,)),
3247-
("{", "}", ast.Set, frozenset({1})),
3248-
]
3249-
for left, right, non_optimized_comparator, optimized_comparator in braces:
3250-
for op, node in operators:
3251-
non_optimized_target = self.wrap_expr(ast.Compare(
3252-
left=ast.Constant(1), ops=[node],
3253-
comparators=[non_optimized_comparator(elts=[ast.Constant(1)])]
3254-
))
3255-
optimized_target = self.wrap_expr(ast.Compare(
3256-
left=ast.Constant(1), ops=[node],
3257-
comparators=[ast.Constant(value=optimized_comparator)]
3258-
))
3259-
self.assert_ast(code % (op, left, right), non_optimized_target, optimized_target)
3260-
3261-
def test_folding_iter(self):
3262-
code = "for _ in %s1%s: pass"
3263-
braces = [
3264-
("[", "]", ast.List, (1,)),
3265-
("{", "}", ast.Set, frozenset({1})),
3266-
]
3267-
3268-
for left, right, ast_cls, optimized_iter in braces:
3269-
non_optimized_target = self.wrap_statement(ast.For(
3270-
target=ast.Name(id="_", ctx=ast.Store()),
3271-
iter=ast_cls(elts=[ast.Constant(1)]),
3272-
body=[ast.Pass()]
3273-
))
3274-
optimized_target = self.wrap_statement(ast.For(
3275-
target=ast.Name(id="_", ctx=ast.Store()),
3276-
iter=ast.Constant(value=optimized_iter),
3277-
body=[ast.Pass()]
3278-
))
3279-
3280-
self.assert_ast(code % (left, right), non_optimized_target, optimized_target)
3281-
32823242
def test_folding_type_param_in_function_def(self):
32833243
code = "def foo[%s = 1 + 1](): pass"
32843244

Lib/test/test_compile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ def check_same_constant(const):
798798
f3 = lambda x: x in {("not a name",)}
799799
self.assertIs(f1.__code__.co_consts[0],
800800
f2.__code__.co_consts[0][0])
801-
self.assertIs(next(iter(f3.__code__.co_consts[0])),
801+
self.assertIs(next(iter(f3.__code__.co_consts[1])),
802802
f2.__code__.co_consts[0])
803803

804804
# {0} is converted to a constant frozenset({0}) by the peephole

Lib/test/test_peepholer.py

Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1261,6 +1261,202 @@ def test_optimize_if_const_set(self):
12611261
]
12621262
self.cfg_optimization_test(same, same, consts=[])
12631263

1264+
def test_optimize_literal_list_for_iter(self):
1265+
# for _ in [1, 2]: pass ==> for _ in (1, 2): pass
1266+
before = [
1267+
('LOAD_SMALL_INT', 1, 0),
1268+
('LOAD_SMALL_INT', 2, 0),
1269+
('BUILD_LIST', 2, 0),
1270+
('GET_ITER', None, 0),
1271+
start := self.Label(),
1272+
('FOR_ITER', end := self.Label(), 0),
1273+
('STORE_FAST', 0, 0),
1274+
('JUMP', start, 0),
1275+
end,
1276+
('END_FOR', None, 0),
1277+
('POP_ITER', None, 0),
1278+
('LOAD_CONST', 0, 0),
1279+
('RETURN_VALUE', None, 0),
1280+
]
1281+
after = [
1282+
('LOAD_CONST', 1, 0),
1283+
('GET_ITER', None, 0),
1284+
start := self.Label(),
1285+
('FOR_ITER', end := self.Label(), 0),
1286+
('STORE_FAST', 0, 0),
1287+
('JUMP', start, 0),
1288+
end,
1289+
('END_FOR', None, 0),
1290+
('POP_ITER', None, 0),
1291+
('LOAD_CONST', 0, 0),
1292+
('RETURN_VALUE', None, 0),
1293+
]
1294+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
1295+
1296+
# for _ in [1, x]: pass ==> for _ in (1, x): pass
1297+
before = [
1298+
('LOAD_SMALL_INT', 1, 0),
1299+
('LOAD_NAME', 0, 0),
1300+
('BUILD_LIST', 2, 0),
1301+
('GET_ITER', None, 0),
1302+
start := self.Label(),
1303+
('FOR_ITER', end := self.Label(), 0),
1304+
('STORE_FAST', 0, 0),
1305+
('JUMP', start, 0),
1306+
end,
1307+
('END_FOR', None, 0),
1308+
('POP_ITER', None, 0),
1309+
('LOAD_CONST', 0, 0),
1310+
('RETURN_VALUE', None, 0),
1311+
]
1312+
after = [
1313+
('LOAD_SMALL_INT', 1, 0),
1314+
('LOAD_NAME', 0, 0),
1315+
('BUILD_TUPLE', 2, 0),
1316+
('GET_ITER', None, 0),
1317+
start := self.Label(),
1318+
('FOR_ITER', end := self.Label(), 0),
1319+
('STORE_FAST', 0, 0),
1320+
('JUMP', start, 0),
1321+
end,
1322+
('END_FOR', None, 0),
1323+
('POP_ITER', None, 0),
1324+
('LOAD_CONST', 0, 0),
1325+
('RETURN_VALUE', None, 0),
1326+
]
1327+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
1328+
1329+
def test_optimize_literal_set_for_iter(self):
1330+
# for _ in {1, 2}: pass ==> for _ in (1, 2): pass
1331+
before = [
1332+
('LOAD_SMALL_INT', 1, 0),
1333+
('LOAD_SMALL_INT', 2, 0),
1334+
('BUILD_SET', 2, 0),
1335+
('GET_ITER', None, 0),
1336+
start := self.Label(),
1337+
('FOR_ITER', end := self.Label(), 0),
1338+
('STORE_FAST', 0, 0),
1339+
('JUMP', start, 0),
1340+
end,
1341+
('END_FOR', None, 0),
1342+
('POP_ITER', None, 0),
1343+
('LOAD_CONST', 0, 0),
1344+
('RETURN_VALUE', None, 0),
1345+
]
1346+
after = [
1347+
('LOAD_CONST', 1, 0),
1348+
('GET_ITER', None, 0),
1349+
start := self.Label(),
1350+
('FOR_ITER', end := self.Label(), 0),
1351+
('STORE_FAST', 0, 0),
1352+
('JUMP', start, 0),
1353+
end,
1354+
('END_FOR', None, 0),
1355+
('POP_ITER', None, 0),
1356+
('LOAD_CONST', 0, 0),
1357+
('RETURN_VALUE', None, 0),
1358+
]
1359+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
1360+
1361+
# non constant literal set is not changed
1362+
# for _ in {1, x}: pass ==> for _ in {1, x}: pass
1363+
same = [
1364+
('LOAD_SMALL_INT', 1, 0),
1365+
('LOAD_NAME', 0, 0),
1366+
('BUILD_SET', 2, 0),
1367+
('GET_ITER', None, 0),
1368+
start := self.Label(),
1369+
('FOR_ITER', end := self.Label(), 0),
1370+
('STORE_FAST', 0, 0),
1371+
('JUMP', start, 0),
1372+
end,
1373+
('END_FOR', None, 0),
1374+
('POP_ITER', None, 0),
1375+
('LOAD_CONST', 0, 0),
1376+
('RETURN_VALUE', None, 0),
1377+
]
1378+
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
1379+
1380+
def test_optimize_literal_list_contains(self):
1381+
# x in [1, 2] ==> x in (1, 2)
1382+
before = [
1383+
('LOAD_NAME', 0, 0),
1384+
('LOAD_SMALL_INT', 1, 0),
1385+
('LOAD_SMALL_INT', 2, 0),
1386+
('BUILD_LIST', 2, 0),
1387+
('CONTAINS_OP', 0, 0),
1388+
('POP_TOP', None, 0),
1389+
('LOAD_CONST', 0, 0),
1390+
('RETURN_VALUE', None, 0),
1391+
]
1392+
after = [
1393+
('LOAD_NAME', 0, 0),
1394+
('LOAD_CONST', 1, 0),
1395+
('CONTAINS_OP', 0, 0),
1396+
('POP_TOP', None, 0),
1397+
('LOAD_CONST', 0, 0),
1398+
('RETURN_VALUE', None, 0),
1399+
]
1400+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, (1, 2)])
1401+
1402+
# x in [1, y] ==> x in (1, y)
1403+
before = [
1404+
('LOAD_NAME', 0, 0),
1405+
('LOAD_SMALL_INT', 1, 0),
1406+
('LOAD_NAME', 1, 0),
1407+
('BUILD_LIST', 2, 0),
1408+
('CONTAINS_OP', 0, 0),
1409+
('POP_TOP', None, 0),
1410+
('LOAD_CONST', 0, 0),
1411+
('RETURN_VALUE', None, 0),
1412+
]
1413+
after = [
1414+
('LOAD_NAME', 0, 0),
1415+
('LOAD_SMALL_INT', 1, 0),
1416+
('LOAD_NAME', 1, 0),
1417+
('BUILD_TUPLE', 2, 0),
1418+
('CONTAINS_OP', 0, 0),
1419+
('POP_TOP', None, 0),
1420+
('LOAD_CONST', 0, 0),
1421+
('RETURN_VALUE', None, 0),
1422+
]
1423+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None])
1424+
1425+
def test_optimize_literal_set_contains(self):
1426+
# x in {1, 2} ==> x in (1, 2)
1427+
before = [
1428+
('LOAD_NAME', 0, 0),
1429+
('LOAD_SMALL_INT', 1, 0),
1430+
('LOAD_SMALL_INT', 2, 0),
1431+
('BUILD_SET', 2, 0),
1432+
('CONTAINS_OP', 0, 0),
1433+
('POP_TOP', None, 0),
1434+
('LOAD_CONST', 0, 0),
1435+
('RETURN_VALUE', None, 0),
1436+
]
1437+
after = [
1438+
('LOAD_NAME', 0, 0),
1439+
('LOAD_CONST', 1, 0),
1440+
('CONTAINS_OP', 0, 0),
1441+
('POP_TOP', None, 0),
1442+
('LOAD_CONST', 0, 0),
1443+
('RETURN_VALUE', None, 0),
1444+
]
1445+
self.cfg_optimization_test(before, after, consts=[None], expected_consts=[None, frozenset({1, 2})])
1446+
1447+
# non constant literal set is not changed
1448+
# x in {1, y} ==> x in {1, y}
1449+
same = [
1450+
('LOAD_NAME', 0, 0),
1451+
('LOAD_SMALL_INT', 1, 0),
1452+
('LOAD_NAME', 1, 0),
1453+
('BUILD_SET', 2, 0),
1454+
('CONTAINS_OP', 0, 0),
1455+
('POP_TOP', None, 0),
1456+
('LOAD_CONST', 0, 0),
1457+
('RETURN_VALUE', None, 0),
1458+
]
1459+
self.cfg_optimization_test(same, same, consts=[None], expected_consts=[None])
12641460

12651461
def test_conditional_jump_forward_const_condition(self):
12661462
# The unreachable branch of the jump is removed, the jump

Python/ast_opt.c

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -567,62 +567,6 @@ fold_tuple(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
567567
return make_const(node, newval, arena);
568568
}
569569

570-
/* Change literal list or set of constants into constant
571-
tuple or frozenset respectively. Change literal list of
572-
non-constants into tuple.
573-
Used for right operand of "in" and "not in" tests and for iterable
574-
in "for" loop and comprehensions.
575-
*/
576-
static int
577-
fold_iter(expr_ty arg, PyArena *arena, _PyASTOptimizeState *state)
578-
{
579-
PyObject *newval;
580-
if (arg->kind == List_kind) {
581-
/* First change a list into tuple. */
582-
asdl_expr_seq *elts = arg->v.List.elts;
583-
if (has_starred(elts)) {
584-
return 1;
585-
}
586-
expr_context_ty ctx = arg->v.List.ctx;
587-
arg->kind = Tuple_kind;
588-
arg->v.Tuple.elts = elts;
589-
arg->v.Tuple.ctx = ctx;
590-
/* Try to create a constant tuple. */
591-
newval = make_const_tuple(elts);
592-
}
593-
else if (arg->kind == Set_kind) {
594-
newval = make_const_tuple(arg->v.Set.elts);
595-
if (newval) {
596-
Py_SETREF(newval, PyFrozenSet_New(newval));
597-
}
598-
}
599-
else {
600-
return 1;
601-
}
602-
return make_const(arg, newval, arena);
603-
}
604-
605-
static int
606-
fold_compare(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
607-
{
608-
asdl_int_seq *ops;
609-
asdl_expr_seq *args;
610-
Py_ssize_t i;
611-
612-
ops = node->v.Compare.ops;
613-
args = node->v.Compare.comparators;
614-
/* Change literal list or set in 'in' or 'not in' into
615-
tuple or frozenset respectively. */
616-
i = asdl_seq_LEN(ops) - 1;
617-
int op = asdl_seq_GET(ops, i);
618-
if (op == In || op == NotIn) {
619-
if (!fold_iter((expr_ty)asdl_seq_GET(args, i), arena, state)) {
620-
return 0;
621-
}
622-
}
623-
return 1;
624-
}
625-
626570
static int astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
627571
static int astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
628572
static int astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state);
@@ -783,7 +727,6 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
783727
case Compare_kind:
784728
CALL(astfold_expr, expr_ty, node_->v.Compare.left);
785729
CALL_SEQ(astfold_expr, expr, node_->v.Compare.comparators);
786-
CALL(fold_compare, expr_ty, node_);
787730
break;
788731
case Call_kind:
789732
CALL(astfold_expr, expr_ty, node_->v.Call.func);
@@ -852,8 +795,6 @@ astfold_comprehension(comprehension_ty node_, PyArena *ctx_, _PyASTOptimizeState
852795
CALL(astfold_expr, expr_ty, node_->target);
853796
CALL(astfold_expr, expr_ty, node_->iter);
854797
CALL_SEQ(astfold_expr, expr, node_->ifs);
855-
856-
CALL(fold_iter, expr_ty, node_->iter);
857798
return 1;
858799
}
859800

@@ -940,8 +881,6 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
940881
CALL(astfold_expr, expr_ty, node_->v.For.iter);
941882
CALL_SEQ(astfold_stmt, stmt, node_->v.For.body);
942883
CALL_SEQ(astfold_stmt, stmt, node_->v.For.orelse);
943-
944-
CALL(fold_iter, expr_ty, node_->v.For.iter);
945884
break;
946885
case AsyncFor_kind:
947886
CALL(astfold_expr, expr_ty, node_->v.AsyncFor.target);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy