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
This commit is contained in:
parent
ee2549c2ba
commit
8805a4dad2
|
@ -0,0 +1,3 @@
|
||||||
|
Optimise constant subexpressions that appear as part of named expressions
|
||||||
|
(previously the AST optimiser did not descend into named expressions).
|
||||||
|
Patch by Nick Coghlan.
|
|
@ -7,6 +7,8 @@
|
||||||
static int
|
static int
|
||||||
make_const(expr_ty node, PyObject *val, PyArena *arena)
|
make_const(expr_ty node, PyObject *val, PyArena *arena)
|
||||||
{
|
{
|
||||||
|
// Even if no new value was calculated, make_const may still
|
||||||
|
// need to clear an error (e.g. for division by zero)
|
||||||
if (val == NULL) {
|
if (val == NULL) {
|
||||||
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
if (PyErr_ExceptionMatches(PyExc_KeyboardInterrupt)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -49,7 +51,7 @@ fold_unaryop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||||
of !=. Detecting such cases doesn't seem worthwhile.
|
of !=. Detecting such cases doesn't seem worthwhile.
|
||||||
Python uses </> for 'is subset'/'is superset' operations on sets.
|
Python uses </> for 'is subset'/'is superset' operations on sets.
|
||||||
They don't satisfy not folding laws. */
|
They don't satisfy not folding laws. */
|
||||||
int op = asdl_seq_GET(arg->v.Compare.ops, 0);
|
cmpop_ty op = asdl_seq_GET(arg->v.Compare.ops, 0);
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Is:
|
case Is:
|
||||||
op = IsNot;
|
op = IsNot;
|
||||||
|
@ -63,8 +65,17 @@ fold_unaryop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||||
case NotIn:
|
case NotIn:
|
||||||
op = In;
|
op = In;
|
||||||
break;
|
break;
|
||||||
default:
|
// The remaining comparison operators can't be safely inverted
|
||||||
op = 0;
|
case Eq:
|
||||||
|
case NotEq:
|
||||||
|
case Lt:
|
||||||
|
case LtE:
|
||||||
|
case Gt:
|
||||||
|
case GtE:
|
||||||
|
op = 0; // The AST enums leave "0" free as an "unused" marker
|
||||||
|
break;
|
||||||
|
// No default case, so the compiler will emit a warning if new
|
||||||
|
// comparison operators are added without being handled here
|
||||||
}
|
}
|
||||||
if (op) {
|
if (op) {
|
||||||
asdl_seq_SET(arg->v.Compare.ops, 0, op);
|
asdl_seq_SET(arg->v.Compare.ops, 0, op);
|
||||||
|
@ -224,7 +235,7 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||||
|
|
||||||
PyObject *lv = lhs->v.Constant.value;
|
PyObject *lv = lhs->v.Constant.value;
|
||||||
PyObject *rv = rhs->v.Constant.value;
|
PyObject *rv = rhs->v.Constant.value;
|
||||||
PyObject *newval;
|
PyObject *newval = NULL;
|
||||||
|
|
||||||
switch (node->v.BinOp.op) {
|
switch (node->v.BinOp.op) {
|
||||||
case Add:
|
case Add:
|
||||||
|
@ -263,8 +274,11 @@ fold_binop(expr_ty node, PyArena *arena, _PyASTOptimizeState *state)
|
||||||
case BitAnd:
|
case BitAnd:
|
||||||
newval = PyNumber_And(lv, rv);
|
newval = PyNumber_And(lv, rv);
|
||||||
break;
|
break;
|
||||||
default: // Unknown operator
|
// No builtin constants implement the following operators
|
||||||
|
case MatMult:
|
||||||
return 1;
|
return 1;
|
||||||
|
// No default case, so the compiler will emit a warning if new binary
|
||||||
|
// operators are added without being handled here
|
||||||
}
|
}
|
||||||
|
|
||||||
return make_const(node, newval, arena);
|
return make_const(node, newval, arena);
|
||||||
|
@ -457,8 +471,11 @@ astfold_mod(mod_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||||
case Expression_kind:
|
case Expression_kind:
|
||||||
CALL(astfold_expr, expr_ty, node_->v.Expression.body);
|
CALL(astfold_expr, expr_ty, node_->v.Expression.body);
|
||||||
break;
|
break;
|
||||||
default:
|
// The following top level nodes don't participate in constant folding
|
||||||
|
case FunctionType_kind:
|
||||||
break;
|
break;
|
||||||
|
// No default case, so the compiler will emit a warning if new top level
|
||||||
|
// compilation nodes are added without being handled here
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -567,8 +584,14 @@ astfold_expr(expr_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||||
return make_const(node_, PyBool_FromLong(!state->optimize), ctx_);
|
return make_const(node_, PyBool_FromLong(!state->optimize), ctx_);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
case NamedExpr_kind:
|
||||||
|
CALL(astfold_expr, expr_ty, node_->v.NamedExpr.value);
|
||||||
break;
|
break;
|
||||||
|
case Constant_kind:
|
||||||
|
// Already a constant, nothing further to do
|
||||||
|
break;
|
||||||
|
// No default case, so the compiler will emit a warning if new expression
|
||||||
|
// kinds are added without being handled here
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -686,8 +709,17 @@ astfold_stmt(stmt_ty node_, PyArena *ctx_, _PyASTOptimizeState *state)
|
||||||
case Expr_kind:
|
case Expr_kind:
|
||||||
CALL(astfold_expr, expr_ty, node_->v.Expr.value);
|
CALL(astfold_expr, expr_ty, node_->v.Expr.value);
|
||||||
break;
|
break;
|
||||||
default:
|
// The following statements don't contain any subexpressions to be folded
|
||||||
|
case Import_kind:
|
||||||
|
case ImportFrom_kind:
|
||||||
|
case Global_kind:
|
||||||
|
case Nonlocal_kind:
|
||||||
|
case Pass_kind:
|
||||||
|
case Break_kind:
|
||||||
|
case Continue_kind:
|
||||||
break;
|
break;
|
||||||
|
// No default case, so the compiler will emit a warning if new statement
|
||||||
|
// kinds are added without being handled here
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -700,8 +732,8 @@ astfold_excepthandler(excepthandler_ty node_, PyArena *ctx_, _PyASTOptimizeState
|
||||||
CALL_OPT(astfold_expr, expr_ty, node_->v.ExceptHandler.type);
|
CALL_OPT(astfold_expr, expr_ty, node_->v.ExceptHandler.type);
|
||||||
CALL_SEQ(astfold_stmt, stmt, node_->v.ExceptHandler.body);
|
CALL_SEQ(astfold_stmt, stmt, node_->v.ExceptHandler.body);
|
||||||
break;
|
break;
|
||||||
default:
|
// No default case, so the compiler will emit a warning if new handler
|
||||||
break;
|
// kinds are added without being handled here
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue