123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581(************************************************************************)(* * 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) *)(************************************************************************)openEqualityopenNamesopenPpopenConstropenCErrorsopenUtilopenMod_substopenLocus(* Rewriting rules *)typerew_rule={rew_id:KerName.t;rew_lemma:constr;rew_type:types;rew_pat:constr;rew_ctx:Univ.ContextSet.t;rew_l2r:bool;rew_tac:Genarg.glob_generic_argumentoption}moduleRewRule=structtypet=rew_ruleletrew_lemmar=(r.rew_ctx,r.rew_lemma)letrew_l2rr=r.rew_l2rletrew_tacr=r.rew_tacendmoduleHintIdent=structtypet=rew_ruleletcomparer1r2=KerName.comparer1.rew_idr2.rew_idletconstr_oft=t.rew_patend(* Representation/approximation of terms to use in the dnet:
*
* - no meta or evar (use ['a pattern] for that)
*
* - [Rel]s and [Sort]s are not taken into account (that's why we need
* a second pass of linear filterin on the results - it's not a perfect
* term indexing structure)
*)moduleDTerm=structtype'tt=|DRel|DSort|DRefofGlobRef.t|DProd|DLet|DLambda|DApp|DCaseofcase_info|DFixofintarray*int|DCoFixofint|DIntofUint63.t|DFloatofFloat64.t|DStringofPstring.t|DArrayletcompare_cici1ci2=letc=Label.compare(MutInd.label@@fstci1.ci_ind)(MutInd.label@@fstci2.ci_ind)inifc=0thenletc=Int.compareci1.ci_nparci2.ci_nparinifc=0thenletc=Array.compareInt.compareci1.ci_cstr_ndeclsci2.ci_cstr_ndeclsinifc=0thenArray.compareInt.compareci1.ci_cstr_nargsci2.ci_cstr_nargselsecelsecelsecletcomparet1t2=matcht1,t2with|DRel,DRel->0|DRel,_->-1|_,DRel->1|DSort,DSort->0|DSort,_->-1|_,DSort->1|DRefgr1,DRefgr2->GlobRef.UserOrd.comparegr1gr2|DRef_,_->-1|_,DRef_->1|DProd,DProd->0|DProd,_->-1|_,DProd->1|DLet,DLet->0|DLet,_->-1|_,DLet->1|DLambda,DLambda|DApp,DApp->0|DLambda,_->-1|_,DLambda->1|DApp,_->-1|_,DApp->1|DCaseci1,DCaseci2->compare_cici1ci2|DCase_,_->-1|_,DCase_->1|DFix(i1,j1),DFix(i2,j2)->letc=Int.comparej1j2inifc=0thenArray.compareInt.comparei1i2elsec|DFix_,_->-1|_,DFix_->1|DCoFixi1,DCoFixi2->Int.comparei1i2|DCoFix_,_->-1|_,DCoFix_->1|DInti1,DInti2->Uint63.comparei1i2|DInt_,_->-1|_,DInt_->1|DFloatf1,DFloatf2->Float64.total_comparef1f2|DFloat_,_->-1|_,DFloat_->1|DStrings1,DStrings2->Pstring.compares1s2|DString_,_->-1|_,DString_->1|DArray,DArray->1end(*
* Terms discrimination nets
* Uses the general dnet datatype on DTerm.t
* (here you can restart reading)
*)moduleHintDN:sigtypettypeident=HintIdent.tvalempty:t(** [add c i dn] adds the binding [(c,i)] to [dn]. [c] can be a
closed term or a pattern (with untyped Evars). No Metas accepted *)valadd:Environ.env->constr->ident->t->t(*
* High-level primitives describing specific search problems
*)(** [search_pattern dn c] returns all terms/patterns in dn
matching/matched by c *)valsearch_pattern:Environ.env->t->constr->identlist(** [find_all dn] returns all idents contained in dn *)valfind_all:t->identlistend=structmoduleIdent=HintIdentmodulePTerm=structtypet=unitDTerm.tletcompare=DTerm.compareendmoduleTDnet=Dn.Make(PTerm)(Ident)typet=TDnet.ttypeident=HintIdent.topenDTermopenTDnetletpat_of_constrenvc:(unitDTerm.t*Constr.tlist)option=letopenGlobRefinletrecpat_of_constrc=matchConstr.kindcwith|Rel_->Some(DRel,[])|Sort_->Some(DSort,[])|Vari->Some(DRef(VarRefi),[])|Const(c,u)->Some(DRef(ConstRef(Environ.QConstant.canonizeenvc)),[])|Ind(i,u)->Some(DRef(IndRef(Environ.QInd.canonizeenvi)),[])|Construct(c,u)->Some(DRef(ConstructRef(Environ.QConstruct.canonizeenvc)),[])|Meta_->assertfalse|Evar(i,_)->None|Case(ci,u1,pms1,(c1,_),_iv,c2,ca)->letf_ctx(_,p)=pinSome(DCase(ci),[f_ctxc1;c2]@Array.map_to_listf_ctxca)|Fix((ia,i),(_,ta,ca))->Some(DFix(ia,i),Array.to_listta@Array.to_listca)|CoFix(i,(_,ta,ca))->Some(DCoFix(i),Array.to_listta@Array.to_listca)|Cast(c,_,_)->pat_of_constrc|Lambda(_,t,c)->Some(DLambda,[t;c])|Prod(_,t,u)->Some(DProd,[t;u])|LetIn(_,c,t,u)->Some(DLet,[c;t;u])|App(f,ca)->letlen=Array.lengthcainleta=ca.(len-1)inletca=Array.subca0(len-1)inSome(DApp,[mkApp(f,ca);a])|Proj(p,_,c)->(* UnsafeMonomorphic is fine because the term will only be used
by pat_of_constr which ignores universes *)pat_of_constr(mkApp(UnsafeMonomorphic.mkConst(Projection.constantp),[|c|]))|Inti->Some(DInti,[])|Floatf->Some(DFloatf,[])|Strings->Some(DStrings,[])|Array(_u,t,def,ty)->Some(DArray,Array.to_listt@[def;ty])inpat_of_constrc(*
* Basic primitives
*)letempty=TDnet.emptyletaddenv(c:constr)(id:Ident.t)(dn:t)=(* We used to consider the types of the product as well, but since the dnet
is only computing an approximation rectified by [filtering] we do not
anymore. *)let(ctx,c)=Term.decompose_prod_declscinletc=TDnet.pattern(func->pat_of_constrenvc)cinTDnet.adddncid(* App(c,[t1,...tn]) -> ([c,t1,...,tn-1],tn)
App(c,[||]) -> ([],c) *)letsplit_appsigmac=matchEConstr.kindsigmacwithApp(c,l)->letlen=Array.lengthlinifInt.equallen0then([],c)elseletlast=Array.getl(len-1)inletprev=Array.subl0(len-1)inc::(Array.to_listprev),last|_->assertfalseexceptionCannotFilterletfilteringenvsigmactxcv_pbc1c2=letopenEConstrinletopenVarsinletevm=refEvar.Map.emptyinletdefinecv_pbe1evc1=trylet(e2,c2)=Evar.Map.findev!evminletshift=e1-e2inifTermops.constr_cmpenvsigmacv_pbc1(liftshiftc2)then()elseraiseCannotFilterwithNot_found->evm:=Evar.Map.addev(e1,c1)!evminletrecauxctxcv_pbc1c2=matchEConstr.kindsigmac1,EConstr.kindsigmac2with|App_,App_->let((p1,l1),(p2,l2))=(split_appsigmac1),(split_appsigmac2)inlet()=auxctxcv_pbl1l2inbeginmatchp1,p2with|[],[]->()|(h1::p1),(h2::p2)->auxctxcv_pb(applist(h1,p1))(applist(h2,p2))|_->assertfalseend|Prod(n,t1,c1),Prod(_,t2,c2)->auxctxcv_pbt1t2;aux(ctx+1)cv_pbc1c2|_,Evar(ev,_)->definecv_pbctxevc1|Evar(ev,_),_->definecv_pbctxevc2|_->ifTermops.compare_constr_univenvsigma(funpbc1c2->auxctxpbc1c2;true)cv_pbc1c2then()elseraiseCannotFilter(* TODO: le reste des binders *)intrylet()=auxctxcv_pbc1c2intruewithCannotFilter->falseletalign_prod_letinsigmaca=let(lc,_)=EConstr.decompose_prod_declssigmacinlet(l,a)=EConstr.decompose_prod_declssigmaainletlc=List.lengthlcinletn=List.lengthlinifn<lctheninvalid_arg"align_prod_letin";letl1=CList.firstnlclinn-lc,EConstr.it_mkProd_or_LetInal1letdecompenvpat=matchpat_of_constrenvpatwith|None->Dn.Everything|Some(lbl,args)->Dn.Label(lbl,args)letsearch_patternenvdncpat=let_dctx,dpat=Term.decompose_prod_declscpatinletwhole_c=EConstr.of_constrcpatinList.fold_left(funaccid->letc_id=EConstr.of_constr@@Ident.constr_ofidinlet(ctx,wc)=tryalign_prod_letinEvd.emptywhole_cc_id(* FIXME *)withInvalid_argument_->0,c_idiniffilteringenvEvd.emptyctxConversion.CUMULwhole_cwcthenid::accelseacc)(TDnet.lookupdn(func->decompenvc)dpat)[]letfind_alldn=TDnet.lookupdn(fun()->Everything)()endtyperewrite_db={rdb_hintdn:HintDN.t;rdb_order:intKNmap.t;rdb_maxuid:int;}letempty_rewrite_db={rdb_hintdn=HintDN.empty;rdb_order=KNmap.empty;rdb_maxuid=0;}(* Summary and Object declaration *)letrewtab=Summary.ref(String.Map.empty:rewrite_dbString.Map.t)~name:"autorewrite"letraw_find_basebas=String.Map.findbas!rewtabletfind_basebas=tryraw_find_basebaswithNot_found->user_err(str"Rewriting base "++strbas++str" does not exist.")letfind_rewritesbas=letdb=find_basebasinletsortr1r2=Int.compare(KNmap.findr2.rew_iddb.rdb_order)(KNmap.findr1.rew_iddb.rdb_order)inList.sortsort(HintDN.find_alldb.rdb_hintdn)letfind_matchesenvbaspat=letbase=find_basebasinletres=HintDN.search_patternenvbase.rdb_hintdnpatinletsortr1r2=Int.compare(KNmap.findr2.rew_idbase.rdb_order)(KNmap.findr1.rew_idbase.rdb_order)inList.sortsortresletprint_rewrite_hintdbbas=letenv=Global.env()inletsigma=Evd.from_envenvin(str"Database "++strbas++fnl()++prlist_with_sepfnl(funh->str(ifh.rew_l2rthen"rewrite -> "else"rewrite <- ")++Printer.pr_lconstr_envenvsigmah.rew_lemma++str" of type "++Printer.pr_lconstr_envenvsigmah.rew_type++Option.cata(funtac->str" then use tactic "++Pputils.pr_glb_genericenvsigmatac)(mt())h.rew_tac)(find_rewritesbas))typeraw_rew_rule=(constrUniv.in_universe_context_set*bool*Genarg.raw_generic_argumentoption)CAst.tlettclMAP_revfargs=List.fold_left(funaccuarg->Tacticals.tclTHENaccu(farg))(Proofview.tclUNIT())args(* Applies all the rules of one base *)letone_basewherecondstac_mainbas=letlrul=find_rewritesbasinletrewritedirctac=letc=(EConstr.of_constrc,Tactypes.NoBindings)ingeneral_rewrite~where~l2r:dirAllOccurrences~freeze:true~dep:false~with_evars:false~tac:(tac,conds)cinlettry_rewritehtc=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletsubst,ctx'=UnivGen.fresh_universe_context_set_instanceh.rew_ctxinletsubst=Sorts.QVar.Map.empty,substinletc'=Vars.subst_univs_level_constrsubsth.rew_lemmainletsigma=Evd.merge_context_setEvd.univ_flexiblesigmactx'inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(rewriteh.rew_l2rc'tc)endinletopenProofview.NotationsinProofview.tclProofInfo[@ocaml.warning"-3"]>>=fun(_name,poly)->letevalh=lettac=matchh.rew_tacwith|None->Proofview.tclUNIT()|Some(Genarg.GenArg(Genarg.Glbwitwit,tac))->letist={Geninterp.lfun=Id.Map.empty;poly;extra=Geninterp.TacStore.empty}inFtactic.run(Geninterp.interpwitisttac)(fun_->Proofview.tclUNIT())inTacticals.tclREPEAT_MAIN(Tacticals.tclTHENFIRST(try_rewritehtac)tac_main)inletlrul=tclMAP_revevallrulinTacticals.tclREPEAT_MAIN(Proofview.tclPROGRESSlrul)(* The AutoRewrite tactic *)letautorewrite?(conds=Naive)tac_mainlbas=Tacticals.tclREPEAT_MAIN(Proofview.tclPROGRESS(tclMAP_rev(funbas->(one_baseNonecondstac_mainbas))lbas))letautorewrite_multi_in?(conds=Naive)idltac_mainlbas=Proofview.Goal.enterbeginfungl->(* let's check at once if id exists (to raise the appropriate error) *)let_=List.map(funid->Tacmach.pf_get_hypidgl)idlinTacticals.tclMAP(funid->Tacticals.tclREPEAT_MAIN(Proofview.tclPROGRESS(tclMAP_rev(funbas->(one_base(Someid)condstac_mainbas))lbas)))idlendletautorewrite_in?(conds=Naive)id=autorewrite_multi_in~conds[id]letgen_auto_multi_rewritecondstac_mainlbascl=lettry_do_hypstreat_idl=autorewrite_multi_in~conds(List.maptreat_idl)tac_mainlbasinletconcl_tac=(ifcl.concl_occs!=NoOccurrencesthenautorewrite~condstac_mainlbaselseProofview.tclUNIT())inifnot(Locusops.is_all_occurrencescl.concl_occs)&&cl.concl_occs!=NoOccurrencesthenletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"The \"at\" syntax isn't available yet for the autorewrite tactic.")elsematchcl.onhypswith|Some[]->concl_tac|Somel->Tacticals.tclTHENFIRSTconcl_tac(try_do_hyps(fun((_,id),_)->id)l)|None->lethyp_tac=(* try to rewrite in all hypothesis (except maybe the rewritten one) *)Proofview.Goal.enterbeginfungl->letids=Tacmach.pf_ids_of_hypsglintry_do_hyps(funid->id)idsendinTacticals.tclTHENFIRSTconcl_tachyp_tacletauto_multi_rewrite?(conds=Naive)lemscl=Proofview.wrap_exceptions(fun()->gen_auto_multi_rewriteconds(Proofview.tclUNIT())lemscl)(* Same hack as auto hints: we generate an essentially unique identifier for
rewrite hints. *)letfresh_key=letid=Summary.ref~name:"REWHINT-COUNTER"0infun()->letcur=incrid;!idinletlbl=Id.of_string("_"^string_of_intcur)inletkn=Lib.make_knlblinlet(mp,_)=KerName.reprknin(* We embed the full path of the kernel name in the label so that
the identifier should be unique. This ensures that including
two modules together won't confuse the corresponding labels. *)letlbl=Id.of_string_soft(Printf.sprintf"%s#%i"(ModPath.to_stringmp)cur)inKerName.makemp(Label.of_idlbl)letauto_multi_rewrite_with?(conds=Naive)tac_mainlbascl=letonconcl=matchcl.Locus.concl_occswithNoOccurrences->false|_->trueinmatchonconcl,cl.Locus.onhypswith|false,Some[_]|true,Some[]|false,Some[]->(* autorewrite with .... in clause using tac n'est sur que
si clause represente soit le but soit UNE hypothese
*)Proofview.wrap_exceptions(fun()->gen_auto_multi_rewritecondstac_mainlbascl)|_->letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(strbrk"autorewrite .. in .. using can only be used either with a unique hypothesis or on the conclusion.")(* Functions necessary to the library object declaration *)letcache_hintrewrite(rbase,lrl)=letbase=tryraw_find_baserbasewithNot_found->empty_rewrite_dbinletfoldaccur={rdb_hintdn=HintDN.add(Global.env())r.rew_patraccu.rdb_hintdn;rdb_order=KNmap.addr.rew_idaccu.rdb_maxuidaccu.rdb_order;rdb_maxuid=accu.rdb_maxuid+1;}inletbase=List.fold_leftfoldbaselrlinrewtab:=String.Map.addrbasebase!rewtabletsubst_hintrewrite(subst,(rbase,listasnode))=letsubst_hintsubsthint=letid'=subst_knsubsthint.rew_idinletcst'=subst_mpssubsthint.rew_lemmainlettyp'=subst_mpssubsthint.rew_typeinletpat'=subst_mpssubsthint.rew_patinlett'=Option.Smart.map(Gensubst.generic_substitutesubst)hint.rew_tacinifhint.rew_id==id'&&hint.rew_lemma==cst'&&hint.rew_type==typ'&&hint.rew_tac==t'&&hint.rew_pat==pat'thenhintelse{hintwithrew_lemma=cst';rew_type=typ';rew_pat=pat';rew_tac=t'}inletlist'=List.Smart.map(funh->subst_hintsubsth)listiniflist'==listthennodeelse(rbase,list')(* Declaration of the Hint Rewrite library object *)letinGlobalHintRewrite:string*rew_rulelist->Libobject.obj=letopenLibobjectindeclare_object@@superglobal_object_nodischarge"HINT_REWRITE_GLOBAL"~cache:cache_hintrewrite~subst:(Somesubst_hintrewrite)letinExportHintRewrite:string*rew_rulelist->Libobject.obj=letopenLibobjectindeclare_object@@global_object_nodischarge~cat:Hints.hint_cat"HINT_REWRITE_EXPORT"~cache:cache_hintrewrite~subst:(Somesubst_hintrewrite)typehypinfo={hyp_ty:EConstr.types;hyp_pat:EConstr.constr;}letdecompose_applied_relationenvsigmacctypeleft2right=letfind_relty=(* FIXME: this is nonsense, we generate evars and then we drop the
corresponding evarmap. This sometimes works because [Term_dnet] performs
evar surgery via [Termops.filtering]. *)letsigma,ty=EClause.make_evar_clauseenvsigmatyinlet(_,args)=EConstr.decompose_appsigmaty.EClause.cl_conclinletlen=Array.lengthargsinif2<=lenthenletc1=args.(len-2)inletc2=args.(len-1)inSome(ifleft2rightthenc1elsec2)elseNoneinmatchfind_relctypewith|Somec->Some{hyp_pat=c;hyp_ty=ctype}|None->letctx,t'=Reductionops.whd_decompose_prod_declsenvsigmactypein(* Search for underlying eq *)letctype=EConstr.it_mkProd_or_LetInt'ctxinmatchfind_relctypewith|Somec->Some{hyp_pat=c;hyp_ty=ctype}|None->Noneletfind_applied_relation?locenvsigmacleft2right=letctype=Retyping.get_type_ofenvsigma(EConstr.of_constrc)inmatchdecompose_applied_relationenvsigmacctypeleft2rightwith|Somec->c|None->user_err?loc(str"The type"++spc()++Printer.pr_econstr_envenvsigmactype++spc()++str"of this term does not end with an applied relation.")(* To add rewriting rules to a base *)letadd_rew_rules~localitybaselrul=letenv=Global.env()inletsigma=Evd.from_envenvinletist=Genintern.empty_glob_sign~strict:true(Global.env())inletinterntac=snd(Genintern.generic_internisttac)inletmap{CAst.loc;v=((c,ctx),b,t)}=letsigma=Evd.merge_context_setEvd.univ_rigidsigmactxinletinfo=find_applied_relation?locenvsigmacbinletpat=EConstr.Unsafe.to_constrinfo.hyp_patinletuid=fresh_key()in{rew_id=uid;rew_lemma=c;rew_type=EConstr.Unsafe.to_constrinfo.hyp_ty;rew_pat=pat;rew_ctx=ctx;rew_l2r=b;rew_tac=Option.mapinternt}inletlrul=List.mapmaplrulinletopenHintsinmatchlocalitywith|Local->cache_hintrewrite(base,lrul)|SuperGlobal->let()=ifLib.sections_are_opened()thenCErrors.user_errPp.(str"This command does not support the global attribute in sections.");inLib.add_leaf(inGlobalHintRewrite(base,lrul))|Export->let()=ifLib.sections_are_opened()thenCErrors.user_errPp.(str"This command does not support the export attribute in sections.");inLib.add_leaf(inExportHintRewrite(base,lrul))