123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400(******************************************************************************)(* 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/>. *)(* *)(******************************************************************************)openAutoopenContext.NamedopenEConstropenHintsopenNamesopenPpopenProofviewopenProofview.NotationsopenTacticsopenTermopsopenUtilopenBacktrackingopenProofutilsopenWp_auto(* All the definitions below come from coq-core hidden library (i.e not visible in the API) *)leteauto_unif_flags:Unification.unify_flags=auto_flags_of_stateTransparentState.fulllete_give_exact?(flags:Unification.unify_flags=eauto_unif_flags)(c:types):unittactic=Goal.enterbeginfungl->letsigma,t1=Tacmach.pf_type_ofglcinlett2=Tacmach.pf_conclglinifoccur_existentialsigmat1||occur_existentialsigmat2thenTacticals.tclTHENLIST[Unsafe.tclEVARSsigma;Clenv.unify~cv_pb:CUMUL~flagst1;exact_no_checkc]elseexact_checkcendlete_assumption:tracetactic=TraceTactics.typedGoalEnterbeginfungl->lethyps=Goal.hypsglinletsigma=Goal.sigmaglinletconcl=Tacmach.pf_conclglinifList.is_emptyhypsthenTacticals.tclZEROMSG(str"No applicable tactic.")elseletnot_ground=occur_existentialsigmaconclinletmap(decl:('a,types)Declaration.pt):tracetactic=letid=Declaration.get_iddeclinlett=Declaration.get_typedeclinbeginifnot_ground||occur_existentialsigmatthenClenv.unify~cv_pb:CUMUL~flags:eauto_unif_flagst<*>exact_no_check(mkVarid)elseexact_check(mkVarid)end<*>tclUNIT@@singleton_tracetrue(str"eassumption")(str"")intclTraceFirst(List.mapmaphyps)endletunify_e_resolve(flags:Unification.unify_flags)(h:hint):unittactic=Hints.hint_res_pf~with_evars:true~with_classes:true~flagshlete_exact(h:hint):unittactic=Goal.enterbeginfungl->letenv=Goal.envglinletsigma=Goal.sigmaglinletsigma,c=Hints.fresh_hintenvsigmahinUnsafe.tclEVARSsigma<*>e_give_exactcend(* All the definitions below are inspired by the coq-core hidden library (i.e not visible in the API) but modified for Waterproof *)(**
Cost to solve a hint
*)typecost={cost_priority:int;cost_subgoals:int;}(**
Type alias containing functions that will return a [Hints.hint_db]
*)typedelayed_db=Environ.env->Evd.evar_map->hint_db(**
State of the search
*)typesearch_state={depth:int;(** Depth remaining before failing *)tactics_resolution:(Proofview_monad.goal_with_state*delayed_db)list;trace:trace;(** Current trace *)}letsearch_tactics_factory(initial_state:search_state):(moduleMergeablewithtypeelt=search_state)=(modulestructtypeelt=search_stateletempty=initial_stateletmergestate1state2={state1withtrace=merge_tracesstate1.tracestate2.trace}end:Mergeablewithtypeelt=search_state)(**
Creates an initial state
*)letinitial_state(log:bool)(evk:Proofview_monad.goal_with_state)(local_db:delayed_db)(n:int):search_state={depth=n;tactics_resolution=[(evk,local_db)];trace=new_tracelog}(**
Prints a debug header
*)letpr_dbg_header()=Feedback.msg_notice(str"(* info wp_eauto: *)")lettclTraceComplete(t:tracetactic):tracetactic=t>>=funres->(tclINDEPENDENT(letinfo=Exninfo.reify()intclZERO~info(SearchBoundno_trace)))<*>tclUNITresletrece_trivial_fail_db(db_list:hint_dblist)(local_db:hint_db)(forbidden_tactics:Pp.tlist):tracetactic=letnext=TraceTactics.typedGoalEnterbeginfungl->letd=Declaration.get_id@@Tacmach.pf_last_hypglinletlocal_db=push_resolve_hyp(Tacmach.pf_envgl)(Tacmach.projectgl)dlocal_dbine_trivial_fail_dbdb_listlocal_dbforbidden_tacticsendinTraceTactics.typedGoalEnterbeginfungl->letsecvars=compute_secvarsglinlettacl=e_assumption::(Tactics.intro<*>next)::(e_trivial_resolve(Tacmach.pf_envgl)(Tacmach.projectgl)db_listlocal_dbsecvars(Tacmach.pf_conclgl)forbidden_tactics)intclTraceFirst(List.maptclTraceCompletetacl)endandesearch_find(env:Environ.env)(sigma:Evd.evar_map)(db_list:hint_dblist)(local_db:hint_db)(secvars:Id.Pred.t)(concl:types)(forbidden_tactics:Pp.tlist):(tracetactic*cost*Pp.t)list=lethint_of_db:hint_db->FullHint.tlist=hintmap_ofenvsigmasecvarsconclinletflagged_hints:(Unification.unify_flags*FullHint.t)list=List.map_append(fundb->letflags=auto_flags_of_state(Hint_db.transparent_statedb)inList.map(funx->(flags,x))(hint_of_dbdb))(local_db::db_list)in(* Returns a tactic, its priority (which is an approximation of the cost), and its representation from the current state and a [Hints.FullHint.t] *)lettac_of_hint((state,hint):(Unification.unify_flags*FullHint.t)):tracetactic*cost*Pp.t=letpriority=matchFullHint.reprhintwith|Unfold_nth_->1|_->FullHint.priorityhintinlettac:hinthint_ast->tracetactic=function|Res_pfh->unify_e_resolvestateh<*>tclUNIT@@no_trace|ERes_pfh->unify_e_resolvestateh<*>tclUNIT@@no_trace|Give_exacth->e_exacth<*>tclUNIT@@singleton_tracetrue(str"exact")(str"")|Res_pf_THEN_trivial_failh->(unify_e_resolvestateh)<*>(e_trivial_fail_dbdb_listlocal_dbforbidden_tactics)|Unfold_nthc->TraceTactics.typedGoalEnterbeginfungl->ifexists_evaluable_reference(Goal.envgl)cthenTacticals.tclPROGRESS(reduce(Genredexpr.Unfold[Locus.AllOccurrences,c])Locusops.onConcl)<*>tclUNIT@@singleton_tracetrue(str"unfold")(str"")elseletinfo=Exninfo.reify()inTacticals.tclFAIL~info(str"Unbound reference")end|Extern(pat,tacast)->conclPatternconclpattacast<*>tclUNIT@@singleton_tracetrue(str"")(str"extern")inletsubgoals=matchFullHint.subgoalshintwith|Somesubgoals->subgoals|None->priorityinletcost={cost_priority=priority;cost_subgoals=subgoals}inletpr_hint(h:FullHint.t)(env:Environ.env)(sigma:Evd.evar_map):t*t=letorigin=matchFullHint.databasehwith|None->mt()|Somen->strnin(Proofutils.pr_hintenvsigmah,origin)in(* We cannot determine statically the cost of subgoals of an Extern hint, so approximate it by the hint's priority. *)lettactic=tclLOG(pr_hinthint)(FullHint.runhinttac)forbidden_tacticsin(tactic,cost,FullHint.printenvsigmahint)in(List.maptac_of_hintflagged_hints)ande_trivial_resolve(env:Environ.env)(sigma:Evd.evar_map)(db_list:hint_dblist)(local_db:hint_db)(secvars:Id.Pred.t)(gl:types)(forbidden_tactics:Pp.tlist):tracetacticlist=letfilter(tac,pr,_)=ifpr.cost_priority=0thenSometacelseNoneintryList.map_filterfilter(esearch_findenvsigmadb_listlocal_dbsecvarsglforbidden_tactics)withNot_found->[](**
The goal is solved if the cost of solving is null
*)letis_solved(cost:cost):bool=cost.cost_subgoals=0(* Solved comes first *)letsolve_order(c1:cost)(c2:cost):int=match(is_solvedc1,is_solvedc2)with|(true,true)|(false,false)->0|(false,true)->1|(true,false)->-1(**
Compare two states
Ordering of states is lexicographic:
1. tactics known to solve the goal
2. priority
3. number of generated goals
*)letcompare((_,c1,_):(tracetactic*cost*Pp.t))((_,c2,_):(tracetactic*cost*Pp.t))=letsolve_ordering=solve_orderc1c2inletpriority_ordering=Int.comparec1.cost_priorityc2.cost_priorityinifsolve_ordering!=0thensolve_orderingelseifpriority_ordering!=0thenpriority_orderingelseInt.comparec1.cost_subgoalsc2.cost_subgoals(**
Returns the list of tactics that will be tried for the proof search
It always begins with [assumption] and [intros] (not exactly them but equivalent with evar support), then uses the hint databases
*)letbranching(n:int)(delayed_database:delayed_db)(dblist:hint_dblist)(local_lemmas:Tactypes.delayed_open_constrlist)(forbidden_tactics:Pp.tlist):(bool*delayed_db*tracetactic*Pp.t)listtactic=Goal.enter_onebeginfungl->letenv=Goal.envglinletsigma=Goal.sigmaglinletconcl=Goal.conclglinlethyps=EConstr.named_contextenvinletdb=delayed_databaseenvsigmainletsecvars=secvars_of_hypshypsin(* Construction of tactics equivalent to [assumption] *)letassumption_tacs:(bool*delayed_db*tracetactic*Pp.t)list=(* Ensure that no goal is generated *)letmkdb(env:Environ.env)(sigma:Evd.evar_map):'a=assertfalseinletmap_assum(id:variable):(bool*delayed_db*tracetactic*Pp.t)=lethint=str"exact"++str" "++Id.printidin(false,mkdb,tclLOG(fun__->(hint,str""))(e_give_exact(mkVarid)<*>tclUNITno_trace)forbidden_tactics,hint)inList.mapmap_assum(ids_of_named_contexthyps)in(* Construction of tactic equivalent to [intros] *)letintro_tac:(bool*delayed_db*tracetactic*Pp.t)=letmkdb(env:Environ.env)(sigma:Evd.evar_map):hint_db=push_resolve_hypenvsigma(Declaration.get_id(List.hd(EConstr.named_contextenv)))dbin(false,mkdb,tclLOG(fun__->(str"intro",str""))(Tactics.intro<*>tclUNITno_trace)forbidden_tactics,str"intro")in(* Construction of tactics derivated from hint databases *)letrec_tacs:(bool*delayed_db*tracetactic*Pp.t)listtactic=letmkdb(env:Environ.env)(sigma:Evd.evar_map):hint_db=lethyps'=EConstr.named_contextenvinifhyps'==hypsthendbelsemake_local_hint_dbenvsigma~ts:TransparentState.fulltruelocal_lemmasintryesearch_findenvsigmadblistdbsecvarsconclforbidden_tactics|>List.sortcompare|>List.map(fun(tac,_,pp)->(true,mkdb,tac,pp))|>tclUNITwithNot_found->tclUNIT[]inrec_tacs>>=funrec_tacs->tclUNIT(assumption_tacs@intro_tac::rec_tacs)end(**
Actual search function
*)letresolve_esearch(max_depth:int)(dblist:hint_dblist)(local_lemmas:Tactypes.delayed_open_constrlist)(state:search_state)(must_use_tactics:Pp.tlist)(forbidden_tactics:Pp.tlist):search_statetactic=letrecexplore(state:search_state)(previous_envs:(EConstr.named_context*EConstr.constr)list)=ifstate.depth=0thentclZERO(SearchBoundno_trace)elsematchstate.tactics_resolutionwith|[]->tclUNITstate|(gl,db)::rest->tclEVARMAP>>=funsigma->matchUnsafe.undefinedsigma[gl]with|[]->explore{statewithtactics_resolution=rest;trace=incr_trace_depthstate.trace}previous_envs|gl::_->Unsafe.tclSETGOALS[gl]<*>branchingstate.depthdbdblistlocal_lemmasforbidden_tactics>>=funtacs->letcast((isrec,mkdb,tac,pp):(bool*delayed_db*tracetactic*Pp.t)):search_statetactic=tclONCEtac>>=funtrace->Unsafe.tclGETGOALS>>=funlgls->tclEVARMAP>>=funsigma->letjoin(gl:Proofview_monad.goal_with_state):(Proofview_monad.goal_with_state*delayed_db)=(gl,mkdb)inletdepth=ifisrec&¬@@List.is_emptylglsthenpredstate.depthelsestate.depthintclUNIT{depth;tactics_resolution=List.mapjoinlgls@rest;trace=merge_tracesstate.tracetrace}intacs|>List.mapcast|>explore_manyprevious_envs>>=funs->ifstate.depth=max_depththentrace_check_usedmust_use_tacticss.trace>>=funtrace->tclUNITselsetclUNITsandexplore_many(previous_envs:(EConstr.named_context*EConstr.constr)list)(tactic_list:search_statetacticlist):search_statetactic=matchtactic_listwith|[]->tclZERO(SearchBoundno_trace)|tac::l->tclORELSE(tac>>=funstate->letmoduleSearchState=(val(search_tactics_factory{statewithtactics_resolution=[]}))inletmoduleStateTactics=TypedTactics(SearchState)inletsearch_goal_enter:(Goal.t->search_statetactic)->search_statetactic=StateTactics.typedGoalEnterinsearch_goal_enter@@fungoal->ifList.mem(Goal.hypsgoal,Goal.conclgoal)previous_envsthentclZERO(SearchBoundno_trace)elseexplorestate@@(Goal.hypsgoal,Goal.conclgoal)::previous_envs)(fun(e,_)->matchewith|SearchBoundtrace->explore_manyprevious_envsl|_->explore_manyprevious_envsl)inexplorestate[](**
Searches a sequence of at most [n] tactics within [db_list] and [lems] that solves the goal
The goal can contain evars
*)letesearch(log:bool)(depth:int)(lems:Tactypes.delayed_open_constrlist)(db_list:hint_dblist)(must_use_tactics:Pp.tlist)(forbidden_tactics:Pp.tlist):tracetactic=TraceTactics.typedGoalEnterbeginfungl->letlocal_dbenvsigma=make_local_hint_dbenvsigma~ts:TransparentState.fulltruelemsinlettac(s:search_state):search_statetactic=resolve_esearchdepthdb_listlemssmust_use_tacticsforbidden_tacticsintclORELSEbeginletevk=goal_with_state(Goal.goalgl)(Goal.stategl)intac(initial_statelogevklocal_dbdepth)>>=funs->assert(List.is_emptys.tactics_resolution);Unsafe.tclSETGOALS[]<*>tclUNITs.traceendbeginfun(exn,info)->matchexnwith|SearchBoundtrace->tclUNITtrace|_->tclZERO~infoexnendend(**
Generates the {! wp_eauto} function
*)letgen_wp_eauto(log:bool)?(n:int=5)(lems:Tactypes.delayed_open_constrlist)(dbnames:hint_db_namelistoption)(must_use_tactics:Pp.tlist)(forbidden_tactics:Pp.tlist):tracetactic=wrap_hint_warning@@TraceTactics.typedGoalEnterbeginfungl->letdb_list=matchdbnameswith|Somedbnames->make_db_listdbnames|None->current_pure_db()intclTryDbgpr_dbg_header@@TraceTactics.typedThen(tclUNIT@@new_tracelog)@@esearchlognlemsdb_listmust_use_tacticsforbidden_tacticsend(**
Waterproof eauto
This function is a rewrite around {! Eauto.eauto} with the same arguments to be able to retrieve which hints have been used in case of success.
The code structure has been rearranged to match the one of [wp_auto.wp_auto].
*)letwp_eauto(log:bool)(n:int)(lems:Tactypes.delayed_open_constrlist)(db_names:hint_db_namelist):tracetactic=gen_wp_eautolog~nlems(Somedb_names)[][](**
Restricted Waterproof eauto
This function acts the same as {! wp_eauto} but will fail if all proof found contain at least one must-use lemma that is unused or one hint that is in the [forbidden] list.
*)letrwp_eauto(log:bool)(n:int)(lems:Tactypes.delayed_open_constrlist)(dbnames:hint_db_namelist)(must_use_tactics:Pp.tlist)(forbidden_tactics:Pp.tlist):tracetactic=gen_wp_eautolog~nlems(Somedbnames)must_use_tacticsforbidden_tactics