aboutsummaryrefslogtreecommitdiffhomepage
path: root/plugins/extraction/common.ml
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/extraction/common.ml')
-rw-r--r--plugins/extraction/common.ml237
1 files changed, 155 insertions, 82 deletions
diff --git a/plugins/extraction/common.ml b/plugins/extraction/common.ml
index 0941ed50f..41ce4d2d4 100644
--- a/plugins/extraction/common.ml
+++ b/plugins/extraction/common.ml
@@ -20,7 +20,14 @@ open Mlutil
open Modutil
open Mod_subst
-let string_of_id id = ascii_of_ident (Names.string_of_id id)
+let string_of_id id =
+ let s = Names.string_of_id id in
+ for i = 0 to String.length s - 2 do
+ if s.[i] = '_' && s.[i+1] = '_' then warning_id s
+ done;
+ ascii_of_ident s
+
+let is_mp_bound = function MPbound _ -> true | _ -> false
(*s Some pretty-print utility functions. *)
@@ -62,17 +69,14 @@ let unquote s =
for i=0 to String.length s - 1 do if s.[i] = '\'' then s.[i] <- '~' done;
s
-let rec dottify = function
+let rec qualify delim = function
| [] -> assert false
| [s] -> s
- | s::[""] -> s
- | s::l -> (dottify l)^"."^s
+ | ""::l -> qualify delim l
+ | s::l -> s^delim^(qualify delim l)
-let rec pseudo_qualify up = function
- | [] -> assert false
- | [s] -> s
- | s::[""] -> s
- | s::l -> (if up then "" else "_")^(pseudo_qualify true l)^"__"^s
+let dottify = qualify "."
+let pseudo_qualify = qualify "__"
(*s Uppercase/lowercase renamings. *)
@@ -183,23 +187,34 @@ let mpfiles_add, mpfiles_mem, mpfiles_list, mpfiles_clear =
register_cleanup clear;
(add,mem,list,clear)
+(*s List of module parameters that we should alpha-rename *)
+
+let params_ren_add, params_ren_mem =
+ let m = ref MPset.empty in
+ let add mp = m:=MPset.add mp !m
+ and mem mp = MPset.mem mp !m
+ and clear () = m:=MPset.empty
+ in
+ register_cleanup clear;
+ (add,mem)
+
(*s table indicating the visible horizon at a precise moment,
i.e. the stack of structures we are inside.
- The sequence of [mp] parts should have the following form:
- [X.Y; X; A.B.C; A.B; A; ...], i.e. each addition should either
- be a [MPdot] over the last entry, or something new, mainly
- [MPself], or [MPfile] at the beginning.
+ a [MPfile] at the beginning, and then more and more [MPdot]
+ over this [MPfile].
- - The [content] part is used to recoard all the names already
- seen at this level.
+ - the [params] are the [MPbound] when [mp] is a functor,
+ the innermost [MPbound] coming first in the list.
- - The [subst] part is here mainly for printing signature
- (in which names are still short, i.e. relative to a [msid]).
+ - The [content] part is used to record all the names already
+ seen at this level.
*)
type visible_layer = { mp : module_path;
- content : ((kind*string),unit) Hashtbl.t }
+ params : module_path list;
+ content : ((kind*string),label) Hashtbl.t }
let pop_visible, push_visible, get_visible =
let vis = ref [] in
@@ -210,15 +225,15 @@ let pop_visible, push_visible, get_visible =
if get_phase () = Impl && modular () && is_modfile v.mp
then add_mpfiles_content v.mp v.content;
vis := List.tl !vis
- and push mp =
- vis := { mp = mp; content = Hashtbl.create 97 } :: !vis
+ and push mp mps =
+ vis := { mp = mp; params = mps; content = Hashtbl.create 97 } :: !vis
and get () = !vis
in (pop,push,get)
let get_visible_mps () = List.map (function v -> v.mp) (get_visible ())
let top_visible () = match get_visible () with [] -> assert false | v::_ -> v
let top_visible_mp () = (top_visible ()).mp
-let add_visible ks = Hashtbl.add (top_visible ()).content ks ()
+let add_visible ks l = Hashtbl.add (top_visible ()).content ks l
(* table of local module wrappers used to provide non-ambiguous names *)
@@ -284,7 +299,10 @@ let rec mp_renaming_fun mp = match mp with
let lmp = mp_renaming mp in
if lmp = [""] then (modfstlev_rename l)::lmp
else (modular_rename Mod (id_of_label l))::lmp
- | MPbound mbid -> [modular_rename Mod (id_of_mbid mbid)]
+ | MPbound mbid ->
+ let s = modular_rename Mod (id_of_mbid mbid) in
+ if not (params_ren_mem mp) then [s]
+ else let i,_,_ = repr_mbid mbid in [s^"__"^string_of_int i]
| MPfile _ when not (modular ()) -> assert false (* see [at_toplevel] above *)
| MPfile _ ->
assert (get_phase () = Pre);
@@ -296,7 +314,9 @@ let rec mp_renaming_fun mp = match mp with
and mp_renaming =
let add,get,_ = mktable true in
- fun x -> try get x with Not_found -> let y = mp_renaming_fun x in add x y; y
+ fun x ->
+ try if is_mp_bound (base_mp x) then raise Not_found; get x
+ with Not_found -> let y = mp_renaming_fun x in add x y; y
(*s Renamings creation for a [global_reference]: we build its fully-qualified
name in a [string list] form (head is the short name). *)
@@ -320,7 +340,9 @@ let ref_renaming_fun (k,r) =
let ref_renaming =
let add,get,_ = mktable true in
- fun x -> try get x with Not_found -> let y = ref_renaming_fun x in add x y; y
+ fun x ->
+ try if is_mp_bound (base_mp (modpath_of_r (snd x))) then raise Not_found; get x
+ with Not_found -> let y = ref_renaming_fun x in add x y; y
(* [visible_clash mp0 (k,s)] checks if [mp0-s] of kind [k]
can be printed as [s] in the current context of visible
@@ -338,12 +360,38 @@ let mpfiles_clash mp0 ks =
clash (fun mp -> Hashtbl.mem (get_mpfiles_content mp)) mp0 ks
(List.rev (mpfiles_list ()))
+let rec params_lookup mp0 ks = function
+ | [] -> false
+ | param :: _ when mp0 = param -> true
+ | param :: params ->
+ if ks = (Mod, List.hd (mp_renaming param)) then params_ren_add param;
+ params_lookup mp0 ks params
+
let visible_clash mp0 ks =
let rec clash = function
| [] -> false
| v :: _ when v.mp = mp0 -> false
- | v :: _ when Hashtbl.mem v.content ks -> true
- | _ :: vis -> clash vis
+ | v :: vis ->
+ let b = Hashtbl.mem v.content ks in
+ if b && not (is_mp_bound mp0) then true
+ else begin
+ if b then params_ren_add mp0;
+ if params_lookup mp0 ks v.params then false
+ else clash vis
+ end
+ in clash (get_visible ())
+
+(* Same, but with verbose output (and mp0 shouldn't be a MPbound) *)
+
+let visible_clash_dbg mp0 ks =
+ let rec clash = function
+ | [] -> None
+ | v :: _ when v.mp = mp0 -> None
+ | v :: vis ->
+ try Some (v.mp,Hashtbl.find v.content ks)
+ with Not_found ->
+ if params_lookup mp0 ks v.params then None
+ else clash vis
in clash (get_visible ())
(* After the 1st pass, we can decide which modules will be opened initially *)
@@ -370,81 +418,106 @@ let opened_libraries () =
(* First, a function that factorize the printing of both [global_reference]
and module names for ocaml. When [k=Mod] then [olab=None], otherwise it
contains the label of the reference to print.
- Invariant: [List.length ls >= 2], simpler situations are handled elsewhere. *)
-
-let pp_ocaml_gen k mp ls olab =
- try (* what is the largest prefix of [mp] that belongs to [visible]? *)
- let prefix = common_prefix_from_list mp (get_visible_mps ()) in
- let delta = mp_length mp - mp_length prefix in
- assert (k <> Mod || mp <> prefix); (* mp as whole module isn't in itself *)
- let ls = list_firstn (delta + if k = Mod then 0 else 1) ls in
- let s,ls' = list_sep_last ls in
- (* Reference r / module path mp is of the form [<prefix>.s.<List.rev ls'>].
- Difficulty: in ocaml the prefix part cannot be used for
- qualification (we are inside it) and the rest of the long
- name may be hidden.
- Solution: we duplicate the _definition_ of r / mp in a Coq__XXX module *)
- let k' = if ls' = [] then k else Mod in
- if visible_clash prefix (k',s) then
- let front = if ls' = [] && k <> Mod then [s] else ls' in
- let lab = (* label associated with s *)
- if delta = 0 && k <> Mod then Option.get olab
- else get_nth_label_mp delta mp
- in
- try dottify (front @ [check_duplicate prefix lab])
- with Not_found ->
- assert (get_phase () = Pre); (* otherwise it's too late *)
- add_duplicate prefix lab; dottify ls
- else dottify ls
- with Not_found ->
- (* [mp] belongs to a closed module, not one of [visible]. *)
- let base = base_mp mp in
- let base_s,ls1 = list_sep_last ls in
- let s,ls2 = list_sep_last ls1 in
- (* [List.rev ls] is [base_s :: s :: List.rev ls2] *)
- let k' = if ls2 = [] then k else Mod in
- if modular () && (mpfiles_mem base) &&
- (not (mpfiles_clash base (k',s))) &&
- (not (visible_clash base (k',s)))
- then (* Standard situation of an object in another file: *)
- (* Thanks to the "open" of this file we remove its name *)
- dottify ls1
- else if visible_clash base (Mod,base_s) then
- error_module_clash base_s
- else dottify ls
-
-let pp_haskell_gen k mp ls =
- if not (modular ()) then List.hd ls
- else match List.rev ls with
+ [rls] is the string list giving the qualified name, short name at the end.
+ Invariant: [List.length rls >= 2], simpler situations are handled elsewhere. *)
+
+(* [pp_ocaml_bound] : [mp] starts with a [MPbound] *)
+
+let pp_ocaml_bound k base mp rls olab =
+ (* clash with a MPbound will be detected and fixed by renaming this MPbound *)
+ if get_phase () = Pre then ignore (visible_clash base (Mod,List.hd rls));
+ dottify rls
+
+(* [pp_ocaml_local] : [mp] has something in common with [top_visible ()]
+ but isn't equal to it *)
+
+let pp_ocaml_local k _ mp rls olab =
+ (* what is the largest prefix of [mp] that belongs to [visible]? *)
+ let prefix = common_prefix_from_list mp (get_visible_mps ()) in
+ assert (k <> Mod || mp <> prefix); (* mp as whole module isn't in itself *)
+ let rls = list_skipn (mp_length prefix) rls in
+ let k',s,rls' = match rls with
| [] -> assert false
- | s::ls' ->
- (if base_mp mp <> top_visible_mp () then s ^ "." else "") ^
- (pseudo_qualify (upperkind k) (List.rev ls'))
+ | [s] -> k,s,[]
+ | s::rls' -> Mod,s,rls'
+ in
+ (* Reference r / module path mp is of the form [<prefix>.s.<rls'>].
+ Difficulty: in ocaml the prefix part cannot be used for
+ qualification (we are inside it) and the rest of the long
+ name may be hidden.
+ Solution: we duplicate the _definition_ of r / mp in a Coq__XXX module *)
+ if not (visible_clash prefix (k',s)) then dottify rls
+ else
+ let front = if rls' = [] && k <> Mod then [s] else rls' in
+ let lab = (* label associated with s *)
+ if prefix = mp then Option.get olab (* here k<>Mod, see assert above *)
+ else get_nth_label_mp (mp_length mp - mp_length prefix) mp
+ in
+ try dottify (check_duplicate prefix lab :: front)
+ with Not_found ->
+ assert (get_phase () = Pre); (* otherwise it's too late *)
+ add_duplicate prefix lab; dottify rls
+
+(* [pp_ocaml_extern] : [mp] isn't local, it is defined in another [MPfile]. *)
+
+let pp_ocaml_extern k base _ rls _ =
+ match rls with
+ | [] | [_] -> assert false
+ | base_s :: s :: rls' ->
+ let k' = if rls' = [] then k else Mod in
+ if modular () && (mpfiles_mem base) &&
+ (not (mpfiles_clash base (k',s))) &&
+ (not (visible_clash base (k',s)))
+ then (* Standard situation of an object in another file: *)
+ (* Thanks to the "open" of this file we remove its name *)
+ dottify (s::rls')
+ else match visible_clash_dbg base (Mod,base_s) with
+ | None -> dottify rls
+ | Some (mp,l) -> error_module_clash base (MPdot (mp,l))
+
+let pp_ocaml_gen k mp rls olab =
+ let base = base_mp mp in
+ let is_local = (base = base_mp (top_visible_mp ())) in
+ let pp = match base with
+ | MPdot _ -> assert false
+ | MPbound _ -> pp_ocaml_bound
+ | MPfile _ when is_local -> pp_ocaml_local
+ | MPfile _ -> pp_ocaml_extern
+ in pp k base mp rls olab
+
+let pp_haskell_gen k mp rls = match rls with
+ | [] -> assert false
+ | s::rls' ->
+ (if base_mp mp <> top_visible_mp () then s ^ "." else "") ^
+ (if upperkind k then "" else "_") ^ pseudo_qualify rls'
let pp_global k r =
let ls = ref_renaming (k,r) in
assert (List.length ls > 1);
let s = List.hd ls in
+ let l = label_of_r r in
let mp = modpath_of_r r in
if mp = top_visible_mp () then
(* simpliest situation: definition of r (or use in the same context) *)
(* we update the visible environment *)
- (add_visible (k,s); unquote s)
- else match lang () with
- | Scheme -> unquote s (* no modular Scheme extraction... *)
- | Haskell -> pp_haskell_gen k mp ls
- | Ocaml -> pp_ocaml_gen k mp ls (Some (label_of_r r))
+ (add_visible (k,s) l; unquote s)
+ else
+ let rls = List.rev ls in (* for what come next it's easier this way *)
+ match lang () with
+ | Scheme -> unquote s (* no modular Scheme extraction... *)
+ | Haskell -> if modular () then pp_haskell_gen k mp rls else s
+ | Ocaml -> pp_ocaml_gen k mp rls (Some (label_of_r r))
(* The next function is used only in Ocaml extraction...*)
let pp_module mp =
let ls = mp_renaming mp in
if List.length ls = 1 then dottify ls
else match mp with
- | MPdot (mp0,_) when mp0 = top_visible_mp () ->
+ | MPdot (mp0,l) when mp0 = top_visible_mp () ->
(* simpliest situation: definition of mp (or use in the same context) *)
(* we update the visible environment *)
let s = List.hd ls in
- add_visible (Mod,s); s
- | _ -> pp_ocaml_gen Mod mp ls None
+ add_visible (Mod,s) l; s
+ | _ -> pp_ocaml_gen Mod mp (List.rev ls) None