1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
|
module Obs.Typing
import Control.Monad.Maybe
import Data.Nat
import Decidable.Equality
import Obs.Logging
import Obs.NormalForm
import Obs.NormalForm.Normalise
import Obs.Sort
import Obs.Substitution
import Obs.Term
import Obs.Typing.Context
import Obs.Typing.Conversion
import System
import Text.Bounded
import Text.PrettyPrint.Prettyprinter
import Text.PrettyPrint.Prettyprinter.Render.Terminal
%default total
-- Loggings ----------------------------------------------------------------------
mismatch : (expected, got : Doc ann) -> Logging ann a
mismatch lhs rhs = fatal $
hang 2 (pretty "expected" <+> line <+> lhs) <+> comma <+> line <+>
hang 2 (pretty "got" <+> line <+> align rhs)
typeMismatch : Doc ann -> Doc ann -> Logging ann a
typeMismatch lhs rhs = inScope "type mismatch" $ mismatch lhs rhs
sortMismatch : Doc ann -> Doc ann -> Logging ann a
sortMismatch lhs rhs = inScope "sort mismatch" $ mismatch lhs rhs
-- Utilities -------------------------------------------------------------------
MkPair : (s, s' : Sort)
-> NormalForm (isSet s) ctx
-> NormalForm (isSet s') ctx
-> NormalForm (isSet (max s s')) ctx
MkPair Prop Prop t u = u
MkPair Prop s'@(Set _) t u = Cnstr $ Pair Prop s' Oh t u
MkPair s@(Set _) s' t u = Cnstr $ Pair s s' Oh t u
-- Checking and Inference ------------------------------------------------------
check : (tyCtx : TyContext ctx)
-> (s : Sort)
-> NormalForm True ctx
-> WithBounds (Term (length ctx))
-> Logging ann (NormalForm (isSet s) ctx)
checkType : (tyCtx : TyContext ctx)
-> (s : Sort)
-> WithBounds (Term (length ctx))
-> Logging ann (NormalForm True ctx)
checkType ctx s tm = check ctx (suc s) (cast s) tm
infer : (tyCtx : TyContext ctx)
-> WithBounds (Term (length ctx))
-> Logging ann (s ** (NormalForm True ctx, NormalForm (isSet s) ctx))
inferType : {default typeMismatch tag : forall a . Doc ann -> Doc ann -> Logging ann a}
-> (tyCtx : TyContext ctx)
-> WithBounds (Term (length ctx))
-> Logging ann (Sort, NormalForm True ctx)
inferType ctx tm = inBounds tm.bounds $ do
(Set _ ** (Cnstr (Sort s), a)) <- infer ctx tm
| (_ ** (a, t)) => tag (pretty "sort") (pretty a)
pure (s, a)
check ctx s ty tm = inBounds tm.bounds $ case (ty, tm.val) of
(Cnstr (Pi l l' _ a b), Lambda var t) => do
info "encountered lambda"
trace $ pretty {ann} "checking body has type" <++> pretty b
t <- check (ctx ::< MkFreeVar l a var.val) l' b (assert_smaller tm t)
let Yes Refl = decEq s (l ~> l')
| No _ => fatal "internal sort mismatch"
pure $ case l' of
Prop => Irrel
Set _ => Cnstr (Lambda l var.val t)
(_, Lambda var t) => typeMismatch (pretty "function") (pretty ty)
(Cnstr (Sigma l l' var a b), Pair t u) => do
info "encountered pair"
trace $ pretty {ann} "checking first has type" <++> pretty a
t <- check ctx l a (assert_smaller tm t)
b <- subst1 t b
trace $ pretty {ann} "checking second has type" <++> pretty b
u <- check ctx l' b (assert_smaller tm u)
let Yes Refl = decEq s (max l l')
| No _ => fatal "internal sort mismatch"
pure $ MkPair l l' t u
(_, Pair t u) => typeMismatch (pretty "pair type") (pretty ty)
(_, Absurd t) => do
info "encountered absurd"
trace "checking proof of contradiction"
Irrel <- check ctx Prop (Cnstr Bottom) (assert_smaller tm t)
pure $ doAbsurd _
(a, t) => do
info "falling through to inference"
(l ** (b, t)) <- infer ctx tm
trace $ pretty {ann} "inferred type is" <++> pretty b
let Yes Refl = decEq s l
| No _ => typeMismatch (pretty a) (pretty b)
a' <- subst a (reduce ctx)
b' <- subst b (reduce ctx)
Just _ <- inScope "convert" $ runMaybeT $ convert (suc s) (cast s) a' b'
| Nothing => inScope "conversion failed" $ mismatch (pretty a) (pretty b)
pure t
infer ctx tm = inBounds tm.bounds $ case tm.val of
(Var var i) => do
info $ pretty {ann} "encountered variable" <++> pretty tm.val
let def = index' ctx i
pure (def.sort ** (def.ty, def.tm))
(Sort s) => do
info $ pretty {ann} "encountered sort" <++> pretty tm.val
pure (suc (suc s) ** (cast (suc s), cast s))
(Pi var a b) => do
info "encountered pi"
(s, a) <- inferType ctx (assert_smaller tm a)
trace $ pretty {ann} "domain type is" <++> pretty a
(s', b) <- inferType (ctx ::< MkFreeVar s a var.val) (assert_smaller tm b)
trace $ pretty {ann} "codomain type is" <++> pretty b
pure (suc (s ~> s') ** (cast (s ~> s'), Cnstr (Pi s s' var.val a b)))
(Lambda var t) => fatal "cannot infer type of lambda"
(App t u) => do
info "encountered application"
(s ** (ty@(Cnstr (Pi l l' _ a b)), t)) <- infer ctx (assert_smaller tm t)
| (_ ** (ty, _)) => inBounds t.bounds $ typeMismatch (pretty "function") (pretty ty)
trace $ pretty {ann} "function has type" <++> pretty ty
trace $ pretty {ann} "checking argument has type" <++> pretty a
u <- check ctx l a (assert_smaller tm u)
trace $ pretty {ann} "argument is well typed"
let Yes Refl = decEq s (l ~> l')
| No _ => fatal "internal sort mismatch"
ty <- subst1 u b
tm <- doApp t u
pure (l' ** (ty, rewrite sym $ impredicative l l' in tm))
(Sigma var a b) => do
info "encountered sigma"
(s, a) <- inferType ctx (assert_smaller tm a)
trace $ pretty {ann} "first type is" <++> pretty a
(s', b) <- inferType (ctx ::< MkFreeVar s a var.val) (assert_smaller tm b)
trace $ pretty {ann} "second type is" <++> pretty b
pure (suc (max s s') ** (cast (max s s'), Cnstr (Sigma s s' var.val a b)))
(Pair t u) => fatal "cannot infer type of pair"
(Fst t) => do
info "encountered first"
(s ** (ty@(Cnstr (Sigma l l' _ a b)), t)) <- infer ctx (assert_smaller tm t)
| (_ ** (ty, _)) => inBounds t.bounds $ typeMismatch (pretty "pair type") (pretty ty)
trace $ pretty {ann} "pair has type" <++> pretty ty
let Yes Refl = decEq s (max l l')
| No _ => fatal "internal sort mismatch"
let ty = a
tm <- doFst (isSet l) (isSet l') (rewrite sym $ maxIsSet l l' in t)
pure (l ** (ty, tm))
(Snd t) => do
info "encountered second"
(s ** (ty@(Cnstr (Sigma l l' _ a b)), t)) <- infer ctx (assert_smaller tm t)
| (_ ** (ty, _)) => inBounds t.bounds $ typeMismatch (pretty "pair type") (pretty ty)
trace $ pretty {ann} "pair has type" <++> pretty ty
let Yes Refl = decEq s (max l l')
| No _ => fatal "internal sort mismatch"
let t = rewrite sym $ maxIsSet l l' in t
fst <- doFst (isSet l) (isSet l') t
ty <- subst1 fst b
tm <- doSnd (isSet l) (isSet l') t
pure (l' ** (ty, tm))
Bool => do
info "encountered bool"
pure (Set 1 ** (cast (Set 0), Cnstr Bool))
True => do
info "encountered true"
pure (Set 0 ** (Cnstr Bool, Cnstr True))
False => do
info "encountered false"
pure (Set 0 ** (Cnstr Bool, Cnstr False))
(If var a t u v) => do
info "encountered if"
trace "checking discriminant is bool"
t <- check ctx (Set 0) (Cnstr Bool) (assert_smaller tm t)
trace "discriminant is well typed"
(s, a) <- inferType (ctx ::< MkFreeVar (Set 0) (Cnstr Bool) var.val) (assert_smaller tm a)
ty <- subst1 (Cnstr True) a
trace $ pretty {ann} "checking true branch has type" <++> pretty ty
u <- check ctx s ty (assert_smaller tm u)
ty <- subst1 (Cnstr False) a
trace $ pretty {ann} "checking false branch has type" <++> pretty ty
v <- check ctx s ty (assert_smaller tm v)
ty <- subst1 t a
tm <- doIf t u v
pure (s ** (ty, tm))
Top => do
info "encountered top"
pure (Set 0 ** (cast Prop, Cnstr Top))
Point => do
info "encountered point"
pure (Prop ** (Cnstr Top, Irrel))
Bottom => do
info "encountered bottom"
pure (Set 0 ** (cast Prop, Cnstr Bottom))
(Absurd t) => fatal "cannot infer type of absurd"
(Equal t u) => do
info "encountered equal"
(s ** (a, t)) <- infer ctx (assert_smaller tm t)
trace $ pretty {ann} "left side has type" <++> pretty a
u <- check ctx s a (assert_smaller tm u)
trace "right side is well typed"
eq <- doEqual (isSet s) a t u
pure (Set 0 ** (cast Prop, eq))
(Refl t) => do
info "encountered refl"
(s ** (a, t)) <- infer ctx (assert_smaller tm t)
trace "argument is well typed"
eq <- doEqual (isSet s) a t t
pure (Prop ** (eq, Irrel))
(Transp b t u t' e) => do
info "encountered transp"
(s ** (a, t)) <- infer ctx (assert_smaller tm t)
trace $ pretty {ann} "index type is" <++> pretty a
t' <- check ctx s a (assert_smaller tm t')
trace "new index is well typed"
b <- check ctx (s ~> Set 0) (Cnstr $ Pi s (Set 0) "" a (cast Prop)) (assert_smaller tm b)
trace "transporting in Prop"
oldB <- doApp b t
u <- check ctx Prop oldB (assert_smaller tm u)
trace "old-indexed proposition is well typed"
eq <- doEqual (isSet s) a t t'
trace $ pretty {ann} "checking equality has type" <++> pretty eq
e <- check ctx Prop eq (assert_smaller tm e)
trace "equality is well typed"
newB <- doApp b t'
pure (Prop ** (newB, Irrel))
(Cast a b e t) => do
info "encountered cast"
(s, a) <- inferType ctx (assert_smaller tm a)
trace $ pretty {ann} "old type is" <++> pretty a
b <- checkType ctx s (assert_smaller tm b)
trace $ pretty {ann} "new type is" <++> pretty b
eq <- doEqual True (cast s) a b
e <- check ctx Prop eq (assert_smaller tm e)
trace "equality is well typed"
t <- check ctx s a (assert_smaller tm t)
trace "value is well typed"
tm <- doCast (isSet s) a b t
pure (s ** (b, tm))
(CastId t) => do
info "encountered castRefl"
(s ** (a, t)) <- infer ctx (assert_smaller tm t)
trace $ pretty {ann} "argument has type" <++> pretty a
lhs <- doCast (isSet s) a a t
ret <- doEqual (isSet s) a lhs t
pure (Prop ** (ret, Irrel))
-- Checking Definitions and Blocks ---------------------------------------------
checkDef : Context ctx -> Term.Definition (length ctx) -> Logging ann (NormalForm.Definition ctx)
checkDef ctx def = inBounds def.name.bounds $ inScope "check" $ do
debug $ "inferring type of \{def.name.val}"
(sort, ty) <-
inferType {tag = \lhs, rhs => inScope "invalid declaration" $ mismatch lhs rhs}
(fromContext ctx) def.ty
debug $ pretty {ann} "\{def.name.val} has type" <++> pretty ty
tm <- check (fromContext ctx) sort ty def.tm
debug $ pretty {ann} "\{def.name.val} is well typed with value" <++> pretty tm
pure $ MkDefinition {name = def.name, ty, tm, sort}
export
checkBlock : Block n -> Logging ann (ctx ** (Context ctx, length ctx = n))
checkBlock [] = pure ([] ** ([], Refl))
checkBlock (blk :< def) = do
(_ ** (ctx, Refl)) <- checkBlock blk
def <- checkDef ctx def
pure (_ ** (ctx :< def, Refl))
|