123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487(************************************************************************)(* * The Coq Proof Assistant / The Coq 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) *)(************************************************************************)(* Created from contents that was formerly in termops.ml and
nameops.ml, Nov 2009 *)(* This file is about generating new or fresh names and dealing with
alpha-renaming *)openUtilopenNamesopenTermopenConstropenContextopenEnvironopenEConstropenVarsopenNameopsopenLibnamesopenContext.Rel.DeclarationmoduleRelDecl=Context.Rel.Declaration(** General evar naming using intro patterns *)typeintro_pattern_naming_expr=|IntroIdentifierofId.t|IntroFreshofId.t|IntroAnonymousletintro_pattern_naming_eqnam1nam2=matchnam1,nam2with|IntroAnonymous,IntroAnonymous->true|IntroIdentifierid1,IntroIdentifierid2->Names.Id.equalid1id2|IntroFreshid1,IntroFreshid2->Names.Id.equalid1id2|_->false(**********************************************************************)(* Conventional names *)letdefault_prop_string="H"letdefault_prop_ident=Id.of_stringdefault_prop_stringletdefault_small_string="H"letdefault_small_ident=Id.of_stringdefault_small_stringletdefault_type_string="X"letdefault_type_ident=Id.of_stringdefault_type_stringletdefault_non_dependent_string="H"letdefault_non_dependent_ident=Id.of_stringdefault_non_dependent_stringletdefault_dependent_ident=Id.of_string"x"letdefault_generated_non_letter_string="x"(**********************************************************************)(* Globality of identifiers *)letis_imported_modpath=function|MPfiledp->letrecfind_prefix=function|MPfiledp1->not(DirPath.equaldp1dp)|MPdot(mp,_)->find_prefixmp|MPbound(_)->falseinfind_prefix(Lib.current_mp())|_->falseletis_imported_ref=letopenGlobRefinfunction|VarRef_->false|IndRef(kn,_)|ConstructRef((kn,_),_)->letmp=MutInd.modpathkninis_imported_modpathmp|ConstRefkn->letmp=Constant.modpathkninis_imported_modpathmpletlocateid=matchNametab.locate_extended_nowarn(qualid_of_identid)with|TrueGlobalr->r|Abbrev_->raiseNot_foundletis_globalid=tryletref=locateidinnot(is_imported_refref)withNot_found->falseletis_constructorid=trymatchlocateidwith|GlobRef.ConstructRef_->true|_->falsewithNot_found->falseletis_section_variableenvid=trylet_=Environ.lookup_namedidenvintruewithNot_found->false(**********************************************************************)(* Generating "intuitive" names from its type *)letglobal_of_constr=letopenGlobRefinfunction|Const(c,_)->ConstRefc|Ind(i,_)->IndRefi|Construct(c,_)->ConstructRefc|Varid->VarRefid|_->assertfalselethead_namesigmac=(* Find the head constant of a constr if any *)letrechdrecc=matchEConstr.kindsigmacwith|Prod(_,_,c)|Lambda(_,_,c)|LetIn(_,_,_,c)|Cast(c,_,_)|App(c,_)->hdrecc|Proj(kn,_,_)->Some(Label.to_id(Constant.label(Projection.constantkn)))|Const_|Ind_|Construct_|Var_asc->Some(Nametab.basename_of_global(global_of_constrc))|Fix((_,i),(lna,_,_))|CoFix(i,(lna,_,_))->Some(matchlna.(i).binder_namewithNameid->id|_->assertfalse)|Sort_|Rel_|Meta_|Evar_|Case_|Int_|Float_|String_|Array_->Noneinhdreccletlowercase_first_charid=(* First character of a constr *)lets=Id.to_stringidinmatchUnicode.split_at_first_letterswith|None->(* General case: nat -> n *)Unicode.lowercase_first_chars|Some(s,s')->ifString.lengths'=0then(* No letter, e.g. __, or __'_, etc. *)default_generated_non_letter_stringelses^Unicode.lowercase_first_chars'letsort_hdchar=function|SProp->"P"|Prop->"P"|Set->"S"|Type_|QSort_->"T"lethdcharenvsigmac=letrechdreckc=matchEConstr.kindsigmacwith|Prod(_,_,c)|Lambda(_,_,c)|LetIn(_,_,_,c)->hdrec(k+1)c|Cast(c,_,_)|App(c,_)->hdreckc|Proj(kn,_,_)->lowercase_first_char(Label.to_id(Constant.label(Projection.constantkn)))|Const(kn,_)->lowercase_first_char(Label.to_id(Constant.labelkn))|Ind(x,_)->(trylowercase_first_char(Nametab.basename_of_global(GlobRef.IndRefx))withNot_foundwhen!Flags.in_debugger->"zz")|Construct(x,_)->(trylowercase_first_char(Nametab.basename_of_global(GlobRef.ConstructRefx))withNot_foundwhen!Flags.in_debugger->"zz")|Varid->lowercase_first_charid|Sorts->sort_hdchar(ESorts.kindsigmas)|Reln->(ifn<=kthen"p"(* the initial term is flexible product/function *)elsetrymatchletd=lookup_rel(n-k)envinget_named,get_typedwith|Nameid,_->lowercase_first_charid|Anonymous,t->hdrec0(lift(n-k)t)withNot_found->"y")|Fix((_,i),(lna,_,_))|CoFix(i,(lna,_,_))->letid=matchlna.(i).binder_namewithNameid->id|_->assertfalseinlowercase_first_charid|Evar_(* We could do better... *)|Meta_|Case_->"y"|Int_->"i"|Float_->"f"|String_->"s"|Array_->"a"inhdrec0cletid_of_name_using_hdcharenvsigmaa=function|Anonymous->Id.of_string(hdcharenvsigmaa)|Nameid->idletnamed_hdenvsigmaa=function|Anonymous->Name(Id.of_string(hdcharenvsigmaa))|x->xletmkProd_nameenvsigma(n,a,b)=mkProd(map_annot(named_hdenvsigmaa)n,a,b)letmkLambda_nameenvsigma(n,a,b)=mkLambda(map_annot(named_hdenvsigmaa)n,a,b)letlambda_name=mkLambda_nameletprod_name=mkProd_nameletprod_createenvsigma(r,a,b)=mkProd(make_annot(named_hdenvsigmaaAnonymous)r,a,b)letlambda_createenvsigma(r,a,b)=mkLambda(make_annot(named_hdenvsigmaaAnonymous)r,a,b)letname_assumptionenvsigma=function|LocalAssum(na,t)->LocalAssum(map_annot(named_hdenvsigmat)na,t)|LocalDef(na,c,t)->LocalDef(map_annot(named_hdenvsigmac)na,c,t)letname_contextenvsigmahyps=snd(List.fold_left(fun(env,hyps)d->letd'=name_assumptionenvsigmadin(push_reld'env,d'::hyps))(env,[])(List.revhyps))letmkProd_or_LetIn_nameenvsigmabd=mkProd_or_LetIn(name_assumptionenvsigmad)bletmkLambda_or_LetIn_nameenvsigmabd=mkLambda_or_LetIn(name_assumptionenvsigmad)bletit_mkProd_or_LetIn_nameenvsigmabhyps=it_mkProd_or_LetInb(name_contextenvsigmahyps)letit_mkLambda_or_LetIn_nameenvsigmabhyps=it_mkLambda_or_LetInb(name_contextenvsigmahyps)(**********************************************************************)(* Fresh names *)(* Introduce a mode where auto-generated names are mangled
to test dependence of scripts on auto-generated names.
We also supply a version which only adds a prefix. *)let{Goptions.get=get_mangle_names}=Goptions.declare_bool_option_and_ref~key:["Mangle";"Names"]~value:false()let{Goptions.get=get_mangle_names_light}=Goptions.declare_bool_option_and_ref~key:["Mangle";"Names";"Light"]~value:false()let{Goptions.get=mangle_names_prefix}=Goptions.declare_interpreted_string_option_and_ref~key:["Mangle";"Names";"Prefix"]~value:("_")(funx->Id.to_string(tryId.of_stringxwith|CErrors.UserError_->CErrors.user_errPp.(str("Not a valid identifier: \""^x^"\"."))))(funx->x)()(** The name "foo" becomes "_0" if we get_mangle_names and "_foo" if
get_mangle_names_light is also set. Otherwise it is left alone. *)letmangle_idid=letprfx=mangle_names_prefix()inifget_mangle_names()thenifget_mangle_names_light()thenId.of_string(prfx^Id.to_stringid)elseId.of_string(prfx^"0")elseid(* Looks for next "good" name by lifting subscript *)letnext_ident_away_from_post_manglingidbad=letrecname_recid=ifbadidthenname_rec(increment_subscriptid)elseidinname_recidletnext_ident_away_fromidbad=letid=mangle_ididinnext_ident_away_from_post_manglingidbad(* Restart subscript from x0 if name starts with xN, or x00 if name
starts with x0N, etc *)letrestart_subscriptid=ifnot(has_subscriptid)thenidelse(* It would probably be better with something in the spirit of
*** make_ident id (Some 0) *** but compatibility would be lost... *)forget_subscriptidletvisible_idssigma(nenv,c)=letaccu=ref(GlobRef.Set_env.empty,Int.Set.empty,Id.Set.empty)inletrecvisible_idsnc=matchEConstr.kindsigmacwith|Const_|Ind_|Construct_|Var_asc->let(gseen,vseen,ids)=!accuinletg=global_of_constrcinifnot(GlobRef.Set_env.memggseen)thenletgseen=GlobRef.Set_env.addggseeninletids=matchNametab.shortest_qualid_of_globalId.Set.emptygwith|short->letdir,id=repr_qualidshortinifDirPath.is_emptydirthenId.Set.addididselseids|exceptionNot_found->(* This may happen if given pathological terms or when manipulating
open modules *)idsinaccu:=(gseen,vseen,ids)|Relp->let(gseen,vseen,ids)=!accuinifp>n&¬(Int.Set.mem(p-n)vseen)thenletvseen=Int.Set.add(p-n)vseeninletname=trySome(List.nthnenv(p-n-1))withInvalid_argument_|Failure_->(* Unbound index: may happen in debug and actually also
while computing temporary implicit arguments of an
inductive type *)Noneinletids=matchnamewith|Some(Nameid)->Id.Set.addidids|_->idsinaccu:=(gseen,vseen,ids)|Evar(_,argsasev)->(* Useful for at least debugger: do the same as in iter_with_binders *)(* except that Not_found is not fatal *)beginmatchEvd.expand_existentialsigmaevwith|args->List.iter(visible_idsn)args|exceptionNot_foundwhen!Flags.in_debugger->SList.Skip.iter(visible_idsn)argsend|_->EConstr.iter_with_binderssigmasuccvisible_idsncinlet()=visible_ids1cin(* n = 1 to count the binder to rename *)let(_,_,ids)=!accuinids(* Now, there are different renaming strategies... *)(* 1- Looks for a fresh name for printing in cases pattern *)letnext_name_away_in_cases_patternsigmaenv_tnaavoid=letid=matchnawithNameid->id|Anonymous->default_dependent_identinletvisible=visible_idssigmaenv_tinletbadid=Id.Set.memidavoid||is_constructorid||Id.Set.memidvisibleinnext_ident_away_fromidbad(* 2- Looks for a fresh name for introduction in goal *)(* The legacy strategy for renaming introduction variables is not very uniform:
- if the name to use is fresh in the context but used as a global
name, then a fresh name is taken by finding a free subscript
starting from the current subscript;
- but if the name to use is not fresh in the current context, the fresh
name is taken by finding a free subscript starting from 0 *)letnext_ident_away_in_goalenvidavoid=letid=ifId.Set.memidavoidthenrestart_subscriptidelseidinletbadid=Id.Set.memidavoid||(is_globalid&¬(is_section_variableenvid))innext_ident_away_fromidbadletnext_name_away_in_goalenvnaavoid=letid=matchnawith|Nameid->id|Anonymous->default_non_dependent_identinnext_ident_away_in_goalenvidavoid(* 3- Looks for next fresh name outside a list that is moreover valid
as a global identifier; the legacy algorithm is that if the name is
already used in the list, one looks for a name of same base with
lower available subscript; if the name is not in the list but is
used globally, one looks for a name of same base with lower subscript
beyond the current subscript *)letnext_global_ident_awayidavoid=letid=ifId.Set.memidavoidthenrestart_subscriptidelseidinletbadid=Id.Set.memidavoid||Global.exists_objlabel(Label.of_idid)innext_ident_away_fromidbad(* 4- Looks for next fresh name outside a list; if name already used,
looks for same name with lower available subscript *)letnext_ident_awayidavoid=letid=mangle_ididinifId.Set.memidavoidthennext_ident_away_from_post_mangling(restart_subscriptid)(funid->Id.Set.memidavoid)elseidletnext_name_away_with_defaultdefaultnaavoid=letid=matchnawithNameid->id|Anonymous->Id.of_stringdefaultinnext_ident_awayidavoidletreserved_type_name=ref(funt->Anonymous)letset_reserved_typed_namef=reserved_type_name:=fletnext_name_away_with_default_using_typesdefaultnaavoidt=letid=matchnawith|Nameid->id|Anonymous->match!reserved_type_nametwith|Nameid->id|Anonymous->Id.of_stringdefaultinnext_ident_awayidavoidletnext_name_away=next_name_away_with_defaultdefault_non_dependent_stringletmake_all_rel_context_name_differentenvsigmactx=letavoid=ref(Id.Set.union(Context.Rel.to_vars(Environ.rel_contextenv))(ids_of_named_context_val(named_context_valenv)))inContext.Rel.fold_outside(fundecl(newenv,ctx)->letna=named_hdnewenvsigma(RelDecl.get_typedecl)(RelDecl.get_namedecl)inletid=next_name_awayna!avoidinavoid:=Id.Set.addid!avoid;letdecl=RelDecl.set_name(Nameid)declinpush_reldeclnewenv,decl::ctx)ctx~init:(env,[])letmake_all_name_differentenvsigma=(* FIXME: this is inefficient, but only used in printing *)letsign=named_context_valenvinletrels=rel_contextenvinletenv0=reset_with_named_contextsignenvinletenv,_=make_all_rel_context_name_differentenv0sigmarelsinenv(* 5- Looks for next fresh name outside a list; avoids also to use names that
would clash with short name of global references; if name is already used,
looks for name of same base with lower available subscript beyond current
subscript *)letnext_ident_away_for_default_printingsigmaenv_tidavoid=letvisible=visible_idssigmaenv_tinletbadid=Id.Set.memidavoid||Id.Set.memidvisibleinnext_ident_away_fromidbadletnext_name_away_for_default_printingsigmaenv_tnaavoid=letid=matchnawith|Nameid->id|Anonymous->(* In principle, an anonymous name is not dependent and will not be *)(* taken into account by the function compute_displayed_name_in; *)(* just in case, invent a valid name *)default_non_dependent_identinnext_ident_away_for_default_printingsigmaenv_tidavoid(**********************************************************************)(* Displaying terms avoiding bound variables clashes *)(* Renaming strategy introduced in December 1998:
- Rule number 1: all names, even if unbound and not displayed, contribute
to the list of names to avoid
- Rule number 2: only the dependency status is used for deciding if
a name is displayed or not
Example:
bool_ind: "forall (P:bool->Prop)(f:(P true))(f:(P false))(b:bool), P b" is
displayed "forall P:bool->Prop, P true -> P false -> forall b:bool, P b"
but f and f0 contribute to the list of variables to avoid (knowing
that f and f0 are how the f's would be named if introduced, assuming
no other f and f0 are already used).
*)typerenaming_flags=(* The term is the body of a binder and the environment excludes this binder *)(* so, there is a missing binder in the environment *)|RenamingForCasesPatternof(Name.tlist*constr)|RenamingForGoal|RenamingElsewhereForof(Name.tlist*constr)letnext_name_for_displayenvsigmaflags=matchflagswith|RenamingForCasesPatternenv_t->next_name_away_in_cases_patternsigmaenv_t|RenamingForGoal->next_name_away_in_goalenv|RenamingElsewhereForenv_t->next_name_away_for_default_printingsigmaenv_t(* Remark: Anonymous var may be dependent in Evar's contexts *)letcompute_displayed_name_in_gen_polynoccurn_funenvsigmaflagsavoidnac=ifnoccurn_funsigma1cthenAnonymous,avoidelseletfresh_id=next_name_for_displayenvsigmaflagsnaavoidinNamefresh_id,Id.Set.addfresh_idavoidletcompute_displayed_name_in=compute_displayed_name_in_gen_polynoccurnletcompute_displayed_name_in_genfenvsigma=(* only flag which does not need a constr, maybe to be refined *)letflag=RenamingForGoalincompute_displayed_name_in_gen_polyfenvsigmaflagletcompute_displayed_let_name_inenvsigmaflagsavoidna=letfresh_id=next_name_for_displayenvsigmaflagsnaavoidin(Namefresh_id,Id.Set.addfresh_idavoid)