Skip to content

Add pure expression inliner infrastructure for quantifier bodies#4567

Open
feliperodri wants to merge 4 commits intomodel-checking:mainfrom
feliperodri:inline-pure-expressions
Open

Add pure expression inliner infrastructure for quantifier bodies#4567
feliperodri wants to merge 4 commits intomodel-checking:mainfrom
feliperodri:inline-pure-expressions

Conversation

@feliperodri
Copy link
Copy Markdown
Contributor

What

Adds Expr::substitute_symbol() and inline_as_pure_expr() — infrastructure for inlining function calls as side-effect-free expression trees. This is a prerequisite for generating CBMC quantifier bodies without StatementExpression nodes, which will enable nested quantifier support.

Why

CBMC rejects side effects inside quantifier expressions (forall/exists). Today, Kani's handle_quantifiers post-pass inlines function calls into StatementExpression nodes, which CBMC accepts via convert_statement_expression, but this breaks for nested quantifiers (inner quantifiers are treated as side effects, causing a CBMC invariant violation crash).

The pure expression inliner resolves this by producing expression trees that contain no statements, no gotos, and no labels; only pure expressions that CBMC can handle directly, including in nested contexts.

How

Expr::substitute_symbol() (cprover_bindings/src/goto_program/expr.rs)

  • Recursively replaces all occurrences of a symbol with a replacement expression across all 25+ ExprValue variants.

inline_as_pure_expr() (kani-compiler/src/codegen_cprover_gotoc/context/goto_ctx.rs)

  • For a function call: looks up the body in the symbol table, collects all assignments, resolves the return expression through the assignment chain, substitutes parameters with arguments, and flattens StatementExpression nodes (e.g., checked arithmetic).
  • Recursively inlines nested function calls.
  • Handles Forall/Exists sub-expressions for future nested quantifier support.

Soundness note

When flattening StatementExpression nodes from checked arithmetic (e.g., %, +), the inliner drops Assert/Assume statements that check for overflow and division by zero. This is a known trade-off documented in docs/dev/pure-expression-inliner.md: CBMC requires pure expressions in quantifier bodies, and runtime checks are inherently side effects. A future improvement could hoist these checks outside the quantifier as preconditions.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.

@github-actions github-actions bot added Z-EndToEndBenchCI Tag a PR to run benchmark CI Z-CompilerBenchCI Tag a PR to run benchmark CI labels Mar 30, 2026
@feliperodri feliperodri marked this pull request as ready for review March 30, 2026 04:10
@feliperodri feliperodri requested a review from a team as a code owner March 30, 2026 04:10
Add Expr::substitute_symbol() and inline_as_pure_expr() for inlining
function calls as side-effect-free expression trees. This infrastructure
is needed to generate CBMC quantifier bodies without StatementExpression
nodes, which will enable nested quantifier support.

Changes:
- cprover_bindings: Expr::substitute_symbol() — recursive symbol
  replacement across all ExprValue variants, with 6 unit tests
- goto_ctx.rs: inline_as_pure_expr() — inlines function calls by
  extracting return expressions, resolving intermediate variables,
  and substituting parameters. Handles StatementExpression flattening
  for checked arithmetic (drops Assert/Assume runtime checks).
- docs/dev/pure-expression-inliner.md — developer documentation
  including soundness implications

Soundness note: The pure inliner drops overflow and division-by-zero
checks when flattening StatementExpression nodes from checked arithmetic.
This is a known trade-off — CBMC requires pure expressions in quantifier
bodies, and runtime checks are side effects.

The existing handle_quantifiers post-pass is NOT modified — this is
purely additive infrastructure.

Signed-off-by: Felipe R. Monteiro <felisous@amazon.com>
Signed-off-by: Felipe R. Monteiro <felisous@amazon.com>
- substitute_symbol returns (Expr, bool) for reliable change detection
  instead of Debug-format string comparison
- Graceful fallback for recursive functions (tracing::warn + return
  original) instead of assert! panic
- Diagnostics for edge cases: multiple assignments to same variable,
  non-symbol return expressions, unknown UnaryOperator variants
- Public API cleanup: inline_as_pure_expr_toplevel wrapper hides the
  visited set implementation detail
- Document StatementExpression non-recursion in substitute_symbol

Signed-off-by: Felipe R. Monteiro <felisous@amazon.com>
@feliperodri feliperodri force-pushed the inline-pure-expressions branch from 0fcc5c3 to 8b2600a Compare March 30, 2026 04:33
Comment on lines +10 to +20
## Soundness Implications

**Checked arithmetic in quantifier bodies**: When flattening `StatementExpression`
nodes (e.g., from checked division or remainder), the pure inliner drops the
`Assert` and `Assume` statements that check for overflow and division by zero.

- **Division by zero** inside a quantifier body will NOT be detected.
- **Arithmetic overflow** inside a quantifier body will NOT be detected.

**Future improvement**: The dropped assertions could be hoisted outside the
quantifier as preconditions, preserving soundness while keeping the body pure.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tautschnig this is similar to CBMC behavior, correct? Let me know if this raises any concerns.

- Replace destructuring assignment with .0 for substitute_symbol calls
- Restore step-by-step 'How It Works' section in dev documentation

Signed-off-by: Felipe R. Monteiro <felisous@amazon.com>
@feliperodri feliperodri added [C] Internal Tracks some internal work. I.e.: Users should not be affected. Z-Contracts Issue related to code contracts labels Mar 30, 2026
@feliperodri feliperodri added this to the Contracts milestone Mar 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[C] Internal Tracks some internal work. I.e.: Users should not be affected. Z-CompilerBenchCI Tag a PR to run benchmark CI Z-Contracts Issue related to code contracts Z-EndToEndBenchCI Tag a PR to run benchmark CI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant