package inferno

  1. Overview
  2. Docs

Source file SolverHi.ml

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
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
(******************************************************************************)
(*                                                                            *)
(*                                  Inferno                                   *)
(*                                                                            *)
(*                       François Pottier, Inria Paris                        *)
(*                                                                            *)
(*  Copyright Inria. All rights reserved. This file is distributed under the  *)
(*  terms of the MIT License, as described in the file LICENSE.               *)
(*                                                                            *)
(******************************************************************************)

open UnifierSig
open SolverSig

module Make
  (X : TEVAR)
  (S : STRUCTURE)
  (O : OUTPUT with type 'a structure = 'a S.structure)
= struct

(* -------------------------------------------------------------------------- *)

(* We rely on the low-level solver interface. *)

module Lo =
  SolverLo.Make(X)(S)(O)

open Lo

type variable =
  Lo.variable

(* -------------------------------------------------------------------------- *)

(* We now set up the applicative functor API, or combinator API, to the
   solver. The constraint construction phase and the witness decoding phase
   are packaged together, with two benefits: 1- the syntax of constraints and
   witnesses, as well as the details of write-once references, are hidden; 2-
   the client can write code in a compositional and declarative style, under
   the illusion that constructing a query immediately gives rise to an
   answer. *)

(* The client is allowed to construct objects of type ['a co]. Such an object is
   a pair of a constraint and a continuation. It is evaluated in two phases. In
   the first phase, the constraint is solved. In the second phase, the continuation
   is invoked. It is allowed to examine the witness, and must produce a value of
   type ['a]. *)

(* The continuation has access to an environment of type [env]. For the moment,
   the environment is just a type decoder. *)

type env =
  decoder

type 'a co =
  rawco * (env -> 'a)

(* -------------------------------------------------------------------------- *)

(* The type ['a co] forms an applicative functor. *)

let pure a =
  CTrue,
  fun _env -> a

let (^&) (rc1, k1) (rc2, k2) =
  CConj (rc1, rc2),
  fun env -> (k1 env, k2 env)

let map f (rc, k) =
  rc,
  fun env -> f (k env)

(* The function [<$$>] is just [map] with reversed argument order. *)

let (<$$>) a f =
  map f a

(* The function [^^], a variation of [^&], also builds a conjunction constraint,
   but drops the first component of the resulting pair, and keeps only the second
   component. [f ^^ g] is equivalent to [f ^& g <$$> snd]. *)

let (^^) (rc1, k1) (rc2, k2) =
  CConj (rc1, rc2),
  fun env ->
    let _ = k1 env in
    k2 env

(* The type ['a co] does not form a monad. Indeed, there is no way of defining
   a [bind] combinator. *)

(* A note on syntax. We need [--] to bind tighter than [^&], which in turn
   must bind tighter than [<$$>]. This explains in part our choice of operator
   names. *)

(* -------------------------------------------------------------------------- *)

(* Existential quantification. *)

let exist_aux t f =
  (* Create a fresh unifier variable [v]. *)
  let v = fresh t in
  (* Pass [v] to the client. *)
  let rc, k = f v in
  (* Wrap the constraint [c] in an existential quantifier, *)
  CExist (v, rc),
  (* and construct a continuation which returns a pair of the witness for [v]
     and the value of the underlying continuation [k]. *)
  fun env ->
    let decode = env in
    (decode v, k env)

let exist f =
  exist_aux None f

let construct t f =
  exist_aux (Some t) f

let exist_aux_ t f =
  let v = fresh t in
  let rc, k = f v in
  CExist (v, rc),
  (* Keep the original continuation. The client doesn't need the witness. *)
  k

let exist_ f =
  exist_aux_ None f
  (* This is logically equivalent to [exist f <$$> snd], but saves a
     call to [decode] as well as some memory allocation. *)

let construct_ t f =
  exist_aux_ (Some t) f

let lift f v1 t2 =
  construct_ t2 (fun v2 ->
    f v1 v2
  )

(* -------------------------------------------------------------------------- *)

(* Deep types. *)

type deep_ty =
  | DeepVar of variable
  | DeepStructure of deep_ty S.structure

(* Conversion of deep types to shallow types. *)

(* Our API is so constrained that this seems extremely difficult to implement
   from the outside. So, we provide it, for the user's convenience. In fact,
   even here, inside the abstraction, implementing this conversion is slightly
   tricky. *)

let build dty f =
  (* Accumulate a list of the fresh variables that we create. *)
  let vs = ref [] in
  (* [convert] converts a deep type to a variable. *)
  let rec convert dty =
    match dty with
    | DeepVar v ->
        v
    | DeepStructure s ->
        (* First recursively convert our children, then allocate a fresh
           variable [v] to stand for the root. Record its existence in the
           list [vs]. *)
        let v = fresh (Some (S.map convert s)) in
        vs := v :: !vs;
        v
  in
  (* Convert the deep type [dty] and pass the variable that stands for its
     root the user function [f]. *)
  let rc, k = f (convert dty) in
  (* Then, create a bunch of existential quantifiers, in an arbitrary order. *)
  List.fold_left (fun rc v -> CExist (v, rc)) rc !vs,
  (* Keep an unchanged continuation. *)
  k

(* -------------------------------------------------------------------------- *)

(* Equations. *)

let (--) v1 v2 =
  CEq (v1, v2),
  fun _env -> ()

let (---) v t =
  lift (--) v t

(* If [construct_] was not exposed, [lift] could also be defined (outside this
   module) in terms of [exist_] and [---], as follows. This definition seems
   slower, though; its impact on the test suite is quite large. *)

let _other_lift f v1 t2 =
  exist_ (fun v2 ->
    v2 --- t2 ^^
    f v1 v2
  )

(* -------------------------------------------------------------------------- *)

(* Instantiation constraints. *)

let instance x v =
  (* In the constraint construction phase, create a write-once reference,
     and stick it into the constraint, for the solver to fill. *)
  let witnesses = WriteOnceRef.create() in
  CInstance (x, v, witnesses),
  fun env ->
    let decode = env in
    (* In the decoding phase, read this write-once reference, so as to
       obtain the list of witnesses. Decode them, and return them to
       the user. *)
    List.map decode (WriteOnceRef.get witnesses)

(* [instance_ x v] is equivalent to [instance x v <$$> ignore]. *)

let instance_ x v =
  let witnesses = WriteOnceRef.create() in
  CInstance (x, v, witnesses),
  fun _env ->
    (* In the decoding phase, there is nothing to do. *)
    ()

(* -------------------------------------------------------------------------- *)

(* Constraint abstractions. *)

(* The [CDef] form is so trivial that it deserves its own syntax. Viewing it
   as a special case of [CLet] would be more costly (by a constant factor). *)

let def x v (rc, k) =
  CDef (x, v, rc),
  k

(* The general form of [CLet] involves two constraints, the left-hand side and
   the right-hand side, yet it defines a *family* of constraint abstractions,
   bound the term variables [xs]. *)

let letn xs f1 (rc2, k2) =
  (* For each term variable [x], create a fresh type variable [v], as in
     [CExist]. Also, create an uninitialized scheme hook, which will receive
     the type scheme of [x] after the solver runs. *)
  let xvss = List.map (fun x ->
    x, fresh None, WriteOnceRef.create()
  ) xs in
  (* Pass the vector of type variables to the user-supplied function [f1],
     as in [CExist]. *)
  let vs = List.map (fun (_, v, _) -> v) xvss in
  let rc1, k1 = f1 vs in
  (* Create one more write-once reference, which will receive the list of
     all generalizable variables in the left-hand side. *)
  let generalizable_hook = WriteOnceRef.create() in
  (* Build a [CLet] constraint. *)
  CLet (xvss, rc1, rc2, generalizable_hook),
  fun env ->
    (* In the decoding phase, read the write-once references, *)
    let decode = env in
    let generalizable =
      List.map decode_variable (WriteOnceRef.get generalizable_hook)
    and ss =
      List.map (fun (_, _, scheme_hook) ->
        decode_scheme decode (WriteOnceRef.get scheme_hook)
      ) xvss
    in
    (* and return their values to the user, in addition to the values
       produced by the continuations [k1] and [k2]. *)
    ss, generalizable, k1 env, k2 env

(* The auxiliary function [single] asserts that its argument [xs] is a
   singleton list, and extracts its unique element. *)

let single xs =
  match xs with
  | [ x ] ->
      x
  | _ ->
      assert false

(* [let1] is a special case of [letn], where only one term variable is bound. *)

let let1 x f1 c2 =
  letn [ x ] (fun vs -> f1 (single vs)) c2 <$$>
  fun (ss, generalizable, v1, v2) -> (single ss, generalizable, v1, v2)

(* [let0] is a special case of [letn], where no term variable is bound, and
   the right-hand side is [CTrue]. We require using this form at the toplevel
   of every constraint. *)

let let0 c1 =
  letn [] (fun _ -> c1) (pure ()) <$$>
  fun (_, generalizable, v1, ()) -> (generalizable, v1)

(* -------------------------------------------------------------------------- *)

(* Correlation with the source code. *)

type range =
  Lexing.position * Lexing.position

let correlate range (rc, k) =
  CRange (range, rc), k

(* -------------------------------------------------------------------------- *)

(* Running a constraint. *)

(* The constraint [c] should have been constructed by [let0], otherwise we
   risk encountering variables that we cannot register. Recall that
   [G.register] must not be called unless [G.enter] has been invoked first. Of
   course, we could accept any old constraint from the user and silently wrap
   it in [let0], but then, what would we do with the toplevel quantifiers? *)

include struct
  [@@@warning "-4"] (* yes, I know the following pattern matching is fragile *)

let ok rc =
  match rc with
  | CLet (_, _, CTrue, _) ->
      (* The argument of [solve] should be constructed by [let0]. *)
      true
  | _ ->
      false

end

(* Solving, or running, a constraint. *)

exception Unbound = Lo.Unbound
exception Unify of range * O.ty * O.ty
exception Cycle of range * O.ty

let solve rectypes (rc, k) =
  assert (ok rc);
  begin try
    (* Solve the constraint. *)
    Lo.solve rectypes rc
  with
    (* Catch the unifier's exceptions and decode their arguments on the fly.
       This may be a waste of time, as the client may not need us to do this
       decoding, but this allows us to offer a nice & simple interface. Note
       that the cyclic decoder is required here, even if [rectypes] is [false],
       as recursive types can appear before the occurs check is successfully
       run. *)
  | Lo.Unify (range, v1, v2) ->
      let decode = new_decoder true (* cyclic decoder *) in
      raise (Unify (range, decode v1, decode v2))
  | Lo.Cycle (range, v) ->
      let decode = new_decoder true (* cyclic decoder *) in
      raise (Cycle (range, decode v))
  end;
  (* Create a suitable decoder. *)
  let decode = new_decoder rectypes in
  (* Invoke the client continuation. *)
  let env = decode in
  k env

end