123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425(******************************************************************************)(* This file is part of Waterproof-lib. *)(* *)(* Waterproof-lib is free software: you can redistribute it and/or modify *)(* it under the terms of the GNU General Public License as published by *)(* the Free Software Foundation, either version 3 of the License, or *)(* (at your option) any later version. *)(* *)(* Waterproof-lib is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU General Public License for more details. *)(* *)(* You should have received a copy of the GNU General Public License *)(* along with Waterproof-lib. If not, see <https://www.gnu.org/licenses/>. *)(* *)(******************************************************************************)openCErrorsopenConstropenEqualityopenGenargopenLtac_plugin.TacargopenLocusopenNamesopenPpopenProofviewopenProofview.NotationsopenUtilopenBacktrackingopenProofutils(* All the definitions below come from coq-core hidden library (i.e not visible in the API) *)typeraw_rew_rule=(Constr.tUniv.in_universe_context_set*bool*raw_generic_argumentoption)CAst.t(** 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}moduleHintIdent=structtypet=rew_ruleletcomparer1r2=KerName.comparer1.rew_idr2.rew_idend(**
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|DArrayletcompare_cici1ci2=letc=Ind.CanOrd.compareci1.ci_indci2.ci_indinifc=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.CanOrd.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|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:constr->ident->t->t(** [find_all dn] returns all idents contained in dn *)valfind_all:t->identlistend=structmoduleIdent=HintIdentmodulePTerm=structtypet=unitDTerm.tletcompare=DTerm.compareendmoduleTDnet=Dn.Make(PTerm)(Ident)openDTermtypet=TDnet.ttypeident=HintIdent.tletpat_of_constrc:(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(ConstRefc),[])|Ind(i,u)->Some(DRef(IndRefi),[])|Construct(c,u)->Some(DRef(ConstructRefc),[])|Meta_->assertfalse|Evar(i,_)->None|Case(ci,u1,pms1,(c1,_),_iv,c2,ca)->Some(DCase(ci),[sndc1;c2]@Array.map_to_listsndca)|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])(* Same change as in the autorewrite library: the motivation there is:
UnsafeMonomorphic is fine because the term will only be used
by pat_of_constr which ignores universes *)|Proj(p,_,c)->pat_of_constr@@mkApp(UnsafeMonomorphic.mkConst@@Projection.constantp,[|c|])|Inti->Some(DInti,[])|Floatf->Some(DFloatf,[])|Array(_u,t,def,ty)->Some(DArray,Array.to_listt@[def;ty])inpat_of_constrc(* Basic primitives *)letempty=TDnet.emptyletadd(c:constr)(id:Ident.t)(dn:t)=let(ctx,c)=Term.decompose_prod_declscinletc=TDnet.patternpat_of_constrcinTDnet.adddncidletfind_alldn=TDnet.lookupdn(fun()->Everything)()end(** Type of rewrite databases *)typerewrite_db={rdb_hintdn:HintDN.t;rdb_order:intKNmap.t;rdb_maxuid:int;}typehypinfo={hyp_ty:EConstr.types;hyp_pat:EConstr.constr;}(** Empty rewrite database *)letempty_rewrite_db={rdb_hintdn=HintDN.empty;rdb_order=KNmap.empty;rdb_maxuid=0;}(*
Returns a unique identifier at each call
*)letfresh_key:unit->KerName.t=letid=ref0infun()->letcur=incrid;!idinletlbl=Id.of_string("_"^string_of_intcur)inletkn=Lib.make_knlblinlet(mp,_)=KerName.reprkninletlbl=Id.of_string_soft(Printf.sprintf"%s#%i"(ModPath.to_stringmp)cur)inKerName.makemp(Label.of_idlbl)letdecompose_applied_relation(env:Environ.env)(sigma:Evd.evar_map)(c:constr)(ctype:Evd.econstr)(left2right:bool):hypinfooption=letfind_relty=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->None(* All the definitions below are inspired by the coq-core hidden library (i.e not visible in the API) but modified for Waterproof *)letadd_rew_rules(rewrite_database:rewrite_db)(rew_rules:rew_rulelist):rewrite_db=List.fold_left(funaccur->{rdb_hintdn=HintDN.addr.rew_patraccu.rdb_hintdn;rdb_order=KNmap.addr.rew_idaccu.rdb_maxuidaccu.rdb_order;rdb_maxuid=accu.rdb_maxuid+1;})rewrite_databaserew_rulesmoduleRewriteDatabase:Mergeablewithtypeelt=rewrite_db=structtypeelt=rewrite_dbletempty=empty_rewrite_dbletmergerewrite_db1rewrite_db2=add_rew_rulesrewrite_db1(HintDN.find_allrewrite_db2.rdb_hintdn)endmoduleRewriteDatabaseTactics=TypedTactics(RewriteDatabase)letfind_rewrites(rewrite_database:rewrite_db):rew_rulelist=letsortr1r2=Int.compare(KNmap.findr2.rew_idrewrite_database.rdb_order)(KNmap.findr1.rew_idrewrite_database.rdb_order)inList.sortsort(HintDN.find_allrewrite_database.rdb_hintdn)(** Applies all the rules of one hint rewrite database *)letone_base(where:variableoption)(tactic:tracetactic)(rewrite_database:rewrite_db):unittactic=letrew_rules=find_rewritesrewrite_databaseinletrewrite(dir:bool)(c:constr)(tac:unittactic):unittactic=letc=(EConstr.of_constrc,Tactypes.NoBindings)ingeneral_rewrite~where~l2r:dirAllOccurrences~freeze:true~dep:false~with_evars:false~tac:(tac,AllMatches)cinlettry_rewrite(rule:rew_rule)(tac:unittactic):unittactic=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletsubst,ctx'=UnivGen.fresh_universe_context_set_instancerule.rew_ctxinletsubst=Sorts.QVar.Map.empty,substinletc'=Vars.subst_univs_level_constrsubstrule.rew_lemmainletsigma=Evd.merge_context_setEvd.univ_flexiblesigmactx'inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(rewriterule.rew_l2rc'tac)endinleteval(rule:rew_rule)=lettac=matchrule.rew_tacwith|None->Proofview.tclUNIT()|Some(Genarg.GenArg(Genarg.Glbwitwit,tac))->letist={Geninterp.lfun=Id.Map.empty;poly=false;extra=Geninterp.TacStore.empty}inFtactic.run(Geninterp.interpwitisttac)(fun_->Proofview.tclUNIT())inTacticals.tclREPEAT_MAIN(tclTHEN(try_rewriteruletac)(tclIGNOREtactic))inletrules=tclMAP_revevalrew_rulesinTacticals.tclREPEAT_MAIN@@Proofview.tclPROGRESSrules(** The [autorewrite] tactic *)letautorewrite(tac:tracetactic)(rewrite_database:rewrite_db):unittactic=Tacticals.tclREPEAT_MAIN(Proofview.tclPROGRESS@@one_baseNonetacrewrite_database)letautorewrite_multi_in(idl:variablelist)(tac:tracetactic)(rewrite_database:rewrite_db):unittactic=Proofview.Goal.enterbeginfungl->Tacticals.tclMAP(funid->Tacticals.tclREPEAT_MAIN(Proofview.tclPROGRESS@@one_base(Someid)tacrewrite_database))idlendlettry_do_hyps(treat_id:'a->variable)(l:'alist):tracetactic->rewrite_db->unittactic=autorewrite_multi_in(List.maptreat_idl)letgen_auto_multi_rewrite(tac:tracetactic)(cl:clause)(rewrite_tab:rewrite_db):unittactic=letconcl_tac=(ifcl.concl_occs!=NoOccurrencesthenautorewritetacrewrite_tabelseProofview.tclUNIT())inifnot@@Locusops.is_all_occurrencescl.concl_occs&&cl.concl_occs!=NoOccurrencesthenTacticals.tclZEROMSG~info:(Exninfo.reify())(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)ltacrewrite_tab)|None->lethyp_tac=Proofview.Goal.enterbeginfungl->letids=Tacmach.pf_ids_of_hypsglintry_do_hyps(funid->id)idstacrewrite_tabendinTacticals.tclTHENFIRSTconcl_tachyp_tacletfind_applied_relation?(loc:Loc.toption)(env:Environ.env)sigmacleft2right=letctype=Retyping.get_type_ofenvsigma(EConstr.of_constrc)inmatchdecompose_applied_relationenvsigmacctypeleft2rightwith|Somec->c|None->user_err?loc(str"The type "++Printer.pr_econstr_envenvsigmactype++str" of this term does not end with an applied relation.")letfill_rewrite_tab(env:Environ.env)(sigma:Evd.evar_map)(rule:raw_rew_rule)(rewrite_database:rewrite_db):rewrite_db=letist=Genintern.empty_glob_sign~strict:trueenvinletintern(tac:raw_generic_argument):glob_generic_argument=snd(Genintern.generic_internisttac)inletto_rew_rule({CAst.loc;v=((c,ctx),b,t)}:raw_rew_rule):rew_rule=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}inadd_rew_rulesrewrite_database[to_rew_rulerule](** Prints the current rewrite hint database *)letprint_rewrite_hintdb(env:Environ.env)(sigma:Evd.evar_map)(rewrite_database:rewrite_db)=str"Local rewrite database"++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_rewritesrewrite_database)(**
Converts a given hypothesis into a raw rule than can be added to the hint rewrite database
*)letto_raw_rew_rule(env:Environ.env)(sigma:Evd.evar_map)(hyp:Constrexpr.constr_expr):raw_rew_rule=leteconstr,context=Constrintern.interp_constrenvsigmahypinletconstr=EConstr.to_constrsigmaeconstrinletuniv_ctx=UState.context_setcontextinletctx=(Global.push_context_set~strict:trueuniv_ctx;Univ.ContextSet.empty)inCAst.make?loc:(Constrexpr_ops.constr_lochyp)((constr,ctx),true,Option.map(in_gen(rawwitwit_ltac))None)(**
This function will add in the rewrite hint database "core" every hint possible created from the hypothesis
*)letfill_local_rewrite_database():rewrite_dbtactic=RewriteDatabaseTactics.typedGoalEnter@@fungoal->letenv=Goal.envgoalinletsigma=Goal.sigmagoalinlethyps=List.map(fundecl->Constrexpr_ops.mkIdentC@@Context.Named.Declaration.get_iddecl)(Goal.hypsgoal)inletnew_rules=List.map(to_raw_rew_ruleenvsigma)hypsintclUNIT@@List.fold_left(funaccrule->tryfill_rewrite_tabenvsigmaruleaccwith_->acc)RewriteDatabase.emptynew_rules(**
Waterproof autorewrite
This tactic is a rewrite of the coq-core's [autorewrite] tactic that will only consider current hypothesis as rewrite hints.
*)letwp_autorewrite?(print_hints:bool=false)(log:bool)(tac:tracetactic):unittactic=letclause={onhyps=Some[];concl_occs=Locus.AllOccurrences}infill_local_rewrite_database()>>=funrewrite_db->Goal.enter@@beginfungoal->letenv=Goal.envgoalinletsigma=Goal.sigmagoalinifprint_hintsthenFeedback.msg_notice@@print_rewrite_hintdbenvsigmarewrite_db;iflogthenFeedback.msg_notice@@str"(* application of wp_autorewrite *)";Tacticals.tclREPEAT@@tclPROGRESS@@gen_auto_multi_rewritetacclauserewrite_dbend>>=fun_->tclUNIT()