|
|
|
@ -38,7 +38,7 @@ let fresh_id : unit -> int =
|
|
|
|
|
(* Ranks can be thought of as de Bruijn levels. Indeed, whenever the left-hand
|
|
|
|
|
side of a [CLet] constraint is entered, the current rank is incremented by
|
|
|
|
|
one. Thus, the rank of a variable indicates where (that is, at which [CLet]
|
|
|
|
|
construct) this variable is (existentially) bound. *)
|
|
|
|
|
construct) this variable is bound. *)
|
|
|
|
|
|
|
|
|
|
(* [base_rank] is the smallest rank. We set [base_rank = 0], so every rank is
|
|
|
|
|
nonnegative. We take advantage of this property and use ranks as indices
|
|
|
|
@ -50,24 +50,28 @@ type rank =
|
|
|
|
|
let base_rank =
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* We want to be able to mark an equivalence class as either "ordinary" or
|
|
|
|
|
"generic". The unifier works with ordinary variables only; it never sees a
|
|
|
|
|
(* Rigid variables have a fixed rank -- lowering their rank is an error --
|
|
|
|
|
and a fixed leaf structure -- unifying them with a non-leaf structure
|
|
|
|
|
is an error. *)
|
|
|
|
|
|
|
|
|
|
(* We want to be able to mark an equivalence class as either "active" or
|
|
|
|
|
"generic". The unifier works with active variables only; it never sees a
|
|
|
|
|
generic variable. Generic variables are used as part of the representation
|
|
|
|
|
of schemes: when we create a scheme, we mark the variables that can be
|
|
|
|
|
generalized as generic; when we instantiate a scheme, we replace every
|
|
|
|
|
generic variable with a fresh ordinary variables. *)
|
|
|
|
|
generic variable with a fresh active variables. *)
|
|
|
|
|
|
|
|
|
|
(* The generic variables always form a prefix of the structure: the children
|
|
|
|
|
of a generic variable can be generic or ordinary; the children of an
|
|
|
|
|
ordinary variable must be ordinary. *)
|
|
|
|
|
of a generic variable can be generic or active; the children of an
|
|
|
|
|
active variable must be active. *)
|
|
|
|
|
|
|
|
|
|
(* An ordinary variable is intended to become generic some day. A generic
|
|
|
|
|
variable never becomes ordinary again. *)
|
|
|
|
|
(* An active variable is intended to become generic some day. A generic
|
|
|
|
|
variable never becomes active again. *)
|
|
|
|
|
|
|
|
|
|
type generic =
|
|
|
|
|
bool (* [true] is generic; [false] is ordinary *)
|
|
|
|
|
type status = Rigid | Flexible | Generic
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
@ -76,7 +80,7 @@ type generic =
|
|
|
|
|
locally, in various places, to store short-lived information. In several
|
|
|
|
|
places, it allows us to save the need for a hash table. *)
|
|
|
|
|
|
|
|
|
|
(* As long as a class is ordinary, the content of the [mark] field must be a
|
|
|
|
|
(* As long as a class is active, the content of the [mark] field must be a
|
|
|
|
|
value that has been generated in the past by [fresh_mark()]. This ensures
|
|
|
|
|
that we can generate a new mark using [fresh_mark()] and that this new mark
|
|
|
|
|
is distinct from the value of every [mark] field. This is exploited in the
|
|
|
|
@ -87,7 +91,7 @@ type generic =
|
|
|
|
|
used by [instantiate] to (temporarily) store an index into an array. *)
|
|
|
|
|
|
|
|
|
|
(* This convention relies on the fact that a generic variable never becomes
|
|
|
|
|
ordinary again. *)
|
|
|
|
|
active again. *)
|
|
|
|
|
|
|
|
|
|
type mark =
|
|
|
|
|
int
|
|
|
|
@ -113,7 +117,7 @@ type 'a data = {
|
|
|
|
|
id: int;
|
|
|
|
|
mutable structure: 'a S.structure;
|
|
|
|
|
mutable rank: rank;
|
|
|
|
|
mutable generic: generic;
|
|
|
|
|
mutable status: status;
|
|
|
|
|
mutable mark: mark;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -136,12 +140,10 @@ module Data = struct
|
|
|
|
|
let dummy : mark =
|
|
|
|
|
fresh_mark()
|
|
|
|
|
|
|
|
|
|
let make structure rank =
|
|
|
|
|
let make structure rank status =
|
|
|
|
|
let id = fresh_id() in
|
|
|
|
|
(* A fresh variable is ordinary, not generic. *)
|
|
|
|
|
let generic = false in
|
|
|
|
|
let mark = dummy in
|
|
|
|
|
{ id; structure; rank; generic; mark }
|
|
|
|
|
{ id; structure; rank; status; mark }
|
|
|
|
|
|
|
|
|
|
let map f data =
|
|
|
|
|
{ data with structure = S.map f data.structure }
|
|
|
|
@ -176,12 +178,32 @@ module Data = struct
|
|
|
|
|
let structure = S.conjunction equate data1.structure data2.structure in
|
|
|
|
|
(* The new rank is the minimum of the two ranks. *)
|
|
|
|
|
let rank = min data1.rank data2.rank in
|
|
|
|
|
(* The unifier never acts on generic variables. *)
|
|
|
|
|
assert (not data1.generic && not data2.generic);
|
|
|
|
|
let generic = false in
|
|
|
|
|
let fail () = raise InconsistentConjunction in
|
|
|
|
|
(* If either variables is rigid, the conjunction is also rigid. *)
|
|
|
|
|
let status = match data1.status, data2.status with
|
|
|
|
|
| Generic, _ | _, Generic ->
|
|
|
|
|
(* The unifier never acts on generic variables. *)
|
|
|
|
|
assert false
|
|
|
|
|
| Flexible, Flexible ->
|
|
|
|
|
Flexible
|
|
|
|
|
(* One cannot unify two rigid variables, *)
|
|
|
|
|
| Rigid, Rigid ->
|
|
|
|
|
fail ()
|
|
|
|
|
(* ... or lower the rank of a rigid variable, *)
|
|
|
|
|
| Rigid, Flexible ->
|
|
|
|
|
if (rank < data1.rank) then fail ();
|
|
|
|
|
(* ... or give it a non-leaf structure. *)
|
|
|
|
|
if not (S.is_leaf structure) then fail ();
|
|
|
|
|
Rigid
|
|
|
|
|
| Flexible, Rigid ->
|
|
|
|
|
if (rank < data2.rank) then fail ();
|
|
|
|
|
(* ... or give it a non-leaf structure. *)
|
|
|
|
|
if not (S.is_leaf structure) then fail ();
|
|
|
|
|
Rigid
|
|
|
|
|
in
|
|
|
|
|
(* There is no need to preserve marks during unification. *)
|
|
|
|
|
let mark = dummy in
|
|
|
|
|
{ id; structure; rank; generic; mark }
|
|
|
|
|
{ id; structure; rank; status; mark }
|
|
|
|
|
|
|
|
|
|
let is_leaf data =
|
|
|
|
|
S.is_leaf data.structure
|
|
|
|
@ -278,9 +300,11 @@ let register { pool; _ } v r =
|
|
|
|
|
(* When a fresh unification variable is created, it receives the current rank,
|
|
|
|
|
and it is immediately registered at this rank. *)
|
|
|
|
|
|
|
|
|
|
let fresh state structure =
|
|
|
|
|
let fresh state rigidity structure =
|
|
|
|
|
let r = state.young in
|
|
|
|
|
let v = U.fresh (Data.make structure r) in
|
|
|
|
|
if rigidity = Rigid then
|
|
|
|
|
assert (S.is_leaf structure);
|
|
|
|
|
let v = U.fresh (Data.make structure r rigidity) in
|
|
|
|
|
register state v r;
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
@ -365,7 +389,7 @@ let discover_young_generation state =
|
|
|
|
|
let data = get v in
|
|
|
|
|
Hashtbl.replace table data.id ();
|
|
|
|
|
let r = data.rank in
|
|
|
|
|
assert (not data.generic);
|
|
|
|
|
assert (data.status <> Generic);
|
|
|
|
|
assert (base_rank <= r && r <= state.young);
|
|
|
|
|
by_rank.(r) <- v :: by_rank.(r)
|
|
|
|
|
);
|
|
|
|
@ -434,8 +458,8 @@ let update_ranks state generation =
|
|
|
|
|
let rec traverse v : rank =
|
|
|
|
|
let data = get v in
|
|
|
|
|
(* The downward propagation phase can never reach a generic variable.
|
|
|
|
|
Indeed, a generic variable is never a child of an ordinary variable. *)
|
|
|
|
|
assert (not data.generic);
|
|
|
|
|
Indeed, a generic variable is never a child of an active variable. *)
|
|
|
|
|
assert (data.status <> Generic);
|
|
|
|
|
(* If [v] was visited before, then its rank must be below [k], as we
|
|
|
|
|
adjust ranks on the way down already. *)
|
|
|
|
|
if data.mark = visited then
|
|
|
|
@ -513,7 +537,7 @@ let generalize state generation : variable list =
|
|
|
|
|
else begin
|
|
|
|
|
assert (r = state.young);
|
|
|
|
|
(* Make this variable generic, ... *)
|
|
|
|
|
data.generic <- true;
|
|
|
|
|
data.status <- Generic;
|
|
|
|
|
(* ... and keep it in the result list, if it has no structure. *)
|
|
|
|
|
Data.is_leaf data
|
|
|
|
|
end
|
|
|
|
@ -524,7 +548,7 @@ let generalize state generation : variable list =
|
|
|
|
|
|
|
|
|
|
(* We are in charge of constructing and instantiating schemes, that is, graph
|
|
|
|
|
fragments that contain generic (universally quantified, to-be-copied)
|
|
|
|
|
variables and can have outgoing edges towards ordinary (not-to-be-copied)
|
|
|
|
|
variables and can have outgoing edges towards active (not-to-be-copied)
|
|
|
|
|
variables. *)
|
|
|
|
|
|
|
|
|
|
(* Schemes are built when we exit the left-hand side of a [CLet] constraint,
|
|
|
|
@ -537,11 +561,11 @@ let generalize state generation : variable list =
|
|
|
|
|
|
|
|
|
|
type scheme = {
|
|
|
|
|
|
|
|
|
|
(* A root variable [root]. This variable may be generic or ordinary. *)
|
|
|
|
|
(* A root variable [root]. This variable may be generic or active. *)
|
|
|
|
|
root: variable;
|
|
|
|
|
|
|
|
|
|
(* A list of all of the variables that form the generic fragment of this
|
|
|
|
|
scheme. These variables are replaced with fresh ordinary variables when
|
|
|
|
|
scheme. These variables are replaced with fresh active variables when
|
|
|
|
|
the scheme is instantiated. The order of the variables in this list does
|
|
|
|
|
not matter. Every variable in this list is generic. The rank of these
|
|
|
|
|
variables is irrelevant, but it is the value of [state.young] at the
|
|
|
|
@ -604,7 +628,7 @@ let schemify (root : variable) : scheme =
|
|
|
|
|
|
|
|
|
|
let rec traverse v =
|
|
|
|
|
let data = get v in
|
|
|
|
|
if data.generic && data.mark <> visited then begin
|
|
|
|
|
if data.status = Generic && data.mark <> visited then begin
|
|
|
|
|
(* This variable is generic and has not yet been visited. Mark it as
|
|
|
|
|
visited. Insert it into the accumulator. Traverse its descendants.
|
|
|
|
|
The variable must be marked before the recursive call, so as to
|
|
|
|
@ -683,15 +707,15 @@ let exit ~rectypes state roots =
|
|
|
|
|
|
|
|
|
|
let instantiate state { generics; quantifiers; root } =
|
|
|
|
|
|
|
|
|
|
(* Create a copy [v'] of every generic variable [v]. At this point, the
|
|
|
|
|
(* Create a flexible copy [v'] of every generic variable [v]. At this point, the
|
|
|
|
|
variable [v'] carries no structure. Record the correspondence between
|
|
|
|
|
[v] and [v'] in the mapping. *)
|
|
|
|
|
let mapping : variable array =
|
|
|
|
|
generics |> Array.mapi (fun i v ->
|
|
|
|
|
let data = get v in
|
|
|
|
|
assert (data.generic);
|
|
|
|
|
assert (data.status = Generic);
|
|
|
|
|
data.mark <- i;
|
|
|
|
|
fresh state S.leaf
|
|
|
|
|
fresh state Flexible S.leaf
|
|
|
|
|
)
|
|
|
|
|
in
|
|
|
|
|
|
|
|
|
@ -700,7 +724,7 @@ let instantiate state { generics; quantifiers; root } =
|
|
|
|
|
[v] is generic, then it must be a member of the set [generics].) *)
|
|
|
|
|
let copy (v : variable) : variable =
|
|
|
|
|
let data = get v in
|
|
|
|
|
if data.generic then
|
|
|
|
|
if data.status = Generic then
|
|
|
|
|
let i = data.mark in
|
|
|
|
|
mapping.(i)
|
|
|
|
|
else
|
|
|
|
@ -720,10 +744,19 @@ let instantiate state { generics; quantifiers; root } =
|
|
|
|
|
|
|
|
|
|
(* Debugging utilities. *)
|
|
|
|
|
|
|
|
|
|
let pp_status ppf s =
|
|
|
|
|
match s with
|
|
|
|
|
| Generic ->
|
|
|
|
|
Printf.fprintf ppf "generic"
|
|
|
|
|
| Flexible ->
|
|
|
|
|
Printf.fprintf ppf "flexible"
|
|
|
|
|
| Rigid ->
|
|
|
|
|
Printf.fprintf ppf "rigid"
|
|
|
|
|
|
|
|
|
|
let show_variable v =
|
|
|
|
|
let data = get v in
|
|
|
|
|
Printf.printf "id = %d, rank = %d, generic = %b\n"
|
|
|
|
|
data.id data.rank data.generic
|
|
|
|
|
Printf.printf "id = %d, rank = %d, status = %a\n"
|
|
|
|
|
data.id data.rank pp_status data.status
|
|
|
|
|
|
|
|
|
|
let show_pool state k =
|
|
|
|
|
Printf.printf "Pool %d:\n" k;
|
|
|
|
|