Skip to content

Commit 8805a4d

Browse files
authored
bpo-42282: Fold constants inside named expressions (GH-23190)
* The AST optimiser wasn't descending into named expressions, so any constant subexpressions weren't being folded at compile time * Remove "default:" clauses inside the AST optimiser code to reduce the risk of similar bugs passing unnoticed in future compiler changes
1 parent ee2549c commit 8805a4d

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Optimise constant subexpressions that appear as part of named expressions
2+
(previously the AST optimiser did not descend into named expressions).
3+
Patch by Nick Coghlan.

Python/ast_opt.c

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
static int
88
make_const(expr_ty node, PyObject *val, PyArena *arena)
99
{
10+
// Even if no new value was calculated, make_const may still
11+
// need to clear an error (e.g. for division by zero)
1012
if (val == NULL) {
1113
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
1214
return 0;
@@ -49,7 +51,7 @@ fold_unaryop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
4951
of !=. Detecting such cases doesn't seem worthwhile.
5052
Python uses </> for 'is subset'/'is superset' operations on sets.
5153
They don't satisfy not folding laws. */
52-
int op = asdl_seq_GET(arg->v.Compare.ops, 0);
54+
cmpop_ty op = asdl_seq_GET(arg->v.Compare.ops, 0);
5355
switch (op) {
5456
case Is:
5557
op = IsNot;
@@ -63,8 +65,17 @@ fold_unaryop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
6365
case NotIn:
6466
op = In;
6567
break;
66-
default:
67-
op = 0;
68+
// The remaining comparison operators can't be safely inverted
69+
case Eq:
70+
case NotEq:
71+
case Lt:
72+
case LtE:
73+
case Gt:
74+
case GtE:
75+
op = 0; // The AST enums leave "0" free as an "unused" marker
76+
break;
77+
// No default case, so the compiler will emit a warning if new
78+
// comparison operators are added without being handled here
6879
}
6980
if (op) {
7081
asdl_seq_SET(arg->v.Compare.ops, 0, op);
@@ -224,7 +235,7 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
224235

225236
PyObject *lv = lhs->v.Constant.value;
226237
PyObject *rv = rhs->v.Constant.value;
227-
PyObject *newval;
238+
PyObject *newval = NULL;
228239

229240
switch (node->v.BinOp.op) {
230241
case Add:
@@ -263,8 +274,11 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
263274
case BitAnd:
264275
newval = PyNumber_And(lv, rv);
265276
break;
266-
default: // Unknown operator
277+
// No builtin constants implement the following operators
278+
case MatMult:
267279
return 1;
280+
// No default case, so the compiler will emit a warning if new binary
281+
// operators are added without being handled here
268282
}
269283

270284
return make_const(node, newval, arena);
@@ -457,8 +471,11 @@ astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
457471
case Expression_kind:
458472
CALL(astfold_expr, expr_ty, node_->v.Expression.body);
459473
break;
460-
default:
474+
// The following top level nodes don't participate in constant folding
475+
case FunctionType_kind:
461476
break;
477+
// No default case, so the compiler will emit a warning if new top level
478+
// compilation nodes are added without being handled here
462479
}
463480
return 1;
464481
}
@@ -567,8 +584,14 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
567584
return make_const(node_, PyBool_FromLong(!state->optimize), ctx_);
568585
}
569586
break;
570-
default:
587+
case NamedExpr_kind:
588+
CALL(astfold_expr, expr_ty, node_->v.NamedExpr.value);
589+
break;
590+
case Constant_kind:
591+
// Already a constant, nothing further to do
571592
break;
593+
// No default case, so the compiler will emit a warning if new expression
594+
// kinds are added without being handled here
572595
}
573596
return 1;
574597
}
@@ -686,8 +709,17 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
686709
case Expr_kind:
687710
CALL(astfold_expr, expr_ty, node_->v.Expr.value);
688711
break;
689-
default:
690-
break;
712+
// The following statements don't contain any subexpressions to be folded
713+
case Import_kind:
714+
case ImportFrom_kind:
715+
case Global_kind:
716+
case Nonlocal_kind:
717+
case Pass_kind:
718+
case Break_kind:
719+
case Continue_kind:
720+
break;
721+
// No default case, so the compiler will emit a warning if new statement
722+
// kinds are added without being handled here
691723
}
692724
return 1;
693725
}
@@ -700,8 +732,8 @@ astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState
700732
CALL_OPT(astfold_expr, expr_ty, node_->v.ExceptHandler.type);
701733
CALL_SEQ(astfold_stmt, stmt, node_->v.ExceptHandler.body);
702734
break;
703-
default:
704-
break;
735+
// No default case, so the compiler will emit a warning if new handler
736+
// kinds are added without being handled here
705737
}
706738
return 1;
707739
}

0 commit comments

Comments
 (0)