aboutsummaryrefslogtreecommitdiff
path: root/engine/univNames.ml
blob: e89dcedb9cd04e1f061cde9b4995253cc79e1212 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
(************************************************************************)
(*         *   The Coq Proof Assistant / The Coq Development Team       *)
(*  v      *   INRIA, CNRS and contributors - Copyright 1999-2018       *)
(* <O___,, *       (see CREDITS file for the list of authors)           *)
(*   \VV/  **************************************************************)
(*    //   *    This file is distributed under the terms of the         *)
(*         *     GNU Lesser General Public License Version 2.1          *)
(*         *     (see LICENSE file for the text of the license)         *)
(************************************************************************)

open Util
open Names
open Univ


let qualid_of_level l =
  match Level.name l with
  | Some (d, n as na)  ->
    begin
    try Nametab.shortest_qualid_of_universe na
    with Not_found ->
      let name = Id.of_string_soft (string_of_int n) in
      Libnames.make_qualid d name
    end
  | None ->
    Libnames.qualid_of_ident @@ Id.of_string_soft (Level.to_string l)

let pr_with_global_universes l = Libnames.pr_qualid (qualid_of_level l)

(** Global universe information outside the kernel, to handle
    polymorphic universe names in sections that have to be discharged. *)

(** Local universe names of polymorphic references *)

type universe_binders = Univ.Level.t Names.Id.Map.t

let empty_binders = Id.Map.empty

let universe_binders_table = Summary.ref GlobRef.Map.empty ~name:"universe binders"

let universe_binders_of_global ref : Id.t list =
  try
    let l = GlobRef.Map.find ref !universe_binders_table in l
  with Not_found -> []

let cache_ubinder (_,(ref,l)) =
  universe_binders_table := GlobRef.Map.add ref l !universe_binders_table

let subst_ubinder (subst,(ref,l as orig)) =
  let ref' = fst (Globnames.subst_global subst ref) in
  if ref == ref' then orig else ref', l

let name_universe lvl =
  (** Best-effort naming from the string representation of the level. This is
      completely hackish and should be solved in upper layers instead. *)
  Id.of_string_soft (Level.to_string lvl)

let discharge_ubinder (_,(ref,l)) =
  (** Expand polymorphic binders with the section context *)
  let info = Lib.section_segment_of_reference ref in
  let sec_inst = Array.to_list (Instance.to_array (info.Lib.abstr_subst)) in
  let map lvl = match Level.name lvl with
    | None -> (* Having Prop/Set/Var as section universes makes no sense *)
      assert false
    | Some na ->
      try
        let qid = Nametab.shortest_qualid_of_universe na in
        snd (Libnames.repr_qualid qid)
      with Not_found -> name_universe lvl
  in
  let l = List.map map sec_inst @ l in
  Some (ref, l)

let ubinder_obj : GlobRef.t * Id.t list -> Libobject.obj =
  let open Libobject in
  declare_object { (default_object "universe binder") with
    cache_function = cache_ubinder;
    load_function = (fun _ x -> cache_ubinder x);
    classify_function = (fun x -> Substitute x);
    subst_function = subst_ubinder;
    discharge_function = discharge_ubinder;
    rebuild_function = (fun x -> x); }

let register_universe_binders ref ubinders =
  (** TODO: change the API to register a [Name.t list] instead. This is the last
      part of the code that depends on the internal representation of names in
      abstract contexts, but removing it requires quite a rework of the
      callers. *)
  let univs = AUContext.instance (Global.universes_of_global ref) in
  let revmap = Id.Map.fold (fun id lvl accu -> LMap.add lvl id accu) ubinders LMap.empty in
  let map lvl =
    try LMap.find lvl revmap
    with Not_found -> name_universe lvl
  in
  let ubinders = Array.map_to_list map (Instance.to_array univs) in
  if not (List.is_empty ubinders) then Lib.add_anonymous_leaf (ubinder_obj (ref, ubinders))

type univ_name_list = Names.lname list

let universe_binders_with_opt_names ref names =
  let orig = universe_binders_of_global ref in
  let udecl = match names with
  | None -> orig
  | Some udecl ->
    try
      List.map2 (fun orig {CAst.v = na} ->
          match na with
          | Anonymous -> orig
          | Name id -> id) orig udecl
    with Invalid_argument _ ->
      let len = List.length orig in
      CErrors.user_err ~hdr:"universe_binders_with_opt_names"
        Pp.(str "Universe instance should have length " ++ int len)
  in
  let fold i acc na = Names.Id.Map.add na (Level.var i) acc in
  List.fold_left_i fold 0 empty_binders udecl