|
|
|
@ -11,7 +11,7 @@
|
|
|
|
|
|
|
|
|
|
open Signatures
|
|
|
|
|
|
|
|
|
|
module Make (S : STRUCTURE_LEAF) = struct
|
|
|
|
|
module Make (UserS : HSTRUCTURE) = struct
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
@ -50,13 +50,8 @@ type rank =
|
|
|
|
|
let base_rank =
|
|
|
|
|
0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* 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
|
|
|
|
@ -71,15 +66,7 @@ let base_rank =
|
|
|
|
|
(* An active variable is intended to become generic some day. A generic
|
|
|
|
|
variable never becomes active again. *)
|
|
|
|
|
|
|
|
|
|
type status = Rigid | Flexible | Generic
|
|
|
|
|
|
|
|
|
|
type rigidity = Rigid | Flexible
|
|
|
|
|
|
|
|
|
|
let status_of_rigidity : rigidity -> status = function
|
|
|
|
|
| Flexible ->
|
|
|
|
|
Flexible
|
|
|
|
|
| Rigid ->
|
|
|
|
|
Rigid
|
|
|
|
|
type status = Active | Generic
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
@ -109,6 +96,16 @@ let fresh_mark : unit -> mark =
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* We define a [structure] type which is either abstract or a user-defined
|
|
|
|
|
structure from the module S.
|
|
|
|
|
Rigid variables have abstract strucutres.
|
|
|
|
|
*)
|
|
|
|
|
|
|
|
|
|
module AS = Structure.AbstractS(UserS)
|
|
|
|
|
module S = Structure.OptionS(AS)
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
|
|
|
|
|
|
(* The type ['a data] groups all of the information that we associate with an
|
|
|
|
|
equivalence class. *)
|
|
|
|
|
|
|
|
|
@ -125,21 +122,47 @@ type 'a data = {
|
|
|
|
|
id: id;
|
|
|
|
|
mutable structure: 'a S.structure;
|
|
|
|
|
mutable rank: rank;
|
|
|
|
|
mutable scope: rank;
|
|
|
|
|
mutable status: status;
|
|
|
|
|
mutable mark: mark;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
exception VariableScopeEscape of int * int
|
|
|
|
|
|
|
|
|
|
(* [adjust_rank data k] is equivalent to [data.rank <- min k data.rank].
|
|
|
|
|
Only flexible variables can decrease their ranks. *)
|
|
|
|
|
(* Ranks correspond to binding sites / De Bruijn levels, and they also
|
|
|
|
|
determine a lexical regions within the source program: the part of
|
|
|
|
|
the program where the variable is "in scope". Lowering the rank of
|
|
|
|
|
a variable moves its binding site closer to the root of the term
|
|
|
|
|
and widens the lexical region.
|
|
|
|
|
|
|
|
|
|
Certain types are only valid (well-formed) within certain lexical
|
|
|
|
|
regions. For example the type [int -> a], if [a] is a locally-bound
|
|
|
|
|
rigid/polymorphic variable, is only valid within the scope of [a].
|
|
|
|
|
In addition to a "rank", our inference variables will also have a
|
|
|
|
|
"scope", that tracks their lexical region of validity. This "scope"
|
|
|
|
|
is also represented by the [rank] type, and the well-formedness
|
|
|
|
|
invariant is that the [rank] of a variable must always be above its
|
|
|
|
|
[scope]: its binding site lies within its region of
|
|
|
|
|
well-formedness. *)
|
|
|
|
|
|
|
|
|
|
exception VariableScopeEscape of { rank: rank; scope: rank }
|
|
|
|
|
|
|
|
|
|
(* The rank of variable must always be above its scope. *)
|
|
|
|
|
let check_rank_scope (data : 'a data) =
|
|
|
|
|
if data.rank < data.scope then
|
|
|
|
|
raise (VariableScopeEscape {rank = data.rank; scope = data.scope})
|
|
|
|
|
|
|
|
|
|
let adjust_rank (data : 'a data) (k : rank) =
|
|
|
|
|
if k < data.rank then
|
|
|
|
|
if data.status = Flexible then
|
|
|
|
|
data.rank <- k
|
|
|
|
|
else
|
|
|
|
|
raise (VariableScopeEscape (k, data.rank))
|
|
|
|
|
assert (data.status = Active);
|
|
|
|
|
if k < data.rank then begin
|
|
|
|
|
data.rank <- k;
|
|
|
|
|
check_rank_scope data;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let adjust_scope (data : 'a data) (s : rank) =
|
|
|
|
|
assert (data.status = Active);
|
|
|
|
|
if s > data.scope then begin
|
|
|
|
|
data.scope <- s;
|
|
|
|
|
check_rank_scope data;
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
(* The module [Data] satisfies the signature [USTRUCTURE] required by the
|
|
|
|
|
functor [Unifier.Make]. *)
|
|
|
|
@ -155,10 +178,10 @@ module Data = struct
|
|
|
|
|
let dummy : mark =
|
|
|
|
|
fresh_mark()
|
|
|
|
|
|
|
|
|
|
let make structure rank status =
|
|
|
|
|
let make structure ~rank ~scope status =
|
|
|
|
|
let id = fresh_id() in
|
|
|
|
|
let mark = dummy in
|
|
|
|
|
{ id; structure; rank; status; mark }
|
|
|
|
|
{ id; structure; rank; scope; status; mark }
|
|
|
|
|
|
|
|
|
|
let map f data =
|
|
|
|
|
{ data with structure = S.map f data.structure }
|
|
|
|
@ -187,45 +210,37 @@ module Data = struct
|
|
|
|
|
fresh identifier. Here, we keep an arbitrary identifier. *)
|
|
|
|
|
|
|
|
|
|
let conjunction equate data1 data2 =
|
|
|
|
|
assert (data1.status <> Generic);
|
|
|
|
|
assert (data2.status <> Generic);
|
|
|
|
|
let id = data1.id in
|
|
|
|
|
(* The new structural constraint is the logical conjunction of the two
|
|
|
|
|
structural constraints. *)
|
|
|
|
|
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
|
|
|
|
|
adjust_rank data1 rank;
|
|
|
|
|
adjust_rank data2 rank;
|
|
|
|
|
let fail () = raise InconsistentConjunction in
|
|
|
|
|
(* If either variables is rigid, the conjunction is also rigid. *)
|
|
|
|
|
let status : status =
|
|
|
|
|
match data1.status, data2.status with
|
|
|
|
|
| Generic, _
|
|
|
|
|
| _, Generic ->
|
|
|
|
|
assert false
|
|
|
|
|
| Flexible, Flexible ->
|
|
|
|
|
Flexible
|
|
|
|
|
(* One cannot unify two rigid variables, *)
|
|
|
|
|
| Rigid, Rigid ->
|
|
|
|
|
fail ()
|
|
|
|
|
| Rigid, Flexible ->
|
|
|
|
|
(* ... or give it a non-leaf structure. *)
|
|
|
|
|
if not (S.is_leaf structure) then fail ();
|
|
|
|
|
Rigid
|
|
|
|
|
| Flexible, Rigid ->
|
|
|
|
|
(* ... or give it a non-leaf structure. *)
|
|
|
|
|
if not (S.is_leaf structure) then fail ();
|
|
|
|
|
Rigid
|
|
|
|
|
in
|
|
|
|
|
(* The new scope is the maximum of the two scopes. *)
|
|
|
|
|
let scope = max data1.scope data2.scope in
|
|
|
|
|
(* If the scope becomes greater than the rank, it means we tried to use
|
|
|
|
|
* an equation outside of its scope. *)
|
|
|
|
|
if rank < scope then
|
|
|
|
|
raise (VariableScopeEscape { rank; scope });
|
|
|
|
|
let status : status = Active in
|
|
|
|
|
(* There is no need to preserve marks during unification. *)
|
|
|
|
|
let mark = dummy in
|
|
|
|
|
{ id; structure; rank; status; mark }
|
|
|
|
|
{ id; structure; rank; scope; status; mark }
|
|
|
|
|
|
|
|
|
|
let is_leaf data =
|
|
|
|
|
S.is_leaf data.structure
|
|
|
|
|
|
|
|
|
|
let project data =
|
|
|
|
|
let project (data : 'a structure) =
|
|
|
|
|
data.structure
|
|
|
|
|
|
|
|
|
|
let rank (data : 'a structure) =
|
|
|
|
|
data.rank
|
|
|
|
|
|
|
|
|
|
let scope (data : 'a structure) =
|
|
|
|
|
data.scope
|
|
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
@ -319,12 +334,21 @@ let register v r =
|
|
|
|
|
(* When a fresh unification variable is created, it receives the current rank,
|
|
|
|
|
and it is immediately registered at this rank. *)
|
|
|
|
|
|
|
|
|
|
let fresh (rigidity : rigidity) structure =
|
|
|
|
|
let r = state.young in
|
|
|
|
|
if rigidity = Rigid then
|
|
|
|
|
assert (S.is_leaf structure);
|
|
|
|
|
let v = U.fresh (Data.make structure r (status_of_rigidity rigidity)) in
|
|
|
|
|
register v r;
|
|
|
|
|
let fresh structure =
|
|
|
|
|
let rank = state.young in
|
|
|
|
|
let scope = base_rank in
|
|
|
|
|
let v = U.fresh (Data.make structure ~rank ~scope Active) in
|
|
|
|
|
register v rank;
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
|
let fresh_rigid =
|
|
|
|
|
let fresh_abstract = Utils.gensym() in
|
|
|
|
|
fun () ->
|
|
|
|
|
let structure = Some (Structure.Abstract (fresh_abstract ())) in
|
|
|
|
|
let rank = state.young in
|
|
|
|
|
let scope = rank in
|
|
|
|
|
let v = U.fresh (Data.make structure ~rank ~scope Active) in
|
|
|
|
|
register v rank;
|
|
|
|
|
v
|
|
|
|
|
|
|
|
|
|
(* -------------------------------------------------------------------------- *)
|
|
|
|
@ -473,7 +497,7 @@ let update_ranks generation =
|
|
|
|
|
|
|
|
|
|
(* A postcondition of [traverse v] is [U.rank v <= k]. (This is downward
|
|
|
|
|
propagation.) [traverse v] returns the updated rank of [v]. *)
|
|
|
|
|
let rec traverse v : rank =
|
|
|
|
|
let rec traverse v =
|
|
|
|
|
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 active variable. *)
|
|
|
|
@ -500,21 +524,38 @@ let update_ranks generation =
|
|
|
|
|
it must now be [k]. *)
|
|
|
|
|
assert (data.rank = k);
|
|
|
|
|
(* If [v] carries some structure, ... *)
|
|
|
|
|
if not (Data.is_leaf data) then
|
|
|
|
|
(* ... traverse [v]'s children; on the way back up, compute the
|
|
|
|
|
maximum of their ranks, and use it to adjust [v]'s rank. *)
|
|
|
|
|
if not (Data.is_leaf data) then begin
|
|
|
|
|
(* Update the rank and scope of all children. *)
|
|
|
|
|
Data.iter traverse data;
|
|
|
|
|
(* On the way back up, compute the maximum of their
|
|
|
|
|
scopes, and use it to adjust [v]'s scope.
|
|
|
|
|
|
|
|
|
|
Note: we are not sure that this propagation is strictly
|
|
|
|
|
necessary. We must detect all cases where a variable
|
|
|
|
|
rank goes below its scope, but it may be enough to
|
|
|
|
|
propagate ranks "down" and never propagate scopes "up".
|
|
|
|
|
However propagating scopes in this way follows more
|
|
|
|
|
closely the intended meaning of scopes as "lexical
|
|
|
|
|
region of vadility / well-formedness": if a variable
|
|
|
|
|
[v] is only valid within scope 10, then a variable
|
|
|
|
|
representing the type (int -> v) arguably is also only
|
|
|
|
|
valid within scope 10. *)
|
|
|
|
|
adjust_scope data (
|
|
|
|
|
Data.fold (fun child accu ->
|
|
|
|
|
max (get child).scope accu
|
|
|
|
|
) data data.scope
|
|
|
|
|
);
|
|
|
|
|
(* As an optimization we also adjust [v]'s rank, which needs
|
|
|
|
|
not be higher than its children ranks and its scope. *)
|
|
|
|
|
adjust_rank data (
|
|
|
|
|
Data.fold (fun child accu ->
|
|
|
|
|
max (traverse child) accu
|
|
|
|
|
) data base_rank (* the base rank is neutral for [max] *)
|
|
|
|
|
)
|
|
|
|
|
max (get child).rank accu
|
|
|
|
|
) data data.scope
|
|
|
|
|
);
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end;
|
|
|
|
|
(* In allowed cases, return [v]'s final rank. *)
|
|
|
|
|
data.rank
|
|
|
|
|
|
|
|
|
|
in
|
|
|
|
|
let traverse v = ignore (traverse v) in
|
|
|
|
|
List.iter traverse generation.by_rank.(k)
|
|
|
|
|
|
|
|
|
|
done
|
|
|
|
@ -733,7 +774,7 @@ let instantiate { generics; quantifiers; root } =
|
|
|
|
|
let data = get v in
|
|
|
|
|
assert (data.status = Generic);
|
|
|
|
|
data.mark <- i;
|
|
|
|
|
fresh Flexible S.leaf
|
|
|
|
|
fresh None
|
|
|
|
|
)
|
|
|
|
|
in
|
|
|
|
|
|
|
|
|
@ -766,10 +807,8 @@ let pp_status ppf s =
|
|
|
|
|
match s with
|
|
|
|
|
| Generic ->
|
|
|
|
|
Printf.fprintf ppf "generic"
|
|
|
|
|
| Flexible ->
|
|
|
|
|
Printf.fprintf ppf "flexible"
|
|
|
|
|
| Rigid ->
|
|
|
|
|
Printf.fprintf ppf "rigid"
|
|
|
|
|
| Active ->
|
|
|
|
|
Printf.fprintf ppf "active"
|
|
|
|
|
|
|
|
|
|
let show_variable v =
|
|
|
|
|
let data = get v in
|
|
|
|
|