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)
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