Skip to content

Commit c246c69

Browse files
syntax: expr_strictly_free_of_region (closes blocker 5) (#201)
## Summary Reformulates the region-freedom predicate to drop the shadow short- circuit in `ERegion r' body`, addressing **blocker 5** (predicate- too-weak in the L1 region-shrinkage lemma family) identified by the 2026-05-28 audit (task #29). - New `expr_strictly_free_of_region` in `Syntax.v` — identical to `expr_free_of_region` except the `ERegion _ body` case recurses unconditionally (no `String.eqb` shadow short-circuit). - New `expr_strictly_free_implies_free` lemma (Qed) — trivial structural induction establishing that strict freedom implies weak freedom. - L1 lemma family migrated to take `expr_strictly_free_of_region` as the precondition: `region_shrink_preserves_typing_l1_gen_m`, `region_shrink_preserves_typing_l1_gen`, `region_shrink_preserves_typing_l1`, `preservation_l3_region_active_echo`, and the top-level `preservation_l3` theorem. ## Audit constraints honored - **Zero new** `Admitted` / `Axiom` declarations. Counts preserved exactly versus `main`: 4 `admit.` + 3 `Admitted.` in `Semantics_L1.v`, 1 `Admitted.` in `Semantics.v`. - The shadowed-sub-case admits at `Semantics_L1.v:553/621` (now shifted by added comments) **remain**. They are blocked by a list-vs-multiset structural mismatch (Phase D), **not** by predicate weakness. Strengthening the predicate does **not** close them — confirmed by the audit and verified empirically here (same admit counts after migration). - No patching of legacy `Theorem preservation` in `Semantics.v` (provably false per `Counterexample.v`) or the legacy `Typing.v` judgment, per the 2026-05-27 owner directive. ## Scope clarification This PR **does not** close blocker 3 — that requires Phase D (L2 effect-typed `TFun` work, branch `phase-d-slice-1-tfun-eff`). The step rules `S_Region_Exit` / `S_Region_Exit_Echo` in `Semantics.v` still emit the **weak** `expr_free_of_region` premise. Migrating those is a separate concern (legacy operational semantics) and is intentionally **not** done in this PR per the task brief. The L1 lemma chain (`preservation_l3_*` -> `region_shrink_preserves_typing_l1_*`) is internally consistent on the strict predicate; bridging the weak step-rule premise to the strict L1 lemma precondition is a follow-up. ## Build All 9 `.v` files compile clean under `coqc 8.18.0` from a fresh `.vo` state (Syntax, Typing, TypingL1, Modality, Semantics, Semantics_L1, Counterexample, Echo, TypingL2). ## Test plan - [x] `coqc 8.18.0` clean build of all 9 `.v` files from scratch - [x] No new `Admitted` / `Axiom` declarations (counts match main) - [x] Pre-existing 2 inner admits at `Semantics_L1.v:553/621` remain unchanged (line-shifted by added comments) - [x] Pre-existing outer `Admitted` at `Semantics_L1.v` (region shrink helper) remains unchanged - [x] Commit GPG-signed Audit reference: task #29 (2026-05-28). Blocker 5 closure. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 570ee45 commit c246c69

2 files changed

Lines changed: 120 additions & 8 deletions

File tree

formal/Semantics_L1.v

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,21 @@ Qed.
428428
(analog of [region_env_perm_typing] and [region_add_typing])
429429
is needed to close it; bridging options are listed in the
430430
case's own comment. *)
431+
(** Migrated to [expr_strictly_free_of_region] as the precondition
432+
(blocker 5 reformulation, 2026-05-28). The strict predicate gives
433+
the body strictly MORE information than the weak one — in
434+
particular, the [T_Region_*_L1] cases no longer rely on a
435+
shadow-short-circuited [True] in the [rr = r] subcase. The
436+
residual [admit] at the [T_Region_Active_L1] [rr = r] sub-case
437+
is blocked by a list-vs-multiset structural mismatch (Phase D
438+
work, NOT by predicate weakness — strengthening the predicate
439+
does not close it). The [admit] remains; see the case's own
440+
comment for resolution options. *)
431441
Lemma region_shrink_preserves_typing_l1_gen_m :
432442
forall m R G e T R' G',
433443
R ; G |=L1[m] e : T -| R' ; G' ->
434444
forall r,
435-
expr_free_of_region r e ->
445+
expr_strictly_free_of_region r e ->
436446
remove_first r R ; G |=L1[m] e : T -| remove_first r R' ; G'.
437447
Proof.
438448
intros m R G e T R' G' Ht.
@@ -532,7 +542,11 @@ Proof.
532542
* intro Hin. apply H. eapply remove_first_subset; exact Hin.
533543
* exact H0.
534544
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H1].
535-
* specialize (IHHt rr Hfree).
545+
* (* Strict-predicate migration: [Hfree] is uniformly the body's
546+
strict-freedom (no conditional to discharge). Still need
547+
[simpl in IHHt] to unfold [remove_first rr (r :: R)] into
548+
[r :: remove_first rr R] via the [rr <> r] guard. *)
549+
specialize (IHHt rr Hfree).
536550
simpl in IHHt.
537551
destruct (String.eqb rr r) eqn:Heq2.
538552
-- exfalso. apply String.eqb_eq in Heq2. apply Heq. exact Heq2.
@@ -601,7 +615,9 @@ Proof.
601615
* intro Hin. apply H. eapply remove_first_subset; exact Hin.
602616
* exact H0.
603617
* apply remove_first_preserves_other; [intro Hbad; apply Heq; exact Hbad | exact H1].
604-
* specialize (IHHt rr Hfree).
618+
* (* Strict-predicate migration: same as T_Region_L1 above —
619+
[simpl in IHHt] still needed to unfold [remove_first]. *)
620+
specialize (IHHt rr Hfree).
605621
simpl in IHHt.
606622
destruct (String.eqb rr r) eqn:Heq2.
607623
-- exfalso. apply String.eqb_eq in Heq2. apply Heq. exact Heq2.
@@ -656,7 +672,7 @@ Lemma region_shrink_preserves_typing_l1_gen :
656672
forall R G e T R' G',
657673
has_type_l1_linear R G e T R' G' ->
658674
forall r,
659-
expr_free_of_region r e ->
675+
expr_strictly_free_of_region r e ->
660676
has_type_l1_linear (remove_first r R) G e T (remove_first r R') G'.
661677
Proof.
662678
intros R G e T R' G' Ht r Hfree.
@@ -668,7 +684,7 @@ Lemma region_shrink_preserves_typing_l1 :
668684
is_value v ->
669685
has_type_l1_linear R G v T R' G' ->
670686
~ In r (free_regions T) ->
671-
expr_free_of_region r v ->
687+
expr_strictly_free_of_region r v ->
672688
has_type_l1_linear (remove_first r R) G v T (remove_first r R') G'.
673689
Proof.
674690
intros R G v T R' G' r Hv Ht HnotT Hfree.
@@ -1741,15 +1757,23 @@ Admitted.
17411757
Region shrinkage then provides the post-step witness typing
17421758
at [remove_first r R], and [T_Echo_L1] wraps it. *)
17431759

1760+
(** Strict-predicate migration (blocker 5, 2026-05-28): the
1761+
region-shrinkage helper this lemma calls now requires
1762+
[expr_strictly_free_of_region], so the corresponding premise
1763+
here is also tightened. The legacy step rules
1764+
[S_Region_Exit_Echo] in [Semantics.v] still emit the weak
1765+
[expr_free_of_region]; bridging the step-rule premise to the
1766+
L1 lemma precondition is a follow-up (legacy operational
1767+
semantics — see PR description). *)
17441768
Lemma preservation_l3_region_active_echo :
17451769
forall m R G r v T R_body G',
17461770
(* T_Region_Active_L1_Echo premises *)
17471771
In r R ->
17481772
~ In r (Typing.free_regions T) ->
17491773
has_type_l1 m R G v T R_body G' ->
1750-
(* S_Region_Exit_Echo premises *)
1774+
(* S_Region_Exit_Echo premises (strict variant per blocker 5) *)
17511775
is_value v ->
1752-
expr_free_of_region r v ->
1776+
expr_strictly_free_of_region r v ->
17531777
(* Conclusion: post-step types at [TEcho T] *)
17541778
has_type_l1 m (remove_first r R) G
17551779
(EEcho T v) (TEcho T) (remove_first_L1 r R_body) G'.
@@ -1806,7 +1830,7 @@ Theorem preservation_l3 :
18061830
~ In r (Typing.free_regions T) ->
18071831
has_type_l1 m R G v T R_body G' ->
18081832
is_value v ->
1809-
expr_free_of_region r v ->
1833+
expr_strictly_free_of_region r v ->
18101834
has_type_l1 m (remove_first r R) G
18111835
(EEcho T v) (TEcho T) (remove_first_L1 r R_body) G')
18121836
/\

formal/Syntax.v

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,94 @@ Fixpoint expr_free_of_region (r : region_name) (e : expr) : Prop :=
208208
| EObserve e' => expr_free_of_region r e'
209209
end.
210210

211+
(** ** Strict Region-Freedom (reformulation, closes blocker 5)
212+
213+
[expr_strictly_free_of_region r e] mirrors [expr_free_of_region]
214+
EXCEPT in the [ERegion r' body] case, where it recurses
215+
UNCONDITIONALLY (no shadow short-circuit on [r = r']).
216+
217+
Why a strict variant exists. The weak [expr_free_of_region]
218+
short-circuits to [True] when [ERegion r' body] shadows [r]
219+
(the [String.eqb r r'] guard). That weakness is sound for the
220+
plain capability-list reading of [R], where a nested
221+
[ERegion r ...] statically masks the outer [r] and the body
222+
cannot "reach back." But under the L1 [T_Region_Active_L1]
223+
judgment — and more sharply under L3 re-entry semantics —
224+
a body can witness the outer [r] through count-monotonicity /
225+
multiset accounting that the syntactic shadow does NOT
226+
discharge. The audit (task #29, 2026-05-28, blocker 5) traced
227+
one class of "predicate too weak" failures in the region-
228+
shrinkage lemma family to exactly this short-circuit accepting
229+
expressions whose body legitimately references the outer [r].
230+
231+
The strict variant is the conservative reading: an expression
232+
is strictly free of [r] iff [r] never appears anywhere inside,
233+
even under a same-named [ERegion r ...] shadow. It implies the
234+
weak variant (see [expr_strictly_free_implies_free] below).
235+
236+
Scope. The strict predicate is intended to replace the weak one
237+
in the L1 region-shrinkage lemma family
238+
([region_shrink_preserves_typing_l1_gen_m] and friends in
239+
[Semantics_L1.v]) and downstream consumers
240+
([preservation_l3_region_active_echo]). The legacy step rules
241+
[S_Region_Exit] / [S_Region_Exit_Echo] in [Semantics.v] still
242+
emit the weak [expr_free_of_region] premise; migrating those
243+
is a separate concern (legacy operational semantics) and may
244+
or may not happen in this PR (see PR description for current
245+
scope).
246+
247+
Note. The L1 shadowed-sub-case [admit] at
248+
[Semantics_L1.v:553/621] is blocked by a list-vs-multiset
249+
structural mismatch (Phase D work), NOT by predicate weakness.
250+
Strengthening to [expr_strictly_free_of_region] does NOT close
251+
those admits. *)
252+
253+
Fixpoint expr_strictly_free_of_region (r : region_name) (e : expr) : Prop :=
254+
match e with
255+
| EUnit | EBool _ | EI32 _ | EVar _ => True
256+
| EStringNew r' _ => r <> r'
257+
| EStringConcat e1 e2 | ELet e1 e2 | ELetLin e1 e2
258+
| EApp e1 e2 | EPair e1 e2 =>
259+
expr_strictly_free_of_region r e1 /\ expr_strictly_free_of_region r e2
260+
| EStringLen e' | EFst e' | ESnd e' | EInl _ e' | EInr _ e'
261+
| EBorrow e' | EDeref e' | EDrop e' | ECopy e' | ELam _ e' =>
262+
expr_strictly_free_of_region r e'
263+
| EIf e1 e2 e3 | ECase e1 e2 e3 =>
264+
expr_strictly_free_of_region r e1 /\ expr_strictly_free_of_region r e2
265+
/\ expr_strictly_free_of_region r e3
266+
(** The strictness difference: no [String.eqb] shadow short-circuit.
267+
Always descend into the body. *)
268+
| ERegion _ e' => expr_strictly_free_of_region r e'
269+
| ELoc _ r' => r <> r'
270+
| EEcho _ e' => expr_strictly_free_of_region r e'
271+
| EObserve e' => expr_strictly_free_of_region r e'
272+
end.
273+
274+
(** [expr_strictly_free_of_region] strictly strengthens
275+
[expr_free_of_region]: every strictly-free expression is also
276+
weakly-free. The converse fails on (e.g.)
277+
[ERegion r (EStringNew r _)] where the weak variant accepts
278+
via shadow short-circuit but the strict variant rejects.
279+
280+
Proof. Structural induction. The only non-trivial case is
281+
[ERegion r' body]: the weak version short-circuits to [True]
282+
when [r = r'], which is implied by anything; the strict
283+
version's recursive hypothesis discharges the weak version's
284+
recursive case when [r <> r']. *)
285+
Lemma expr_strictly_free_implies_free :
286+
forall r e, expr_strictly_free_of_region r e -> expr_free_of_region r e.
287+
Proof.
288+
intros r e. induction e; simpl; intros H; try exact I; try assumption;
289+
try (apply IHe; exact H);
290+
try (destruct H as [H1 H2]; split; [apply IHe1 | apply IHe2]; assumption);
291+
try (destruct H as [H1 [H2 H3]]; split;
292+
[apply IHe1; assumption | split; [apply IHe2 | apply IHe3]; assumption]).
293+
- (* ERegion r' e *)
294+
destruct (String.eqb r r0) eqn:Heq.
295+
+ exact I.
296+
+ apply IHe; exact H.
297+
Qed.
298+
211299
(** ** Typing Contexts (De Bruijn) *)
212300

213301
(** A typing context is a list of (type, used?) pairs.

0 commit comments

Comments
 (0)