123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208(************************************************************************)(* * 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) *)(************************************************************************)openConstrinternopenPatternopsopenPpopenCAstopenNamegenopenGenredexpropenGlob_termopenGlob_opsopenTacredopenCErrorsopenUtilopenNamesopenNameopsopenLibnamesopenTacmachopenTactic_debugopenConstrexpropenTermopsopenTacexpropenGenargopenGeninterpopenStdargopenTacargopenPrinteropenPretypingopenTactypesopenTacticsopenLocusopenTacinternopenTaccoerceopenProofview.NotationsopenContext.Named.DeclarationopenLtac_pretypeletltac_trace_info=Tactic_debug.ltac_trace_infolethas_type:typea.Val.t->atyped_abstract_argument_type->bool=funvwit->letVal.Dyn(t,_)=vinlett'=matchval_tagwitwith|Val.Baset'->t'|_->assertfalse(* not used in this module *)inmatchVal.eqtt'with|None->false|SomeRefl->trueletprj:typea.aVal.typ->Val.t->aoption=funtv->letVal.Dyn(t',x)=vinmatchVal.eqtt'with|None->None|SomeRefl->Somexletin_listtagv=lettag=matchtagwithVal.Basetag->tag|_->assertfalseinVal.Dyn(Val.typ_list,List.map(funx->Val.Dyn(tag,x))v)letin_genwitv=lett=matchval_tagwitwith|Val.Baset->t|_->assertfalse(* not used in this module *)inVal.Dyn(t,v)letout_genwitv=lett=matchval_tagwitwith|Val.Baset->t|_->assertfalse(* not used in this module *)inmatchprjtvwithNone->assertfalse|Somex->xletval_tagwit=val_tag(topwitwit)letpr_argument_typearg=letVal.Dyn(tag,_)=arginVal.prtagtypevalue=Val.tletpush_applapplargs=matchapplwith|UnnamedAppl->UnnamedAppl|GlbAppll->GlbAppl(List.map(fun(h,vs)->(h,vs@args))l)letpr_genericarg=letVal.Dyn(tag,_)=arginstr"<"++Val.prtag++str":("++Pptactic.pr_valuePptactic.ltoparg++str")>"letpr_applhvs=Pptactic.pr_ltac_constanth++spc()++Pp.prlist_with_sepspcpr_genericvsletrecname_with_listapplt=matchapplwith|[]->t|(h,vs)::l->Proofview.Trace.name_tactic(fun()->pr_applhvs)(name_with_listlt)letname_if_globapplt=matchapplwith|UnnamedAppl->t|GlbAppll->name_with_listltletcombine_applappl1appl2=matchappl1,appl2with|UnnamedAppl,a|a,UnnamedAppl->a|GlbAppll1,GlbAppll2->GlbAppl(l2@l1)letof_tacvaluev=in_gen(topwitwit_tacvalue)vletto_tacvaluev=out_gen(topwitwit_tacvalue)v(* Debug reference *)letdebug=refDebugOff(* Sets the debugger on or off *)letset_debugpos=debug:=pos(* Gives the state of debug; disabled in worker processes *)letget_debug()=ifFlags.async_proofs_is_worker()thenDebugOffelse!debugletlog_trace=reffalseletis_traced()=!log_trace||!debug<>DebugOff||!Flags.profile_ltac(** More naming applications *)letname_vfunapplvle=ifis_traced()&&has_typevle(topwitwit_tacvalue)thenmatchto_tacvaluevlewith|VFun(appl0,trace,loc,lfun,vars,t)->of_tacvalue(VFun(combine_applappl0appl,trace,loc,lfun,vars,t))|_->vleelsevlemoduleTacStore=Geninterp.TacStoreletf_avoid_ids:Id.Set.tTacStore.field=TacStore.field()(* ids inherited from the call context (needed to get fresh ids) *)letf_debug:debug_infoTacStore.field=TacStore.field()letf_trace:ltac_traceTacStore.field=TacStore.field()letf_loc:Loc.tTacStore.field=TacStore.field()(* Signature for interpretation: val_interp and interpretation functions *)typeinterp_sign=Geninterp.interp_sign={lfun:valueId.Map.t;poly:bool;extra:TacStore.t}letadd_extra_tracetraceextra=TacStore.setextraf_tracetraceletextract_traceist=ifis_traced()thenmatchTacStore.getist.extraf_tracewith|None->[],[]|Sometrace->traceelse[],[]letadd_extra_loclocextra=matchlocwith|None->extra|Someloc->TacStore.setextraf_loclocletextract_locist=TacStore.getist.extraf_locletensure_loclocist=matchlocwith|None->ist|Someloc->matchextract_locistwith|None->{istwithextra=TacStore.setist.extraf_locloc}|Some_->istletprint_top_valenvv=Pptactic.pr_valuePptactic.ltopvletcatching_errorcall_tracefail(e,info)=letinner_trace=Option.default[](Exninfo.getinfoltac_trace_info)inifList.is_emptycall_trace&&List.is_emptyinner_tracethenfail(e,info)elsebeginassert(CErrors.noncriticale);(* preserved invariant *)letinner_trace=List.filter(funi->not(List.memqicall_trace))inner_traceinletnew_trace=inner_trace@call_traceinletlocated_exc=(e,Exninfo.addinfoltac_trace_infonew_trace)infaillocated_excendletupdate_locloc(e,infoase')=leteloc=Loc.get_locinfoinifLoc.finereloc(Someloc)thene'else(* eloc missing or refers to inside of Ltac function *)(e,Loc.add_locinfoloc)letcatch_error_with_trace_locloccall_tracefx=tryfxwithewhenCErrors.noncriticale->lete=Exninfo.captureeinlete=Option.cata(funloc->update_locloce)elocincatching_errorcall_traceExninfo.iraiseeletcatch_error_locloctac=matchlocwith|None->tac|Someloc->Proofview.tclORELSEtac(funexn->let(e,info)=update_loclocexninProofview.tclZERO~infoe)letwrap_errortack=ifis_traced()thenProofview.tclORELSEtackelsetacletwrap_error_locloctack=ifis_traced()thenletk=matchlocwith|None->k|Someloc->fune->k(update_locloce)inProofview.tclORELSEtackelsecatch_error_locloctacletcatch_error_taccall_tracetac=wrap_errortac(catching_errorcall_trace(fun(e,info)->Proofview.tclZERO~infoe))letcatch_error_tac_locloccall_tracetac=wrap_error_locloctac(catching_errorcall_trace(fun(e,info)->Proofview.tclZERO~infoe))letcurr_debugist=matchTacStore.getist.extraf_debugwith|None->DebugOff|Somelevel->levelletpr_closureenvistbody=letpp_body=Pptactic.pr_glob_tacticenvbodyinletpr_sep()=fnl()inletpr_iarg(id,arg)=letarg=pr_argument_typearginhov0(Id.printid++spc()++str":"++spc()++arg)inletpp_iargs=v0(prlist_with_seppr_seppr_iarg(Id.Map.bindingsist))inpp_body++fnl()++str"in environment "++fnl()++pp_iargsletpr_inspectenvexprresult=letpp_expr=Pptactic.pr_glob_tacticenvexprinletpp_result=ifhas_typeresult(topwitwit_tacvalue)thenmatchto_tacvalueresultwith|VFun(_,_,_,ist,ul,b)->letbody=ifList.is_emptyulthenbelseCAst.make(TacFun(ul,b))instr"a closure with body "++fnl()++pr_closureenvistbody|VRec(ist,body)->str"a recursive closure"++fnl()++pr_closureenv!istbodyelseletpp_type=pr_argument_typeresultinstr"an object of type"++spc()++pp_typeinpp_expr++fnl()++str"this is "++pp_result(* Transforms an id into a constr if possible, or fails with Not_found *)letconstr_of_idenvid=EConstr.mkVar(let_=Environ.lookup_namedidenvinid)(** Generic arguments : table of interpretation functions *)letpush_tracecallist=ifis_traced()thenmatchTacStore.getist.extraf_tracewith|None->[call],[ist.lfun]|Some(trace,varmaps)->call::trace,ist.lfun::varmapselse[],[]letpropagate_traceistlocidv=ifhas_typev(topwitwit_tacvalue)thenlettacv=to_tacvaluevinmatchtacvwith|VFun(appl,_,_,lfun,it,b)->letkn=matchapplwith|GlbAppl((kn,_)::_)->Somekn|_->Noneinlett=ifList.is_emptyitthenbelseCAst.make(TacFun(it,b))inlettrace=push_trace(loc,LtacVarCall(kn,id,t))istinletans=VFun(appl,trace,loc,lfun,it,b)inProofview.tclUNIT(of_tacvalueans)|_->Proofview.tclUNITvelseProofview.tclUNITvletappend_tracetracev=ifhas_typev(topwitwit_tacvalue)thenmatchto_tacvaluevwith|VFun(appl,trace',loc,lfun,it,b)->of_tacvalue(VFun(appl,trace',loc,lfun,it,b))|_->velsev(* Dynamically check that an argument is a tactic *)letcoerce_to_tacticlocidv=letfail()=user_err?loc(str"Variable "++Id.printid++str" should be bound to a tactic.")inifhas_typev(topwitwit_tacvalue)thenlettacv=to_tacvaluevinmatchtacvwith|VFun(appl,trace,_,lfun,it,b)->of_tacvalue(VFun(appl,trace,loc,lfun,it,b))|_->fail()elsefail()letintro_pattern_of_identid=CAst.make@@IntroNaming(IntroIdentifierid)letvalue_of_identid=in_gen(topwitwit_intro_pattern)(intro_pattern_of_identid)let(+++)lfun1lfun2=Id.Map.foldId.Map.addlfun1lfun2letextend_values_with_bindings(ln,lm)lfun=letof_cubc=matchcwith|[],c->Value.of_constrc|_->in_gen(topwitwit_constr_under_binders)cin(* For compatibility, bound variables are visible only if no other
binding of the same name exists *)letaccu=Id.Map.mapvalue_of_identlninletaccu=lfun+++accuinId.Map.fold(funidcaccu->Id.Map.addid(of_cubc)accu)lmaccu(***************************************************************************)(* Evaluation/interpretation *)letis_variableenvid=Id.List.memid(ids_of_named_context(Environ.named_contextenv))letdebugging_stepistpp=matchcurr_debugistwith|DebugOnlev->Tactic_debug.defer_output(fun_->(str"Level "++intlev++str": "++pp()++fnl()))|_->Proofview.NonLogical.return()letdebugging_exception_stepistsignal_anomalyepp=letexplain_exc=ifsignal_anomalythenexplain_logic_errorelseexplain_logic_error_no_anomalyindebugging_stepist(fun()->pp()++spc()++str"raised the exception"++fnl()++explain_exce)letensure_freshnessenv=(* We anonymize declarations which we know will not be used *)(* This assumes that the original context had no rels *)process_rel_context(funde->EConstr.push_rel(Context.Rel.Declaration.set_nameAnonymousd)e)env(* Raise Not_found if not in interpretation sign *)lettry_interp_ltac_varcoerceistenv{loc;v=id}=letv=Id.Map.findidist.lfunintrycoercevwithCannotCoerceTos->Taccoerce.error_ltac_variable?locidenvvsletinterp_ltac_varcoerceistenvlocid=trytry_interp_ltac_varcoerceistenvlocidwithNot_found->anomaly(str"Detected '"++Id.printlocid.v++str"' as ltac var at interning time.")letinterp_identistenvsigmaid=trytry_interp_ltac_var(coerce_var_to_identfalseenvsigma)ist(Some(env,sigma))(CAst.makeid)withNot_found->id(* Interprets an optional identifier, bound or fresh *)letinterp_nameistenvsigma=function|Anonymous->Anonymous|Nameid->Name(interp_identistenvsigmaid)letinterp_intro_pattern_varlocistenvsigmaid=trytry_interp_ltac_var(coerce_to_intro_patternsigma)ist(Some(env,sigma))(CAst.make?locid)withNot_found->IntroNaming(IntroIdentifierid)letinterp_intro_pattern_naming_varlocistenvsigmaid=trytry_interp_ltac_var(coerce_to_intro_pattern_namingsigma)ist(Some(env,sigma))(CAst.make?locid)withNot_found->IntroIdentifieridletinterp_intist({loc;v=id}aslocid)=trytry_interp_ltac_varcoerce_to_intistNonelocidwithNot_found->user_err?loc(str"Unbound variable "++Id.printid++str".")letinterp_int_or_varist=function|ArgVarlocid->interp_intistlocid|ArgArgn->nletinterp_int_or_var_as_listist=function|ArgVar({v=id}aslocid)->(trycoerce_to_int_or_var_list(Id.Map.findidist.lfun)withNot_found|CannotCoerceTo_->[ArgArg(interp_intistlocid)])|ArgArgnasx->[x]letinterp_int_or_var_lististl=List.flatten(List.map(interp_int_or_var_as_listist)l)(* Interprets a bound variable (especially an existing hypothesis) *)letinterp_hypistenvsigma({loc;v=id}aslocid)=(* Look first in lfun for a value coercible to a variable *)trytry_interp_ltac_var(coerce_to_hypenvsigma)ist(Some(env,sigma))locidwithNot_found->(* Then look if bound in the proof context at calling time *)ifis_variableenvidthenidelseLoc.raise?loc(Logic.RefinerError(env,sigma,Logic.NoSuchHypid))letinterp_hyp_list_as_lististenvsigma({loc;v=id}asx)=trycoerce_to_hyp_listenvsigma(Id.Map.findidist.lfun)withNot_found|CannotCoerceTo_->[interp_hypistenvsigmax]letinterp_hyp_lististenvsigmal=List.flatten(List.map(interp_hyp_list_as_lististenvsigma)l)letinterp_referenceistenvsigma=function|ArgArg(_,r)->r|ArgVar{loc;v=id}->trytry_interp_ltac_var(coerce_to_referencesigma)ist(Some(env,sigma))(CAst.make?locid)withNot_found->tryGlobRef.VarRef(get_id(Environ.lookup_namedidenv))withNot_foundasexn->let_,info=Exninfo.captureexninNametab.error_global_not_found~info(qualid_of_ident?locid)lettry_interp_evaluableenv(loc,id)=letv=Environ.lookup_namedidenvinmatchvwith|LocalDef_->EvalVarRefid|_->error_not_evaluable(GlobRef.VarRefid)letinterp_evaluableistenvsigma=function|ArgArg(r,Some{loc;v=id})->(* Maybe [id] has been introduced by Intro-like tactics *)begintrytry_interp_evaluableenv(loc,id)withNot_foundasexn->matchrwith|EvalConstRef_->r|_->let_,info=Exninfo.captureexninNametab.error_global_not_found~info(qualid_of_ident?locid)end|ArgArg(r,None)->r|ArgVar{loc;v=id}->trytry_interp_ltac_var(coerce_to_evaluable_refenvsigma)ist(Some(env,sigma))(CAst.make?locid)withNot_found->trytry_interp_evaluableenv(loc,id)withNot_foundasexn->let_,info=Exninfo.captureexninNametab.error_global_not_found~info(qualid_of_ident?locid)(* Interprets an hypothesis name *)letinterp_occurrencesistoccs=Locusops.occurrences_map(interp_int_or_var_listist)occsletinterp_hyp_locationistenvsigma((occs,id),hl)=((interp_occurrencesistoccs,interp_hypistenvsigmaid),hl)letinterp_hyp_location_list_as_lististenvsigma((occs,id),hlasx)=matchoccs,hlwith|AllOccurrences,InHyp->List.map(funid->((AllOccurrences,id),InHyp))(interp_hyp_list_as_lististenvsigmaid)|_,_->[interp_hyp_locationistenvsigmax]letinterp_hyp_location_lististenvsigmal=List.flatten(List.map(interp_hyp_location_list_as_lististenvsigma)l)letinterp_clauseistenvsigma{onhyps=ol;concl_occs=occs}:clause={onhyps=Option.map(interp_hyp_location_lististenvsigma)ol;concl_occs=interp_occurrencesistoccs}(* Interpretation of constructions *)(* Extract the constr list from lfun *)letextract_ltac_constr_valuesistenv=letfoldidvaccu=tryletc=coerce_to_constrenvvinId.Map.addidcaccuwithCannotCoerceTo_->accuinId.Map.foldfoldist.lfunId.Map.empty(** ppedrot: I have changed the semantics here. Before this patch, closure was
implemented as a list and a variable could be bound several times with
different types, resulting in its possible appearance on both sides. This
could barely be defined as a feature... *)(* Extract the identifier list from lfun: join all branches (what to do else?)*)letrecintropattern_idsaccu{loc;v=pat}=matchpatwith|IntroNaming(IntroIdentifierid)->Id.Set.addidaccu|IntroAction(IntroOrAndPattern(IntroAndPatternl))->List.fold_leftintropattern_idsaccul|IntroAction(IntroOrAndPattern(IntroOrPatternll))->List.fold_leftintropattern_idsaccu(List.flattenll)|IntroAction(IntroInjectionl)->List.fold_leftintropattern_idsaccul|IntroAction(IntroApplyOn({v=c},pat))->intropattern_idsaccupat|IntroNaming(IntroAnonymous|IntroFresh_)|IntroAction(IntroWildcard|IntroRewrite_)|IntroForthcoming_->acculetextract_idsidslfunaccu=letfoldidvaccu=ifhas_typev(topwitwit_intro_pattern)thenlet{v=ipat}=out_gen(topwitwit_intro_pattern)vinifId.List.memididsthenaccuelseintropattern_idsaccu(CAst.makeipat)elseaccuinId.Map.foldfoldlfunacculetdefault_fresh_id=Id.of_string"H"letinterp_fresh_idistenvsigmal=letextract_identistenvsigmaid=trytry_interp_ltac_var(coerce_to_ident_not_freshsigma)ist(Some(env,sigma))(CAst.makeid)withNot_found->idinletids=List.map_filter(functionArgVar{v=id}->Someid|_->None)linletavoid=matchTacStore.getist.extraf_avoid_idswith|None->Id.Set.empty|Somel->linletavoid=extract_idsidsist.lfunavoidinletid=ifList.is_emptylthendefault_fresh_idelselets=String.concat""(List.map(function|ArgArgs->s|ArgVar{v=id}->Id.to_string(extract_identistenvsigmaid))l)inlets=ifCLexer.is_keyword(Pcoq.get_keyword_state())sthens^"0"elsesinId.of_stringsinTactics.fresh_id_in_envavoididenv(* Extract the uconstr list from lfun *)letextract_ltac_constr_contextistenvsigma=letadd_uconstridvmap=tryId.Map.addid(coerce_to_uconstrv)mapwithCannotCoerceTo_->mapinletadd_constridvmap=tryId.Map.addid(coerce_to_constrenvv)mapwithCannotCoerceTo_->mapinletadd_identidvmap=tryId.Map.addid(coerce_var_to_identfalseenvsigmav)mapwithCannotCoerceTo_->mapinletfoldidv{idents;typed;untyped;genargs}=letidents=add_identidvidentsinlettyped=add_constridvtypedinletuntyped=add_uconstridvuntypedin{idents;typed;untyped;genargs}inletempty={idents=Id.Map.empty;typed=Id.Map.empty;untyped=Id.Map.empty;genargs=ist.lfun}inId.Map.foldfoldist.lfunempty(** Significantly simpler than [interp_constr], to interpret an
untyped constr, it suffices to adjoin a closure environment. *)letinterp_glob_closureistenvsigma?(kind=WithoutTypeConstraint)?(pattern_mode=false)(term,term_expr_opt)=letclosure=extract_ltac_constr_contextistenvsigmainmatchterm_expr_optwith|None->{closure;term}|Someterm_expr->(* If at toplevel (term_expr_opt<>None), the error can be due to
an incorrect context at globalization time: we retype with the
now known intros/lettac/inversion hypothesis names *)letconstr_context=Id.Set.union(Id.Map.domainclosure.typed)(Id.Map.domainclosure.untyped)inletltacvars={ltac_vars=constr_context;ltac_bound=Id.Map.domainist.lfun;ltac_extra=Genintern.Store.empty;}in{closure;term=intern_genkind~strict_check:false~pattern_mode~ltacvarsenvsigmaterm_expr}letinterp_uconstristenvsigmac=interp_glob_closureistenvsigmacletinterp_genkindistpattern_modeflagsenvsigmac=letkind_for_intern=matchkindwithOfType_->WithoutTypeConstraint|_->kindinlet{closure=constrvars;term}=interp_glob_closureistenvsigma~kind:kind_for_intern~pattern_modecinletvars={ltac_constrs=constrvars.typed;ltac_uconstrs=constrvars.untyped;ltac_idents=constrvars.idents;ltac_genargs=ist.lfun;}inletloc=loc_of_glob_constrterminlettrace=push_trace(loc,LtacConstrInterp(env,sigma,term,vars))istinlet(stack,_)=tracein(* save and restore the current trace info because the called routine later starts
with an empty trace *)Tactic_debug.push_chunktrace;trylet(evd,c)=catch_error_with_trace_loclocstack(understand_ltacflagsenvsigmavarskind)termin(* spiwack: to avoid unnecessary modifications of tacinterp, as this
function already use effect, I call [run] hoping it doesn't mess
up with any assumption. *)Proofview.NonLogical.run(db_constr(curr_debugist)envevdc);Tactic_debug.pop_chunk();(evd,c)withreraise->letreraise=Exninfo.capturereraiseinTactic_debug.pop_chunk();Exninfo.iraisereraiseletconstr_flags()={use_coercions=true;use_typeclasses=UseTC;solve_unification_constraints=true;fail_evar=true;expand_evars=true;program_mode=false;polymorphic=false;}(* Interprets a constr; expects evars to be solved *)letinterp_constr_genkindistenvsigmac=letflags={(constr_flags())withpolymorphic=ist.Geninterp.poly}ininterp_genkindistfalseflagsenvsigmacletinterp_constr=interp_constr_genWithoutTypeConstraintletinterp_type=interp_constr_genIsTypeletopen_constr_use_classes_flags()={use_coercions=true;use_typeclasses=UseTC;solve_unification_constraints=true;fail_evar=false;expand_evars=false;program_mode=false;polymorphic=false;}letopen_constr_no_classes_flags()={use_coercions=true;use_typeclasses=NoUseTC;solve_unification_constraints=true;fail_evar=false;expand_evars=false;program_mode=false;polymorphic=false;}letpure_open_constr_flags={use_coercions=true;use_typeclasses=NoUseTC;solve_unification_constraints=true;fail_evar=false;expand_evars=false;program_mode=false;polymorphic=false;}(* Interprets an open constr *)letinterp_open_constr?(expected_type=WithoutTypeConstraint)?(flags=open_constr_no_classes_flags())istenvsigmac=interp_genexpected_typeistfalseflagsenvsigmacletinterp_open_constr_with_classes?(expected_type=WithoutTypeConstraint)istenvsigmac=interp_genexpected_typeistfalse(open_constr_use_classes_flags())envsigmacletinterp_pure_open_constrist=interp_genWithoutTypeConstraintistfalsepure_open_constr_flagsletinterp_typed_patternistenvsigma(_,c,_)=letsigma,c=interp_genWithoutTypeConstraintisttruepure_open_constr_flagsenvsigmacin(* FIXME: it is necessary to be unsafe here because of the way we handle
evars in the pretyper. Sometimes they get solved eagerly. *)legacy_bad_pattern_of_constrenvsigmac(* Interprets a constr expression *)letinterp_constr_in_compound_listinj_fundest_funinterp_funistenvsigmal=lettry_expand_ltac_varsigmax=trymatchDAst.get(fst(dest_funx))with|GVarid->letv=Id.Map.findidist.lfuninsigma,List.mapinj_fun(coerce_to_constr_listenvv)|_->raiseNot_foundwithCannotCoerceTo_|Not_found->(* dest_fun, List.assoc may raise Not_found *)letsigma,c=interp_funistenvsigmaxinsigma,[c]inletsigma,l=List.fold_left_maptry_expand_ltac_varsigmalinsigma,List.flattenlletinterp_constr_lististenvsigmac=interp_constr_in_compound_list(funx->x)(funx->x)interp_constristenvsigmacletinterp_open_constr_list=interp_constr_in_compound_list(funx->x)(funx->x)interp_open_constr(* Interprets a reduction expression *)letinterp_unfoldistenvsigma(occs,qid)=(interp_occurrencesistoccs,interp_evaluableistenvsigmaqid)letinterp_flagistenvsigmared={redwithrConst=List.map(interp_evaluableistenvsigma)red.rConst}letinterp_constr_with_occurrencesistenvsigma(occs,c)=let(sigma,c_interp)=interp_constristenvsigmacinsigma,(interp_occurrencesistoccs,c_interp)letinterp_closed_typed_pattern_with_occurrencesistenvsigma(occs,a)=letp=matchawith|Inl(ArgVar{loc;v=id})->(* This is the encoding of an ltac var supposed to be bound
prioritary to an evaluable reference and otherwise to a constr
(it is an encoding to satisfy the "union" type given to Simpl) *)letcoerce_eval_ref_or_constrx=tryInl(coerce_to_evaluable_refenvsigmax)withCannotCoerceTo_->letc=coerce_to_closed_constrenvxinInr(pattern_of_constrenvsigmac)in(trytry_interp_ltac_varcoerce_eval_ref_or_constrist(Some(env,sigma))(CAst.make?locid)withNot_foundasexn->let_,info=Exninfo.captureexninNametab.error_global_not_found~info(qualid_of_ident?locid))|Inl(ArgArg_asb)->Inl(interp_evaluableistenvsigmab)|Inrc->Inr(interp_typed_patternistenvsigmac)ininterp_occurrencesistoccs,pletinterp_constr_with_occurrences_and_name_as_list=interp_constr_in_compound_list(func->((AllOccurrences,c),Anonymous))(function((occs,c),Anonymous)whenoccs==AllOccurrences->c|_->raiseNot_found)(funistenvsigma(occ_c,na)->let(sigma,c_interp)=interp_constr_with_occurrencesistenvsigmaocc_cinsigma,(c_interp,interp_nameistenvsigmana))letinterp_red_expristenvsigma=function|Unfoldl->sigma,Unfold(List.map(interp_unfoldistenvsigma)l)|Foldl->let(sigma,l_interp)=interp_constr_lististenvsigmalinsigma,Foldl_interp|Cbvf->sigma,Cbv(interp_flagistenvsigmaf)|Cbnf->sigma,Cbn(interp_flagistenvsigmaf)|Lazyf->sigma,Lazy(interp_flagistenvsigmaf)|Patternl->let(sigma,l_interp)=Evd.MonadR.List.map_right(funcsigma->interp_constr_with_occurrencesistenvsigmac)lsigmainsigma,Patternl_interp|Simpl(f,o)->sigma,Simpl(interp_flagistenvsigmaf,Option.map(interp_closed_typed_pattern_with_occurrencesistenvsigma)o)|CbvVmo->sigma,CbvVm(Option.map(interp_closed_typed_pattern_with_occurrencesistenvsigma)o)|CbvNativeo->sigma,CbvNative(Option.map(interp_closed_typed_pattern_with_occurrencesistenvsigma)o)|(Red|Hnf|ExtraRedExpr_asr)->sigma,rletinterp_may_evalfistenvsigma=function|ConstrEval(r,c)->let(sigma,redexp)=interp_red_expristenvsigmarinlet(sigma,c_interp)=fistenvsigmacinlet(redfun,_)=Redexpr.reduction_of_red_exprenvredexpinredfunenvsigmac_interp|ConstrContext({loc;v=s},c)->let(sigma,ic)=fistenvsigmacinletctxt=trytry_interp_ltac_varcoerce_to_constr_contextist(Some(env,sigma))(CAst.make?locs)withNot_found->user_err?loc(str"Unbound context identifier"++Id.prints++str".")inletc=Constr_matching.instantiate_contextctxticinTyping.solve_evarsenvsigmac|ConstrTypeOfc->let(sigma,c_interp)=fistenvsigmacinlet(sigma,t)=Typing.type_of~refresh:trueenvsigmac_interpin(sigma,t)|ConstrTermc->tryfistenvsigmacwithreraise->letreraise=Exninfo.capturereraisein(* spiwack: to avoid unnecessary modifications of tacinterp, as this
function already use effect, I call [run] hoping it doesn't mess
up with any assumption. *)Proofview.NonLogical.run(debugging_exception_stepistfalse(fstreraise)(fun()->str"interpretation of term "++pr_glob_constr_envenvsigma(fstc)));Exninfo.iraisereraise(* Interprets a constr expression possibly to first evaluate *)letinterp_constr_may_evalistenvsigmac=let(sigma,csr)=tryinterp_may_evalinterp_constristenvsigmacwithreraise->letreraise=Exninfo.capturereraisein(* spiwack: to avoid unnecessary modifications of tacinterp, as this
function already use effect, I call [run] hoping it doesn't mess
up with any assumption. *)Proofview.NonLogical.run(debugging_exception_stepistfalse(fstreraise)(fun()->str"evaluation of term"));Exninfo.iraisereraiseinbegin(* spiwack: to avoid unnecessary modifications of tacinterp, as this
function already use effect, I call [run] hoping it doesn't mess
up with any assumption. *)Proofview.NonLogical.run(db_constr(curr_debugist)envsigmacsr);sigma,csrend(** TODO: should use dedicated printers *)letmessage_of_valuev=letpr_with_envpr=Ftactic.enterbeginfungl->Ftactic.return(pr(pf_envgl)(projectgl))endinletopenGenprintinmatchgeneric_val_printvwith|TopPrinterBasicpr->Ftactic.return(pr())|TopPrinterNeedsContextpr->pr_with_envpr|TopPrinterNeedsContextAndLevel{default_ensure_surrounded;printer}->pr_with_env(funenvsigma->printerenvsigmadefault_ensure_surrounded)letinterp_message_tokenist=function|MsgStrings->Ftactic.return(strs)|MsgIntn->Ftactic.return(intn)|MsgIdent{loc;v=id}->letv=trySome(Id.Map.findidist.lfun)withNot_found->Noneinmatchvwith|None->Ftactic.lift(letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(Id.printid++str" not found."))|Somev->message_of_valuevletinterp_messageistl=letopenFtacticinFtactic.List.map(interp_message_tokenist)l>>=funl->Ftactic.return(prlist_with_sepspc(funx->x)l)letrecinterp_intro_patternistenvsigma=with_loc_val(fun?loc->function|IntroActionpat->letpat=interp_intro_pattern_actionistenvsigmapatinCAst.make?loc@@IntroActionpat|IntroNaming(IntroIdentifierid)->CAst.make?loc@@interp_intro_pattern_varlocistenvsigmaid|IntroNamingpat->CAst.make?loc@@IntroNaming(interp_intro_pattern_naminglocistenvsigmapat)|IntroForthcoming_asx->CAst.make?locx)andinterp_intro_pattern_naminglocistenvsigma=function|IntroFreshid->IntroFresh(interp_identistenvsigmaid)|IntroIdentifierid->interp_intro_pattern_naming_varlocistenvsigmaid|IntroAnonymousasx->xandinterp_intro_pattern_actionistenvsigma=function|IntroOrAndPatternl->letl=interp_or_and_intro_patternistenvsigmalinIntroOrAndPatternl|IntroInjectionl->letl=interp_intro_pattern_list_as_lististenvsigmalinIntroInjectionl|IntroApplyOn({loc;v=c},ipat)->letcenvsigma=interp_open_constristenvsigmacinletipat=interp_intro_patternistenvsigmaipatinIntroApplyOn(CAst.make?locc,ipat)|IntroWildcard|IntroRewrite_asx->xandinterp_or_and_intro_patternistenvsigma=function|IntroAndPatternl->letl=List.map(interp_intro_patternistenvsigma)linIntroAndPatternl|IntroOrPatternll->letll=List.map(interp_intro_pattern_list_as_lististenvsigma)llinIntroOrPatternllandinterp_intro_pattern_list_as_lististenvsigma=function|[{loc;v=IntroNaming(IntroIdentifierid)}]asl->(trycoerce_to_intro_pattern_list?locsigma(Id.Map.findidist.lfun)withNot_found|CannotCoerceTo_->List.map(interp_intro_patternistenvsigma)l)|l->List.map(interp_intro_patternistenvsigma)lletinterp_intro_pattern_naming_optionistenvsigma=function|None->None|Somelpat->Some(map_with_loc(fun?locpat->interp_intro_pattern_naminglocistenvsigmapat)lpat)letinterp_or_and_intro_pattern_optionistenvsigma=function|None->None|Some(ArgVar{loc;v=id})->(matchinterp_intro_pattern_varlocistenvsigmaidwith|IntroAction(IntroOrAndPatternl)->Some(CAst.make?locl)|_->user_err?loc(str"Cannot coerce to a disjunctive/conjunctive pattern."))|Some(ArgArg{loc;v=l})->letl=interp_or_and_intro_patternistenvsigmalinSome(CAst.make?locl)letinterp_intro_pattern_optionistenvsigma=function|None->None|Someipat->letipat=interp_intro_patternistenvsigmaipatinSomeipatletinterp_in_hyp_asistenvsigma(id,ipat)=letipat=interp_intro_pattern_optionistenvsigmaipatin(interp_hypistenvsigmaid,ipat)letinterp_binding_nameistenvsigma=function|AnonHypn->AnonHypn|NamedHypid->(* If a name is bound, it has to be a quantified hypothesis *)(* user has to use other names for variables if these ones clash with *)(* a name intended to be used as a (non-variable) identifier *)trytry_interp_ltac_var(coerce_to_quantified_hypothesissigma)ist(Some(env,sigma))idwithNot_found->NamedHypidletinterp_declared_or_quantified_hypothesisistenvsigma=function|AnonHypn->AnonHypn|NamedHypid->trytry_interp_ltac_var(coerce_to_decl_or_quant_hypsigma)ist(Some(env,sigma))idwithNot_found->NamedHypidletinterp_bindingistenvsigma{loc;v=(b,c)}=letsigma,c=interp_open_constristenvsigmacinsigma,(CAst.make?loc(interp_binding_nameistenvsigmab,c))letinterp_bindingsistenvsigma=function|NoBindings->sigma,NoBindings|ImplicitBindingsl->letsigma,l=interp_open_constr_lististenvsigmalinsigma,ImplicitBindingsl|ExplicitBindingsl->letsigma,l=List.fold_left_map(interp_bindingistenv)sigmalinsigma,ExplicitBindingslletinterp_constr_with_bindingsistenvsigma(c,bl)=letsigma,bl=interp_bindingsistenvsigmablinletsigma,c=interp_constristenvsigmacinsigma,(c,bl)letinterp_open_constr_with_bindingsistenvsigma(c,bl)=letsigma,bl=interp_bindingsistenvsigmablinletsigma,c=interp_open_constristenvsigmacinsigma,(c,bl)letloc_of_bindings=function|NoBindings->None|ImplicitBindingsl->loc_of_glob_constr(fst(List.lastl))|ExplicitBindingsl->(List.lastl).locletinterp_open_constr_with_bindings_locist((c,_),blascb)=letloc1=loc_of_glob_constrcinletloc2=loc_of_bindingsblinletloc=Loc.merge_optloc1loc2inletfenvsigma=interp_open_constr_with_bindingsistenvsigmacbin(loc,f)letinterp_destruction_argistglarg=matchargwith|keep,ElimOnConstrc->keep,ElimOnConstrbeginfunenvsigma->interp_open_constr_with_bindingsistenvsigmacend|keep,ElimOnAnonHypnasx->x|keep,ElimOnIdent{loc;v=id}->leterror()=user_err?loc(strbrk"Cannot coerce "++Id.printid++strbrk" neither to a quantified hypothesis nor to a term.")inlettry_cast_idid'=ifTactics.is_quantified_hypothesisid'glthenkeep,ElimOnIdent(CAst.make?locid')else(keep,ElimOnConstrbeginfunenvsigma->try(sigma,(constr_of_idenvid',NoBindings))withNot_found->user_err?loc(Id.printid++strbrk" binds to "++Id.printid'++strbrk" which is neither a declared nor a quantified hypothesis.")end)intry(* FIXME: should be moved to taccoerce *)letv=Id.Map.findidist.lfuninifhas_typev(topwitwit_intro_pattern)thenletv=out_gen(topwitwit_intro_pattern)vinmatchvwith|{v=IntroNaming(IntroIdentifierid)}->try_cast_idid|_->error()elseifhas_typev(topwitwit_hyp)thenletid=out_gen(topwitwit_hyp)vintry_cast_ididelseifhas_typev(topwitwit_int)thenkeep,ElimOnAnonHyp(out_gen(topwitwit_int)v)elsematchValue.to_constrvwith|None->error()|Somec->keep,ElimOnConstr(funenvsigma->(sigma,(c,NoBindings)))withNot_found->(* We were in non strict (interactive) mode *)ifTactics.is_quantified_hypothesisidglthenkeep,ElimOnIdent(CAst.make?locid)elseletc=(DAst.make?loc@@GVarid,Some(CAst.make@@CRef(qualid_of_ident?locid,None)))inletfenvsigma=let(sigma,c)=interp_open_constristenvsigmacin(sigma,(c,NoBindings))inkeep,ElimOnConstrf(* Associates variables with values and gives the remaining variables and
values *)lethead_with_value(lvar,lval)=letrechead_with_value_reclacc=function|([],[])->(lacc,[],[])|(vr::tvr,ve::tve)->(matchvrwith|Anonymous->head_with_value_reclacc(tvr,tve)|Namev->head_with_value_rec((v,ve)::lacc)(tvr,tve))|(vr,[])->(lacc,vr,[])|([],ve)->(lacc,[],ve)inhead_with_value_rec[](lvar,lval)(** [interp_context ctxt] interprets a context (as in
{!Matching.matching_result}) into a context value of Ltac. *)letinterp_contextctxt=in_gen(topwitwit_constr_context)ctxtleteval_patternlfunistenvsigma(bvars,_,pat)=(bvars,Constr_matching.instantiate_patternenvsigmalfunpat)letread_patternlfunistenvsigma=function|Subterm(ido,c)->Subterm(ido,eval_patternlfunistenvsigmac)|Termc->Term(eval_patternlfunistenvsigmac)(* Reads the hypotheses of a Match Context rule *)letcons_and_check_nameidl=ifId.List.memidlthenuser_err(str"Hypothesis pattern-matching variable "++Id.printid++str" used twice in the same pattern.")elseid::lletrecread_match_goal_hypslfunistenvsigmalidh=function|(Hyp({loc;v=na}aslocna,mp))::tl->letlidh'=Name.fold_rightcons_and_check_namenalidhinHyp(locna,read_patternlfunistenvsigmamp)::(read_match_goal_hypslfunistenvsigmalidh'tl)|(Def({loc;v=na}aslocna,mv,mp))::tl->letlidh'=Name.fold_rightcons_and_check_namenalidhinDef(locna,read_patternlfunistenvsigmamv,read_patternlfunistenvsigmamp)::(read_match_goal_hypslfunistenvsigmalidh'tl)|[]->[](* Reads the rules of a Match Context or a Match *)letrecread_match_rulelfunistenvsigma=function|(Alltc)::tl->(Alltc)::(read_match_rulelfunistenvsigmatl)|(Pat(rl,mp,tc))::tl->Pat(read_match_goal_hypslfunistenvsigma[]rl,read_patternlfunistenvsigmamp,tc)::read_match_rulelfunistenvsigmatl|[]->[](* Fully evaluate an untyped constr *)lettype_uconstr?(flags=(constr_flags()))?(expected_type=WithoutTypeConstraint)istc=letflags={flagswithpolymorphic=ist.Geninterp.poly}inbeginfunenvsigma->Pretyping.understand_uconstr~flags~expected_typeenvsigmacend(* Interprets an l-tac expression into a value *)letrecval_interpist?(appl=UnnamedAppl)(tac:glob_tactic_expr):Val.tFtactic.t=(* The name [appl] of applied top-level Ltac names is ignored in
[value_interp]. It is installed in the second step by a call to
[name_vfun], because it gives more opportunities to detect a
[VFun]. Otherwise a [Ltac t := let x := .. in tac] would never
register its name since it is syntactically a let, not a
function. *)let(loc,tac2)=CAst.(tac.loc,tac.v)inletvalue_interpist=matchtac2with|TacFun(it,body)->Ftactic.return(of_tacvalue(VFun(UnnamedAppl,extract_traceist,extract_locist,ist.lfun,it,body)))|TacLetIn(true,l,u)->interp_letrecistlu|TacLetIn(false,l,u)->interp_letinistlu|TacMatchGoal(lz,lr,lmr)->interp_match_goalistlzlrlmr|TacMatch(lz,c,lmr)->interp_matchistlzclmr|TacArgv->interp_tacargistv|_->(* Delayed evaluation *)Ftactic.return(of_tacvalue(VFun(UnnamedAppl,extract_traceist,extract_locist,ist.lfun,[],tac)))inletopenFtacticinControl.check_for_interrupt();matchcurr_debugistwith|DebugOnlev->letevalv=letist={istwithextra=TacStore.setist.extraf_debugv}invalue_interpist>>=funv->return(name_vfunapplv)inTactic_debug.debug_promptlevtacevalist.lfun(TacStore.getist.extraf_trace)|_->value_interpist>>=funv->return(name_vfunapplv)andeval_tactic_ististtac:unitProofview.tactic=let(loc,tac2)=CAst.(tac.loc,tac.v)inmatchtac2with|TacAtomt->letcall=LtacAtomCalltinlet(stack,_)=push_trace(loc,call)istinProfile_ltac.do_profilestack(catch_error_tac_loclocstack(interp_atomicistt))|TacFun_|TacLetIn_|TacMatchGoal_|TacMatch_->interp_tacticisttac|TacId[]->Proofview.tclLIFT(db_breakpoint(curr_debugist)[])|TacIds->letmsgnl=letopenFtacticininterp_messageists>>=funmsg->return(hov0msg,hov0msg)inletprint(_,msgnl)=Proofview.(tclLIFT(NonLogical.print_infomsgnl))inletlog(msg,_)=Proofview.Trace.log(fun()->msg)inletbreak=Proofview.tclLIFT(db_breakpoint(curr_debugist)s)inFtactic.runmsgnlbeginfunmsgnl->printmsgnl<*>logmsgnl<*>breakend|TacFail(g,n,s)->letmsg=interp_messageistsinlettac~infol=Tacticals.tclFAILn~info(interp_int_or_varistn)linlettac=matchgwith|TacLocal->letinfo=Exninfo.reify()infunl->Proofview.tclINDEPENDENT(tac~infol)|TacGlobal->letinfo=Exninfo.reify()intac~infoinFtactic.runmsgtac|TacProgresstac->Tacticals.tclPROGRESS(interp_tacticisttac)|TacAbstract(t,ido)->letcall=LtacMLCalltacinlet(stack,_)=push_trace(None,call)istinProfile_ltac.do_profilestack(catch_error_tacstackbeginProofview.Goal.enterbeginfungl->Abstract.tclABSTRACT(Option.map(interp_identist(pf_envgl)(projectgl))ido)(interp_tacticistt)endend)|TacThen(t1,t)->Tacticals.tclTHEN(interp_tacticistt1)(interp_tacticistt)|TacDispatchtl->Proofview.tclDISPATCH(List.map(interp_tacticist)tl)|TacExtendTac(tf,t,tl)->Proofview.tclEXTEND(Array.map_to_list(interp_tacticist)tf)(interp_tacticistt)(Array.map_to_list(interp_tacticist)tl)|TacThens(t1,tl)->Tacticals.tclTHENS(interp_tacticistt1)(List.map(interp_tacticist)tl)|TacThens3parts(t1,tf,t,tl)->Tacticals.tclTHENS3PARTS(interp_tacticistt1)(Array.map(interp_tacticist)tf)(interp_tacticistt)(Array.map(interp_tacticist)tl)|TacDo(n,tac)->Tacticals.tclDO(interp_int_or_varistn)(interp_tacticisttac)|TacTimeout(n,tac)->Tacticals.tclTIMEOUT(interp_int_or_varistn)(interp_tacticisttac)|TacTime(s,tac)->Tacticals.tclTIMEs(interp_tacticisttac)|TacTrytac->Tacticals.tclTRY(interp_tacticisttac)|TacRepeattac->Tacticals.tclREPEAT(interp_tacticisttac)|TacOr(tac1,tac2)->Tacticals.tclOR(interp_tacticisttac1)(interp_tacticisttac2)|TacOncetac->Tacticals.tclONCE(interp_tacticisttac)|TacExactlyOncetac->Tacticals.tclEXACTLY_ONCE(interp_tacticisttac)|TacIfThenCatch(t,tt,te)->Tacticals.tclIFCATCH(interp_tacticistt)(fun()->interp_tacticisttt)(fun()->interp_tacticistte)|TacOrelse(tac1,tac2)->Tacticals.tclORELSE(interp_tacticisttac1)(interp_tacticisttac2)|TacFirstl->Tacticals.tclFIRST(List.map(interp_tacticist)l)|TacSolvel->Tacticals.tclSOLVE(List.map(interp_tacticist)l)|TacArg_->Ftactic.run(val_interp(ensure_loclocist)tac)(funv->tactic_of_valueistv)|TacSelect(sel,tac)->Goal_select.tclSELECTsel(interp_tacticisttac)(* For extensions *)|TacAlias(s,l)->letalias=Tacenv.interp_aliassinProofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->let(>>=)=Ftactic.bindinletinterp_vars=Ftactic.List.map(funv->interp_tacargistv)linlettacl=letaddvarxvaccu=Id.Map.addxvaccuinletlfun=List.fold_right2addvaralias.Tacenv.alias_argslist.lfuninlettrace=push_trace(loc,LtacNotationCalls)istinletist={lfun;poly;extra=add_extra_locloc(add_extra_tracetraceist.extra)}inval_interpistalias.Tacenv.alias_body>>=funv->Ftactic.lift(tactic_of_valueistv)inlettac=Ftactic.with_envinterp_vars>>=fun(env,lr)->letname()=Pptactic.pr_alias(funv->print_top_valenvv)0slrinProofview.Trace.name_tacticname(taclr)(* spiwack: this use of name_tactic is not robust to a
change of implementation of [Ftactic]. In such a situation,
some more elaborate solution will have to be used. *)inlettac=letlen1=List.lengthalias.Tacenv.alias_argsinletlen2=List.lengthliniflen1=len2thentacelseletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"Arguments length mismatch: \
expected "++intlen1++str", found "++intlen2)inFtactic.runtac(fun()->Proofview.tclUNIT())|TacML(opn,l)->lettrace=push_trace(Loc.tag?loc@@LtacMLCalltac)istinletist={istwithextra=TacStore.setist.extraf_tracetrace;}inlettac=Tacenv.interp_ml_tacticopninletargs=Ftactic.List.map_right(funa->interp_tacargista)linlettacargs=letname()=Pptactic.pr_extend(funv->print_top_val()v)0opnargsinlet(stack,_)=traceinProofview.Trace.name_tacticname(catch_error_tac_loclocstack(tacargsist))inFtactic.runargstacandforce_vrecistv:Val.tFtactic.t=ifhas_typev(topwitwit_tacvalue)thenletv=to_tacvaluevinmatchvwith|VRec(lfun,body)->val_interp{istwithlfun=!lfun}body|v->Ftactic.return(of_tacvaluev)elseFtactic.returnvandinterp_ltac_reference?loc'mustbetacistr:Val.tFtactic.t=matchrwith|ArgVar{loc;v=id}->letv=tryId.Map.findidist.lfunwithNot_found->in_gen(topwitwit_hyp)idinletopenFtacticinforce_vrecistv>>=beginfunv->Ftactic.lift(propagate_traceistlocidv)>>=funv->ifmustbetacthenFtactic.return(coerce_to_tacticlocidv)elseFtactic.returnvend|ArgArg(loc,r)->Proofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->letids=extract_ids[]ist.lfunId.Set.emptyinletloc_info=(Option.defaultlocloc',LtacNameCallr)inletextra=TacStore.setist.extraf_avoid_idsidsinlettrace=push_traceloc_infoistinletextra=TacStore.setextraf_tracetraceinletist={lfun=Id.Map.empty;poly;extra}inletappl=GlbAppl[r,[]]in(* We call a global ltac reference: add a loc on its executation only if not
already in another global reference *)letist=ensure_loclocistinlet(stack,_)=traceinProfile_ltac.do_profilestack~count_call:false(catch_error_tac_loc(* loc for interpretation *)locstack(val_interp~applist(Tacenv.interp_ltacr)))andinterp_tacargistarg:Val.tFtactic.t=matchargwith|TacGeneric(_,arg)->interp_genargistarg|Referencer->interp_ltac_referencefalseistr|ConstrMayEvalc->Ftactic.enterbeginfungl->letsigma=projectglinletenv=Proofview.Goal.envglinlet(sigma,c_interp)=interp_constr_may_evalistenvsigmacinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Ftactic.return(Value.of_constrc_interp))end|TacCall{v=(r,[])}->interp_ltac_referencetrueistr|TacCall{loc;v=(f,l)}->let(>>=)=Ftactic.bindininterp_ltac_referencetrueistf>>=funfv->Ftactic.List.map(funa->interp_tacargista)l>>=funlargs->interp_applocistfvlargs|TacFreshIdl->Ftactic.enterbeginfungl->letid=interp_fresh_idist(pf_envgl)(projectgl)linFtactic.return(in_gen(topwitwit_intro_pattern)(CAst.make@@IntroNaming(IntroIdentifierid)))end|TacPretypec->Ftactic.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletc=interp_uconstristenvsigmacinlet(sigma,c)=type_uconstristcenvsigmainProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Ftactic.return(Value.of_constrc))end|TacNumgoals->Ftactic.liftbeginletopenProofview.NotationsinProofview.numgoals>>=funi->Proofview.tclUNIT(Value.of_inti)end|Tacexpt->val_interpistt(* Interprets an application node *)andinterp_applocistfvlargs:Val.tFtactic.t=Proofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->let(>>=)=Ftactic.bindinletfail~info=Tacticals.tclZEROMSG~info(str"Illegal tactic application.")inifhas_typefv(topwitwit_tacvalue)thenmatchto_tacvaluefvwith(* if var=[] and body has been delayed by val_interp, then body
is not a tactic that expects arguments.
Otherwise Ltac goes into an infinite loop (val_interp puts
a VFun back on body, and then interp_app is called again...) *)|(VFun(appl,trace,_,olfun,(_::_asvar),body)|VFun(appl,trace,_,olfun,([]asvar),({CAst.v=(TacFun_)}|{CAst.v=(TacLetIn_)}|{CAst.v=(TacMatchGoal_)}|{CAst.v=(TacMatch_)}|{CAst.v=(TacArg_)}asbody)))->let(extfun,lvar,lval)=head_with_value(var,largs)inletfoldaccu(id,v)=Id.Map.addidvaccuinletnewlfun=List.fold_leftfoldolfunextfuninifList.is_emptylvarthenbeginwrap_errorbeginletist={lfun=newlfun;poly;extra=TacStore.setist.extraf_tracetrace}inlet(stack,_)=traceinProfile_ltac.do_profilestack~count_call:false(catch_error_tac_loclocstack(val_interp(ensure_loclocist)body))>>=funv->Ftactic.return(name_vfun(push_applappllargs)v)endbeginfun(e,info)->Proofview.tclLIFT(debugging_exception_stepistfalsee(fun()->str"evaluation"))<*>Proofview.tclZERO~infoeendend>>=funv->(* No errors happened, we propagate the trace *)letv=append_tracetracevinletcall_debugenv=Proofview.tclLIFT(debugging_stepist(fun()->str"evaluation returns"++fnl()++pr_valueenvv))inbeginletopenGenprintinmatchgeneric_val_printvwith|TopPrinterBasic_->call_debugNone|TopPrinterNeedsContext_|TopPrinterNeedsContextAndLevel_->Proofview.Goal.enter(fungl->call_debug(Some(pf_envgl,projectgl)))end<*>ifList.is_emptylvalthenFtactic.returnvelseinterp_applocistvlvalelseFtactic.return(of_tacvalue(VFun(push_applappllargs,trace,loc,newlfun,lvar,body)))|(VFun(appl,trace,_,olfun,[],body))->letextra_args=List.lengthlargsinletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"Illegal tactic application: got "++str(string_of_intextra_args)++str" extra "++str(String.pluralextra_args"argument")++str".")|VRec(_,_)->letinfo=Exninfo.reify()infail~infoelseletinfo=Exninfo.reify()infail~info(* Gives the tactic corresponding to the tactic value *)andtactic_of_valueistvle=ifhas_typevle(topwitwit_tacvalue)thenmatchto_tacvaluevlewith|VFun(appl,trace,loc,lfun,[],t)->Proofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->letist={lfun=lfun;poly;(* todo: debug stack needs "trace" but that gives incorrect results for profiling
Couldn't figure out how to make them play together. Currently no way both can
be enabled. Perhaps profiling should be redesigned as suggested in profile_ltac.mli *)extra=TacStore.setist.extraf_trace(if!Flags.profile_ltacthen([],[])elsetrace);}inlettac=name_if_globappl(eval_tactic_ististt)inlet(stack,_)=traceinProfile_ltac.do_profilestack(catch_error_tac_loclocstacktac)|VFun(appl,(stack,_),loc,vmap,vars,_)->lettactic_nm=matchapplwithUnnamedAppl->"An unnamed user-defined tactic"|GlbApplapps->letnms=List.map(fun(kn,_)->string_of_qualid(Tacenv.shortest_qualid_of_tactickn))appsinmatchnmswith[]->assertfalse|kn::_->"The user-defined tactic \""^kn^"\""(* TODO: when do we not have a singleton? *)inletnumargs=List.lengthvarsinletgivenargs=List.map(fun(arg,_)->Names.Id.to_stringarg)(Names.Id.Map.bindingsvmap)inletnumgiven=List.lengthgivenargsinletinfo=Exninfo.reify()incatch_error_tacstack@@Tacticals.tclZEROMSG~infoPp.(strtactic_nm++str" was not fully applied:"++spc()++str"There is a missing argument for variable"++spc()++Name.print(List.hdvars)++(ifnumargs>1thenspc()++str"and "++int(numargs-1)++str" more"elsemt())++pr_comma()++(matchnumgivenwith|0->str"no arguments at all were provided."|1->str"1 argument was provided."|_->intnumgiven++str" arguments were provided."))|VRec_->letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"A fully applied tactic is expected.")elseifhas_typevle(topwitwit_tactic)thenlettac=out_gen(topwitwit_tactic)vleintactic_of_valueisttacelseletname=letDyn(t,_)=vleinVal.reprtinletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"Expression does not evaluate to a tactic (got a "++strname++str").")(* Interprets the clauses of a recursive LetIn *)andinterp_letrecistllcu=Proofview.tclUNIT()>>=fun()->(* delay for the effects of [lref], just in case. *)letlref=refist.lfuninletfoldaccu({v=na},b)=letv=of_tacvalue(VRec(lref,CAst.make(TacArgb)))inName.fold_right(funid->Id.Map.addidv)naaccuinletlfun=List.fold_leftfoldist.lfunllcinlet()=lref:=lfuninletist={istwithlfun}inval_interpistu(* Interprets the clauses of a LetIn *)andinterp_letinistllcu=letrecfoldlfun=function|[]->letist={istwithlfun}inval_interpistu|({v=na},body)::defs->Ftactic.bind(interp_tacargistbody)(funv->fold(Name.fold_right(funid->Id.Map.addidv)nalfun)defs)infoldist.lfunllc(** [interp_match_success lz ist succ] interprets a single matching success
(of type {!Tactic_matching.t}). *)andinterp_match_successist{Tactic_matching.subst;context;terms;lhs}=Proofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->let(>>=)=Ftactic.bindinletlctxt=Id.Map.mapinterp_contextcontextinlethyp_subst=Id.Map.mapValue.of_constrtermsinletlfun=extend_values_with_bindingssubst(lctxt+++hyp_subst+++ist.lfun)inletist={istwithlfun}inval_interpistlhs>>=funv->ifhas_typev(topwitwit_tacvalue)thenmatchto_tacvaluevwith|VFun(appl,trace,loc,lfun,[],t)->letist={lfun=lfun;poly;extra=TacStore.setist.extraf_tracetrace}inlettac=eval_tactic_ististtinletdummy=VFun(appl,extract_traceist,loc,Id.Map.empty,[],CAst.make(TacId[]))inlet(stack,_)=traceincatch_error_tacstack(tac<*>Ftactic.return(of_tacvaluedummy))|_->Ftactic.returnvelseFtactic.returnv(** [interp_match_successes lz ist s] interprets the stream of
matching of successes [s]. If [lz] is set to true, then only the
first success is considered, otherwise further successes are tried
if the left-hand side fails. *)andinterp_match_successeslzists=letgeneral=letopenTacticalsinletbreak(e,info)=matchewith|FailError(0,_)->None|FailError(n,s)->Some(FailError(predn,s),info)|_->NoneinProofview.tclBREAKbreaks>>=funans->interp_match_successistansinmatchlzwith|General->general|Select->begin(* Only keep the first matching result, we don't backtrack on it *)lets=Proofview.tclONCEsins>>=funans->interp_match_successistansend|Once->(* Once a tactic has succeeded, do not backtrack anymore *)Proofview.tclONCEgeneral(* Interprets the Match expressions *)andinterp_matchistlzconstrlmr=let(>>=)=Ftactic.bindinbeginwrap_error(interp_ltac_constristconstr)beginfunction|(e,info)->Proofview.tclLIFT(debugging_exception_stepisttruee(fun()->str"evaluation of the matched expression"))<*>Proofview.tclZERO~infoeendend>>=funconstr->Ftactic.enterbeginfungl->letsigma=projectglinletenv=Proofview.Goal.envglinletilr=read_match_rule(extract_ltac_constr_valuesistenv)istenvsigmalmrininterp_match_successeslzist(Tactic_matching.match_termenvsigmaconstrilr)end(* Interprets the Match Context expressions *)andinterp_match_goalistlzlrlmr=Ftactic.enterbeginfungl->letsigma=projectglinletenv=Proofview.Goal.envglinlethyps=Proofview.Goal.hypsglinlethyps=iflrthenList.revhypselsehypsinletconcl=Proofview.Goal.conclglinletilr=read_match_rule(extract_ltac_constr_valuesistenv)istenvsigmalmrininterp_match_successeslzist(Tactic_matching.match_goalenvsigmahypsconclilr)end(* Interprets extended tactic generic arguments *)andinterp_genargistx:Val.tFtactic.t=letopenFtactic.Notationsin(* Ad-hoc handling of some types. *)lettag=genarg_tagxinifargument_type_eqtag(unquote(topwit(wit_listwit_hyp)))theninterp_genarg_var_lististxelseifargument_type_eqtag(unquote(topwit(wit_listwit_constr)))theninterp_genarg_constr_lististxelseletGenArg(Glbwitwit,x)=xinmatchwitwith|ListArgwit->letmapx=interp_genargist(Genarg.in_gen(glbwitwit)x)inFtactic.List.mapmapx>>=funl->Ftactic.return(Val.Dyn(Val.typ_list,l))|OptArgwit->beginmatchxwith|None->Ftactic.return(Val.Dyn(Val.typ_opt,None))|Somex->interp_genargist(Genarg.in_gen(glbwitwit)x)>>=funx->Ftactic.return(Val.Dyn(Val.typ_opt,Somex))end|PairArg(wit1,wit2)->let(p,q)=xininterp_genargist(Genarg.in_gen(glbwitwit1)p)>>=funp->interp_genargist(Genarg.in_gen(glbwitwit2)q)>>=funq->Ftactic.return(Val.Dyn(Val.typ_pair,(p,q)))|ExtraArgs->Geninterp.interpwitistx(** returns [true] for genargs which have the same meaning
independently of goals. *)andinterp_genarg_constr_lististx=Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletlc=Genarg.out_gen(glbwit(wit_listwit_constr))xinlet(sigma,lc)=interp_constr_lististenvsigmalcinletlc=in_list(val_tagwit_constr)lcinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Ftactic.returnlc)endandinterp_genarg_var_lististx=Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletlc=Genarg.out_gen(glbwit(wit_listwit_hyp))xinletlc=interp_hyp_lististenvsigmalcinletlc=in_list(val_tagwit_hyp)lcinFtactic.returnlcend(* Interprets tactic expressions : returns a "constr" *)andinterp_ltac_constriste:EConstr.tFtactic.t=let(>>=)=Ftactic.bindinbeginwrap_error(val_interpiste)beginfunction(err,info)->matcherrwith|Not_found->Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinProofview.tclLIFTbegindebugging_stepist(fun()->str"evaluation failed for"++fnl()++Pptactic.pr_glob_tacticenve)end<*>Proofview.tclZERONot_foundend|err->Proofview.tclZERO~infoerrendend>>=funresult->Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglintryletcresult=coerce_to_closed_constrenvresultinProofview.tclLIFTbegindebugging_stepist(fun()->Pptactic.pr_glob_tacticenve++fnl()++str" has value "++fnl()++pr_econstr_envenvsigmacresult)end<*>Ftactic.returncresultwithCannotCoerceTo_asexn->let_,info=Exninfo.captureexninletenv=Proofview.Goal.envglinTacticals.tclZEROMSG~info(str"Must evaluate to a closed term"++fnl()++str"offending expression: "++fnl()++pr_inspectenveresult)end(* Interprets tactic expressions : returns a "tactic" *)andinterp_tacticisttac:unitProofview.tactic=Ftactic.run(val_interpisttac)(funv->tactic_of_valueistv)(* Provides a "name" for the trace to atomic tactics *)andname_atomic?envtacexprtac:unitProofview.tactic=beginmatchenvwith|Somee->Proofview.tclUNITe|None->Proofview.tclENVend>>=funenv->Proofview.tclEVARMAP>>=funsigma->letname()=Pptactic.pr_atomic_tacticenvsigmatacexprinProofview.Trace.name_tacticnametac(* Interprets a primitive tactic *)andinterp_atomicisttac:unitProofview.tactic=matchtacwith(* Basic tactics *)|TacIntroPattern(ev,l)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletl'=interp_intro_pattern_list_as_lististenvsigmalinname_atomic~env(TacIntroPattern(ev,l))(* spiwack: print uninterpreted, not sure if it is the
expected behaviour. *)(Tactics.intro_patternsevl')end|TacApply(a,ev,cb,cl)->(* spiwack: until the tactic is in the monad *)Proofview.Trace.name_tactic(fun()->Pp.str"<apply>")beginProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletl=List.map(fun(k,c)->letloc,f=interp_open_constr_with_bindings_locistcin(k,(CAst.make?locf)))cbinlettac=matchclwith|[]->Tactics.apply_with_delayed_bindings_genaevl|cl->letcl=List.map(interp_in_hyp_asistenvsigma)clinList.fold_right(fun(id,ipat)->Tactics.apply_delayed_inaevidlipat)clTacticals.tclIDTACintacendend|TacElim(ev,(keep,cb),cbo)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletsigma,cb=interp_open_constr_with_bindingsistenvsigmacbinletsigma,cbo=Option.fold_left_map(interp_open_constr_with_bindingsistenv)sigmacboinletnamed_tac=lettac=Tactics.elimevkeepcbcboinname_atomic~env(TacElim(ev,(keep,cb),cbo))tacinTacticals.tclWITHHOLESevnamed_tacsigmaend|TacCase(ev,(keep,cb))->Proofview.Goal.enterbeginfungl->letsigma=projectglinletenv=Proofview.Goal.envglinletsigma,cb=interp_open_constr_with_bindingsistenvsigmacbinletnamed_tac=lettac=Tactics.general_case_analysisevkeepcbinname_atomic~env(TacCase(ev,(keep,cb)))tacinTacticals.tclWITHHOLESevnamed_tacsigmaend|TacMutualFix(id,n,l)->(* spiwack: until the tactic is in the monad *)Proofview.Trace.name_tactic(fun()->Pp.str"<mutual fix>")beginProofview.Goal.enterbeginfungl->letenv=pf_envglinletfsigma(id,n,c)=let(sigma,c_interp)=interp_typeistenvsigmacinsigma,(interp_identistenvsigmaid,n,c_interp)inlet(sigma,l_interp)=Evd.MonadR.List.map_right(funcsigma->fsigmac)l(projectgl)inTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Tactics.mutual_fix(interp_identistenvsigmaid)nl_interp0)endend|TacMutualCofix(id,l)->(* spiwack: until the tactic is in the monad *)Proofview.Trace.name_tactic(fun()->Pp.str"<mutual cofix>")beginProofview.Goal.enterbeginfungl->letenv=pf_envglinletfsigma(id,c)=let(sigma,c_interp)=interp_typeistenvsigmacinsigma,(interp_identistenvsigmaid,c_interp)inlet(sigma,l_interp)=Evd.MonadR.List.map_right(funcsigma->fsigmac)l(projectgl)inTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Tactics.mutual_cofix(interp_identistenvsigmaid)l_interp0)endend|TacAssert(ev,b,t,ipat,c)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinlet(sigma,c)=letexpected_type=ifOption.is_emptytthenWithoutTypeConstraintelseIsTypeinletflags=open_constr_use_classes_flags()ininterp_open_constr~expected_type~flagsistenvsigmacinletipat'=interp_intro_pattern_optionistenvsigmaipatinlettac=Option.map(Option.map(interp_tacticist))tinTacticals.tclWITHHOLESev(name_atomic~env(TacAssert(ev,b,Option.map(Option.mapignore)t,ipat,c))(Tactics.forwardbtacipat'c))sigmaend|TacGeneralizecl->Proofview.Goal.enterbeginfungl->letsigma=projectglinletenv=Proofview.Goal.envglinletsigma,cl=interp_constr_with_occurrences_and_name_as_lististenvsigmaclinTacticals.tclWITHHOLESfalse(name_atomic~env(TacGeneralizecl)(Generalize.generalize_gencl))sigmaend|TacLetTac(ev,na,c,clp,b,eqpat)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletclp=interp_clauseistenvsigmaclpinleteqpat=interp_intro_pattern_naming_optionistenvsigmaeqpatinifLocusops.is_nowhereclp(* typically "pose" *)then(* We try to fully-typecheck the term *)letflags=open_constr_use_classes_flags()inlet(sigma,c_interp)=interp_open_constr~flagsistenvsigmacinletna=interp_nameistenvsigmanainletlet_tac=ifbthenTactics.pose_tacnac_interpelseletid=Option.default(CAst.makeIntroAnonymous)eqpatinletwith_eq=Some(true,id)inTactics.letin_tacwith_eqnac_interpNoneLocusops.nowhereinTacticals.tclWITHHOLESev(name_atomic~env(TacLetTac(ev,na,c_interp,clp,b,eqpat))let_tac)sigmaelse(* We try to keep the pattern structure as much as possible *)letlet_pat_tacbnaccleqpat=letid=Option.default(CAst.makeIntroAnonymous)eqpatinletwith_eq=ifbthenNoneelseSome(true,id)inTactics.letin_pat_tacevwith_eqnacclinlet(sigma',c)=interp_pure_open_constristenvsigmacinname_atomic~env(TacLetTac(ev,na,c,clp,b,eqpat))(Tacticals.tclWITHHOLESev(let_pat_tacb(interp_nameistenvsigmana)(Somesigma,c)clpeqpat)sigma')end(* Derived basic tactics *)|TacInductionDestruct(isrec,ev,(l,el))->(* spiwack: some unknown part of destruct needs the goal to be
prenormalised. *)Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletl=List.mapbeginfun(c,(ipato,ipats),cls)->(* TODO: move sigma as a side-effect *)(* spiwack: the [*p] variants are for printing *)letcp=cinletc=interp_destruction_argistglcinletipato=interp_intro_pattern_naming_optionistenvsigmaipatoinletipatsp=ipatsinletipats=interp_or_and_intro_pattern_optionistenvsigmaipatsinletcls=Option.map(interp_clauseistenvsigma)clsin((c,(ipato,ipats),cls),(cp,(ipato,ipatsp),cls))endlinletl,lp=List.splitlinletsigma,el=Option.fold_left_map(interp_open_constr_with_bindingsistenv)sigmaelinTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(name_atomic~env(TacInductionDestruct(isrec,ev,(lp,el)))(Induction.induction_destructisrecev(l,el)))end(* Conversion *)|TacReduce(r,cl)->Proofview.Goal.enterbeginfungl->let(sigma,r_interp)=interp_red_exprist(pf_envgl)(projectgl)rinTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Tactics.reducer_interp(interp_clauseist(pf_envgl)(projectgl)cl))end|TacChange(check,None,c,cl)->(* spiwack: until the tactic is in the monad *)Proofview.Trace.name_tactic(fun()->Pp.str"<change>")beginProofview.Goal.enterbeginfungl->letis_onhyps=matchcl.onhypswith|None|Some[]->true|_->falseinletis_onconcl=matchcl.concl_occswith|AtLeastOneOccurrence|AllOccurrences|NoOccurrences->true|_->falseinletc_interppatvarsenvsigma=letlfun'=Id.Map.fold(funidclfun->Id.Map.addid(Value.of_constrc)lfun)patvarsist.lfuninletist={istwithlfun=lfun'}inifis_onhyps&&is_onconcltheninterp_typeistenvsigmacelseinterp_constristenvsigmacinTactics.change~checkNonec_interp(interp_clauseist(pf_envgl)(projectgl)cl)endend|TacChange(check,Someop,c,cl)->(* spiwack: until the tactic is in the monad *)Proofview.Trace.name_tactic(fun()->Pp.str"<change>")beginProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletop=interp_typed_patternistenvsigmaopinletto_catch=functionNot_found->true|e->CErrors.is_anomalyeinletc_interppatvarsenvsigma=letlfun'=Id.Map.fold(funidclfun->Id.Map.addid(Value.of_constrc)lfun)patvarsist.lfuninletenv=ensure_freshnessenvinletist={istwithlfun=lfun'}intryinterp_constristenvsigmacwithewhento_catche(* Hack *)->user_err(strbrk"Failed to get enough information from the left-hand side to type the right-hand side.")inTactics.change~check(Someop)c_interp(interp_clauseistenvsigmacl)endend(* Equality and inversion *)|TacRewrite(ev,l,cl,by)->Proofview.Goal.enterbeginfungl->letl'=List.map(fun(b,m,(keep,c))->letfenvsigma=interp_open_constr_with_bindingsistenvsigmacin(b,m,keep,f))linletenv=Proofview.Goal.envglinletsigma=projectglinletcl=interp_clauseistenvsigmaclinname_atomic~env(TacRewrite(ev,l,cl,Option.mapignoreby))(Equality.general_multi_rewriteevl'cl(Option.map(funby->Tacticals.tclCOMPLETE(interp_tacticistby),Equality.Naive)by))end|TacInversion(DepInversion(k,c,ids),hyp)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinlet(sigma,c_interp)=matchcwith|None->sigma,None|Somec->let(sigma,c_interp)=interp_constristenvsigmacinsigma,Somec_interpinletdqhyps=interp_declared_or_quantified_hypothesisistenvsigmahypinletids_interp=interp_or_and_intro_pattern_optionistenvsigmaidsinTacticals.tclWITHHOLESfalse(name_atomic~env(TacInversion(DepInversion(k,c_interp,ids),dqhyps))(Inv.dinvkc_interpids_interpdqhyps))sigmaend|TacInversion(NonDepInversion(k,idl,ids),hyp)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinlethyps=interp_hyp_lististenvsigmaidlinletdqhyps=interp_declared_or_quantified_hypothesisistenvsigmahypinletids_interp=interp_or_and_intro_pattern_optionistenvsigmaidsinname_atomic~env(TacInversion(NonDepInversion(k,hyps,ids),dqhyps))(Inv.inv_clausekids_interphypsdqhyps)end|TacInversion(InversionUsing(c,idl),hyp)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinlet(sigma,c_interp)=interp_constristenvsigmacinletdqhyps=interp_declared_or_quantified_hypothesisistenvsigmahypinlethyps=interp_hyp_lististenvsigmaidlinTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(name_atomic~env(TacInversion(InversionUsing(c_interp,hyps),dqhyps))(Leminv.lemInv_clausedqhypsc_interphyps))end(* Initial call for interpretation *)letdefault_ist()=letextra=TacStore.setTacStore.emptyf_debug(get_debug())in{lfun=Id.Map.empty;poly=false;extra=extra}leteval_tactict=ifget_debug()<>DebugOffthenProofview.tclUNIT()>>=fun()->(* delay for [default_ist] *)Proofview.tclLIFT(db_initializetrue)<*>eval_tactic_ist(default_ist())telseProofview.tclUNIT()>>=fun()->(* delay for [default_ist] *)eval_tactic_ist(default_ist())tleteval_tactic_ististt=Proofview.tclLIFT(db_initializefalse)<*>eval_tactic_ististt(** FFI *)moduleValue=structincludeTaccoerce.Valueletof_closureisttac=letclosure=VFun(UnnamedAppl,extract_traceist,None,ist.lfun,[],tac)inof_tacvalueclosureletapply_exprfargs=letfoldarg(i,vars,lfun)=letid=Id.of_string("x"^string_of_inti)inletx=Reference(ArgVarCAst.(makeid))in(succi,x::vars,Id.Map.addidarglfun)inlet(_,args,lfun)=List.fold_rightfoldargs(0,[],Id.Map.empty)inletlfun=Id.Map.add(Id.of_string"F")flfuninletist={(default_ist())withlfun=lfun;}inist,CAst.make@@TacArg(TacCall(CAst.make(ArgVarCAst.(make@@Id.of_string"F"),args)))(** Apply toplevel tactic values *)letapply(f:value)(args:valuelist)=letist,tac=apply_exprfargsineval_tactic_ististtacletapply_val(f:value)(args:valuelist)=letist,tac=apply_exprfargsinval_interpisttacend(* globalization + interpretation *)letinterp_tac_genlfunavoid_idsdebugt=Proofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletextra=TacStore.setTacStore.emptyf_debugdebuginletextra=TacStore.setextraf_avoid_idsavoid_idsinletist={lfun;poly;extra}inletltacvars=Id.Map.domainlfunineval_tactic_istist(intern_pure_tactic{(Genintern.empty_glob_sign~strict:falseenv)withltacvars}t)endletinterpt=interp_tac_genId.Map.emptyId.Set.empty(get_debug())t(* MUST be marshallable! *)typeltac_expr={global:bool;ast:Tacexpr.raw_tactic_expr;}(* Used to hide interpretation for pretty-print, now just launch tactics *)(* [global] means that [t] should be internalized outside of goals. *)lethide_interp{global;ast}=lethide_interpenv=letist=Genintern.empty_glob_sign~strict:falseenvinlette=intern_pure_tacticistastinlett=eval_tacticteintinifglobalthenProofview.tclENV>>=funenv->hide_interpenvelseProofview.Goal.enterbeginfungl->hide_interp(Proofview.Goal.envgl)endletComTactic.Interpreterhide_interp=ComTactic.register_tactic_interpreter"ltac1"hide_interp(***************************************************************************)(** Register standard arguments *)letregister_interp0witf=letopenFtactic.Notationsinletinterpistv=fistv>>=funv->Ftactic.return(Val.inject(val_tagwit)v)inGeninterp.register_interp0witinterpletdef_internistx=(ist,x)letdef_subst_x=xletdef_interpistx=Ftactic.returnxletdeclare_uniformt=Genintern.register_intern0tdef_intern;Gensubst.register_subst0tdef_subst;register_interp0tdef_interplet()=declare_uniformwit_unitlet()=declare_uniformwit_intlet()=declare_uniformwit_natlet()=declare_uniformwit_boollet()=declare_uniformwit_stringletliftf=();funistx->Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinFtactic.return(fistenvsigmax)endletliftsf=();funistx->Ftactic.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet(sigma,v)=fistenvsigmaxinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(* FIXME once we don't need to catch side effects *)(Proofview.tclTHEN(Proofview.Unsafe.tclSETENV(Global.env()))(Ftactic.returnv))endletinterp_bindings'istbl=Ftactic.returnbeginfunenvsigma->interp_bindingsistenvsigmablendletinterp_constr_with_bindings'istc=Ftactic.returnbeginfunenvsigma->interp_constr_with_bindingsistenvsigmacendletinterp_open_constr_with_bindings'istc=Ftactic.returnbeginfunenvsigma->interp_open_constr_with_bindingsistenvsigmacendletinterp_destruction_arg'istc=Ftactic.enterbeginfungl->Ftactic.return(interp_destruction_argistglc)endletinterp_pre_identistenvsigmas=s|>Id.of_string|>interp_identistenvsigma|>Id.to_stringlet()=register_interp0wit_int_or_var(funistn->Ftactic.return(interp_int_or_varistn));register_interp0wit_nat_or_var(funistn->Ftactic.return(interp_int_or_varistn));register_interp0wit_smart_global(liftinterp_reference);register_interp0wit_ref(liftinterp_reference);register_interp0wit_pre_ident(liftinterp_pre_ident);register_interp0wit_ident(liftinterp_ident);register_interp0wit_hyp(liftinterp_hyp);register_interp0wit_intropattern(liftinterp_intro_pattern)[@warning"-3"];register_interp0wit_simple_intropattern(liftinterp_intro_pattern);register_interp0wit_clause_dft_concl(liftinterp_clause);register_interp0wit_constr(liftsinterp_constr);register_interp0wit_tacvalue(funistv->Ftactic.returnv);register_interp0Redexpr.wit_red_expr(liftsinterp_red_expr);register_interp0wit_quant_hyp(liftinterp_declared_or_quantified_hypothesis);register_interp0wit_open_constr(liftsinterp_open_constr);register_interp0wit_bindingsinterp_bindings';register_interp0wit_constr_with_bindingsinterp_constr_with_bindings';register_interp0wit_open_constr_with_bindingsinterp_open_constr_with_bindings';register_interp0wit_destruction_arginterp_destruction_arg';()let()=letinterpisttac=Ftactic.return(Value.of_closureisttac)inregister_interp0wit_tacticinterplet()=letinterpisttac=eval_tactic_ististtac>>=fun()->Ftactic.return()inregister_interp0wit_ltacinterplet()=register_interp0wit_uconstr(funistc->Ftactic.enterbeginfungl->Ftactic.return(interp_uconstrist(Proofview.Goal.envgl)(Tacmach.projectgl)c)end)(***************************************************************************)(* Other entry points *)letval_interpisttack=Ftactic.run(val_interpisttac)kletinterp_ltac_constristck=Ftactic.run(interp_ltac_constristc)kletinterp_redexpenvsigmar=letist=default_ist()inletgist=Genintern.empty_glob_sign~strict:trueenvininterp_red_expristenvsigma(intern_red_exprgistr)(***************************************************************************)(* Backwarding recursive needs of tactic glob/interp/eval functions *)let_=leteval?loc~polyenvsigmatycontac=letlfun=GlobEnv.lfunenvinletextra=TacStore.setTacStore.emptyf_debug(get_debug())inletist={lfun;poly;extra;}inlettac=eval_tactic_ististtacin(* EJGA: We should also pass the proof name if desired, for now
poly seems like enough to get reasonable behavior in practice
*)letname=Id.of_string"ltac_gen"inletsigma,ty=matchtyconwith|Somety->sigma,ty|None->GlobEnv.new_type_evarenvsigma~src:(loc,Evar_kinds.InternalHole)inlet(c,sigma)=Proof.refine_by_tactic~name~poly(GlobEnv.renamed_envenv)sigmatytacinletj={Environ.uj_val=EConstr.of_constrc;uj_type=ty}in(j,sigma)inGlobEnv.register_constr_interp0wit_tacticevalletvernac_debugb=set_debug(ifbthenTactic_debug.DebugOn0elseTactic_debug.DebugOff)let()=letopenGoptionsindeclare_bool_option{optstage=Summary.Stage.Interp;optdepr=None;optkey=["Ltac";"Debug"];optread=(fun()->get_debug()!=Tactic_debug.DebugOff);optwrite=vernac_debug}let()=letopenGoptionsindeclare_bool_option{optstage=Summary.Stage.Interp;optdepr=None;optkey=["Ltac";"Backtrace"];optread=(fun()->!log_trace);optwrite=(funb->log_trace:=b)}let()=Hook.setVernacentries.interp_redexp_hookinterp_redexp