Skip to content

feat(laurel): Add direct operational semantics for Laurel IR#511

Open
olivier-aws wants to merge 2 commits intostrata-org:mainfrom
olivier-aws:feat/laurel-direct-semantics
Open

feat(laurel): Add direct operational semantics for Laurel IR#511
olivier-aws wants to merge 2 commits intostrata-org:mainfrom
olivier-aws:feat/laurel-direct-semantics

Conversation

@olivier-aws
Copy link
Contributor

Summary

This PR implements Option A from the design document design-formal-semantics-for-laurel-ir.md: a standalone big-step operational semantics for all ~35 Laurel StmtExpr constructors, independent of Core semantics.

Changes

New Files

  1. Strata/Languages/Laurel/LaurelSemantics.lean (~800 LOC)

    • Core semantic definitions including LaurelValue, LaurelStore, LaurelHeap, Outcome types
    • Mutually inductive EvalLaurelStmt/EvalLaurelBlock relations
    • Coverage: literals, variables, primitive operations, control flow (if/while/block/exit/return), assignments, verification constructs (assert/assume), static calls, OO features (new/field select/instance call/reference equals/is type/as type), and specification constructs
  2. Strata/Languages/Laurel/LaurelSemanticsProps.lean (~500 LOC)

    • Store monotonicity theorems
    • Store operation determinism
    • catchExit and evalPrimOp properties
    • Block value semantics
    • Full evaluation determinism theorem (stated, admitted pending mutual induction proof)
  3. StrataTest/Languages/Laurel/LaurelSemanticsTest.lean (~300 LOC)

    • 30+ concrete evaluation tests covering all major constructs
    • Tests for literals, identifiers, primitive ops, blocks, if-then-else, exit/return propagation, local variables, assert/assume, nested control flow
  4. docs/designs/design-formal-semantics-for-laurel-ir.md

    • Design document describing three options (A: direct, B: shallow, C: hybrid)
    • This implementation is Option A, serving as a reference semantics for future translation correctness proofs

Key Design Decisions

  • Outcome type explicitly models non-local control flow (exit/return)
  • EvalArgs uses the expression evaluator δ (non-mutual) for call argument evaluation, avoiding Lean 4 mutual inductive limitations
  • Heap model is separate from store for OO features
  • Specification constructs (forall, exists, old, fresh, assigned, contract_of) are delegated to the expression evaluator δ
  • Static/instance calls restore the caller store after body evaluation

Bug Fixes (second commit)

  • Fixed EvalLaurelBlock non-determinism by adding rest ≠ [] side condition to cons_normal
  • Added void-returning procedure rules (static_call_return_void, instance_call_return, instance_call_return_void)
  • Fixed bindParams to enforce lexical scoping (start from empty store)
  • Added documentation for purity requirements and known limitations

Known Limitations

  • Determinism theorem is admitted (requires careful mutual induction)
  • While loop semantics only captures terminating executions (inductive)
  • Call argument evaluation uses δ rather than full EvalLaurelStmt
  • Translation correctness proof is out of scope (separate task)

Testing

All tests pass:

lake build

Related

Design document: docs/designs/design-formal-semantics-for-laurel-ir.md

Implement Option A from design-formal-semantics-for-laurel-ir.md: a
standalone big-step operational semantics for all ~35 Laurel StmtExpr
constructors, independent of Core semantics.

New files:
- Strata/Languages/Laurel/LaurelSemantics.lean: Core semantic definitions
  including LaurelValue, LaurelStore, LaurelHeap, Outcome types, and
  mutually inductive EvalLaurelStmt/EvalLaurelBlock relations covering
  literals, variables, primitive operations, control flow (if/while/
  block/exit/return), assignments, verification constructs (assert/
  assume), static calls, OO features (new/field select/instance call/
  reference equals/is type/as type), and specification constructs
  (forall/exists/old/fresh/assigned/prove by/contract of).

- Strata/Languages/Laurel/LaurelSemanticsProps.lean: Properties including
  store monotonicity (UpdateStore/InitStore preserve definedness),
  store operation determinism, catchExit properties, evalPrimOp
  determinism, and block value semantics. Full evaluation determinism
  theorem is stated but admitted (requires mutual induction).

- StrataTest/Languages/Laurel/LaurelSemanticsTest.lean: 30+ concrete
  evaluation tests covering literals, identifiers, primitive ops (add,
  and, not, lt, strconcat), blocks (empty, singleton, multi-statement),
  if-then-else (true/false/no-else), exit (propagation, labeled catch,
  non-matching), return (with/without value, short-circuit), local
  variables (init/uninit), assert/assume, prove-by, nested control
  flow, and evalPrimOp/catchExit unit tests.

- docs/designs/design-formal-semantics-for-laurel-ir.md: Design document
  describing three options (A: direct, B: shallow, C: hybrid) with
  recommendation for Option C. This implementation is Option A, serving
  as a reference semantics for translation correctness proofs.

Design decisions:
- Outcome type explicitly models non-local control flow (exit/return)
- EvalArgs uses the expression evaluator δ (non-mutual) for call
  argument evaluation, avoiding Lean 4 mutual inductive limitations
- Heap model is separate from store for OO features
- Specification constructs (forall, exists, old, fresh, assigned,
  contract_of) are delegated to the expression evaluator δ
- Static/instance calls restore the caller store after body evaluation

Known limitations:
- Determinism theorem is admitted (sorry) - requires careful mutual
  induction over EvalLaurelStmt/EvalLaurelBlock
- While loop semantics only captures terminating executions (inductive)
- Call argument evaluation uses δ rather than full EvalLaurelStmt
  (avoids mutual inductive nesting issues in Lean 4)
- Translation correctness proof is out of scope (separate task)
…mantics

Bug fixes:
- Fix EvalLaurelBlock non-determinism: add rest ≠ [] side condition to
  cons_normal so it cannot overlap with last_normal on singleton lists.
  last_normal handles [s] → value of s; cons_normal handles (s :: rest)
  where rest is non-empty.
- Add static_call_return_void, instance_call_return, and
  instance_call_return_void rules for void-returning procedures
  (.ret none). Previously these caused evaluation to get stuck.
- Fix bindParams to start from an empty store instead of the caller
  store, enforcing lexical scoping. Callees can no longer accidentally
  read caller locals.

Documentation:
- Document purity requirement for assert_true/assume_true conditions.
- Document intentionally omitted constructors (Abstract, All, Hole).
- Document known limitations (multi-target Assign, pure argument eval).
- Update design document: replace Option C recommendation with Option A
  decision record reflecting the actual implementation choice.
@olivier-aws olivier-aws requested a review from a team March 3, 2026 23:21
EvalLaurelStmt δ π h σ s h₁ σ₁ o₁ →
EvalLaurelStmt δ π h σ s h₂ σ₂ o₂ →
h₁ = h₂ ∧ σ₁ = σ₂ ∧ o₁ = o₂ := by
sorry
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we actually do the proof?

EvalLaurelBlock δ π h σ ss h₁ σ₁ o₁ →
EvalLaurelBlock δ π h σ ss h₂ σ₂ o₂ →
h₁ = h₂ ∧ σ₁ = σ₂ ∧ o₁ = o₂ := by
sorry
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we actually do the proof?

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.

2 participants