(**************************************************************************) (* *) (* OCaml *) (* *) (* Ulysse Gérard, Thomas Refis, Tarides *) (* *) (* Copyright 2021 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. *) (* *) (**************************************************************************) module Uid = struct type t = | Compilation_unit of string | Item of { comp_unit: string; id: int; from: Unit_info.intf_or_impl } | Internal | Predef of string include Identifiable.Make(struct type nonrec t = t let equal (x : t) y = x = y let compare (x : t) y = compare x y let hash (x : t) = Hashtbl.hash x let pp_intf_or_impl fmt = function | Unit_info.Intf -> Format.pp_print_string fmt "[intf]" | Unit_info.Impl -> () let print fmt = function | Internal -> Format.pp_print_string fmt "" | Predef name -> Format.fprintf fmt "" name | Compilation_unit s -> Format.pp_print_string fmt s | Item { comp_unit; id; from } -> Format.fprintf fmt "%a%s.%d" pp_intf_or_impl from comp_unit id let output oc t = let fmt = Format.formatter_of_out_channel oc in print fmt t end) let id = ref (-1) let reinit () = id := (-1) let mk ~current_unit = let comp_unit, from = let open Unit_info in match current_unit with | None -> "", Impl | Some ui -> modname ui, kind ui in incr id; Item { comp_unit; id = !id; from } let of_compilation_unit_id id = if not (Ident.persistent id) then Misc.fatal_errorf "Types.Uid.of_compilation_unit_id %S" (Ident.name id); Compilation_unit (Ident.name id) let of_predef_id id = if not (Ident.is_predef id) then Misc.fatal_errorf "Types.Uid.of_predef_id %S" (Ident.name id); Predef (Ident.name id) let internal_not_actually_unique = Internal let for_actual_declaration = function | Item _ -> true | _ -> false end module Sig_component_kind = struct type t = | Value | Type | Constructor | Label | Module | Module_type | Extension_constructor | Class | Class_type let to_string = function | Value -> "value" | Type -> "type" | Constructor -> "constructor" | Label -> "label" | Module -> "module" | Module_type -> "module type" | Extension_constructor -> "extension constructor" | Class -> "class" | Class_type -> "class type" let can_appear_in_types = function | Value | Extension_constructor -> false | Type | Constructor | Label | Module | Module_type | Class | Class_type -> true end module Item = struct module T = struct type t = string * Sig_component_kind.t let compare = compare let name (name, _) = name let kind (_, kind) = kind let make str ns = str, ns let value id = Ident.name id, Sig_component_kind.Value let type_ id = Ident.name id, Sig_component_kind.Type let constr id = Ident.name id, Sig_component_kind.Constructor let label id = Ident.name id, Sig_component_kind.Label let module_ id = Ident.name id, Sig_component_kind.Module let module_type id = Ident.name id, Sig_component_kind.Module_type let extension_constructor id = Ident.name id, Sig_component_kind.Extension_constructor let class_ id = Ident.name id, Sig_component_kind.Class let class_type id = Ident.name id, Sig_component_kind.Class_type let print fmt (name, ns) = Format.fprintf fmt "%S[%s]" name (Sig_component_kind.to_string ns) end include T module Map = Map.Make(T) end type var = Ident.t type t = { uid: Uid.t option; desc: desc; approximated: bool } and desc = | Var of var | Abs of var * t | App of t * t | Struct of t Item.Map.t | Alias of t | Leaf | Proj of t * Item.t | Comp_unit of string | Error of string let print fmt t = let print_uid_opt = Format.pp_print_option (fun fmt -> Format.fprintf fmt "<%a>" Uid.print) in let rec aux fmt { uid; desc } = match desc with | Var id -> Format.fprintf fmt "%s%a" (Ident.name id) print_uid_opt uid | Abs (id, t) -> let rec collect_idents = function | { uid = None; desc = Abs(id, t) } -> let (ids, body) = collect_idents t in id :: ids, body | body -> ([], body) in let (other_idents, body) = collect_idents t in let pp_idents fmt idents = let idents_names = List.map Ident.name idents in let pp_sep fmt () = Format.fprintf fmt ",@ " in Format.pp_print_list ~pp_sep Format.pp_print_string fmt idents_names in Format.fprintf fmt "Abs@[%a@,(@[%a,@ @[%a@]@])@]" print_uid_opt uid pp_idents (id :: other_idents) aux body | App (t1, t2) -> Format.fprintf fmt "@[%a(@,%a)%a@]" aux t1 aux t2 print_uid_opt uid | Leaf -> Format.fprintf fmt "<%a>" (Format.pp_print_option Uid.print) uid | Proj (t, item) -> begin match uid with | None -> Format.fprintf fmt "@[%a@ .@ %a@]" aux t Item.print item | Some uid -> Format.fprintf fmt "@[(%a@ .@ %a)<%a>@]" aux t Item.print item Uid.print uid end | Comp_unit name -> Format.fprintf fmt "CU %s" name | Struct map -> let print_map fmt = Item.Map.iter (fun item t -> Format.fprintf fmt "@[%a ->@ %a;@]@," Item.print item aux t ) in if Item.Map.is_empty map then Format.fprintf fmt "@[{%a}@]" print_uid_opt uid else Format.fprintf fmt "{@[%a@,%a@]}" print_uid_opt uid print_map map | Alias t -> Format.fprintf fmt "Alias@[(@[%a@,%a@])@]" print_uid_opt uid aux t | Error s -> Format.fprintf fmt "Error %s" s in if t.approximated then Format.fprintf fmt "@[(approx)@ %a@]@;" aux t else Format.fprintf fmt "@[%a@]@;" aux t let rec strip_head_aliases = function | { desc = Alias t; _ } -> strip_head_aliases t | t -> t let fresh_var ?(name="shape-var") uid = let var = Ident.create_local name in var, { uid = Some uid; desc = Var var; approximated = false } let for_unnamed_functor_param = Ident.create_local "()" let var uid id = { uid = Some uid; desc = Var id; approximated = false } let abs ?uid var body = { uid; desc = Abs (var, body); approximated = false } let str ?uid map = { uid; desc = Struct map; approximated = false } let alias ?uid t = { uid; desc = Alias t; approximated = false} let leaf uid = { uid = Some uid; desc = Leaf; approximated = false } let approx t = { t with approximated = true} let proj ?uid t item = match t.desc with | Leaf -> (* When stuck projecting in a leaf we propagate the leaf as a best effort *) approx t | Struct map -> begin try Item.Map.find item map with Not_found -> approx t (* ill-typed program *) end | _ -> { uid; desc = Proj (t, item); approximated = false } let app ?uid f ~arg = { uid; desc = App (f, arg); approximated = false } let decompose_abs t = match t.desc with | Abs (x, t) -> Some (x, t) | _ -> None let dummy_mod = { uid = None; desc = Struct Item.Map.empty; approximated = false } let of_path ~find_shape ~namespace path = (* We need to handle the following cases: Path of constructor: M.t.C [Pextra_ty("M.t", "C")] Path of label: M.t.lbl [Pextra_ty("M.t", "lbl")] Path of label of inline record: M.t.C.lbl [Pextra_ty(Pextra_ty("M.t", "C"), "lbl")] *) let rec aux : Sig_component_kind.t -> Path.t -> t = fun ns -> function | Pident id -> find_shape ns id | Pdot (path, name) -> proj (aux Module path) (name, ns) | Papply (p1, p2) -> app (aux Module p1) ~arg:(aux Module p2) | Pextra_ty (path, extra) -> begin match extra, ns, path with | Pcstr_ty name, Label, Pextra_ty _ -> (* Handle the M.t.C.lbl case *) proj (aux Constructor path) (name, ns) | Pcstr_ty name, _, _ -> proj (aux Type path) (name, ns) | Pext_ty, _, _ -> aux Extension_constructor path end in aux namespace path let for_persistent_unit s = { uid = Some (Uid.of_compilation_unit_id (Ident.create_persistent s)); desc = Comp_unit s; approximated = false } let leaf_for_unpack = { uid = None; desc = Leaf; approximated = false } let set_uid_if_none t uid = match t.uid with | None -> { t with uid = Some uid } | _ -> t module Map = struct type shape = t type nonrec t = t Item.Map.t let empty = Item.Map.empty let add t item shape = Item.Map.add item shape t let add_value t id uid = Item.Map.add (Item.value id) (leaf uid) t let add_value_proj t id shape = let item = Item.value id in Item.Map.add item (proj shape item) t let add_type t id shape = Item.Map.add (Item.type_ id) shape t let add_type_proj t id shape = let item = Item.type_ id in Item.Map.add item (proj shape item) t let add_constr t id shape = Item.Map.add (Item.constr id) shape t let add_constr_proj t id shape = let item = Item.constr id in Item.Map.add item (proj shape item) t let add_label t id uid = Item.Map.add (Item.label id) (leaf uid) t let add_label_proj t id shape = let item = Item.label id in Item.Map.add item (proj shape item) t let add_module t id shape = Item.Map.add (Item.module_ id) shape t let add_module_proj t id shape = let item = Item.module_ id in Item.Map.add item (proj shape item) t let add_module_type t id uid = Item.Map.add (Item.module_type id) (leaf uid) t let add_module_type_proj t id shape = let item = Item.module_type id in Item.Map.add item (proj shape item) t let add_extcons t id shape = Item.Map.add (Item.extension_constructor id) shape t let add_extcons_proj t id shape = let item = Item.extension_constructor id in Item.Map.add item (proj shape item) t let add_class t id uid = Item.Map.add (Item.class_ id) (leaf uid) t let add_class_proj t id shape = let item = Item.class_ id in Item.Map.add item (proj shape item) t let add_class_type t id uid = Item.Map.add (Item.class_type id) (leaf uid) t let add_class_type_proj t id shape = let item = Item.class_type id in Item.Map.add item (proj shape item) t end