123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406(************************************************************************)(* * The Rocq Prover / The Rocq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \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) *)(************************************************************************)openNamesopenNameops(** This file contains methods for manipulating qualified evar names. *)moduleEvSet=Evar.SetmoduleEvMap=Evar.Map(** If true, this option enables automatic generation of goal names. *)let{Goptions.get=generate_goal_names}=Goptions.declare_bool_option_and_ref~key:["Generate";"Goal";"Names"]~value:false()(** Internal representation of qualified evar names.
Example: "x.y.z" is represented as [{ basename: "z"; path: ["y"; "x"] }] *)moduleEvarQualid:sigtypet={basename:Id.t;path:Id.tlist}valmake:Libnames.qualid->tvalrepr:t->Libnames.full_pathend=structtypet={basename:Id.t;path:Id.tlist}letmakepath=let(dp,id)=Libnames.repr_qualidpathin{basename=id;path=DirPath.reprdp}letrepr{basename;path}=Libnames.make_path(DirPath.makepath)basenameend(** Module for evar name resolution, using a reversed trie.
Example: given evars "true.A" (?X1), "true.A" (?X2) and "false.A" (?X3), we
have the following trie:
[
{
A -> {
true (?X1, ?X2),
false (?X3)
}
}
]
In this representation, determining whether a qualified name is unambiguous
amounts to checking whether the node has a single value. For example, "A"
does not resolve to an evar, "true.A" is ambiguous (?X1, ?X2), while
"false.A" (?X3) is unambiguous.
*)moduleNameResolution:sigtypet(** Returns an empty trie. *)valempty:t(** Adds a new binding for the evar at the shortest unambiguous suffix of the
given qualified name, if possible. If there is no such suffix, creates an
ambiguous node. *)valadd:EvarQualid.t->Evar.t->t->t(** Transfers the qualified name of the first evar to the second evar. *)valtransfer:EvarQualid.t->Evar.t->Evar.t->t->t(** Removes the qualified name of the given evar from name resolution. *)valremove:EvarQualid.t->Evar.t->t->t(** Returns the shortest unambiguous name of the given qualified name.
Raises [Not_found] if the evar is not present at a suffix of the qualified
name. *)valshortest_name:EvarQualid.t->Evar.t->t->EvarQualid.t(** Returns the list of bindings for the given qualified name. *)valfind:EvarQualid.t->t->Evar.Set.t(** Returns true if there exists a binding that has the given basename. *)valmem_basename:Id.t->t->boolend=struct(** Represents a trie node. For code deduplication reasons, the root is also a node
with an empty value. *)typet={value:Evar.Set.t;children:tId.Map.t}openEvarQualidletempty={value=Evar.Set.empty;children=Id.Map.empty}letis_empty{value;children}=Evar.Set.is_emptyvalue&&Id.Map.is_emptychildrenletrecaddpathevnode=matchpathwith|segment::rest->letupdate=function|Somechild->Some(addrestevchild)|None->Some{value=Evar.Set.singletonev;children=Id.Map.empty}in{nodewithchildren=Id.Map.updatesegmentupdatenode.children}|[]->{nodewithvalue=Evar.Set.addevnode.value}letadd{basename;path}evtrie=add(basename::path)evtrieletrectransferpathevev'node=matchpathwith|segment::rest->{nodewithchildren=Id.Map.modifysegment(fun_child->transferrestevev'child)node.children}|[]->{nodewithvalue=Evar.Set.addev'(Evar.Set.removeevnode.value)}lettransfer{basename;path}evev'trie=transfer(basename::path)evev'trielet[@tail_mod_cons]recshortest_namepathevnode=ifEvar.Set.memevnode.valuethen[]elsematchpathwith|segment::rest->segment::shortest_namerestev(Id.Map.findsegmentnode.children)|[]->raiseNot_foundletshortest_name{basename;path}evtrie=matchshortest_name(basename::path)evtriewith|basename::path->{basename;path}|[]->assertfalseletrecfindpathnode=matchpathwith|segment::rest->beginmatchId.Map.find_optsegmentnode.childrenwith|Somesegment->findrestsegment|None->Evar.Set.emptyend|[]->node.valueletfind{basename;path}trie=find(basename::path)trieletrecremovepathevnode=matchpathwith|segment::rest->letupdate_child=function|Somechild->removerestevchild|None->Noneinletnode={nodewithchildren=Id.Map.updatesegmentupdate_childnode.children}in(* Prune empty nodes. *)ifis_emptynodethenNoneelseSomenode|[]->letnode={nodewithvalue=Evar.Set.removeevnode.value}inifis_emptynodethenNoneelseSomenodeletremove{basename;path}evtrie=matchremove(basename::path)evtriewith|Sometrie->trie|None->emptyletmem_basenamebasenametrie=Id.Map.membasenametrie.childrenendtypet={basename_map:Id.tEvMap.t;(** Map from evar to basename. *)name_resolution:NameResolution.t;(** Trie for resolving qualified names to evars. *)fresh_gen:Fresh.t;(** Fresh basename generator (to support [refine ?[?A]]) *)parent_map:Evar.tEvMap.t;(** Map from evar to its parent, if any. *)children_map:EvSet.tEvMap.t;(** Map from an evar to its children that are pending. Essentially the
reverse of [parent_map]. *)removed_evars:EvSet.t;(** Set of evars marked for removal, and thus unfocusable, whose names are still used as
the parent of an open goal. *)}letempty={basename_map=EvMap.empty;name_resolution=NameResolution.empty;fresh_gen=Fresh.empty;parent_map=EvMap.empty;children_map=EvMap.empty;removed_evars=EvSet.empty}(** Returns the absolute path of [ev], obtained by following the [parent_map]. *)let[@tail_mod_cons]recpathevevn=matchEvMap.find_optevevn.parent_mapwith|Someparent->beginmatchEvMap.find_optparentevn.basename_mapwith|Someparent_name->parent_name::pathparentevn|None->[]end|None->[](** Return the absolute qualified name of [ev]. *)letabsolute_nameevevn=matchEvMap.find_optevevn.basename_mapwith|Somebasename->SomeEvarQualid.{basename;path=pathevevn}|None->None(** Returns the shortest name that resolves to [ev], or [None] if [ev] does not
resolve to a name. *)letshortest_nameevevn=matchabsolute_nameevevnwith|Somename->Some(NameResolution.shortest_namenameevevn.name_resolution)|None->None(* Returns the set of focusable evars that have the given qualid as name. *)letget_matching_evarsqualidevn=letevs=NameResolution.findqualidevn.name_resolutionin(* Do not consider removed evars as conflicts for name resolution purposes *)Evar.Set.diffevsevn.removed_evarsletregister_parentevparentevn=letadd_child=function|Somechildren->Some(EvSet.addevchildren)|None->Some(EvSet.singletonev)in{evnwithparent_map=EvMap.addevparentevn.parent_map;children_map=EvMap.updateparentadd_childevn.children_map}letaddbasenameev?parentevn=letevn=matchparentwith|Someparent->register_parentevparentevn|None->evninletqualid=EvarQualid.{basename;path=pathevevn}in{evnwithbasename_map=EvMap.addevbasenameevn.basename_map;name_resolution=NameResolution.addqualidevevn.name_resolution;fresh_gen=Fresh.addbasenameevn.fresh_gen}letadd_freshbasenameev?parentevn=letevn=matchparentwith|Someparent->register_parentevparentevn|None->evninletqualid=EvarQualid.{basename;path=pathevevn}inletconflicts=get_matching_evarsqualidevninifEvar.Set.is_emptyconflictsthen(* No need to give the parent since it's already registered *)addbasenameevevnelse(* Generate a fresh basename and try again. *)letbasename,fresh_gen=Fresh.freshbasenameevn.fresh_geninaddbasenameev{evnwithfresh_gen}letrecremoveevevn=matchEvMap.find_optevevn.basename_mapwith|None->evn|Somebasename->(* When defining an evar and making its name unresolvable, there are two scenarios:
- The evar has no remaining children, in which case we can safely remove
it from all maps since it is not used for name resolution. We also try
to remove recursively its parent, since solving the evar might have
closed the parent.
- The evar has some children which might rely on the parent's name for
name resolution. In that case, we simply add it to [removed_evars] (so
that [name_of ev] fails), and removal will occur when all children will
be solved. *)letchildren=matchEvMap.find_optevevn.children_mapwith|Somechildren->children|None->EvSet.emptyinifEvSet.is_emptychildrenthenletparent=EvMap.find_optevevn.parent_mapinletname_resolution=matchshortest_nameevevnwith|Somename->NameResolution.removenameevevn.name_resolution|None->assertfalseinletevn={basename_map=EvMap.removeevevn.basename_map;name_resolution;fresh_gen=(* If the basename still exists in the new trie, do not remove. *)ifNameResolution.mem_basenamebasenamename_resolutionthenevn.fresh_genelseFresh.removebasenameevn.fresh_gen;children_map=EvMap.removeevevn.children_map;parent_map=EvMap.removeevevn.parent_map;removed_evars=EvSet.removeevevn.removed_evars;}in(* If there is a parent, try to remove it recursively as well. *)matchparentwith|Someparent->letevn=removeparentevnin(* Rollback if removal failed. *){evnwithremoved_evars=EvSet.removeevevn.removed_evars}|None->evnelse(* Mark [ev] as deleted. *){evnwithremoved_evars=EvSet.addevevn.removed_evars}lettransfer_nameevev'evn=(* We assume that [ev] is an open goal, hence undefined and
has no children. *)(* Transfer the name. *)letbasename_map,name_resolution=matchshortest_nameevevnwith|Somename->letbasename_map=EvMap.addev'name.basename(EvMap.removeevevn.basename_map)inletname_resolution=NameResolution.transfernameevev'evn.name_resolutioninbasename_map,name_resolution|None->(* [ev] has no name. *)evn.basename_map,evn.name_resolutionin(* If [ev] has a parent, we update the parent's children. *)letparent_map,children_map=matchEvMap.find_optevevn.parent_mapwith|Someparent->letparent_map=EvMap.addev'parent(EvMap.removeevevn.parent_map)inletchildren_map=EvMap.modifyparent(fun_children->EvSet.addev'(EvSet.removeevchildren))evn.children_mapinparent_map,children_map|None->evn.parent_map,evn.children_mapin{evnwithbasename_map;name_resolution;parent_map;children_map}typeset_kind=|SetEmpty|SetSingletonofEvar.t|SetOtherletclassify_sets=ifEvar.Set.is_emptysthenSetEmptyelseletevk=Evar.Set.choosesinlets=Evar.Set.removeevksinifEvar.Set.is_emptysthenSetSingletonevkelseSetOtherletname_ofevevn=matchshortest_nameevevnwith|None->None|Somename->letconflicts=get_matching_evarsnameevnin(* TODO: we should the caller handle the conflict themselves instead of
generating nonsensical names in linear time. *)matchclassify_setconflictswith|SetEmpty|SetSingleton_->Some(EvarQualid.reprname)|SetOther->letnconflicts=Evar.Set.cardinalconflictsin(* If the qualified name is ambiguous, we append a suffix corresponding to the index in the list. *)let{EvarQualid.basename;path}=nameinleti=nconflicts-CList.indexEvar.equalev(Evar.Set.elementsconflicts)-1inletbasename=ifInt.equali(-1)thenbasenameelseId.of_string((Id.to_stringname.basename)^(string_of_inti))inSome(Libnames.make_path(DirPath.makepath)basename)lethas_nameevevn=not(EvSet.memevevn.removed_evars)&&EvMap.memevevn.basename_maplethas_unambiguous_nameevevn=matchshortest_nameevevnwith|None->false|Somename->letmatches=get_matching_evarsnameevninmatchclassify_setmatcheswith|SetEmpty|SetOther->false|SetSingletone->Evar.equaleevletresolvefpevn=letqualid=EvarQualid.makefpinletevs=get_matching_evarsqualidevninletopenPpinmatchclassify_setevswith|SetEmpty->raiseNot_found|SetSingletonev->ev|SetOther->CErrors.user_err?loc:fp.loc(str"Ambiguous evar name "++Libnames.pr_qualidfp++str".")