Skip to content

Commit 064e019

Browse files
authored
fix(core): isolate path conditions at the procedure boundary (#1391)
Fixes #1390. **Problem:** `Program.eval` threads one `Env` through all procedures. A structured `exit` out of a labeled block doesn't pop its path-condition frames (`.block` exit pops `exprEnv.state`, not `pathConditions`; the exiting path bypasses `Env.merge`), so a procedure's preconditions/assumptions leak into later procedures. A contradictory leaked set then makes the next procedure prove false obligations vacuously — silent unsound pass. Laurel lowers non-final `return` to `exit "$body"`, so ordinary early-return code hits it. **Fix:** reset `pathConditions` to the pre-procedure state in the `.proc` fold (deferred obligations and fresh names carry forward). A `.block`-level reset was rejected — breaks fall-through. **Test:** `ProcedurePathConditionIsolation` — unsatisfiable-precondition `first` + structured `exit`, `second`'s `assert false` must fail. Red without the fix, green with it; full `StrataTest` green (539). Control matrix + scope in #1390.
1 parent a6e0cee commit 064e019

2 files changed

Lines changed: 63 additions & 0 deletions

File tree

Strata/Languages/Core/ProgramEval.lean

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ def eval (E : Env) : Except Strata.DiagnosticModel (List Env × Statistics) :=
5454

5555
| .proc proc _md =>
5656
let (E, procStats) := Procedure.eval declsE proc
57+
-- Reset path conditions to the pre-procedure state so a procedure's
58+
-- assumptions don't leak into later ones: a structured `exit` bypasses
59+
-- `Env.merge` and leaves its frames unpopped, which would otherwise be
60+
-- threaded into the next procedure (strata-org/Strata#1390). Deferred
61+
-- obligations and fresh names carry forward.
62+
let E := { E with pathConditions := declsE.pathConditions }
5763
go rest E (stats.merge procStats)
5864

5965
| .func func _ => do
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/-
2+
Copyright Strata Contributors
3+
4+
SPDX-License-Identifier: Apache-2.0 OR MIT
5+
-/
6+
module
7+
8+
meta import Strata.Languages.Core
9+
import StrataDDM.Integration.Lean.HashCommands
10+
11+
/-
12+
Regression test for strata-org/Strata#1390.
13+
14+
A structured `exit` bypasses `Env.merge`, so a procedure's path conditions used
15+
to leak into later procedures; a contradictory leaked set then proved their
16+
false obligations vacuously. `first` has an unsatisfiable precondition + a
17+
structured `exit`; `second`'s `assert false` must fail, not silently pass.
18+
-/
19+
20+
meta section
21+
open StrataDDM (Program)
22+
23+
namespace Strata
24+
25+
def leakViaStructuredExit : Program :=
26+
#strata
27+
program Core;
28+
29+
procedure first(n : int)
30+
spec {
31+
requires [c1]: (n >= 1);
32+
requires [c2]: (n <= 0);
33+
}
34+
{
35+
body: {
36+
if (n > 5) { exit body; }
37+
}
38+
};
39+
40+
procedure second()
41+
{
42+
assert [bad]: false;
43+
};
44+
#end
45+
46+
/--
47+
info:
48+
Obligation: bad
49+
Property: assert
50+
Result: ❌ fail
51+
-/
52+
#guard_msgs in
53+
#eval Core.verify leakViaStructuredExit (options := .quiet)
54+
55+
end Strata
56+
57+
end

0 commit comments

Comments
 (0)