Skip to content

Fix early return soundness in Laurel: replace assume false with exit#504

Merged
keyboardDrummer merged 3 commits intomainfrom
fabiomadge/early-return-soundness
Mar 5, 2026
Merged

Fix early return soundness in Laurel: replace assume false with exit#504
keyboardDrummer merged 3 commits intomainfrom
fabiomadge/early-return-soundness

Conversation

@fabiomadge
Copy link
Contributor

@fabiomadge fabiomadge commented Mar 3, 2026

Problem

Early returns in Laurel were encoded as result := value; assume false. The assume false made the return path vacuously true, so Core's VCG never checked postconditions on early return paths. A procedure like:

procedure buggy(x: int) returns (r: int)
  ensures r >= 0
{
  if (x < 0) { return x; }  // violates postcondition — was not caught
  return x;
}

passed verification silently.

Fix

Replace assume false with exit $body targeting a labeled body block. The exit skips remaining body statements, but postconditions — appended after the block by ProcedureEval — are still checked on all paths.

Changes

Context

See PR #385 discussion for the original analysis of this soundness issue. Duplicate diagnostics from exit inside if branches are a known Core VCG issue (#419).

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@fabiomadge fabiomadge requested a review from a team March 3, 2026 02:47
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass that eliminates predicate (constrained) types by:
- Inserting assume for values received of predicate type (procedure inputs)
- Inserting assert for values provided of predicate type (assignments, returns)
- Resolving all predicate type references to their base types

New files:
- PredicateTypeElim.lean: the pass
- T09_PredicateTypes.lean: comprehensive tests
- T09_ConstrainedTypes.lean: simple test

Also includes:
- Grammar: constrained type syntax
- Parser: parseConstrainedType, topLevelConstrainedType
- Pipeline: pass wired before heapParameterization

Depends on #504 (early return soundness fix) for postcondition
checking on return paths.
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass that eliminates predicate (constrained) types by:
- Inserting assume for values received of predicate type (procedure inputs)
- Inserting assert for values provided of predicate type (assignments, returns)
- Resolving all predicate type references to their base types

New files:
- PredicateTypeElim.lean: the pass
- T09_PredicateTypes.lean: comprehensive tests
- T09_ConstrainedTypes.lean: simple test

Also includes:
- Grammar: constrained type syntax
- Parser: parseConstrainedType, topLevelConstrainedType
- Pipeline: pass wired before heapParameterization

Depends on #504 (early return soundness fix) for postcondition
checking on return paths.
@fabiomadge fabiomadge changed the title Fix early return soundness: replace assume false with exit Fix early return soundness in Laurel: replace assume false with exit Mar 3, 2026
@fabiomadge fabiomadge changed the title Fix early return soundness in Laurel: replace assume false with exit Fix early return soundness in Laurel: replace assume false with exit Mar 3, 2026
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (167 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel pass (189 lines) that eliminates predicate types:
- assume for inputs and call return values
- assert for assignments, variable init, and call arguments
- resolve all predicate type references to base types in all positions

Grammar: constrained type syntax (6 lines)
Parser: parseConstrainedType + topLevelConstrainedType (19 lines)
Translator: import + pipeline wiring (2 lines)
Test: T09_PredicateTypes — 10 procedures covering all cases

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel elimination pass (ConstrainedTypeElim.lean) that:
- Inserts assume for inputs and call return values
- Inserts assert for assignments, variable init, and call arguments
- Resolves all constrained type references to base types

No Core translator changes needed.

Grammar: constrained type syntax
Parser: parseConstrainedType + topLevelConstrainedType
Test: T09_PredicateTypes — 11 test procedures

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel elimination pass (ConstrainedTypeElim.lean) that:
- Inserts assume for inputs and call return values
- Inserts assert for assignments, variable init, and call arguments
- Resolves all constrained type references to base types

No Core translator changes needed.

Grammar: constrained type syntax
Parser: parseConstrainedType + topLevelConstrainedType
Test: T09_PredicateTypes — 11 test procedures

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel elimination pass (ConstrainedTypeElim.lean) that:
- Inserts assume for inputs and call return values
- Inserts assert for assignments, variable init, and call arguments
- Resolves all constrained type references to base types

No Core translator changes needed.

Grammar: constrained type syntax
Parser: parseConstrainedType + topLevelConstrainedType
Test: T09_PredicateTypes — 11 test procedures

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel elimination pass (ConstrainedTypeElim.lean) that:
- Inserts assume for inputs and call return values
- Inserts assert for assignments, variable init, and call arguments
- Resolves all constrained type references to base types

No Core translator changes needed.

Grammar: constrained type syntax
Parser: parseConstrainedType + topLevelConstrainedType
Test: T09_PredicateTypes — 11 test procedures

Based on #504 (early return soundness fix).
fabiomadge added a commit that referenced this pull request Mar 3, 2026
A Laurel-to-Laurel elimination pass (ConstrainedTypeElim.lean) that:
- Inserts assume for inputs and call return values
- Inserts assert for assignments, variable init, and call arguments
- Resolves all constrained type references to base types

No Core translator changes needed.

Grammar: constrained type syntax
Parser: parseConstrainedType + topLevelConstrainedType
Test: T09_PredicateTypes — 11 test procedures

Based on #504 (early return soundness fix).
keyboardDrummer
keyboardDrummer previously approved these changes Mar 3, 2026
Copy link
Contributor

@keyboardDrummer keyboardDrummer left a comment

Choose a reason for hiding this comment

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

Nice, thanks

@fabiomadge fabiomadge force-pushed the fabiomadge/early-return-soundness branch 2 times, most recently from 9728a16 to 579e349 Compare March 4, 2026 05:37
Early returns were encoded as 'result := value; assume false'. The
assume false made the path vacuously true, so postconditions were
never checked on early return paths.

Now uses exit statement targeting the labeled body block. The exit
skips remaining body statements but postconditions (appended after
the block by ProcedureEval) are still checked on all paths.

Changes:
- LaurelToCoreTranslator: assume false -> exit $body, wrap body
  in labeled block
- TestDiagnostics: support consecutive annotation comments targeting
  the same code line (needed for duplicate diagnostics from #419)
- T8b_EarlyReturnPostconditions: test for the soundness fix
- T3_ControlFlow: updated annotations for #419 duplicates
@fabiomadge fabiomadge force-pushed the fabiomadge/early-return-soundness branch from 579e349 to dd1139d Compare March 4, 2026 05:41
@keyboardDrummer
Copy link
Contributor

keyboardDrummer commented Mar 4, 2026

Looks great! Thanks

Copy link
Contributor

@atomb atomb left a comment

Choose a reason for hiding this comment

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

This seems good to me.

@keyboardDrummer keyboardDrummer added this pull request to the merge queue Mar 5, 2026
Merged via the queue into main with commit 38d51cc Mar 5, 2026
24 checks passed
@keyboardDrummer keyboardDrummer deleted the fabiomadge/early-return-soundness branch March 5, 2026 15:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants