dtlc

dependently-typed lambda calculus toy
git clone git://git.rr3.xyz/dtlc
Log | Files | Refs | README | LICENSE

elab.ml (29470B)


      1 open Util
      2 open Node
      3 module E = Error
      4 open Eval
      5 open Quote
      6 open Unify
      7 module A = Abstract
      8 module I = Internal
      9 module V = Value
     10 
     11 (*module R = Porig.NoneOneTons*)
     12 
     13 exception BlockedOnFuture of Uniq.uniq * int * Name.Var.t
     14 
     15 type mode =
     16 | Infer
     17 | Check of V.value
     18 
     19 (* A clause is a conjunction of tests, and we view an array of clauses
     20  * disjunctively. The casei:int is the index of the body that the match
     21  * expression evaluates to if the clause matches. *)
     22 type clause = {
     23 	tests: I.data_discriminant Uniq.UniqMap.t;
     24 	casei: int;
     25 }
     26 
     27 (* Convention: An elaboration-related function can have two versions, one whose
     28  * name ends with a prime, which can throw an E.Exn, and one whose name does
     29  * not end with a prime, which returns in the result monad.
     30  * TODO: Make sure every function has both versions, even if unused? *)
     31 
     32 let add_name'
     33 : Name.Var.Set.t -> Name.Var.t node -> Name.Var.Set.t * Name.Var.t
     34 = fun name_set {loc; data = name} ->
     35 	if Name.Var.Set.mem name name_set then
     36 		E.abort loc @@ E.ElabDuplicateName name
     37 	else
     38 		(Name.Var.Set.add name name_set, name)
     39 
     40 let rec assign_patt
     41 : V.context -> I.patt -> V.value -> Uniq.uniq -> V.context
     42 = fun ctx {names; disc} vtyp var ->
     43 	let (ctx, _) = Ctx.assign ctx names vtyp (V.Arb var) in
     44 
     45 	match disc with
     46 	| I.DWild _ -> ctx
     47 	| I.DData {tag; tagi; subpatts; datatype} ->
     48 		let {I.binders; I.order} = datatype.ctors.(tagi) in
     49 		let arity = Array.length subpatts in
     50 		let subvars = Uniq.fresh arity in
     51 		let data = V.Data {
     52 			tag;
     53 			tagi;
     54 			args = Array.init arity (fun i -> V.Arb (Uniq.get subvars i));
     55 			(* datatype is formed in a context without subvars bound. *)
     56 			datatype_clo = (ctx.env, datatype);
     57 		} in
     58 		let ctx = Ctx.bind ctx subvars in
     59 		let ctx = Ctx.define ctx var data in
     60 
     61 		(* Assign each subpattern in type-dependency order, because that's the
     62 		 * order that check_disc' elaborates subpatterns in. The order matters
     63 		 * here, because assign_patt binds variables. *)
     64 		let ctx' = ref ctx in
     65 		for i = 0 to arity - 1 do
     66 			let j = order.(i) in
     67 			let subtyp = I.disc_type binders.(j).patt.disc in
     68 			let vsubtyp = eval ctx.env subtyp in
     69 			ctx' := assign_patt !ctx' subpatts.(j) vsubtyp (Uniq.get subvars j)
     70 		done;
     71 
     72 		!ctx'
     73 
     74 (* Elaborate explicits to Some and implicits to None. *)
     75 let elaborate_args'
     76 : 'a. Loc.t -> 'a array -> I.binder array -> 'a option array
     77 = fun loc explicit_args binders ->
     78 	let arity = Array.length binders in
     79 	let explicit_count = Array.length explicit_args in
     80 	let implicit_count = ref 0 in
     81 
     82 	let args = Array.make arity garbage in
     83 	for i = 0 to arity - 1 do
     84 		(* TODO: Once we allow passing an implicit argument explicitly,
     85 		 * this logic will need to change a bit. *)
     86 		args.(i) <- match binders.(i).plicity with
     87 		| Plicity.Ex ->
     88 			let j = i - !implicit_count in
     89 			begin if explicit_count <= j then
     90 				E.abort loc E.ElabArityMismatch
     91 			end;
     92 			Some (explicit_args.(j))
     93 		| Plicity.Im ->
     94 			incr implicit_count;
     95 			None
     96 	done;
     97 
     98 	(* We had enough explicit args, but now ensure we don't have too many. *)
     99 	begin if explicit_count > arity - !implicit_count then
    100 		E.abort loc E.ElabArityMismatch
    101 	end;
    102 
    103 	args
    104 
    105 let rec infer
    106 : V.context -> A.term -> (I.term * V.value, E.error) result
    107 = fun ctx t ->
    108 	try Ok (infer' ctx t) with
    109 	| E.Exn e -> Error e
    110 
    111 and infer'
    112 : V.context -> A.term -> I.term * V.value
    113 = fun ctx t -> elaborate' ctx t Infer
    114 
    115 and check
    116 : V.context -> A.term -> V.value -> (I.term, E.error) result
    117 = fun ctx t vtyp ->
    118 	try Ok (check' ctx t vtyp) with
    119 	| E.Exn e -> Error e
    120 
    121 and check'
    122 : V.context -> A.term -> V.value -> I.term
    123 = fun ctx t vtyp ->
    124 	let (t, _) = elaborate' ctx t (Check (force ctx.env vtyp)) in
    125 	t
    126 
    127 and elaborate
    128 : V.context -> A.term -> mode -> (I.term * V.value, E.error) result
    129 = fun ctx t mode ->
    130 	try Ok (elaborate' ctx t mode) with
    131 	| E.Exn e -> Error e
    132 
    133 and elaborate'
    134 : V.context -> A.term -> mode -> I.term * V.value
    135 = fun ctx {loc; data = t} mode -> match t, mode with
    136 | A.Let (definers, body), mode ->
    137 	let (ctx, definers) = infer_definers' ctx definers in
    138 	let (body, vtyp) = elaborate' ctx body mode in
    139 	(I.Let (definers, body), vtyp)
    140 
    141 | A.Match _, Infer -> E.abort loc E.ElabCantInferMatch
    142 
    143 | A.Match (scrutinee, cases), Check vtyp ->
    144 	(elaborate_match' ctx loc scrutinee cases vtyp, vtyp)
    145 
    146 | A.Annot (tm, typ), Infer ->
    147 	let typ = check' ctx typ Std.v_Type in
    148 	let vtyp = eval ctx.env typ in
    149 	let tm = check' ctx tm vtyp in
    150 	(tm, vtyp)
    151 
    152 | A.App (fn, explicit_args), Infer ->
    153 	let (fn, vtyp) = infer' ctx fn in
    154 	begin match force ctx.env vtyp with
    155 	| V.FnTypeClosure (clo, {multibinder; term = codomain}) ->
    156 		let (args, clo) = check_args' ctx loc explicit_args clo multibinder in
    157 		let vcodomain = eval clo codomain in
    158 		(I.App (fn, args), vcodomain)
    159 	| vtyp -> E.abort loc @@ E.ElabApplyNonFunction (quote ctx.env vtyp)
    160 	end
    161 
    162 | A.Fn {data = {multibinder = params; term = body}; _}, Infer ->
    163 	let (ctx', multibinder) = infer_multibinder' ctx loc params in
    164 	let (body, vcodomain) = infer' ctx' body in
    165 	let codomain = quote ctx'.env vcodomain in
    166 	let fn = I.Fn {multibinder; term = body} in
    167 	let vfntype = V.FnTypeClosure (ctx.env, {multibinder; term = codomain}) in
    168 	(fn, vfntype)
    169 
    170 | A.Fn {data = {multibinder = {data = params; _}; term = body}; _},
    171 		Check (V.FnTypeClosure (clo, fntype) as vfntype) ->
    172 	let {I.multibinder = {binders = domains; order}; I.term = codomain} = fntype in
    173 
    174 	begin if Array.length params <> Array.length domains then
    175 		E.abort loc E.ElabFnArityMismatch
    176 	end;
    177 	let arity = Array.length params in
    178 
    179 	let vars = Uniq.fresh arity in
    180 	let ctx = Ctx.bind ctx vars in
    181 	let clo = Env.bind clo vars in
    182 
    183 	let ctx = ref ctx in
    184 	let name_set = ref Name.Var.Set.empty in
    185 	let params' = Array.make arity garbage in
    186 	for i = 0 to arity - 1 do
    187 		let j = order.(i) in
    188 		let param = params.(j).data in
    189 		let domain = domains.(j) in
    190 
    191 		begin if param.plicity <> domain.plicity then
    192 			E.abort loc E.ElabFnPlicityMismatch
    193 		end;
    194 		let plicity = param.plicity in
    195 
    196 		let vtyp = eval clo (I.disc_type domain.patt.disc) in
    197 		let typ = quote !ctx.env vtyp in
    198 		let (name_set', names) = check_irrefutable_patt'
    199 			!ctx !name_set param.patt vtyp in
    200 		name_set := name_set';
    201 
    202 		let (ctx', _) = Ctx.assign !ctx names vtyp (V.Arb (Uniq.get vars j)) in
    203 		ctx := ctx';
    204 		params'.(j) <- {I.plicity; I.patt = {names; disc = I.DWild typ}}
    205 	done;
    206 
    207 	let vcodomain = eval clo codomain in
    208 	let body = check' !ctx body vcodomain in
    209 
    210 	(I.Fn {multibinder = {binders = params'; order}; term = body}, vfntype)
    211 
    212 | A.Fn _, Check vtyp ->
    213 	let typ = quote ctx.env vtyp in
    214 	E.abort loc @@ E.ElabExpectedFnType typ
    215 
    216 | A.FnType {data = {multibinder = domains; term = codomain}; _}, Infer ->
    217 	let (ctx, multibinder) = infer_multibinder' ctx loc domains in
    218 	let codomain = check' ctx codomain Std.v_Type in
    219 	(I.FnType {multibinder; term = codomain}, Std.v_Type)
    220 
    221 | A.Data _, Infer -> E.abort loc E.ElabCantInferData
    222 
    223 | A.Data ({data = tag; _}, explicit_args),
    224 		Check (V.DataTypeClosure (clo, datatype) as vdatatype) ->
    225 	begin match Name.Tag.Map.find_opt tag datatype.names with
    226 	| Some tagi ->
    227 		let (args, _) = check_args' ctx loc explicit_args clo
    228 			datatype.ctors.(tagi) in
    229 		let datatype = shift_datatype ctx.env clo datatype in
    230 		(I.Data {tag; tagi; args; datatype}, vdatatype)
    231 	| None -> E.abort loc @@ E.ElabUnexpectedTag tag
    232 	end
    233 
    234 | A.Data _, Check vtyp ->
    235 	let typ = quote ctx.env vtyp in
    236 	E.abort loc @@ E.ElabExpectedDataType typ
    237 
    238 | A.TupleData args, Infer ->
    239 	let arity = Array.length args in
    240 
    241 	(* Only needed so that the indices in the type are correct. *)
    242 	let vars = Uniq.fresh arity in
    243 	let clo = Env.bind ctx.env vars in
    244 
    245 	let args' = Array.make arity garbage in
    246 	let binders = Array.make arity garbage in
    247 	for i = 0 to arity - 1 do
    248 		let (arg', vtyp) = infer' ctx args.(i) in
    249 		args'.(i) <- arg';
    250 		let typ = quote clo vtyp in
    251 		binders.(i) <- {
    252 			I.plicity = Plicity.Ex;
    253 			I.patt = {
    254 				names = [||];
    255 				disc = DWild typ;
    256 			};
    257 		}
    258 	done;
    259 
    260 	let tag = Name.Tag.Of (string_of_int arity) in
    261 	let datatype = {
    262 		I.ctors = [|{binders; order = Array.init arity Fun.id}|];
    263 		I.names = Name.Tag.Map.singleton tag 0;
    264 	} in
    265 	(I.Data {tag; tagi = 0; args = args'; datatype},
    266 		V.DataTypeClosure (clo, datatype))
    267 
    268 | A.TupleData args, Check vtyp ->
    269 	(* XXX: Kinda hacky. *)
    270 	let tag = Name.Tag.Of (string_of_int (Array.length args)) in
    271 	elaborate' ctx (loc @$ A.Data (Loc.Nowhere @$ tag, args)) (Check vtyp)
    272 
    273 | A.DataType ctors, Infer ->
    274 	let next = ref 0 in
    275 	let ctors' = Array.make (Name.Tag.Map.cardinal ctors) garbage in
    276 	let go binders =
    277 		let i = !next in
    278 		incr next;
    279 		let (_, multibinder) = infer_multibinder' ctx loc binders in
    280 		ctors'.(i) <- multibinder;
    281 		i in
    282 	let names = Name.Tag.Map.map go ctors in
    283 	(I.DataType {ctors = ctors'; names}, Std.v_Type)
    284 
    285 | A.Nat n, Infer -> (I.Nat n, Std.v_Nat)
    286 
    287 | A.Var ({data = name; _}, i), Infer ->
    288 	begin match Ctx.lookup ctx name i with
    289 	| Some {typ = V.Future (id, j); _} -> raise (BlockedOnFuture (id, j, name))
    290 	| Some {typ; value; _} -> (quote ctx.env value, typ)
    291 	| None -> E.abort loc @@ E.ElabUnboundVar (name, i)
    292 	end
    293 
    294 | A.Builtin b, Infer -> (I.Builtin b, Std.infer b)
    295 
    296 | (A.Annot _ | A.App _ | A.FnType _ | A.DataType _ | A.Nat _
    297 		| A.Var _ | A.Builtin _) as t, Check vexpected ->
    298 	let (t, vinferred) = infer' ctx {loc; data = t} in
    299 	begin try unify ctx.env vexpected vinferred with
    300 	| UnifyFail -> 
    301 		let expected = quote ctx.env vexpected in
    302 		let inferred = quote ctx.env vinferred in
    303 		E.abort loc @@ E.ElabUnifyFail (expected, inferred)
    304 	end;
    305 	(t, vexpected)
    306 
    307 (* Check the given explicit arguments against the given multibinder.
    308  * Return the elaborated terms and the multibinder's closure extended with
    309  * fresh variables defined to the arguments' values. *)
    310 and check_args'
    311 : V.context -> Loc.t -> A.term array -> V.environment -> I.multibinder
    312 	-> I.term array * V.environment
    313 = fun ctx loc explicit_args clo {binders; order} ->
    314 	let arity = Array.length binders in
    315 	let args = elaborate_args' loc explicit_args binders in
    316 
    317 	let vars = Uniq.fresh arity in
    318 	let clo = Env.bind clo vars in
    319 
    320 	let args' = Array.make arity garbage in
    321 	let clo = ref clo in
    322 	for i = 0 to arity - 1 do
    323 		let j = order.(i) in
    324 		let vtyp = eval !clo (I.disc_type binders.(j).patt.disc) in
    325 		let arg = match args.(j) with
    326 		| Some arg -> check' ctx arg vtyp
    327 		| None -> no_impl "implicit args" in
    328 		args'.(j) <- arg;
    329 		let varg = eval ctx.env arg in
    330 		clo := Env.define !clo (Uniq.get vars j) varg
    331 	done;
    332 
    333 	(args', !clo)
    334 
    335 (* Infer the given definers. Return the elaborated definers and the context
    336  * extended with fresh variables for the definers defined to each rhs. *)
    337 (* TODO: We need to check for circularity. Idea for algorithm: Have elaboration
    338  * routines track not only variable usage, but also the least "delay"
    339  * asssociated with each variable usage. E.g., a variable x uses x with delay 0
    340  * (it's used immediately), a function uses the variables used in its body with
    341  * their delay + 1, and a β-redex uses the variables in the lambda with their
    342  * delay - 1. *)
    343 and infer_definers'
    344 : V.context -> A.definer array -> V.context * I.definer array
    345 = fun ctx definers ->
    346 	let ndefiners = Array.length definers in
    347 
    348 	(* To simplify context handling, we work in a weakened contexted from the
    349 	 * very beginning, even though the lhs of each definer is not allowed to
    350 	 * refer to these variables. *)
    351 	let vars = Uniq.fresh ndefiners in
    352 	let ctx = Ctx.bind ctx vars in
    353 
    354 	match definers with
    355 	| [||] -> impossible "Elab.infer_definers': no definers"
    356 
    357 	(* Single non-recursive definer. These are handled specially, because
    358 	 * the type annotation on the lhs is optional. *)
    359 	(* TODO: Maybe we should make this distinction in internal syntax (maybe
    360 	 * abstract syntax as well). I.e., Let vs LetRec. *)
    361 	| [|{loc; data = {recursive = false; lhs; rhs}}|] ->
    362 		let (names, rhs, vtyp) =
    363 			(* First try to infer from lhs (and check rhs). *)
    364 			match infer_irrefutable_patt ctx Name.Var.Set.empty lhs with
    365 			| Ok (_, names, vtyp) ->
    366 				let rhs = check' ctx rhs vtyp in
    367 				(names, rhs, vtyp)
    368 			| Error {data = E.ElabNonInferablePatt; _} ->
    369 				(* Next try to infer from rhs (and check lhs). *)
    370 				let (rhs, vtyp) = infer' ctx rhs in
    371 				let (_, names) = check_irrefutable_patt'
    372 					ctx Name.Var.Set.empty lhs vtyp in
    373 				(names, rhs, vtyp)
    374 			| Error e -> E.abort e.loc e.data in
    375 		let typ = quote ctx.env vtyp in
    376 		(* XXX: vrhs doesn't need to be a RefClo. We could eagerly evaluate it,
    377 		 * or add some other mechanism for laziness (like glued eval). *)
    378 		let vrhs = V.RefClo (ref ctx.env, rhs) in
    379 		let (ctx, _) = Ctx.assign ctx names vtyp (V.Arb (Uniq.get vars 0)) in
    380 		let ctx = Ctx.define ctx (Uniq.get vars 0) vrhs in
    381 		(ctx, [|{names; rhs; typ}|])
    382 
    383 	(* Single recursive definer or many mutually-recursive definers. In either
    384 	 * case, a type annotation on the lhs is mandatory. Probably, we could use
    385 	 * meta-variables/unification to permit omitting annotations in some cases
    386 	 * (e.g., like OCaml does with let rec ... and ...), but we generally try
    387 	 * to avoid meta-variables/unification, because they're unpredictable;
    388 	 * inference should only rely on local information. *)
    389 	| definers ->
    390 		(* Infer each lhs (in the original context, because definitions are
    391 		 * not allowed to have mutually dependent types), and make all
    392 		 * assignments. We later set the active flag to false on assignments
    393 		 * for non-recusive definers, so that the rhs of non-recursive definers
    394 		 * can not access their own assignment. *)
    395 		(* TODO: Actually, we should allow definer types to depend on each
    396 		 * other in a well-ordered fashion, because this allows
    397 		 * induction-recursion. *)
    398 		let ctx_with_asns = ref ctx in
    399 		let name_set = ref Name.Var.Set.empty in
    400 		let namess = Array.make ndefiners garbage in
    401 		let asns = Array.make ndefiners garbage in
    402 		for i = 0 to ndefiners - 1 do
    403 			let {loc; data = {A.lhs; _}} = definers.(i) in
    404 			match infer_irrefutable_patt ctx !name_set lhs with
    405 			| Ok (name_set', names, vtyp) ->
    406 				name_set := name_set';
    407 				namess.(i) <- names;
    408 				let (ctx_with_asns', asn) = Ctx.assign !ctx_with_asns
    409 					names vtyp (V.Arb (Uniq.get vars i)) in
    410 				ctx_with_asns := ctx_with_asns';
    411 				asns.(i) <- asn
    412 			| Error ({data = E.ElabNonInferablePatt; _} as root) -> 
    413 				E.abort loc @@ E.ElabNoLetAnnot root
    414 			| Error e -> E.abort e.loc e.data
    415 		done;
    416 		let ctx = !ctx_with_asns in
    417 
    418 		(* Check each rhs with the appropriate assignments active, but not any
    419 		 * definitions. This means that each definition is "opaque" during the
    420 		 * elaboration of each rhs, i.e., the let-bound variables behave like
    421 		 * fn-bound variables. It's hard to get around this. It might be useful
    422 		 * to elaborate each rhs with every other rhs defined in context, but
    423 		 * that would require every rhs to already be elaborated! Of course,
    424 		 * after elaboration each rhs can see every other rhs, and we achieve
    425 		 * by lazily evaluating each rhs using V.Rec. *)
    426 		let env = ref ctx.env in
    427 		let definers' = Array.make ndefiners garbage in
    428 		for i = 0 to ndefiners - 1 do
    429 			let vtyp = asns.(i).typ in
    430 			let typ = quote ctx.env vtyp in
    431 
    432 			let rhs = definers.(i).data.rhs in
    433 			let rhs =
    434 				if definers.(i).data.recursive then
    435 					check' ctx rhs vtyp
    436 				else begin
    437 					(* The lhs of non-recursive definers is not in scope when
    438 					 * checking the rhs, so mark the lhs as inactive. *)
    439 					asns.(i).active <- false;
    440 					let rhs = check' ctx rhs vtyp in
    441 					asns.(i).active <- true;
    442 					rhs
    443 				end in
    444 			let vrhs = V.RefClo (env, rhs) in
    445 
    446 			env := Env.define !env (Uniq.get vars i) vrhs;
    447 			definers'.(i) <- {I.names = namess.(i); I.rhs; I.typ}
    448 		done;
    449 
    450 		(* TODO: For this situation, it might be nice to abstract the surface
    451 		 * name assignment map from contexts. Then we can say a context is an
    452 		 * environment + a surface name assignment. *)
    453 		({ctx with env = !env}, definers')
    454 
    455 and elaborate_match'
    456 : V.context -> Loc.t -> A.term -> A.case array -> V.value -> I.term
    457 = fun ctx loc scrutinee cases body_vtyp ->
    458 	let (scrutinee, scrutinee_vtyp) = infer' ctx scrutinee in
    459 	let vars = Uniq.fresh 1 in
    460 	let var = Uniq.get vars 0 in
    461 
    462 	let ncases = Array.length cases in
    463 	let cases' = Array.make ncases garbage in
    464 	for i = 0 to ncases - 1 do
    465 		let {data = {A.patt; A.body}; _} = cases.(i) in
    466 
    467 		let (_, patt) = check_patt'
    468 			ctx Name.Var.Set.empty patt scrutinee_vtyp in
    469 
    470 		let ctx = Ctx.bind ctx vars in
    471 		let ctx = assign_patt ctx patt scrutinee_vtyp var in
    472 		let body = check' ctx body body_vtyp in
    473 
    474 		cases'.(i) <- {I.patt; I.body}
    475 	done;
    476 
    477 	let to_clause
    478 	: int -> I.case -> clause
    479 	= fun casei case ->
    480 		let tests = match case.patt.disc with
    481 		| I.DWild _ -> Uniq.UniqMap.empty
    482 		| I.DData data_disc -> Uniq.UniqMap.singleton var data_disc in
    483 		{tests; casei} in
    484 	let clauses = Array.mapi to_clause cases' in
    485 
    486 	let used_cases = Array.make ncases false in
    487 	let env = Env.bind Env.empty vars in
    488 	let switch = elaborate_switch' loc used_cases env clauses in
    489 
    490 	begin match Array.find_index not used_cases with
    491 	| Some i -> E.abort cases.(i).loc E.ElabRedundantCase
    492 	| None -> ()
    493 	end;
    494 
    495 	I.Match {scrutinee; cases = cases'; switch}
    496 
    497 and elaborate_switch'
    498 : Loc.t -> bool array -> V.environment -> clause array -> I.switch
    499 = fun match_loc used_cases env clauses ->
    500 	let clause_count = Array.length clauses in
    501 
    502 	if clause_count > 0 then
    503 		match Uniq.UniqMap.min_binding_opt clauses.(0).tests with
    504 		(* Base case: An empty clause is a vacuous conjunction and hence always
    505 		 * matches. *)
    506 		| None ->
    507 			let casei = clauses.(0).casei in
    508 			used_cases.(casei) <- true;
    509 			I.Goto casei
    510 
    511 		(* Inductive case: Switch on an arbitrary variable in the first clause.
    512 		 * (Actually, here we switch on the least variable (i.e., such that
    513 		 * discriminants are analyzed breadth-first), but that's just so
    514 		 * that the output is predicatable when debugging.)
    515 		 * TODO: We may want to use some heuristic to pick the variable. *)
    516 		| Some (var, {datatype = {ctors; names}; _}) ->
    517 			(* Recursively create a switch tree for each constructor. *)
    518 			let ctor_count = Array.length ctors in
    519 			let subswitches = Array.make ctor_count garbage in
    520 			for tagi = 0 to ctor_count - 1 do
    521 				let arity = Array.length ctors.(tagi).binders in
    522 				subswitches.(tagi) <- elaborate_subswitch'
    523 					match_loc used_cases env clauses var arity tagi
    524 			done;
    525 
    526 			let index = match Env.quote env var with
    527 			| Some ij -> ij
    528 			| None -> impossible "Elab.elaborate_switch: switch on unknown var" in
    529 			I.Switch {index; subswitches; names}
    530 	else
    531 		(* TODO: Give an example of a missing case. *)
    532 		E.abort match_loc E.ElabNonExhaustiveMatch
    533 
    534 and elaborate_subswitch'
    535 : Loc.t -> bool array -> V.environment -> clause array -> Uniq.uniq -> int
    536 	-> int -> I.switch
    537 = fun match_loc used_cases env clauses var arity tagi ->
    538 	let clause_count = Array.length clauses in
    539 	let subvars = Uniq.fresh arity in
    540 
    541 	(* Count the number of clauses in the subproblem. *)
    542 	let subclause_count = ref 0 in
    543 	for i = 0 to clause_count - 1 do
    544 		match Uniq.UniqMap.find_opt var clauses.(i).tests with
    545 		| Some {tagi = tagi'; _} when tagi' = tagi ->
    546 			(* Clause i has a test of form "var = C ...", where C is 
    547 			 * constructor tagi. *)
    548 			incr subclause_count
    549 		| Some _ ->
    550 			(* Clause i has a test of form "var = D ...", where D <> C. *)
    551 			()
    552 		| None ->
    553 			(* Clause i has no opinion on var. *)
    554 			incr subclause_count
    555 	done;
    556 
    557 	(* Collect the subclauses. *)
    558 	let subclauses = Inc_array.make !subclause_count in
    559 	for i = 0 to clause_count - 1 do
    560 		let {tests; casei} = clauses.(i) in
    561 		match Uniq.UniqMap.find_opt var tests with
    562 		| Some {tagi = tagi'; subpatts; _} when tagi' = tagi ->
    563 			(* Clause i has a test of form "var = C (d_1,...,d_n)", where C is
    564 			 * constructor tagi. In the subproblem, replace this clause by
    565 			 * clauses "subvar_j = d_j" for each j such that d_j is a data
    566 			 * discriminant. *)
    567 			let tests = Uniq.UniqMap.remove var tests in
    568 			let tests = ref tests in
    569 			for j = 0 to Array.length subpatts - 1 do
    570 				match subpatts.(j).disc with
    571 				| I.DWild _ -> ()
    572 				| I.DData data_disc ->
    573 					let subvar = Uniq.get subvars j in
    574 					tests := Uniq.UniqMap.add subvar data_disc !tests
    575 			done;
    576 			Inc_array.add subclauses {tests = !tests; casei}
    577 		| Some _ ->
    578 			(* Clause i has a test of form "var = D ...", where D <> C, so
    579 			 * do ignore this clause. *)
    580 			()
    581 		| None ->
    582 			(* Clause i has no opinion on var, so add this clause unchanged
    583 			 * into the subproblem. *)
    584 			Inc_array.add subclauses {tests; casei}
    585 	done;
    586 	let subclauses = Inc_array.to_array subclauses in
    587 
    588 	let env = Env.bind env subvars in
    589 	elaborate_switch' match_loc used_cases env subclauses
    590 
    591 (* TODO: This has evolved into a complete disaster. Rewrite it. *)
    592 and infer_multibinder'
    593 : V.context -> Loc.t -> A.multibinder -> V.context * I.multibinder
    594 = fun ctx loc {data = binders; _} ->
    595 	(* We generate a globally unique id for this multibinder so that we can
    596 	 * determine which BlockedOnFuture exceptions belong to us (see below).
    597 	 * This uniq is not a variable. *)
    598 	let id = Uniq.get (Uniq.fresh 1) 0 in
    599 
    600 	let arity = Array.length binders in
    601 	let vars = Uniq.fresh arity in
    602 	let ctx = Ctx.bind ctx vars in
    603 
    604 	(* Add future assignments to ctx. *)
    605 	let ctx = ref ctx in
    606 	let name_set = ref Name.Var.Set.empty in
    607 	let asns = Array.make arity garbage in
    608 	for i = 0 to arity - 1 do
    609 		(* TODO: We should probably check that the pattern is irrefutable
    610 		 * here. *)
    611 		let names = binders.(i).data.patt.data.names in
    612 		let (name_set, names) = Array.fold_left_map add_name' !name_set names in
    613 		let typ = V.Future (id, i) in
    614 		let value = V.Arb (Uniq.get vars i) in
    615 		let (ctx', asn) = Ctx.assign !ctx names typ value in
    616 		ctx := ctx';
    617 		asns.(i) <- asn
    618 	done;
    619 	let ctx = !ctx in
    620 
    621 	(* The data structure used to track binder states. *)
    622 	let open struct
    623 		module IntSet = Set.Make(Int)
    624 
    625 		type state =
    626 		| Pending of {
    627 			dependency: (int * Name.Var.t) option;
    628 			dependents: IntSet.t;
    629 		}
    630 		| Elaborated of int
    631 
    632 		let start_state = Pending {
    633 			dependency = None;
    634 			dependents = IntSet.empty;
    635 		}
    636 
    637 		let find_cycle states start =
    638 			let rec search cycle i = match states.(i) with
    639 			| Pending {dependency = Some (j, name); _} ->
    640 				if i = j then
    641 					name :: cycle
    642 				else
    643 					search (name :: cycle) j
    644 			| Pending {dependency = None; _} ->
    645 				impossible "Elab.infer_multibinder': find_cycle called with \
    646 					pending binder with no dependency"
    647 			| Elaborated _ ->
    648 				impossible "Elab.infer_multibinder': find_cycle called with \
    649 					elaborated binder" in
    650 			search [] start
    651 	end in
    652 
    653 	let states = Array.make arity start_state in
    654 	let namess = Array.make arity garbage in
    655 
    656 	(* Next index in the topological sorting of <T. *)
    657 	let next = ref 0 in
    658 
    659 	let rec try_binder i =
    660 		let dependents = match states.(i) with
    661 		| Pending {dependency = None; dependents} -> dependents
    662 		| Pending {dependency = Some _; _} ->
    663 			impossible "Elab.infer_multibinder': try_binder called with \
    664 				pending binder with a dependency"
    665 		| Elaborated _ ->
    666 			impossible "Elab.infer_multibinder': try_binder called with \
    667 				elaborated binder" in
    668 		match infer_binder' ctx binders.(i) with
    669 		| (names, vtyp) ->
    670 			namess.(i) <- names;
    671 			asns.(i).typ <- vtyp;
    672 			states.(i) <- Elaborated !next;
    673 			incr next;
    674 			IntSet.iter remove_dependency dependents
    675 		| exception BlockedOnFuture (id', j, name) when id' = id ->
    676 			states.(i) <- Pending {dependency = Some (j, name); dependents};
    677 			states.(j) <- match states.(j) with
    678 			| Pending r ->
    679 				Pending {r with dependents = IntSet.add i r.dependents}
    680 			| Elaborated _ ->
    681 				impossible "Elab.infer_multibinder': BlockedOnFuture raised \
    682 					for elaborated binder"
    683 
    684 	and remove_dependency j =
    685 		let dependents = match states.(j) with
    686 		| Pending {dependency = Some _; dependents} -> dependents
    687 		| Pending {dependency = None; _} ->
    688 			impossible "Elab.infer_multibinder': remove_dependency called \
    689 				with pending binder with no dependency"
    690 		| Elaborated _ ->
    691 			impossible "Elab.infer_multibinder': remove_dependency called \
    692 				with elaborated binder" in
    693 		states.(j) <- Pending {dependency = None; dependents};
    694 		try_binder j in
    695 
    696 	for i = 0 to arity - 1 do
    697 		try_binder i
    698 	done;
    699 
    700 	let binders' = Array.make arity garbage in
    701 	let order = Array.make arity 0 in
    702 	for i = 0 to arity - 1 do
    703 		match states.(i) with
    704 		| Elaborated j ->
    705 			let typ = quote ctx.env asns.(i).typ in
    706 			binders'.(i) <- {
    707 				I.plicity = binders.(i).data.plicity;
    708 				I.patt = {
    709 					names = namess.(i);
    710 					disc = I.DWild typ;
    711 				};
    712 			};
    713 			order.(j) <- i
    714 		| Pending _ ->
    715 			E.abort loc @@ E.ElabBinderCycle (find_cycle states i)
    716 	done;
    717 
    718 	(ctx, {binders = binders'; order})
    719 
    720 and infer_binder'
    721 : V.context -> A.binder -> Name.Var.t array * V.value
    722 = fun ctx {loc; data = {plicity; patt}} ->
    723 	match infer_irrefutable_patt ctx Name.Var.Set.empty patt, plicity with
    724 	| Ok (_, names, vtyp), _ -> (names, vtyp)
    725 	| Error {data = E.ElabNonInferablePatt; _}, Plicity.Im ->
    726 		begin match check_irrefutable_patt ctx Name.Var.Set.empty patt
    727 			Std.v_Type with
    728 		| Ok (_, names) -> (names, Std.v_Type)
    729 		| Error root -> E.abort loc @@ E.ElabUnannotImplicitIsType root
    730 		end
    731 	| Error ({data = E.ElabNonInferablePatt; _} as root), Plicity.Ex ->
    732 		E.abort loc @@ E.ElabNoBinderAnnot root
    733 	| Error e, _ -> E.abort e.loc e.data
    734 
    735 (* Check that each term in the array starting at the given index is a type
    736  * convertible to the given value. *)
    737 and check_types'
    738 : V.context -> A.term array -> int -> V.value -> unit
    739 = fun ctx typs start vtyp ->
    740 	for i = start to Array.length typs - 1 do
    741 		let typ' = check' ctx typs.(i) Std.v_Type in
    742 		let vtyp' = eval ctx.env typ' in
    743 		try unify ctx.env vtyp vtyp' with
    744 		| UnifyFail -> 
    745 			let typ = quote ctx.env vtyp in
    746 			E.abort typs.(i).loc @@ E.ElabUnifyFail (typ, typ')
    747 	done
    748 
    749 and infer_irrefutable_patt
    750 : V.context -> Name.Var.Set.t -> A.patt
    751 	-> (Name.Var.Set.t * Name.Var.t array * V.value, E.error) result
    752 = fun ctx name_set patt ->
    753 	try Ok (infer_irrefutable_patt' ctx name_set patt) with
    754 	| E.Exn e -> Error e
    755 
    756 and infer_irrefutable_patt'
    757 : V.context -> Name.Var.Set.t -> A.patt
    758 	-> Name.Var.Set.t * Name.Var.t array * V.value
    759 = fun ctx name_set ({loc; _} as patt) ->
    760 	let (name_set, patt, vtyp) = infer_patt' ctx name_set patt in
    761 	match patt with
    762 	| {disc = I.DData _; _} -> E.abort loc E.ElabDataPattRefutable
    763 	| {names; disc = I.DWild _} -> (name_set, names, vtyp)
    764 
    765 and check_irrefutable_patt
    766 : V.context -> Name.Var.Set.t -> A.patt -> V.value
    767 	-> (Name.Var.Set.t * Name.Var.t array, E.error) result
    768 = fun ctx name_set patt vtyp ->
    769 	try Ok (check_irrefutable_patt' ctx name_set patt vtyp) with
    770 	| E.Exn e -> Error e
    771 
    772 and check_irrefutable_patt'
    773 : V.context -> Name.Var.Set.t -> A.patt -> V.value
    774 	-> Name.Var.Set.t * Name.Var.t array
    775 = fun ctx name_set ({loc; _} as patt) vtyp ->
    776 	let (name_set, patt) = check_patt' ctx name_set patt vtyp in
    777 	match patt with
    778 	| {disc = I.DData _; _} -> E.abort loc E.ElabDataPattRefutable
    779 	| {names; disc = I.DWild _} -> (name_set, names)
    780 
    781 and infer_patt'
    782 : V.context -> Name.Var.Set.t -> A.patt -> Name.Var.Set.t * I.patt * V.value
    783 = fun ctx name_set {loc; data = patt} -> match patt with
    784 | {typs = [||]; _} -> E.abort loc E.ElabNonInferablePatt
    785 | {names; typs; disc} ->
    786 	let (name_set, names) = Array.fold_left_map add_name' name_set names in
    787 	let typ = check' ctx typs.(0) Std.v_Type in
    788 	let vtyp = eval ctx.env typ in
    789 	check_types' ctx typs 1 vtyp;
    790 	let (names_set, disc) = check_disc' ctx name_set disc vtyp in
    791 	(name_set, {names; disc}, vtyp)
    792 
    793 and check_patt'
    794 : V.context -> Name.Var.Set.t -> A.patt -> V.value -> Name.Var.Set.t * I.patt
    795 = fun ctx name_set {loc; data = {names; typs; disc}} vtyp ->
    796 	let (name_set, names) = Array.fold_left_map add_name' name_set names in
    797 	check_types' ctx typs 0 vtyp;
    798 	let (name_set, disc) = check_disc' ctx name_set disc vtyp in
    799 	(name_set, {names; disc})
    800 
    801 and check_disc'
    802 : V.context -> Name.Var.Set.t -> A.discriminant -> V.value
    803 	-> Name.Var.Set.t * I.discriminant
    804 = fun ctx name_set {loc; data = disc} vtyp -> match disc, force ctx.env vtyp with
    805 | A.DWild, vtyp -> (name_set, I.DWild (quote ctx.env vtyp))
    806 | A.DData {tag = {data = tag; _}; subpatts = explicit_subpatts},
    807 		V.DataTypeClosure (clo, datatype) ->
    808 	begin match Name.Tag.Map.find_opt tag datatype.names with
    809 	| Some tagi -> (* XXX: This is very similar to check_args'. *)
    810 		let datatype' = shift_datatype ctx.env clo datatype in
    811 		let {I.binders; I.order} = datatype.ctors.(tagi) in
    812 		let arity = Array.length binders in
    813 		let vars = Uniq.fresh arity in
    814 		let ctx = Ctx.bind ctx vars in
    815 		let clo = Env.bind clo vars in
    816 
    817 		let name_set = ref name_set in
    818 		let subpatts = elaborate_args' loc explicit_subpatts binders in
    819 		let subpatts' = Array.make arity garbage in
    820 		let ctx = ref ctx in
    821 		for i = 0 to arity - 1 do
    822 			let j = order.(i) in
    823 			let typ = I.disc_type binders.(j).patt.disc in
    824 			let vtyp = eval clo typ in
    825 			let (name_set', subpatt') = match subpatts.(j) with
    826 			| Some patt -> check_patt' !ctx !name_set patt vtyp
    827 			| None -> no_impl "implicit args" in
    828 			name_set := name_set';
    829 			subpatts'.(j) <- subpatt';
    830 			(* XXX: I think this is a quadratic time algorithm. We should
    831 			 * thread a context through the check_disc calls instead of
    832 			 * repeatedly reassigning/rebinding/redefining stuff. *)
    833 			ctx := assign_patt !ctx subpatt' vtyp (Uniq.get vars j)
    834 		done;
    835 
    836 		let disc = I.DData {
    837 			tag;
    838 			tagi;
    839 			subpatts = subpatts';
    840 			datatype = datatype';
    841 		} in
    842 		(!name_set, disc)
    843 
    844 	| None -> E.abort loc @@ E.ElabUnexpectedTag tag
    845 	end
    846 | A.DData _, vtyp -> E.abort loc @@ E.ElabExpectedDataType (quote ctx.env vtyp)