= Constant folding
{wiki}
<Constant folding> replaces a <program expression> whose value is known during translation by a <literal value>. For `x=2+3*4`, first replace the <multiplication> subtree by `Constant(value=12)`, then replace the <addition> subtree by `Constant(value=14)`. The <variable assignment> and its `Store` target survive: the result still assigns to `x`. This illustrates why a bottom-up <abstract syntax tree traversal> is convenient: replacing one child can make its parent eligible for the next replacement.
\Image[/abstract-syntax-tree-constant-folding.png]
{title=<Constant folding> and postorder <abstract syntax tree traversal> for `2+3*4`: replace $3\cdot4$ by $12$, then $2+12$ by $14$}
{id=image-abstract-syntax-tree-constant-folding}
Here is a deliberately small <Python> transformer. Calling `generic_visit` first transforms the children; returning a node replaces the visited node. The location helpers retain or supply the metadata expected for <compilation>. <Python 3.14 documentation/ast — Abstract syntax trees, Python 3.14 documentation>[NodeTransformer, generic_visit, copy_location and fix_missing_locations]{ref}
``
class FoldIntegers(ast.NodeTransformer):
def visit_BinOp(self, node):
self.generic_visit(node)
if (isinstance(node.left, ast.Constant)
and isinstance(node.right, ast.Constant)
and type(node.left.value) is int
and type(node.right.value) is int
and type(node.op) in OPS):
value = OPS[type(node.op)](node.left.value, node.right.value)
return ast.copy_location(ast.Constant(value=value), node)
return node
tree = ast.parse("x=2+3*4", optimize=0)
folded = ast.fix_missing_locations(FoldIntegers().visit(tree))
namespace = {}
exec(compile(folded, "<folded example>", "exec"), namespace)
assert namespace["x"] == 14
assert ast.unparse(folded) == "x = 14"
``
This transformer folds literal <integer> operands only. It leaves `a+3*4` as `a+12`, without inventing a value for the <program identifier> `a`. It leaves calls intact: replacing `0*f()` by `0` could discard a <side effect> or a failure. Nor does it reassociate <floating-point arithmetic>, where changing parentheses can change rounding. For the supported <integer arithmetic>, a short <structural induction> explains correctness: a retained <literal value> has the same value; at each folded binary node, the already-correct child values are combined by exactly the original operation in the original left/right order. This claim concerns the supported fragment's values, not unrestricted equivalence of arbitrary <Python> programs.
Back to article page