Skip to content

Commit 84ec7da

Browse files
MikaelMayeraqjune-awsclaudeshigoel
authored
Remove global variables and modifies clauses from Core, replace returns with out/inout parameters (#759)
This changes (1) the Core procedure declaration syntax from separate input/output parameter lists (`procedure P(x: int) returns (y: int)`) to a unified parameter list with `out`/`inout` modifiers (`procedure P(x: int, out y: int)`), (2) removes the `modifies` clause from procedure specification, (3) removes global variables from Core, and (4) changes the syntax of call `call lhs := f(args)` to `call f(x, out lhs, inout ...)`. **AST changes (summary: Procedure Header -> none, Decl -> global var removal, Call -> has list of in/inout/out args)** - `Procedure.Header.inputs` and `Procedure.Header.outputs` are still there. - CST->AST: partition unified bindings into inputs/outputs based on modifiers (`inout` appears in both inputs and outputs lists) - AST->CST: detect `inout` by comparing input/output name overlap, emit `out`/`inout` prefixes instead of `returns` clause Two new functions on `Procedure.Header` (in `Procedure.lean`) classify parameters by their role: - `Procedure.Header.getInoutParams`: returns parameters that appear in both `inputs` and `outputs` (the intersection). These are the parameters for which `old x` snapshots are meaningful. - `Procedure.Header.getOutputOnlyParams`: returns output parameters that do NOT appear in `inputs`. These are output-only parameters that have no pre-state. - Remove `ioDisjoint` from `WFProcedureProp` (inputs and outputs may now overlap for inout parameters). **Removal of Decl.var (global variables)** - Remove `Decl.var` from `Core.Decl` and `DeclKind.var` from `Core.DeclKind`. Global variables no longer exist in the Core AST. - Remove `Program.getVar?`, `Program.getVarTy?`, `Program.getVarInit?`, `Decl.getVar?`, `Decl.getVar`, and related accessors. - Remove `WFVarProp` from well-formedness definitions and drop the `.var` case from `WFDeclProp`. - `ProcedureType.typeCheck`: old-variable bindings for postconditions are now added only for inout parameters (`getInoutParams`), not for all program-level globals. - `StatementType.typeCheckCmd`: add `areInoutArgsValid` check ensuring that inout call arguments are simple variable references with the same name as the formal parameter. - `CollectSymbols.lean`: remove `collectGlobalSymbols` (always empty). - `ProcedureEval.lean`: old-substitution now only covers input parameters that also appear as outputs (inout), not program globals. - `ProgramEval.lean`, `Core.lean`: remove `.var` evaluation case and statistics counting. **Old-expression rules** - `old(expr)` now applies only to inout parameters (those appearing in both inputs and outputs). For input-only parameters, `old x = x` so the `old` prefix is not emitted. **Boole dialect updates (`Verify.lean`)** The new syntax of Core (`procedure`/`call` with `out`/`inout` params) is rejected, to unchange the Core syntax. A new `procedure_signatures.lean` test covers accepted and rejected procedure declaration and call statement forms. Global variables in Boole are translated into `inout` parameters of procedures (constant globals to plain parameters). - Modified globals (`modifies g`) become inout parameters (in both `allInputs` and `allOutputs`) via `translateProcedureDecl`. - Pre-pass collects global variable types into `TranslateState.globalVarTypes` and per-procedure modifies info into `TranslateState.modifiesMap` via `collectModifiesFromSpec`. - Read-only globals (referenced but not in `modifies`) become input-only parameters so they remain in scope. - `oldifyExpr` takes `currentInoutNames` and only applies `old` prefix to inout variables. For read-only globals, `old g` simplifies to `g`. **Pass updates** - `ProcBodyVerify.lean`: prefix is now `inputInits ++ outputOnlyInits ++ oldInoutInits ++ assumes`. - `ProcBodyVerifyCorrect.lean`: full proof rewrite for the new prefix structure. Added helper lemmas about `getInoutParams` / `getOutputOnlyParams` (subset, disjointness, nodup). All proofs complete with zero sorries. - `CallElim.lean`: old-variable type lookup no longer falls back to program-level globals. **Other changes** - Update all .core.st examples, expected outputs, and test files - Add `CoreIdent.mkOld_injective` lemma (Identifiers.lean) - Add `ioNotOld` to `WFProcedureProp` (no IO var is old-prefixed) - Editor syntax highlights updated for `out`/`inout` keywords --------- Co-authored-by: Juneyoung Lee <lebjuney@amazon.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Juneyoung Lee <136006969+aqjune-aws@users.noreply.github.com> Co-authored-by: Shilpi Goel <shigoel@gmail.com>
1 parent 786e03b commit 84ec7da

165 files changed

Lines changed: 2358 additions & 2321 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DiffTestCore.lean

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def StrataResult.toStr : StrataResult → String
7070
/-- Build a Core program that asserts str.in.re(testStr, regexExpr). -/
7171
def mkProgText (testStr : String) (regexStr : String) : String :=
7272
"program Core;\n" ++
73-
"procedure main() returns () {\n" ++
73+
"procedure main() {\n" ++
7474
s!" assert [match_check]: (str.in.re(\"{escapeForCore testStr}\", {regexStr}));\n" ++
7575
"};"
7676

Examples/DoubleTwice.core.st

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
program Core;
22

3-
procedure Double(n : int) returns (result : int)
3+
procedure Double(n : int, out result : int)
44
spec {
55
ensures [double_correct]: (result == n * 2);
66
}
77
{
88
result := n + n;
99
};
1010

11-
procedure TestProc(x : int) returns (output : int)
11+
procedure TestProc(x : int, out output : int)
1212
spec {
1313
ensures [testProc_result]: (output == x * 4);
1414
}
1515
{
16-
call output := Double(x);
17-
call output := Double(output);
16+
call Double(x, out output);
17+
call Double(output, out output);
1818
};

Examples/HeapReasoning.core.st

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ function isString(v: Box): bool;
1818
function isRef(v: Box): bool;
1919

2020
// Replace by allocation time, one tick per 1) allocation 2) method call, const for each Ref = allocation time, prove disjointness by proving that the allocation is greater
21-
var tickingClock: int;
2221
function allocationTime(r: Ref): int;
2322

2423
// TODO: What happens if allocating in an if-expression
@@ -68,7 +67,7 @@ axiom [nextFieldType]: forall s: Struct :: isContainerStruct(s) ==> (isRef(s[nex
6867
function Container_ctorModifiesFrame(h: Heap, testedRef: Ref, r: Ref, methodEntryTime: int): bool {
6968
testedRef == r || (allocationTime(testedRef) >= methodEntryTime)
7069
}
71-
procedure Container_ctor(heap: Heap, r: Ref, initialLychee: int, initialPineapple: int, initialNext: Ref) returns (newHeap: Heap)
70+
procedure Container_ctor(inout tickingClock: int, heap: Heap, r: Ref, initialLychee: int, initialPineapple: int, initialNext: Ref, out newHeap: Heap)
7271
spec {
7372
// Refs are allocated
7473
free requires tickingClock >= 0;
@@ -120,7 +119,7 @@ function UpdateContainersModifiesFrame(h: Heap, testedRef: Ref, ref1: Ref, metho
120119
}
121120

122121
// Annotated with "modifies ref1"
123-
procedure UpdateContainers(heap: Heap, ref1: Ref, ref2: Ref) returns (newHeap: Heap)
122+
procedure UpdateContainers(inout tickingClock: int, heap: Heap, ref1: Ref, ref2: Ref, out newHeap: Heap)
124123
spec {
125124
// Refs are allocated
126125
free requires tickingClock >= 0;
@@ -190,10 +189,8 @@ function MainModifiesFrame(h: Heap, testedRef: Ref, methodEntryTime: int): bool
190189
}
191190

192191
// annotated with "modifies {}"
193-
procedure Main(heap: Heap) returns (newHeap: Heap)
192+
procedure Main(inout tickingClock: int, heap: Heap, out newHeap: Heap)
194193
spec {
195-
modifies tickingClock;
196-
197194
free requires tickingClock >= 0;
198195
ensures tickingClock >= old tickingClock;
199196

@@ -211,7 +208,7 @@ spec {
211208
assume allocationTime(c1) == tickingClock;
212209
assume isContainer(c1);
213210
tickingClock := tickingClock + 1; // After allocation: increase the tick
214-
call heap_var := Container_ctor(heap_var, c1, 0, 1, null);
211+
call Container_ctor(inout tickingClock, heap_var, c1, 0, 1, null, out heap_var);
215212

216213
assert [c1Lychees0]: isContainer(c1) && UnboxingInt(heap_var[c1][lycheeField]) == 0;
217214
assert [c1Pineapple1]: isContainer(c1) && UnboxingInt(heap_var[c1][pineappleField]) == 1;
@@ -221,7 +218,7 @@ spec {
221218
assume allocationTime(c2) == tickingClock;
222219
assume isContainer(c2);
223220
tickingClock := tickingClock + 1; // After allocation: increase the tick
224-
call heap_var := Container_ctor(heap_var, c2, 0, 0, null);
221+
call Container_ctor(inout tickingClock, heap_var, c2, 0, 0, null, out heap_var);
225222

226223
// c1.next := c2
227224
assert MainModifiesFrame(heap_var, c1, methodEntryTime); // We can do that since the ticking clock has increased
@@ -233,7 +230,7 @@ spec {
233230
var rhs2: Box := BoxingRef(c1);
234231
heap_var := heap_var[c2 := heap_var[c2][nextField := rhs2]];
235232

236-
call heap_var := UpdateContainers(heap_var, c1, c2);
233+
call UpdateContainers(inout tickingClock, heap_var, c1, c2, out heap_var);
237234

238235
assert [c1Lychees1]: isContainer(c1) && UnboxingInt(heap_var[c1][lycheeField]) == 1;
239236
assert [c1Pineapple2]: isContainer(c1) && UnboxingInt(heap_var[c1][pineappleField]) == 2;

Examples/IrrelevantAxioms.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ axiom [g_idempotent]: forall x: int :: g(g(x)) == g(x);
1515
function h(x: int): int;
1616
axiom [h_def]: forall x: int :: h(x) == f(x) + 1;
1717

18-
procedure TestF(x: int) returns (result: int)
18+
procedure TestF(x: int, out result: int)
1919
spec {
2020
ensures [result_positive]: result > 0;
2121
}

Examples/LoopSimple.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
program Core;
22

3-
procedure loopSimple (n: int) returns (r: int)
3+
procedure loopSimple (n: int, out r: int)
44
spec {
55
requires (n >= 0);
66
}

Examples/ProcedureTypeError.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ program Core;
1515
// limitations under the License.
1616
//
1717

18-
procedure SumPositive(a: int, b: int) returns (c: int)
18+
procedure SumPositive(a: int, b: int, out c: int)
1919
spec {
2020
requires [precond1]: 0 <= a;
2121
requires [precond2]: c <= b;

Examples/SafeBvOps.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
program Core;
22

3-
procedure safe_bv_ops(x: bv32, y: bv32) returns (r: bv32)
3+
procedure safe_bv_ops(x: bv32, y: bv32, out r: bv32)
44
{
55
var a: bv32;
66
var b: bv32;

Examples/SarifTest.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
program Core;
22

33
// Simple test program for SARIF output
4-
procedure AddPositive(x : int, y : int) returns (z : int)
4+
procedure AddPositive(x : int, y : int, out z : int)
55
spec {
66
requires [x_positive]: (x > 0);
77
requires [y_positive]: (y > 0);

Examples/SimpleProc.core.st

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,10 @@ program Core;
1515
// limitations under the License.
1616
//
1717

18-
var g : bool;
19-
20-
procedure Test(x : bool) returns (y : bool)
18+
procedure Test(inout g : bool, x : bool, out y : bool)
2119
spec {
2220
ensures (y == x);
2321
ensures (x == y);
24-
ensures (g == old g);
2522
}
2623
{
2724
y := x || x;

Examples/TwoLoops.core.st

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ program Core;
33
const N : int;
44
axiom [ax_N]: (0 <= N);
55

6-
procedure TwoLoops() returns ()
6+
procedure TwoLoops()
77
{
88
var i : int;
99
var j : int;

0 commit comments

Comments
 (0)