2026-04-19 04:17:45 +00:00
|
|
|
|
import BidirTT.Context
|
|
|
|
|
|
import BidirTT.Eval
|
|
|
|
|
|
import BidirTT.Pretty
|
|
|
|
|
|
|
|
|
|
|
|
namespace BidirTT
|
|
|
|
|
|
|
|
|
|
|
|
abbrev TCM := Except String
|
|
|
|
|
|
|
|
|
|
|
|
private def showTy (l : Lvl) (v : Val) : String :=
|
|
|
|
|
|
match quote l v with
|
|
|
|
|
|
| Except.ok tm => prettyTm tm
|
|
|
|
|
|
| Except.error err => s!"<ill-scoped value: {err}>"
|
|
|
|
|
|
|
|
|
|
|
|
mutual
|
|
|
|
|
|
partial def check (cxt : Cxt) : Raw → Val → TCM Tm
|
|
|
|
|
|
| .lam x t, .pi a c => do
|
2026-04-19 15:03:40 +00:00
|
|
|
|
let bodyTy ← cApp c (.neu (.var cxt.lvl))
|
2026-04-19 04:17:45 +00:00
|
|
|
|
let t' ← check (cxt.bind x a) t bodyTy
|
|
|
|
|
|
pure (.lam t')
|
|
|
|
|
|
| .pair t u, .sig a c => do
|
|
|
|
|
|
let t' ← check cxt t a
|
|
|
|
|
|
let vt ← eval cxt.env t'
|
|
|
|
|
|
let bodyTy ← cApp c vt
|
|
|
|
|
|
let u' ← check cxt u bodyTy
|
|
|
|
|
|
pure (.pair t' u')
|
2026-04-19 13:55:05 +00:00
|
|
|
|
| .refl, .id a t u => do
|
|
|
|
|
|
if (← conv cxt.lvl t u) then
|
|
|
|
|
|
pure .refl
|
|
|
|
|
|
else
|
|
|
|
|
|
throw s!"refl cannot inhabit {showTy cxt.lvl (.id a t u)} because the endpoints are not definitionally equal"
|
2026-04-19 04:17:45 +00:00
|
|
|
|
| .letE x a t u, ty => do
|
|
|
|
|
|
let (a', _) ← inferUniverse cxt a
|
|
|
|
|
|
let va := eval cxt.env a'
|
|
|
|
|
|
let va ← va
|
|
|
|
|
|
let t' ← check cxt t va
|
|
|
|
|
|
let vt ← eval cxt.env t'
|
|
|
|
|
|
let u' ← check (cxt.define x vt va) u ty
|
|
|
|
|
|
pure (.letE a' t' u')
|
|
|
|
|
|
| r, ty => do
|
|
|
|
|
|
let (t', ty') ← infer cxt r
|
2026-04-19 13:55:05 +00:00
|
|
|
|
if (← sub cxt.lvl ty' ty) then
|
2026-04-19 04:17:45 +00:00
|
|
|
|
pure t'
|
|
|
|
|
|
else
|
|
|
|
|
|
throw s!"type mismatch: expected {showTy cxt.lvl ty}, got {showTy cxt.lvl ty'}"
|
|
|
|
|
|
|
|
|
|
|
|
partial def infer (cxt : Cxt) : Raw → TCM (Tm × Val)
|
|
|
|
|
|
| .var x =>
|
|
|
|
|
|
match cxt.lookup x with
|
|
|
|
|
|
| some (i, a) => pure (.var i, a)
|
|
|
|
|
|
| none => throw s!"unknown variable {x}"
|
2026-04-19 13:55:05 +00:00
|
|
|
|
| .nat => pure (.nat, .univ 0)
|
|
|
|
|
|
| .zero => pure (.zero, .nat)
|
|
|
|
|
|
| .succ t => do
|
|
|
|
|
|
let t' ← check cxt t .nat
|
|
|
|
|
|
pure (.succ t', .nat)
|
|
|
|
|
|
| .natElim n motive z k ih s scrut => do
|
|
|
|
|
|
let scrut' ← check cxt scrut .nat
|
|
|
|
|
|
let vscrut ← eval cxt.env scrut'
|
|
|
|
|
|
let (motiveBody', level) ← inferUniverse (cxt.bind n .nat) motive
|
|
|
|
|
|
let motive' : Tm := .lam motiveBody'
|
|
|
|
|
|
let vmotive ← eval cxt.env motive'
|
|
|
|
|
|
let zTy ← vApp vmotive .zero
|
|
|
|
|
|
let z' ← check cxt z zTy
|
|
|
|
|
|
let kCxt := cxt.bind k .nat
|
2026-04-19 15:03:40 +00:00
|
|
|
|
let ihTy ← vApp vmotive (.neu (.var cxt.lvl))
|
|
|
|
|
|
let stepTy ← vApp vmotive (.succ (.neu (.var cxt.lvl)))
|
2026-04-19 13:55:05 +00:00
|
|
|
|
let stepBody' ← check (kCxt.bind ih ihTy) s stepTy
|
|
|
|
|
|
let step' : Tm := .lam (.lam stepBody')
|
|
|
|
|
|
let resultTy ← vApp vmotive vscrut
|
|
|
|
|
|
let _ := level
|
|
|
|
|
|
pure (.natElim motive' z' step' scrut', resultTy)
|
|
|
|
|
|
| .unit => pure (.unit, .univ 0)
|
|
|
|
|
|
| .triv => pure (.triv, .unit)
|
|
|
|
|
|
| .unitElim u motive t scrut => do
|
|
|
|
|
|
let scrut' ← check cxt scrut .unit
|
|
|
|
|
|
let vscrut ← eval cxt.env scrut'
|
|
|
|
|
|
let (motiveBody', level) ← inferUniverse (cxt.bind u .unit) motive
|
|
|
|
|
|
let motive' : Tm := .lam motiveBody'
|
|
|
|
|
|
let vmotive ← eval cxt.env motive'
|
|
|
|
|
|
let tTy ← vApp vmotive .triv
|
|
|
|
|
|
let t' ← check cxt t tTy
|
|
|
|
|
|
let resultTy ← vApp vmotive vscrut
|
|
|
|
|
|
let _ := level
|
|
|
|
|
|
pure (.unitElim motive' t' scrut', resultTy)
|
|
|
|
|
|
| .empty => pure (.empty, .univ 0)
|
|
|
|
|
|
| .emptyElim e motive scrut => do
|
|
|
|
|
|
let scrut' ← check cxt scrut .empty
|
|
|
|
|
|
let vscrut ← eval cxt.env scrut'
|
|
|
|
|
|
let (motiveBody', level) ← inferUniverse (cxt.bind e .empty) motive
|
|
|
|
|
|
let motive' : Tm := .lam motiveBody'
|
|
|
|
|
|
let vmotive ← eval cxt.env motive'
|
|
|
|
|
|
let resultTy ← vApp vmotive vscrut
|
|
|
|
|
|
let _ := level
|
|
|
|
|
|
pure (.emptyElim motive' scrut', resultTy)
|
|
|
|
|
|
| .id a t u => do
|
|
|
|
|
|
let (a', level) ← inferUniverse cxt a
|
|
|
|
|
|
let va ← eval cxt.env a'
|
|
|
|
|
|
let t' ← check cxt t va
|
|
|
|
|
|
let u' ← check cxt u va
|
|
|
|
|
|
pure (.id a' t' u', .univ level)
|
|
|
|
|
|
| .refl => throw "cannot infer type of refl, use an annotation"
|
|
|
|
|
|
| .idElim y p motive r target eq => do
|
|
|
|
|
|
let (eq', eqTy) ← infer cxt eq
|
|
|
|
|
|
match eqTy with
|
|
|
|
|
|
| .id a x rhs => do
|
|
|
|
|
|
let (target', targetTy) ← infer cxt target
|
|
|
|
|
|
if !(← conv cxt.lvl targetTy a) then
|
|
|
|
|
|
throw s!"idElim target has type {showTy cxt.lvl targetTy}, but the equality lives over {showTy cxt.lvl a}"
|
|
|
|
|
|
let vtarget ← eval cxt.env target'
|
|
|
|
|
|
if !(← conv cxt.lvl vtarget rhs) then
|
|
|
|
|
|
throw s!"idElim target {showTy cxt.lvl vtarget} does not match the equality endpoint {showTy cxt.lvl rhs}"
|
2026-04-19 15:03:40 +00:00
|
|
|
|
let eqVarTy : Val := .id a x (.neu (.var cxt.lvl))
|
2026-04-19 13:55:05 +00:00
|
|
|
|
let (motiveBody', level) ← inferUniverse ((cxt.bind y a).bind p eqVarTy) motive
|
|
|
|
|
|
let motive' : Tm := .lam (.lam motiveBody')
|
|
|
|
|
|
let vmotive ← eval cxt.env motive'
|
|
|
|
|
|
let reflTy ← vApp vmotive x
|
|
|
|
|
|
let reflTy ← vApp reflTy .refl
|
|
|
|
|
|
let r' ← check cxt r reflTy
|
|
|
|
|
|
let veq ← eval cxt.env eq'
|
|
|
|
|
|
let resultTy ← vApp vmotive vtarget
|
|
|
|
|
|
let resultTy ← vApp resultTy veq
|
|
|
|
|
|
let _ := level
|
|
|
|
|
|
pure (.idElim motive' r' target' eq', resultTy)
|
|
|
|
|
|
| _ => throw s!"expected Id type in idElim, got {showTy cxt.lvl eqTy}"
|
2026-04-19 15:50:59 +00:00
|
|
|
|
| .univ i => pure (.univ i.normalise, .univ i.succ')
|
2026-04-19 04:17:45 +00:00
|
|
|
|
| .app t u => do
|
|
|
|
|
|
let (t', tty) ← infer cxt t
|
|
|
|
|
|
match tty with
|
|
|
|
|
|
| .pi a c => do
|
|
|
|
|
|
let u' ← check cxt u a
|
|
|
|
|
|
let vu ← eval cxt.env u'
|
|
|
|
|
|
let bodyTy ← cApp c vu
|
|
|
|
|
|
pure (.app t' u', bodyTy)
|
|
|
|
|
|
| _ => throw s!"expected Pi type in application, got {showTy cxt.lvl tty}"
|
|
|
|
|
|
| .fst t => do
|
|
|
|
|
|
let (t', tty) ← infer cxt t
|
|
|
|
|
|
match tty with
|
|
|
|
|
|
| .sig a _ => pure (.fst t', a)
|
|
|
|
|
|
| _ => throw s!"expected Sigma type in .1, got {showTy cxt.lvl tty}"
|
|
|
|
|
|
| .snd t => do
|
|
|
|
|
|
let (t', tty) ← infer cxt t
|
|
|
|
|
|
match tty with
|
|
|
|
|
|
| .sig _ c => do
|
|
|
|
|
|
let vt ← eval cxt.env t'
|
|
|
|
|
|
let fstv ← vFst vt
|
|
|
|
|
|
let bodyTy ← cApp c fstv
|
|
|
|
|
|
pure (.snd t', bodyTy)
|
|
|
|
|
|
| _ => throw s!"expected Sigma type in .2, got {showTy cxt.lvl tty}"
|
|
|
|
|
|
| .pi x a b => do
|
|
|
|
|
|
let (a', i) ← inferUniverse cxt a
|
|
|
|
|
|
let va ← eval cxt.env a'
|
|
|
|
|
|
let (b', j) ← inferUniverse (cxt.bind x va) b
|
2026-04-19 15:50:59 +00:00
|
|
|
|
pure (.pi a' b', .univ (i.max' j))
|
2026-04-19 04:17:45 +00:00
|
|
|
|
| .sig x a b => do
|
|
|
|
|
|
let (a', i) ← inferUniverse cxt a
|
|
|
|
|
|
let va ← eval cxt.env a'
|
|
|
|
|
|
let (b', j) ← inferUniverse (cxt.bind x va) b
|
2026-04-19 15:50:59 +00:00
|
|
|
|
pure (.sig a' b', .univ (i.max' j))
|
2026-04-19 04:17:45 +00:00
|
|
|
|
| .ann t a => do
|
|
|
|
|
|
let (a', _) ← inferUniverse cxt a
|
|
|
|
|
|
let va ← eval cxt.env a'
|
|
|
|
|
|
let t' ← check cxt t va
|
|
|
|
|
|
pure (t', va)
|
|
|
|
|
|
| .letE x a t u => do
|
|
|
|
|
|
let (a', _) ← inferUniverse cxt a
|
|
|
|
|
|
let va ← eval cxt.env a'
|
|
|
|
|
|
let t' ← check cxt t va
|
|
|
|
|
|
let vt ← eval cxt.env t'
|
|
|
|
|
|
let (u', uty) ← infer (cxt.define x vt va) u
|
|
|
|
|
|
pure (.letE a' t' u', uty)
|
|
|
|
|
|
| .lam _ _ => throw "cannot infer type of lambda, use an annotation"
|
|
|
|
|
|
| .pair _ _ => throw "cannot infer type of pair, use an annotation"
|
|
|
|
|
|
|
2026-04-19 15:50:59 +00:00
|
|
|
|
partial def inferUniverse (cxt : Cxt) (r : Raw) : TCM (Tm × Level) := do
|
2026-04-19 04:17:45 +00:00
|
|
|
|
let (t, ty) ← infer cxt r
|
|
|
|
|
|
match ty with
|
|
|
|
|
|
| .univ level => pure (t, level)
|
|
|
|
|
|
| _ => throw s!"expected a universe, got {showTy cxt.lvl ty}"
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def checkTop (r : Raw) : TCM (Tm × Val) :=
|
|
|
|
|
|
infer Cxt.empty r
|
|
|
|
|
|
|
|
|
|
|
|
end BidirTT
|