Skip to content

Commit 49fb46a

Browse files
feat(L2): T_App_L2_Eff — Phase D slice 3 option 5a code lands (#209)
## Summary Phase D slice 3 implementation under option 5a (owner-picked in PR #208). Adds `T_App_L2_Eff` as a sibling constructor to `L2_lift_l1` in `has_type_l2`, moving the elimination form of effect-typed `TFunEff` lambdas out of L1 and into L2 per `PRESERVATION-DESIGN.md` §5.1 lines 468-474. ## What changed - **New constructor `T_App_L2_Eff`** in `formal/TypingL2.v` after `L2_lift_l1`. Threading: `e1 : (R → R1)` produces an effect-typed lambda; `e2 : (R1 → R_in)` evaluates the argument matching the lambda's declared input env; `EApp e1 e2 : (R → R_out)` consumes the body's declared output region. No side condition at elimination (the `(forall r, In r R -> In r R_in)` side condition already lives on `T_Lam_L1_*_Eff` slice 2). - **`project_l2_to_l1` refactored to option-return.** `T_App_L2_Eff` cases have no L1 counterpart by design — that is the architectural point of option 5a. The comment block documents the migration path for callers. - **`weaken_modality` rewritten by structural induction.** Old: project to L1, weaken at L1, lift back. New: case on the L2 derivation; `L2_lift_l1` lifts via `linear_to_affine`; `T_App_L2_Eff` weakens the two sub-derivations recursively and reconstructs at Affine. ## Verification - `coqc 8.18.0` full `formal/` build clean (10 .v files) - Zero new `Admitted` / `Axiom` / `admit.` in `TypingL2.v` - `Print Assumptions weaken_modality` → `Closed under the global context` - `Print Assumptions weaken_modality_le` → `Closed under the global context` - `Print Assumptions weaken_modality_le_strict_is_weaken_modality` → `Closed under the global context` ## Owner-directive compliance (CLAUDE.md §🛑 Owner directive 2026-05-27) - ✅ `Semantics.v` untouched (legacy `preservation` left as `Admitted`) - ✅ `Typing.v` untouched (legacy judgment intact for `Counterexample.v`) - ✅ `Counterexample.v` untouched - ✅ No new admits or axioms anywhere - ✅ Cross-layer fix per `PRESERVATION-DESIGN.md` §5.1 — not a single-layer patch ## Companion docs - `formal/PHASE-D-REDESIGN.md` slice 3 sub-sub-addendum (landed in #208) - `.machine_readable/6a2/STATE.a2ml` phase flipped `owner-escalation → implementation` (#208) ## What this does NOT close `preservation_l1` as currently stated cannot Qed under option 5a alone — slice 4a (TFunEff path) closes by inversion vacuity at L1, but slice 4b (legacy `TFun` body-R-rigidity) carries forward into `preservation_l2` via L1 importation as honest inherited debt. ## Next slices (out of scope here) 1. State `preservation_l2` over `has_type_l2`. Effect-typed paths get the slice 4a case for free; legacy `TFun` paths carry forward the L1 admit via `L2_lift_l1`. 2. Phase B Slice 1 (TEcho linearity wire) + Phase C (list-vs-multiset bridge) unlock once `preservation_l2` lands. ## Test plan - [x] `coqc 8.18.0` build clean - [x] `Print Assumptions` confirms zero axioms on new code - [x] Diff scoped to `formal/TypingL2.v` only (no Makefile noise) - [ ] CI green on merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 79cb1b0 commit 49fb46a

1 file changed

Lines changed: 64 additions & 15 deletions

File tree

formal/TypingL2.v

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
Per design-doc §5 and the same "first slice" framing used for
2020
L3 (#166), this PR ships:
2121
22-
- The [has_type_l2] inductive shape — currently with a single
23-
[L2_lift_l1] constructor that lifts any L1 derivation into the
24-
L2 judgment at either mode. This is sound: per §5's
25-
preservation table, "[Affine derivations are L1-safe by
26-
weakening]" and Linear derivations are precisely the strict
27-
L1 derivations.
22+
- The [has_type_l2] inductive shape — with [L2_lift_l1] (lifts
23+
any L1 derivation into the L2 judgment at either mode) and
24+
[T_App_L2_Eff] (effect-typed application; the elimination form
25+
of [TFunEff] lambdas, moved out of L1 per option 5a / Phase D
26+
slice 3 / [PHASE-D-REDESIGN.md] slice 3 sub-sub-addendum). The
27+
L2_lift_l1 case is sound: per §5's preservation table,
28+
"[Affine derivations are L1-safe by weakening]" and Linear
29+
derivations are precisely the strict L1 derivations.
2830
2931
- [weaken_modality] — the headline §5 lemma, Qed. In this
3032
skeleton the proof is a single induction; future PRs will add
@@ -88,7 +90,29 @@ Inductive has_type_l2
8890
| L2_lift_l1 :
8991
forall m R G e T R' G',
9092
TypingL1.has_type_l1 m R G e T R' G' ->
91-
has_type_l2 m R G e T R' G'.
93+
has_type_l2 m R G e T R' G'
94+
(** [T_App_L2_Eff] — effect-typed application (Phase D slice 3,
95+
option 5a). The elimination form for [TFunEff] lambdas. Lives
96+
at L2 because effect-typed application is an effect-semantic
97+
operation (consuming [R_in / R_out] annotations meaningfully)
98+
and the canonical home for it is the typing-layer judgment per
99+
[PRESERVATION-DESIGN.md] §5.1.
100+
101+
Threading: [e1] produces an effect-typed lambda value at
102+
([R → R1]). [e2] evaluates the argument, threading
103+
[R1 → R_in] (the lambda's declared input env). The body runs
104+
[R_in → R_out] per the type, so the whole [EApp] consumes
105+
[R → R_out].
106+
107+
Side condition: none at elimination. The intro side condition
108+
[(forall r, In r R -> In r R_in)] (T_Lam_L1_*_Eff slice 2)
109+
already ensures formation-time region availability; at the
110+
call site [e2] supplies [R_in] explicitly via its output env. *)
111+
| T_App_L2_Eff :
112+
forall m R R1 G G' G'' e1 e2 T1 T2 R_in R_out,
113+
has_type_l2 m R G e1 (TFunEff T1 T2 R_in R_out) R1 G' ->
114+
has_type_l2 m R1 G' e2 T1 R_in G'' ->
115+
has_type_l2 m R G (EApp e1 e2) T2 R_out G''.
92116

93117
(** Notation following the Agda upstream [_;_⊢_ℓ_:_-|_;_]. *)
94118

@@ -114,16 +138,27 @@ Definition lift_l1_to_affine
114138
has_type_l2 Affine R G e T R' G' :=
115139
L2_lift_l1 Affine R G e T R' G' H.
116140

117-
(** Projection: extract the underlying L1 derivation from an L2 one.
118-
Witnesses that the L2 skeleton is a strict extension — no
119-
information loss vs L1. *)
141+
(** Partial projection: extract the underlying L1 derivation from
142+
an L2 one when (and only when) the L2 derivation is a lift.
143+
144+
Post-slice-3 (option 5a), [has_type_l2] also carries
145+
[T_App_L2_Eff], which has no L1 counterpart — effect-typed
146+
application is L2-only by design. The projection is therefore
147+
[option]-valued: [Some H1] for [L2_lift_l1] cases, [None] for
148+
the [T_App_L2_Eff] case.
149+
150+
Callers that previously assumed totality (e.g. the old
151+
[weaken_modality] proof) must instead do structural induction
152+
on the L2 derivation; see [weaken_modality] below for the
153+
updated pattern. *)
120154

121155
Definition project_l2_to_l1
122156
{m R G e T R' G'}
123157
(H : has_type_l2 m R G e T R' G') :
124-
TypingL1.has_type_l1 m R G e T R' G' :=
158+
option (TypingL1.has_type_l1 m R G e T R' G') :=
125159
match H with
126-
| L2_lift_l1 _ _ _ _ _ _ _ H1 => H1
160+
| L2_lift_l1 _ _ _ _ _ _ _ H1 => Some H1
161+
| T_App_L2_Eff _ _ _ _ _ _ _ _ _ _ _ _ _ _ => None
127162
end.
128163

129164
(** ===== Headline §5 lemma: Linear ⇒ Affine weakening =====
@@ -136,15 +171,29 @@ Definition project_l2_to_l1
136171
one constructor case; future mode-specific constructors will
137172
introduce mode-switch cases. *)
138173

174+
(** Post-slice-3 (option 5a), the proof is by structural induction
175+
on the L2 derivation. The [L2_lift_l1] case lifts via
176+
[linear_to_affine]; the [T_App_L2_Eff] case weakens the two
177+
sub-derivations recursively and reconstructs by [T_App_L2_Eff]
178+
at [Affine]. *)
179+
139180
Theorem weaken_modality :
140181
forall {R G e T R' G'},
141182
has_type_l2 Linear R G e T R' G' ->
142183
has_type_l2 Affine R G e T R' G'.
143184
Proof.
144185
intros R G e T R' G' H.
145-
apply lift_l1_to_affine.
146-
apply TypingL1.linear_to_affine.
147-
exact (project_l2_to_l1 H).
186+
remember Linear as m eqn:Hm.
187+
induction H as
188+
[ m' R0 G0 e0 T0 R0' G0' H1
189+
| m' R0 R1 G0 G0' G0'' e1 e2 T1 T2 R_in R_out He1 IHe1 He2 IHe2 ];
190+
subst m'.
191+
- apply lift_l1_to_affine.
192+
apply TypingL1.linear_to_affine.
193+
exact H1.
194+
- eapply T_App_L2_Eff.
195+
+ apply IHe1; reflexivity.
196+
+ apply IHe2; reflexivity.
148197
Qed.
149198

150199
(** Idempotence at [Affine]: an Affine derivation re-weakened to

0 commit comments

Comments
 (0)