Skip to content

Commit 7d0487f

Browse files
author
Jonathan D.A. Jewell
committed
feat(tg-2): decidability of type checking — infer ≡ HasType
Adds the algorithmic type checker `infer : Ctx → Expr → Option Ty` (total, structurally recursive, covering the full language including the echo constructors) and proves it equivalent to the HasType relation: - infer_complete : HasType Γ e τ → infer Γ e = some τ (one line: induction <;> simp_all) - infer_sound : infer Γ e = some τ → HasType Γ e τ - infer_iff_hasType : infer Γ e = some τ ↔ HasType Γ e τ - type_unique : HasType assigns at most one type per (Γ, e) - decidableHasType : Decidable (HasType [] e τ) This is the spec compiler/lib/typecheck.ml refines (TG-3). All green under lean4 v4.14.0, no sorry/axiom/admit. https://claude.ai/code/session_01PgHpCFzwYB7Qy9L6kmR8CE
1 parent 442e04c commit 7d0487f

1 file changed

Lines changed: 137 additions & 0 deletions

File tree

proofs/Tangle.lean

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,4 +726,141 @@ theorem echo_roundtrip_typed (e : Expr) (n : Nat) (h : HasType [] e (.word n)) :
726726
HasType [] (.lower (.echoClose e)) (.word 0) :=
727727
⟨.tResidue _ _ _ _ (.tEchoClose _ _ n h), .tLower _ _ _ _ (.tEchoClose _ _ n h)⟩
728728

729+
-- ═══════════════════════════════════════════════════════════════════════
730+
-- TG-2: DECIDABILITY OF TYPE CHECKING
731+
-- ═══════════════════════════════════════════════════════════════════════
732+
--
733+
-- `infer Γ e` computes the unique type of `e` in context `Γ`, or `none` if `e`
734+
-- is ill-typed. It is the algorithmic counterpart of the `HasType` relation:
735+
-- `infer_sound` + `infer_complete` prove the two equivalent, hence `HasType`
736+
-- is decidable (`decidableHasType`) and types are unique (`type_unique`).
737+
-- This is the specification `compiler/lib/typecheck.ml` is meant to refine
738+
-- (TG-3).
739+
740+
/-- Algorithmic type inference: total, structurally recursive on the AST. -/
741+
def infer (Γ : Ctx) : Expr → Option Ty
742+
| .num _ => some .num
743+
| .str _ => some .str
744+
| .boolLit _ => some .bool
745+
| .identity => some (.word 0)
746+
| .braidLit gs => some (.word (generatorWidth gs))
747+
| .compose e₁ e₂ =>
748+
match infer Γ e₁, infer Γ e₂ with
749+
| some (.word n), some (.word m) => some (.word (max n m))
750+
| _, _ => none
751+
| .tensor e₁ e₂ =>
752+
match infer Γ e₁, infer Γ e₂ with
753+
| some (.word n), some (.word m) => some (.word (n + m))
754+
| _, _ => none
755+
| .pipeline e₁ e₂ =>
756+
match infer Γ e₁, infer Γ e₂ with
757+
| some (.word n), some (.word m) => some (.word (max n m))
758+
| _, _ => none
759+
| .close e =>
760+
match infer Γ e with
761+
| some (.word _) => some (.word 0)
762+
| _ => none
763+
| .add e₁ e₂ =>
764+
match infer Γ e₁, infer Γ e₂ with
765+
| some .num, some .num => some .num
766+
| _, _ => none
767+
| .eq e₁ e₂ =>
768+
match infer Γ e₁, infer Γ e₂ with
769+
| some (.word n), some (.word m) => if n = m then some .bool else none
770+
| some .num, some .num => some .bool
771+
| some .str, some .str => some .bool
772+
| _, _ => none
773+
| .echoClose e =>
774+
match infer Γ e with
775+
| some (.word n) => some (.echo (.word n) (.word 0))
776+
| _ => none
777+
| .lower e =>
778+
match infer Γ e with
779+
| some (.echo _ τ) => some τ
780+
| _ => none
781+
| .residue e =>
782+
match infer Γ e with
783+
| some (.echo ρ _) => some ρ
784+
| _ => none
785+
786+
/-- **Completeness**: every typing derivation is computed by `infer`. -/
787+
theorem infer_complete {Γ : Ctx} {e : Expr} {τ : Ty} :
788+
HasType Γ e τ → infer Γ e = some τ := by
789+
intro h
790+
induction h <;> simp_all [infer]
791+
792+
/-- **Soundness**: every successful inference is a valid typing derivation. -/
793+
theorem infer_sound {Γ : Ctx} {e : Expr} {τ : Ty} :
794+
infer Γ e = some τ → HasType Γ e τ := by
795+
induction e generalizing Γ τ with
796+
| num _ => intro h; simp only [infer, Option.some.injEq] at h; subst h; exact .tNum _ _
797+
| str _ => intro h; simp only [infer, Option.some.injEq] at h; subst h; exact .tStr _ _
798+
| boolLit _ => intro h; simp only [infer, Option.some.injEq] at h; subst h; exact .tBool _ _
799+
| identity => intro h; simp only [infer, Option.some.injEq] at h; subst h; exact .tIdentity _
800+
| braidLit _ => intro h; simp only [infer, Option.some.injEq] at h; subst h; exact .tBraid _ _
801+
| compose e₁ e₂ ih₁ ih₂ =>
802+
intro h; simp only [infer] at h; split at h
803+
next n m he₁ he₂ =>
804+
injection h with h; subst h; exact .tComposeWord _ _ _ n m (ih₁ he₁) (ih₂ he₂)
805+
all_goals simp at h
806+
| tensor e₁ e₂ ih₁ ih₂ =>
807+
intro h; simp only [infer] at h; split at h
808+
next n m he₁ he₂ =>
809+
injection h with h; subst h; exact .tTensorWord _ _ _ n m (ih₁ he₁) (ih₂ he₂)
810+
all_goals simp at h
811+
| pipeline e₁ e₂ ih₁ ih₂ =>
812+
intro h; simp only [infer] at h; split at h
813+
next n m he₁ he₂ =>
814+
injection h with h; subst h
815+
exact .tPipeline _ _ _ _ (.tComposeWord _ _ _ n m (ih₁ he₁) (ih₂ he₂))
816+
all_goals simp at h
817+
| close e ih =>
818+
intro h; simp only [infer] at h; split at h
819+
next k he => injection h with h; subst h; exact .tCloseWord _ _ k (ih he)
820+
all_goals simp at h
821+
| add e₁ e₂ ih₁ ih₂ =>
822+
intro h; simp only [infer] at h; split at h
823+
next he₁ he₂ => injection h with h; subst h; exact .tAddNum _ _ _ (ih₁ he₁) (ih₂ he₂)
824+
all_goals simp at h
825+
| eq e₁ e₂ ih₁ ih₂ =>
826+
intro h; simp only [infer] at h; split at h
827+
next n m he₁ he₂ =>
828+
split at h
829+
next hnm =>
830+
injection h with h; subst h; subst hnm
831+
exact .tEqWord _ _ _ _ (ih₁ he₁) (ih₂ he₂)
832+
next => simp at h
833+
next he₁ he₂ => injection h with h; subst h; exact .tEqNum _ _ _ (ih₁ he₁) (ih₂ he₂)
834+
next he₁ he₂ => injection h with h; subst h; exact .tEqStr _ _ _ (ih₁ he₁) (ih₂ he₂)
835+
all_goals simp at h
836+
| echoClose e ih =>
837+
intro h; simp only [infer] at h; split at h
838+
next k he => injection h with h; subst h; exact .tEchoClose _ _ k (ih he)
839+
all_goals simp at h
840+
| lower e ih =>
841+
intro h; simp only [infer] at h; split at h
842+
next ρ τ' he => injection h with h; subst h; exact .tLower _ _ ρ τ' (ih he)
843+
all_goals simp at h
844+
| residue e ih =>
845+
intro h; simp only [infer] at h; split at h
846+
next ρ τ' he => injection h with h; subst h; exact .tResidue _ _ ρ τ' (ih he)
847+
all_goals simp at h
848+
849+
/-- **Decidability of type checking** (TG-2): `infer` decides `HasType`. -/
850+
theorem infer_iff_hasType {Γ : Ctx} {e : Expr} {τ : Ty} :
851+
infer Γ e = some τ ↔ HasType Γ e τ :=
852+
⟨infer_sound, infer_complete⟩
853+
854+
/-- Types are unique: `HasType` assigns at most one type per (context, term). -/
855+
theorem type_unique {Γ : Ctx} {e : Expr} {τ₁ τ₂ : Ty}
856+
(h₁ : HasType Γ e τ₁) (h₂ : HasType Γ e τ₂) : τ₁ = τ₂ := by
857+
have p₁ := infer_complete h₁
858+
have p₂ := infer_complete h₂
859+
rw [p₁] at p₂
860+
exact Option.some.inj p₂
861+
862+
/-- Type checking in the empty context is decidable. -/
863+
instance decidableHasType (e : Expr) (τ : Ty) : Decidable (HasType [] e τ) :=
864+
decidable_of_iff (infer [] e = some τ) infer_iff_hasType
865+
729866
end Tangle

0 commit comments

Comments
 (0)