(**************************************************************************) (* *) (* OCaml *) (* *) (* Xavier Leroy and Jerome Vouillon, projet Cristal, INRIA Rocquencourt *) (* *) (* Copyright 1996 Institut National de Recherche en Informatique et *) (* en Automatique. *) (* *) (* All rights reserved. This file is distributed under the terms of *) (* the GNU Lesser General Public License version 2.1, with the *) (* special exception on linking described in the file LICENSE. *) (* *) (**************************************************************************) (* Basic operations on core types *) open Misc open Asttypes open Types (**** Sets, maps and hashtables of types ****) module TypeSet = Set.Make(TypeOps) module TypeMap = Map.Make (TypeOps) module TypeHash = Hashtbl.Make(TypeOps) (**** Forward declarations ****) let print_raw = ref (fun _ -> assert false : Format.formatter -> type_expr -> unit) (**** Type level management ****) let generic_level = 100000000 (* Used to mark a type during a traversal. *) let lowest_level = 0 let pivot_level = 2 * lowest_level - 1 (* pivot_level - lowest_level < lowest_level *) (**** Some type creators ****) let new_id = ref (-1) let newty2 level desc = incr new_id; { desc; level; id = !new_id } let newgenty desc = newty2 generic_level desc let newgenvar ?name () = newgenty (Tvar name) (* let newmarkedvar level = incr new_id; { desc = Tvar; level = pivot_level - level; id = !new_id } let newmarkedgenvar () = incr new_id; { desc = Tvar; level = pivot_level - generic_level; id = !new_id } *) (**** Check some types ****) let is_Tvar = function {desc=Tvar _} -> true | _ -> false let is_Tunivar = function {desc=Tunivar _} -> true | _ -> false let dummy_method = "*dummy method*" let default_mty = function Some mty -> mty | None -> Mty_signature [] (**** Definitions for backtracking ****) type change = Ctype of type_expr * type_desc | Ccompress of type_expr * type_desc * type_desc | Clevel of type_expr * int | Cname of (Path.t * type_expr list) option ref * (Path.t * type_expr list) option | Crow of row_field option ref * row_field option | Ckind of field_kind option ref * field_kind option | Ccommu of commutable ref * commutable | Cuniv of type_expr option ref * type_expr option | Ctypeset of TypeSet.t ref * TypeSet.t type changes = Change of change * changes ref | Unchanged | Invalid let trail = Weak.create 1 let log_change ch = match Weak.get trail 0 with None -> () | Some r -> let r' = ref Unchanged in r := Change (ch, r'); Weak.set trail 0 (Some r') (**** Representative of a type ****) let rec field_kind_repr = function Fvar {contents = Some kind} -> field_kind_repr kind | kind -> kind let rec repr_link compress t d = function {desc = Tlink t' as d'} -> repr_link true t d' t' | {desc = Tfield (_, k, _, t') as d'} when field_kind_repr k = Fabsent -> repr_link true t d' t' | t' -> if compress then begin log_change (Ccompress (t, t.desc, d)); t.desc <- d end; t' let repr t = match t.desc with Tlink t' as d -> repr_link false t d t' | Tfield (_, k, _, t') as d when field_kind_repr k = Fabsent -> repr_link false t d t' | _ -> t let rec commu_repr = function Clink r when !r <> Cunknown -> commu_repr !r | c -> c let rec row_field_repr_aux tl = function Reither(_, tl', _, {contents = Some fi}) -> row_field_repr_aux (tl@tl') fi | Reither(c, tl', m, r) -> Reither(c, tl@tl', m, r) | Rpresent (Some _) when tl <> [] -> Rpresent (Some (List.hd tl)) | fi -> fi let row_field_repr fi = row_field_repr_aux [] fi let rec rev_concat l ll = match ll with [] -> l | l'::ll -> rev_concat (l'@l) ll let rec row_repr_aux ll row = match (repr row.row_more).desc with | Tvariant row' -> let f = row.row_fields in row_repr_aux (if f = [] then ll else f::ll) row' | _ -> if ll = [] then row else {row with row_fields = rev_concat row.row_fields ll} let row_repr row = row_repr_aux [] row let rec row_field tag row = let rec find = function | (tag',f) :: fields -> if tag = tag' then row_field_repr f else find fields | [] -> match repr row.row_more with | {desc=Tvariant row'} -> row_field tag row' | _ -> Rabsent in find row.row_fields let rec row_more row = match repr row.row_more with | {desc=Tvariant row'} -> row_more row' | ty -> ty let row_fixed row = let row = row_repr row in row.row_fixed || match (repr row.row_more).desc with Tvar _ | Tnil -> false | Tunivar _ | Tconstr _ -> true | _ -> assert false let static_row row = let row = row_repr row in row.row_closed && List.for_all (fun (_,f) -> match row_field_repr f with Reither _ -> false | _ -> true) row.row_fields let hash_variant s = let accu = ref 0 in for i = 0 to String.length s - 1 do accu := 223 * !accu + Char.code s.[i] done; (* reduce to 31 bits *) accu := !accu land (1 lsl 31 - 1); (* make it signed for 64 bits architectures *) if !accu > 0x3FFFFFFF then !accu - (1 lsl 31) else !accu let proxy ty = let ty0 = repr ty in match ty0.desc with | Tvariant row when not (static_row row) -> row_more row | Tobject (ty, _) -> let rec proxy_obj ty = match ty.desc with Tfield (_, _, _, ty) | Tlink ty -> proxy_obj ty | Tvar _ | Tunivar _ | Tconstr _ -> ty | Tnil -> ty0 | _ -> assert false in proxy_obj ty | _ -> ty0 (**** Utilities for fixed row private types ****) let has_constr_row t = match (repr t).desc with Tobject(t,_) -> let rec check_row t = match (repr t).desc with Tfield(_,_,_,t) -> check_row t | Tconstr _ -> true | _ -> false in check_row t | Tvariant row -> (match row_more row with {desc=Tconstr _} -> true | _ -> false) | _ -> false let is_row_name s = let l = String.length s in if l < 4 then false else String.sub s (l-4) 4 = "#row" let is_constr_row t = match t.desc with Tconstr (Path.Pident id, _, _) -> is_row_name (Ident.name id) | Tconstr (Path.Pdot (_, s, _), _, _) -> is_row_name s | _ -> false (**********************************) (* Utilities for type traversal *) (**********************************) let rec iter_row f row = List.iter (fun (_, fi) -> match row_field_repr fi with | Rpresent(Some ty) -> f ty | Reither(_, tl, _, _) -> List.iter f tl | _ -> ()) row.row_fields; match (repr row.row_more).desc with Tvariant row -> iter_row f row | Tvar _ | Tunivar _ | Tsubst _ | Tconstr _ | Tnil -> Misc.may (fun (_,l) -> List.iter f l) row.row_name | _ -> assert false let iter_type_expr f ty = match ty.desc with Tvar _ -> () | Tarrow (_, ty1, ty2, _) -> f ty1; f ty2 | Ttuple l -> List.iter f l | Tconstr (_, l, _) -> List.iter f l | Tobject(ty, {contents = Some (_, p)}) -> f ty; List.iter f p | Tobject (ty, _) -> f ty | Tvariant row -> iter_row f row; f (row_more row) | Tfield (_, _, ty1, ty2) -> f ty1; f ty2 | Tnil -> () | Tlink ty -> f ty | Tsubst ty -> f ty | Tunivar _ -> () | Tpoly (ty, tyl) -> f ty; List.iter f tyl | Tpackage (_, _, l) -> List.iter f l let rec iter_abbrev f = function Mnil -> () | Mcons(_, _, ty, ty', rem) -> f ty; f ty'; iter_abbrev f rem | Mlink rem -> iter_abbrev f !rem type type_iterators = { it_signature: type_iterators -> signature -> unit; it_signature_item: type_iterators -> signature_item -> unit; it_value_description: type_iterators -> value_description -> unit; it_type_declaration: type_iterators -> type_declaration -> unit; it_extension_constructor: type_iterators -> extension_constructor -> unit; it_module_declaration: type_iterators -> module_declaration -> unit; it_modtype_declaration: type_iterators -> modtype_declaration -> unit; it_class_declaration: type_iterators -> class_declaration -> unit; it_class_type_declaration: type_iterators -> class_type_declaration -> unit; it_module_type: type_iterators -> module_type -> unit; it_class_type: type_iterators -> class_type -> unit; it_type_kind: type_iterators -> type_kind -> unit; it_do_type_expr: type_iterators -> type_expr -> unit; it_type_expr: type_iterators -> type_expr -> unit; it_path: Path.t -> unit; } let iter_type_expr_cstr_args f = function | Cstr_tuple tl -> List.iter f tl | Cstr_record lbls -> List.iter (fun d -> f d.ld_type) lbls let map_type_expr_cstr_args f = function | Cstr_tuple tl -> Cstr_tuple (List.map f tl) | Cstr_record lbls -> Cstr_record (List.map (fun d -> {d with ld_type=f d.ld_type}) lbls) let iter_type_expr_kind f = function | Type_abstract -> () | Type_variant cstrs -> List.iter (fun cd -> iter_type_expr_cstr_args f cd.cd_args; Misc.may f cd.cd_res ) cstrs | Type_record(lbls, _) -> List.iter (fun d -> f d.ld_type) lbls | Type_open -> () let type_iterators = let it_signature it = List.iter (it.it_signature_item it) and it_signature_item it = function Sig_value (_, vd) -> it.it_value_description it vd | Sig_type (_, td, _) -> it.it_type_declaration it td | Sig_typext (_, td, _) -> it.it_extension_constructor it td | Sig_module (_, md, _) -> it.it_module_declaration it md | Sig_modtype (_, mtd) -> it.it_modtype_declaration it mtd | Sig_class (_, cd, _) -> it.it_class_declaration it cd | Sig_class_type (_, ctd, _) -> it.it_class_type_declaration it ctd and it_value_description it vd = it.it_type_expr it vd.val_type and it_type_declaration it td = List.iter (it.it_type_expr it) td.type_params; may (it.it_type_expr it) td.type_manifest; it.it_type_kind it td.type_kind and it_extension_constructor it td = it.it_path td.ext_type_path; List.iter (it.it_type_expr it) td.ext_type_params; iter_type_expr_cstr_args (it.it_type_expr it) td.ext_args; may (it.it_type_expr it) td.ext_ret_type and it_module_declaration it md = it.it_module_type it md.md_type and it_modtype_declaration it mtd = may (it.it_module_type it) mtd.mtd_type and it_class_declaration it cd = List.iter (it.it_type_expr it) cd.cty_params; it.it_class_type it cd.cty_type; may (it.it_type_expr it) cd.cty_new; it.it_path cd.cty_path and it_class_type_declaration it ctd = List.iter (it.it_type_expr it) ctd.clty_params; it.it_class_type it ctd.clty_type; it.it_path ctd.clty_path and it_module_type it = function Mty_ident p | Mty_alias p -> it.it_path p | Mty_signature sg -> it.it_signature it sg | Mty_functor (_, mto, mt) -> may (it.it_module_type it) mto; it.it_module_type it mt and it_class_type it = function Cty_constr (p, tyl, cty) -> it.it_path p; List.iter (it.it_type_expr it) tyl; it.it_class_type it cty | Cty_signature cs -> it.it_type_expr it cs.csig_self; Vars.iter (fun _ (_,_,ty) -> it.it_type_expr it ty) cs.csig_vars; List.iter (fun (p, tl) -> it.it_path p; List.iter (it.it_type_expr it) tl) cs.csig_inher | Cty_arrow (_, ty, cty) -> it.it_type_expr it ty; it.it_class_type it cty and it_type_kind it kind = iter_type_expr_kind (it.it_type_expr it) kind and it_do_type_expr it ty = iter_type_expr (it.it_type_expr it) ty; match ty.desc with Tconstr (p, _, _) | Tobject (_, {contents=Some (p, _)}) | Tpackage (p, _, _) -> it.it_path p | Tvariant row -> may (fun (p,_) -> it.it_path p) (row_repr row).row_name | _ -> () and it_path _p = () in { it_path; it_type_expr = it_do_type_expr; it_do_type_expr; it_type_kind; it_class_type; it_module_type; it_signature; it_class_type_declaration; it_class_declaration; it_modtype_declaration; it_module_declaration; it_extension_constructor; it_type_declaration; it_value_description; it_signature_item; } let copy_row f fixed row keep more = let fields = List.map (fun (l, fi) -> l, match row_field_repr fi with | Rpresent(Some ty) -> Rpresent(Some(f ty)) | Reither(c, tl, m, e) -> let e = if keep then e else ref None in let m = if row.row_fixed then fixed else m in let tl = List.map f tl in Reither(c, tl, m, e) | _ -> fi) row.row_fields in let name = match row.row_name with None -> None | Some (path, tl) -> Some (path, List.map f tl) in { row_fields = fields; row_more = more; row_bound = (); row_fixed = row.row_fixed && fixed; row_closed = row.row_closed; row_name = name; } let rec copy_kind = function Fvar{contents = Some k} -> copy_kind k | Fvar _ -> Fvar (ref None) | Fpresent -> Fpresent | Fabsent -> assert false let copy_commu c = if commu_repr c = Cok then Cok else Clink (ref Cunknown) (* Since univars may be used as row variables, we need to do some encoding during substitution *) let rec norm_univar ty = match ty.desc with Tunivar _ | Tsubst _ -> ty | Tlink ty -> norm_univar ty | Ttuple (ty :: _) -> norm_univar ty | _ -> assert false let rec copy_type_desc ?(keep_names=false) f = function Tvar _ as ty -> if keep_names then ty else Tvar None | Tarrow (p, ty1, ty2, c)-> Tarrow (p, f ty1, f ty2, copy_commu c) | Ttuple l -> Ttuple (List.map f l) | Tconstr (p, l, _) -> Tconstr (p, List.map f l, ref Mnil) | Tobject(ty, {contents = Some (p, tl)}) -> Tobject (f ty, ref (Some(p, List.map f tl))) | Tobject (ty, _) -> Tobject (f ty, ref None) | Tvariant _ -> assert false (* too ambiguous *) | Tfield (p, k, ty1, ty2) -> (* the kind is kept shared *) Tfield (p, field_kind_repr k, f ty1, f ty2) | Tnil -> Tnil | Tlink ty -> copy_type_desc f ty.desc | Tsubst _ -> assert false | Tunivar _ as ty -> ty (* always keep the name *) | Tpoly (ty, tyl) -> let tyl = List.map (fun x -> norm_univar (f x)) tyl in Tpoly (f ty, tyl) | Tpackage (p, n, l) -> Tpackage (p, n, List.map f l) (* Utilities for copying *) let saved_desc = ref [] (* Saved association of generic nodes with their description. *) let save_desc ty desc = saved_desc := (ty, desc)::!saved_desc let saved_kinds = ref [] (* duplicated kind variables *) let new_kinds = ref [] (* new kind variables *) let dup_kind r = (match !r with None -> () | Some _ -> assert false); if not (List.memq r !new_kinds) then begin saved_kinds := r :: !saved_kinds; let r' = ref None in new_kinds := r' :: !new_kinds; r := Some (Fvar r') end (* Restored type descriptions. *) let cleanup_types () = List.iter (fun (ty, desc) -> ty.desc <- desc) !saved_desc; List.iter (fun r -> r := None) !saved_kinds; saved_desc := []; saved_kinds := []; new_kinds := [] (* Mark a type. *) let rec mark_type ty = let ty = repr ty in if ty.level >= lowest_level then begin ty.level <- pivot_level - ty.level; iter_type_expr mark_type ty end let mark_type_node ty = let ty = repr ty in if ty.level >= lowest_level then begin ty.level <- pivot_level - ty.level; end let mark_type_params ty = iter_type_expr mark_type ty let type_iterators = let it_type_expr it ty = let ty = repr ty in if ty.level >= lowest_level then begin mark_type_node ty; it.it_do_type_expr it ty; end in {type_iterators with it_type_expr} (* Remove marks from a type. *) let rec unmark_type ty = let ty = repr ty in if ty.level < lowest_level then begin ty.level <- pivot_level - ty.level; iter_type_expr unmark_type ty end let unmark_iterators = let it_type_expr _it ty = unmark_type ty in {type_iterators with it_type_expr} let unmark_type_decl decl = unmark_iterators.it_type_declaration unmark_iterators decl let unmark_extension_constructor ext = List.iter unmark_type ext.ext_type_params; iter_type_expr_cstr_args unmark_type ext.ext_args; Misc.may unmark_type ext.ext_ret_type let unmark_class_signature sign = unmark_type sign.csig_self; Vars.iter (fun _l (_m, _v, t) -> unmark_type t) sign.csig_vars let unmark_class_type cty = unmark_iterators.it_class_type unmark_iterators cty (*******************************************) (* Memorization of abbreviation expansion *) (*******************************************) (* Search whether the expansion has been memorized. *) let lte_public p1 p2 = (* Private <= Public *) match p1, p2 with | Private, _ | _, Public -> true | Public, Private -> false let rec find_expans priv p1 = function Mnil -> None | Mcons (priv', p2, _ty0, ty, _) when lte_public priv priv' && Path.same p1 p2 -> Some ty | Mcons (_, _, _, _, rem) -> find_expans priv p1 rem | Mlink {contents = rem} -> find_expans priv p1 rem (* debug: check for cycles in abbreviation. only works with -principal let rec check_expans visited ty = let ty = repr ty in assert (not (List.memq ty visited)); match ty.desc with Tconstr (path, args, abbrev) -> begin match find_expans path !abbrev with Some ty' -> check_expans (ty :: visited) ty' | None -> () end | _ -> () *) let memo = ref [] (* Contains the list of saved abbreviation expansions. *) let cleanup_abbrev () = (* Remove all memorized abbreviation expansions. *) List.iter (fun abbr -> abbr := Mnil) !memo; memo := [] let memorize_abbrev mem priv path v v' = (* Memorize the expansion of an abbreviation. *) mem := Mcons (priv, path, v, v', !mem); (* check_expans [] v; *) memo := mem :: !memo let rec forget_abbrev_rec mem path = match mem with Mnil -> assert false | Mcons (_, path', _, _, rem) when Path.same path path' -> rem | Mcons (priv, path', v, v', rem) -> Mcons (priv, path', v, v', forget_abbrev_rec rem path) | Mlink mem' -> mem' := forget_abbrev_rec !mem' path; raise Exit let forget_abbrev mem path = try mem := forget_abbrev_rec !mem path with Exit -> () (* debug: check for invalid abbreviations let rec check_abbrev_rec = function Mnil -> true | Mcons (_, ty1, ty2, rem) -> repr ty1 != repr ty2 | Mlink mem' -> check_abbrev_rec !mem' let check_memorized_abbrevs () = List.for_all (fun mem -> check_abbrev_rec !mem) !memo *) (**********************************) (* Utilities for labels *) (**********************************) let is_optional = function Optional _ -> true | _ -> false let label_name = function Nolabel -> "" | Labelled s | Optional s -> s let prefixed_label_name = function Nolabel -> "" | Labelled s -> "~" ^ s | Optional s -> "?" ^ s let rec extract_label_aux hd l = function [] -> raise Not_found | (l',t as p) :: ls -> if label_name l' = l then (l', t, List.rev hd, ls) else extract_label_aux (p::hd) l ls let extract_label l ls = extract_label_aux [] l ls (**********************************) (* Utilities for backtracking *) (**********************************) let undo_change = function Ctype (ty, desc) -> ty.desc <- desc | Ccompress (ty, desc, _) -> ty.desc <- desc | Clevel (ty, level) -> ty.level <- level | Cname (r, v) -> r := v | Crow (r, v) -> r := v | Ckind (r, v) -> r := v | Ccommu (r, v) -> r := v | Cuniv (r, v) -> r := v | Ctypeset (r, v) -> r := v type snapshot = changes ref * int let last_snapshot = ref 0 let log_type ty = if ty.id <= !last_snapshot then log_change (Ctype (ty, ty.desc)) let link_type ty ty' = log_type ty; let desc = ty.desc in ty.desc <- Tlink ty'; (* Name is a user-supplied name for this unification variable (obtained * through a type annotation for instance). *) match desc, ty'.desc with Tvar name, Tvar name' -> begin match name, name' with | Some _, None -> log_type ty'; ty'.desc <- Tvar name | None, Some _ -> () | Some _, Some _ -> if ty.level < ty'.level then (log_type ty'; ty'.desc <- Tvar name) | None, None -> () end | _ -> () (* ; assert (check_memorized_abbrevs ()) *) (* ; check_expans [] ty' *) let set_level ty level = if ty.id <= !last_snapshot then log_change (Clevel (ty, ty.level)); ty.level <- level let set_univar rty ty = log_change (Cuniv (rty, !rty)); rty := Some ty let set_name nm v = log_change (Cname (nm, !nm)); nm := v let set_row_field e v = log_change (Crow (e, !e)); e := Some v let set_kind rk k = log_change (Ckind (rk, !rk)); rk := Some k let set_commu rc c = log_change (Ccommu (rc, !rc)); rc := c let set_typeset rs s = log_change (Ctypeset (rs, !rs)); rs := s let snapshot () = let old = !last_snapshot in last_snapshot := !new_id; match Weak.get trail 0 with Some r -> (r, old) | None -> let r = ref Unchanged in Weak.set trail 0 (Some r); (r, old) let rec rev_log accu = function Unchanged -> accu | Invalid -> assert false | Change (ch, next) -> let d = !next in next := Invalid; rev_log (ch::accu) d let backtrack (changes, old) = match !changes with Unchanged -> last_snapshot := old | Invalid -> failwith "Btype.backtrack" | Change _ as change -> cleanup_abbrev (); let backlog = rev_log [] change in List.iter undo_change backlog; changes := Unchanged; last_snapshot := old; Weak.set trail 0 (Some changes) let rec rev_compress_log log r = match !r with Unchanged | Invalid -> log | Change (Ccompress _, next) -> rev_compress_log (r::log) next | Change (_, next) -> rev_compress_log log next let undo_compress (changes, _old) = match !changes with Unchanged | Invalid -> () | Change _ -> let log = rev_compress_log [] changes in List.iter (fun r -> match !r with Change (Ccompress (ty, desc, d), next) when ty.desc == d -> ty.desc <- desc; r := !next | _ -> ()) log