123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423(************************************************************************)(* * 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) *)(************************************************************************)openUtilopenNamesopenContextopenLtac_pluginopenProofviewopenNotationsopenSsrcommonopenSsrastmoduleAdaptorDb=structtypekind=Forward|Backward|EquivalencemoduleAdaptorKind=structtypet=kindletcompare=Stdlib.compareendmoduleAdaptorMap=Map.Make(AdaptorKind)letterm_view_adaptor_db=Summary.ref~name:"view_adaptor_db"AdaptorMap.emptyletgetk=tryAdaptorMap.findk!term_view_adaptor_dbwithNot_found->[]letcache_adaptor(k,t)=letlk=getkinifnot(List.exists(Glob_ops.glob_constr_eqt)lk)thenterm_view_adaptor_db:=AdaptorMap.addk(t::lk)!term_view_adaptor_dbletsubst_adaptor(subst,(k,tasa))=lett'=Detyping.subst_glob_constr(Global.env())substtinift'==tthenaelsek,t'letin_db=letopenLibobjectindeclare_object@@global_object_nodischarge"VIEW_ADAPTOR_DB"~cache:cache_adaptor~subst:(Somesubst_adaptor)letdeclarekindterms=List.iter(funterm->Lib.add_leaf(in_db(kind,term)))(List.revterms)end(* Forward View application code *****************************************)letreduce_orl=tclUNIT(List.fold_left(||)falsel)moduleState:sig(* View storage API *)valvsINIT:view:EConstr.t->subject_name:Id.tlist->to_clear:Id.tlist->unittacticvalvsPUSH:(EConstr.t->(EConstr.t*Id.tlist)tactic)->unittacticvalvsCONSUME:(names:Id.tlist->EConstr.t->to_clear:Id.tlist->unittactic)->unittactic(* The bool is the || of the bool returned by the continuations *)valvsCONSUME_IF_PRESENT:(names:Id.tlist->EConstr.toption->to_clear:Id.tlist->booltactic)->booltacticvalvsASSERT_EMPTY:unittacticend=struct(* {{{ *)typevstate={subject_name:Id.tlist;(* top *)(* None if views are being applied to a term *)view:EConstr.t;(* v2 (v1 top) *)to_clear:Id.tlist;}includeSsrcommon.MakeState(structtypestate=vstateoptionletinit=Noneletname="ssrview"end)letvsINIT~view~subject_name~to_clear=tclSET(Some{subject_name;view;to_clear})(** Initializes the state in which view data is accumulated when views are
applied to the first assumption in the goal *)letvsBOOTSTRAP=Goal.enter_one~__LOC__beginfungl->letconcl=Goal.conclglinletid=(* We keep the orig name for checks in "in" tcl *)letopenEConstrinmatchkind_of_type(Goal.sigmagl)conclwith|ProdType({binder_name=Name.Nameid},_,_)whenSsrcommon.is_discharged_idid->id|_->mk_anon_id"view_subject"(Tacmach.pf_ids_of_hypsgl)inletview=EConstr.mkVaridinSsrcommon.tclINTRO_IDid<*>tclSET(Some{subject_name=[id];view;to_clear=[]})endletrecvsPUSHk=tclINDEPENDENT(tclGET(function|Some{subject_name;view;to_clear}->kview>>=fun(view,clr)->tclSET(Some{subject_name;view;to_clear=to_clear@clr})|None->vsBOOTSTRAP<*>vsPUSHk))letrecvsCONSUMEk=tclINDEPENDENT(tclGET(function|Some{subject_name;view;to_clear}->tclSETNone<*>k~names:subject_nameview~to_clear|None->vsBOOTSTRAP<*>vsCONSUMEk))letvsCONSUME_IF_PRESENTk=tclINDEPENDENTL(tclGET1(function|Some{subject_name;view;to_clear}->tclSETNone<*>k~names:subject_name(Someview)~to_clear|None->k~names:[]None~to_clear:[]))>>=reduce_orletvsASSERT_EMPTY=tclGET(function|Some_->anomaly("vsASSERT_EMPTY: not empty")|_->tclUNIT())end(* }}} *)letintern_constr_expr{Genintern.genv;ltacvars=vars}sigmace=letltacvars={Constrintern.empty_ltac_signwithConstrintern.ltac_vars=vars}inConstrintern.intern_genPretyping.WithoutTypeConstraint~ltacvarsgenvsigmace(* Disambiguation of /t
- t is ltac:(tactic args)
- t is a term
To allow for t being a notation, like "Notation foo x := ltac:(foo x)", we
need to internalize t.
*)letis_tac_in_term?extra_scope{annotation;body;glob_env;interp_env}=Goal.(enter_one~__LOC__beginfungoal->letgenv=envgoalinletsigma=sigmagoalinletist=Ssrcommon.option_assert_getglob_env(Pp.str"not a term")in(* We use the env of the goal, not the global one *)letist=letopenGeninternin{ltacvars=ist.ast_ltacvars;extra=ist.ast_extra;intern_sign=ist.ast_intern_sign;genv;strict_check=true;}in(* We open extra_scope *)letbody=matchextra_scopewith|None->body|Somes->CAst.makeConstrexpr.(CDelimiters(DelimUnboundedScope,s,body))in(* We unravel notations *)letg=intern_constr_expristsigmabodyinmatchDAst.getgwith|Glob_term.GGenargxwhenGenarg.has_typex(Genarg.glbwitTacarg.wit_tactic)->tclUNIT(`Tac(Genarg.out_gen(Genarg.glbwitTacarg.wit_tactic)x))|_->tclUNIT(`Term(annotation,interp_env,g))end)(* To inject a constr into a glob_constr we use an Ltac variable *)lettclINJ_CONSTR_ISTistp=letfresh_id=Ssrcommon.mk_internal_id"ssr_inj_constr_in_glob"inletist={istwithGeninterp.lfun=Id.Map.addfresh_id(Taccoerce.Value.of_constrp)ist.Geninterp.lfun}intclUNIT(ist,Glob_term.GVarfresh_id)letmkGHole=DAst.make(Glob_term.GHole(GInternalHole))letrecmkGHolesn=ifn>0thenmkGHole::mkGHoles(n-1)else[]letmkGAppfargs=ifargs=[]thenfelseDAst.make(Glob_term.GApp(f,args))(* From glob_constr to open_constr === (env,sigma,constr) *)letinterp_globistglob=Goal.enter_one~__LOC__beginfungoal->letenv=Goal.envgoalinletsigma=Goal.sigmagoalinSsrprinters.debug_ssr(fun()->Pp.(str"interp-in: "++Printer.pr_glob_constr_envenvsigmaglob));tryletsigma,term=Tacinterp.interp_open_constristenvsigma(glob,None)inSsrprinters.debug_ssr(fun()->Pp.(str"interp-out: "++Printer.pr_econstr_envenvsigmaterm));tclUNIT(env,sigma,term)withewhenCErrors.noncriticale->lete,info=Exninfo.captureeinSsrprinters.debug_ssr(fun()->Pp.(str"interp-err: "++Printer.pr_glob_constr_envenvsigmaglob));tclZERO~infoeend(* Commits the term to the monad *)(* I think we should make the API safe by storing here the original evar map,
* so that one cannot commit it wrongly.
* We could also commit the term automatically, but this makes the code less
* modular, see the 2 functions below that would need to "uncommit" *)lettclKeepOpenConstr(_env,sigma,t)=Unsafe.tclEVARSsigma<*>tclUNITtlettclADD_CLEAR_IF_ID(env,ist,t)x=Ssrprinters.debug_ssr(fun()->Pp.(str"tclADD_CLEAR_IF_ID: "++Printer.pr_econstr_envenvistt));lethd,args=EConstr.decompose_appisttinmatchEConstr.kindisthdwith|Constr.VaridwhenSsrcommon.not_section_idid->tclUNIT(x,[id])|_->tclUNIT(x,[])lettclPAIRpx=tclUNIT(x,p)(* The ssr heuristic : *)(* Estimate a bound on the number of arguments of a raw constr. *)(* This is not perfect, because the unifier may fail to *)(* typecheck the partial application, so we use a minimum of 5. *)(* Also, we don't handle delayed or iterated coercions to *)(* FUNCLASS, which is probably just as well since these can *)(* lead to infinite arities. *)letguess_max_implicitsistglob=Proofview.tclORELSE(interp_globist(mkGAppglob(mkGHoles6))>>=fun(env,sigma,term)->letterm_ty=Retyping.get_type_ofenvsigmaterminletctx,_=Reductionops.whd_decompose_prodenvsigmaterm_tyintclUNIT(List.lengthctx+6))(fun_->tclUNIT5)letpad_to_inductiveistglob=Goal.enter_one~__LOC__beginfungoal->interp_globistglob>>=fun(env,sigma,termasot)->letterm_ty=Retyping.get_type_ofenvsigmaterminletctx,i=Reductionops.whd_decompose_prodenvsigmaterm_tyinletrel_ctx=List.map(fun(a,b)->Context.Rel.Declaration.LocalAssum(a,b))ctxinifnot(Ssrcommon.isAppInd(EConstr.push_rel_contextrel_ctxenv)sigmai)thenTacticals.tclZEROMSGPp.(str"not an inductive")elsetclUNIT(mkGAppglob(mkGHoles(List.lengthctx)))>>=tclADD_CLEAR_IF_IDotend(* There are two ways of "applying" a view to term: *)(* 1- using a view hint if the view is an instance of some *)(* (reflection) inductive predicate. *)(* 2- applying the view if it coerces to a function, adding *)(* implicit arguments. *)(* They require guessing the view hints and the number of *)(* implicits, respectively, which we do by brute force. *)(* Builds v p *)letinterp_view~clear_if_idistvp=letis_specializehd=matchDAst.gethdwithGlob_term.GHole_->true|_->falsein(* We cast the pile of views p into a term p_id *)tclINJ_CONSTR_ISTistp>>=fun(ist,p_id)->letp_id=DAst.makep_idinmatchDAst.getvwith|Glob_term.GApp(hd,rargs)whenis_specializehd->Ssrprinters.debug_ssr(fun()->Pp.(str"specialize"));interp_globist(mkGAppp_idrargs)>>=tclKeepOpenConstr>>=tclPAIR[]|_->Ssrprinters.debug_ssr(fun()->Pp.(str"view"));(* We find out how to build (v p) eventually using an adaptor *)letadaptors=AdaptorDb.(getForward)inProofview.tclORELSE(pad_to_inductiveistv>>=fun(vpad,clr)->Ssrcommon.tclFIRSTa(List.map(funa->interp_globist(mkGAppa[vpad;p_id]))adaptors)>>=tclPAIRclr)(fun_->guess_max_implicitsistv>>=funn->Ssrcommon.tclFIRSTi(funn->interp_globist(mkGAppv(mkGHolesn@[p_id])))n>>=funx->tclADD_CLEAR_IF_IDxx)>>=fun(ot,clr)->ifclear_if_idthentclKeepOpenConstrot>>=tclPAIRclrelsetclKeepOpenConstrot>>=tclPAIR[](* we store in the state (v top), then (v1 (v2 top))... *)letpile_up_view~clear_if_id(annotation,ist,v)=letist=Ssrcommon.option_assert_getist(Pp.str"not a term")inletclear_if_id=clear_if_id&&annotation<>`ParensinState.vsPUSH(funp->interp_view~clear_if_idistvp)letfinalize_views0?(simple_types=true)p=Goal.enter_one~__LOC__beginfung->letenv=Goal.envginletsigma=Goal.sigmaginletevars_of_p=Evd.evars_of_termsigmapinletfilterx_=Evar.Set.memxevars_of_pinletsigma=Evd.push_shelfsigmainletsigma=Typeclasses.resolve_typeclasses~fail:false~filterenvsigmainlet_,sigma=Evd.pop_shelfsigmainletp=Reductionops.nf_evarsigmapinletget_body:typea.aEvd.evar_body->EConstr.t=functionEvd.Evar_definedx->x|Evd.Evar_empty->assertfalseinletevars_of_econstrsigmat=Evarutil.undefined_evars_of_termsigmatinletrigid_ofs=List.fold_left(funlk->ifEvd.is_definedsigmakthenletEvarInfoevi=Evd.findsigmakinletbo=get_body(Evd.evar_bodyevi)ink::l@Evar.Set.elements(evars_of_econstrsigmabo)elsel)[]sinletenv0=Proofview.Goal.envs0inletsigma0=Proofview.Goal.sigmas0inletund0=(* Unassigned evars in the initial goal *)letEvarInfog0info=Evd.findsigma0(Proofview.Goal.goals0)inletg0=Evd.evars_of_filtered_evar_infosigma0g0infoinList.filter(funk->Evar.Set.memkg0)(List.mapfst(Evar.Map.bindings(Evd.undefined_mapsigma0)))inletrigid=rigid_ofund0inletp,to_prune,_ucst=abs_evarsenv0sigma0~rigid(sigma,p)inletp=ifsimple_typesthenabs_ctermenv0sigma0(List.lengthto_prune)pelsepinSsrprinters.debug_ssr(fun()->Pp.(str"view@finalized: "++Printer.pr_econstr_envenvsigmap));letsigma=List.fold_leftEvd.removesigmato_pruneinUnsafe.tclEVARSsigma<*>tclUNITpendletpose_proofsubject_namep=Generalize.generalize[p]<*>beginmatchsubject_namewith|id::_->Ssrcommon.tclRENAME_HD_PROD(Name.Nameid)|_->tclUNIT()end<*>Tactics.reduce_after_refine(* returns true if the last item was a tactic *)letrecapply_all_views_aux~clear_if_idvsfinalizationconclusions0=matchvswith|[]->finalizations0(funnamep->(matchpwith|None->conclusion~to_clear:[]|Somep->pose_proofnamep<*>conclusion~to_clear:name)<*>tclUNITfalse)|v::vs->Ssrprinters.debug_ssr(fun()->Pp.(str"piling..."));is_tac_in_term~extra_scope:"ssripat"v>>=function|`Termv->Ssrprinters.debug_ssr(fun()->Pp.(str"..a term"));pile_up_view~clear_if_idv<*>apply_all_views_aux~clear_if_idvsfinalizationconclusions0|`Tactac->Ssrprinters.debug_ssr(fun()->Pp.(str"..a tactic"));finalizations0(funnamep->(matchpwith|None->tclUNIT()|Somep->pose_proofnamep)<*>Tacinterp.eval_tactictac<*>ifvs=[]thenbeginSsrprinters.debug_ssr(fun()->Pp.(str"..was the last view"));conclusion~to_clear:name<*>tclUNITtrueendelseTactics.clearname<*>tclINDEPENDENTLbeginSsrprinters.debug_ssr(fun()->Pp.(str"..was NOT the last view"));Proofview.Goal.enter_onebeginfungl->apply_all_views_aux~clear_if_idvsfinalizationconclusionglendend>>=reduce_or)letapply_all_viewsvs~conclusion~clear_if_id=letfinalizations0k=State.vsCONSUME_IF_PRESENT(fun~namest~to_clear->matchtwith|None->k[]None|Somet->finalize_views0t>>=funp->k(names@to_clear)(Somep))inProofview.Goal.enter_onebeginfungl->apply_all_views_aux~clear_if_idvsfinalizationconclusionglend(* We apply a view to a term given by the user, e.g. `case/V: x`. `x` is
`subject` *)letapply_all_views_tosubject~simple_typesvs~conclusion=beginletrecprocess_all_vs=function|[]->tclUNIT()|v::vs->is_tac_in_termv>>=function|`Tac_->Ssrcommon.errorstrmPp.(str"tactic view not supported")|`Termv->pile_up_view~clear_if_id:falsev<*>process_all_vsvsinState.vsASSERT_EMPTY<*>State.vsINIT~subject_name:[]~to_clear:[]~view:subject<*>Proofview.Goal.enter_onebeginfuns0->process_all_vsvs<*>State.vsCONSUME(fun~names:_t~to_clear:_->finalize_views0~simple_typest>>=conclusion)endend(* Entry points *********************************************************)lettclIPAT_VIEWS~views:vs?(clear_if_id=false)~conclusion()=tclINDEPENDENTLbeginState.vsASSERT_EMPTY<*>apply_all_viewsvs~conclusion~clear_if_id>>=funwas_tac->State.vsASSERT_EMPTY<*>tclUNITwas_tacend>>=reduce_orlettclWITH_FWD_VIEWS~simple_types~subject~views:vs~conclusion=tclINDEPENDENT(apply_all_views_tosubject~simple_typesvs~conclusion)(* vim: set filetype=ocaml foldmethod=marker: *)