12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781(************************************************************************)(* * 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) *)(************************************************************************)moduleCVars=VarsopenPpopenCErrorsopenUtilopenNamesopenNameopsopenTermopenConstropenContextopenTermopsopenEConstropenVarsopenNamegenopenInductiveopenInductiveopsopenLibnamesopenGlobnamesopenReductionopsopenRetypingopenTacmachopenLogicopenHipatternopenTacticalsopenTacticsopenTacredopenCoqlibopenDeclarationsopenIndrecopenInd_tablesopenEqschemesopenLocusopenLocusopsopenTactypesopenProofview.NotationsopenUnificationopenContext.Named.DeclarationopenCombinatorsmoduleNamedDecl=Context.Named.Declaration(* Options *)typeinj_flags={keep_proof_equalities:bool;injection_pattern_l2r_order:bool;}openGoptionsletuse_injection_pattern_l2r_order=function|None->true|Someflags->flags.injection_pattern_l2r_orderlet{Goptions.get=injection_in_context_flag}=declare_bool_option_and_ref~key:["Structural";"Injection"]~value:false()(* Rewriting tactics *)typedep_proof_flag=bool(* true = support rewriting dependent proofs *)typefreeze_evars_flag=bool(* true = don't instantiate existing evars *)typeorientation=booltypeconditions=|Naive(* Only try the first occurrence of the lemma (default) *)|FirstSolved(* Use the first match whose side-conditions are solved *)|AllMatches(* Rewrite all matches whose side-conditions are solved *)(* Warning : rewriting from left to right only works
if there exists in the context a theorem named <eqname>_<suffsort>_r
with type (A:<sort>)(x:A)(P:A->Prop)(P x)->(y:A)(eqname A y x)->(P y).
If another equality myeq is introduced, then corresponding theorems
myeq_ind_r, myeq_rec_r and myeq_rect_r have to be proven. See below.
-- Eduardo (19/8/97)
*)letrewrite_core_unif_flags={modulo_conv_on_closed_terms=None;use_metas_eagerly_in_conv_on_closed_terms=true;use_evars_eagerly_in_conv_on_closed_terms=false;modulo_delta=TransparentState.empty;modulo_delta_types=TransparentState.empty;check_applied_meta_types=true;use_pattern_unification=true;use_meta_bound_pattern_unification=true;allowed_evars=Evarsolve.AllowedEvars.all;restrict_conv_on_strict_subterms=false;modulo_betaiota=false;modulo_eta=true;}letrewrite_unif_flags={core_unify_flags=rewrite_core_unif_flags;merge_unify_flags=rewrite_core_unif_flags;subterm_unify_flags=rewrite_core_unif_flags;allow_K_in_toplevel_higher_order_unification=false;(* allow_K does not matter in practice because calls w_typed_unify *)resolve_evars=true}letfreeze_initial_evarssigmaflagsnewevars=letinitial=Evd.undefined_mapsigmainletallowedevk=ifEvar.Map.memevkinitialthenfalseelseEvar.Set.memevk(Lazy.forcenewevars)inletallowed_evars=Evarsolve.AllowedEvars.from_predallowedin{flagswithcore_unify_flags={flags.core_unify_flagswithallowed_evars};merge_unify_flags={flags.merge_unify_flagswithallowed_evars};subterm_unify_flags={flags.subterm_unify_flagswithallowed_evars}}letmake_flagsfrzevarssigmaflagsnewevars=iffrzevarsthenfreeze_initial_evarssigmaflagsnewevarselseflagsletside_tactacsidetac=matchsidetacwith|None->tac|Somesidetac->tclTHENSFIRSTntac[|Proofview.tclUNIT()|]sidetacletinstantiate_lemma_allenvflagseqclausel2rconcl=let(_,args)=decompose_app(Clenv.clenv_evdeqclause)(Clenv.clenv_typeeqclause)inletarglen=Array.lengthargsinlet()=ifarglen<2thenuser_errPp.(str"The term provided is not an applied relation.")inletc1=args.(arglen-2)inletc2=args.(arglen-1)inw_unify_to_subterm_all~flagsenv(Clenv.clenv_evdeqclause)((ifl2rthenc1elsec2),concl)letrewrite_conv_closed_core_unif_flags={modulo_conv_on_closed_terms=SomeTransparentState.full;(* We have this flag for historical reasons, it has e.g. the consequence *)(* to rewrite "?x+2" in "y+(1+1)=0" or to rewrite "?x+?x" in "2+(1+1)=0" *)use_metas_eagerly_in_conv_on_closed_terms=true;use_evars_eagerly_in_conv_on_closed_terms=false;(* Combined with modulo_conv_on_closed_terms, this flag allows since 8.2 *)(* to rewrite e.g. "?x+(2+?x)" in "1+(1+2)=0" *)modulo_delta=TransparentState.empty;modulo_delta_types=TransparentState.full;check_applied_meta_types=true;use_pattern_unification=true;(* To rewrite "?n x y" in "y+x=0" when ?n is *)(* a preexisting evar of the goal*)use_meta_bound_pattern_unification=true;allowed_evars=Evarsolve.AllowedEvars.all;restrict_conv_on_strict_subterms=false;modulo_betaiota=false;modulo_eta=true;}letrewrite_conv_closed_unif_flags={core_unify_flags=rewrite_conv_closed_core_unif_flags;merge_unify_flags=rewrite_conv_closed_core_unif_flags;subterm_unify_flags=rewrite_conv_closed_core_unif_flags;allow_K_in_toplevel_higher_order_unification=false;resolve_evars=false}letrewrite_keyed_core_unif_flags={modulo_conv_on_closed_terms=SomeTransparentState.full;(* We have this flag for historical reasons, it has e.g. the consequence *)(* to rewrite "?x+2" in "y+(1+1)=0" or to rewrite "?x+?x" in "2+(1+1)=0" *)use_metas_eagerly_in_conv_on_closed_terms=true;use_evars_eagerly_in_conv_on_closed_terms=false;(* Combined with modulo_conv_on_closed_terms, this flag allows since 8.2 *)(* to rewrite e.g. "?x+(2+?x)" in "1+(1+2)=0" *)modulo_delta=TransparentState.full;modulo_delta_types=TransparentState.full;check_applied_meta_types=true;use_pattern_unification=true;(* To rewrite "?n x y" in "y+x=0" when ?n is *)(* a preexisting evar of the goal*)use_meta_bound_pattern_unification=true;allowed_evars=Evarsolve.AllowedEvars.all;restrict_conv_on_strict_subterms=false;modulo_betaiota=true;modulo_eta=true;}letrewrite_keyed_unif_flags={core_unify_flags=rewrite_keyed_core_unif_flags;merge_unify_flags=rewrite_keyed_core_unif_flags;subterm_unify_flags=rewrite_keyed_core_unif_flags;allow_K_in_toplevel_higher_order_unification=false;resolve_evars=false}lettclNOTSAMEGOALtac=letgoalgl=Proofview.Goal.goalglinProofview.Goal.enterbeginfungl->letsigma=projectglinletev=goalglintac>>=fun()->Proofview.Goal.goals>>=fungls->letcheckaccugl'=gl'>>=fungl'->letaccu=accu||Proofview.Progress.goal_equal~evd:sigma~extended_evd:(projectgl')ev(goalgl')inProofview.tclUNITaccuinProofview.Monad.List.fold_leftcheckfalsegls>>=funhas_same->ifhas_samethentclZEROMSG(str"Tactic generated a subgoal identical to the original goal.")elseProofview.tclUNIT()endletelim_wrapperclsrwtac=letopenPretype_errorsinProofview.tclORELSEbeginmatchclswith|None->(* was tclWEAK_PROGRESS which only fails for tactics generating one
subgoal and did not fail for useless conditional rewritings generating
an extra condition *)tclNOTSAMEGOALrwtac|Some_->rwtacendbeginfunction(e,info)->matchewith|PretypeError(env,evd,NoOccurrenceFound(c',_))->Proofview.tclZERO~info(PretypeError(env,evd,NoOccurrenceFound(c',cls)))|e->Proofview.tclZERO~infoeendletgeneral_elim_clausewith_evarsfrzevarstacclsc(ctx,eqn,args)ll2relim=(* Ad hoc asymmetric general_elim_clause *)letgeneral_elim_clause0rew=letrewrite_elim=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletflags=ifUnification.is_keyed_unification()thenrewrite_keyed_unif_flagselserewrite_conv_closed_unif_flagsin(* We take evars of the type: this may include old evars! For excluding *)(* all old evars, including the ones occurring in the rewriting lemma, *)(* we would have to take the clenv_value *)letnewevars=lazy(Evarutil.undefined_evars_of_termsigma(Clenv.clenv_typerew))inletflags=make_flagsfrzevarssigmaflagsnewevarsinletmetas=Evd.meta_list(Clenv.clenv_evdrew)inletsubmetas=List.map(funmv->mv,Evd.Metamap.findmvmetas)(Clenv.clenv_argumentsrew)ingeneral_elim_clausewith_evarsflagscls(submetas,c,Clenv.clenv_typerew)elimendinProofview.Unsafe.tclEVARS(Evd.clear_metas(Clenv.clenv_evdrew))<*>elim_wrapperclsrewrite_eliminletstrat,tac=matchtacwith|None->Naive,None|Some(tac,Naive)->Naive,Sometac|Some(tac,FirstSolved)->FirstSolved,Some(tclCOMPLETEtac)|Some(tac,AllMatches)->AllMatches,Some(tclCOMPLETEtac)inProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlettyp=matchclswith|None->pf_conclgl|Someid->pf_get_hyp_typidglinletty=it_mkProd_or_LetIn(applist(eqn,args))ctxinleteqclause=Clenv.make_clenv_bindingenvsigma(c,ty)linlettry_clauseevd'=letclenv=Clenv.update_clenv_evdeqclauseevd'inletclenv=Clenv.clenv_pose_dependent_evars~with_evars:trueclenvinside_tac(general_elim_clause0clenv)tacinmatchstratwith|Naive->side_tac(general_elim_clause0eqclause)tac|FirstSolved->letflags=make_flagsfrzevarssigmarewrite_unif_flags(lazyEvar.Set.empty)inletcs=instantiate_lemma_allenvflagseqclausel2rtypintclFIRST(List.maptry_clausecs)|AllMatches->letflags=make_flagsfrzevarssigmarewrite_unif_flags(lazyEvar.Set.empty)inletcs=instantiate_lemma_allenvflagseqclausel2rtypintclMAPtry_clausecsend(* The next function decides in particular whether to try a regular
rewrite or a generalized rewrite.
Approach is to break everything, if [eq] appears in head position
then regular rewrite else try general rewrite.
If occurrences are set, use general rewrite.
*)let(forward_general_setoid_rewrite_clause,general_setoid_rewrite_clause)=Hook.make()(* Do we have a JMeq instance on twice the same domains ? *)letjmeq_same_domenvsigma(rels,eq,args)=letenv=push_rel_contextrelsenvinmatchargswith|[dom1;_;dom2;_]->is_convenvsigmadom1dom2|_->falseleteq_elimination_refl2rsort=letname=ifl2rthenmatchsortwith|InProp->"core.eq.ind_r"|InSProp->"core.eq.sind_r"|InSet|InType|InQSort->"core.eq.rect_r"elsematchsortwith|InProp->"core.eq.ind"|InSProp->"core.eq.sind"|InSet|InType|InQSort->"core.eq.rect"inCoqlib.lib_ref_optname(* find_elim determines which elimination principle is necessary to
eliminate lbeq on sort_of_gl. *)letfind_elimlft2rgtdepcls((_,hdcncl,_)ast)=Proofview.Goal.enter_onebeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletis_global_existsgrc=matchCoqlib.lib_ref_optgrwith|Somegr->isRefXenvsigmagrc|None->falseinletinccl=Option.is_emptyclsinletis_eq=is_global_exists"core.eq.type"hdcnclinletis_jmeq=is_global_exists"core.JMeq.type"hdcncl&&jmeq_same_domenvsigmatinif(is_eq||is_jmeq)&¬depthenletsort=elimination_sort_of_clauseclsglinletc=matchEConstr.kindsigmahdcnclwith|Ind(ind_sp,u)->beginmatchlft2rgt,clswith|Sometrue,None|Somefalse,Some_->beginmatchifis_eqtheneq_elimination_reftruesortelseNonewith|Somer->destConstRefr|None->letc1=destConstRef(lookup_eliminatorenvind_spsort)inletmp,l=KerName.repr(Constant.canonicalc1)inletl'=Label.of_id(add_suffix(Label.to_idl)"_r")inletc1'=Global.constant_of_delta_kn(KerName.makempl')inifnot(Environ.mem_constantc1'(Global.env()))thenuser_err(str"Cannot find rewrite principle "++Label.printl'++str".");c1'end|_->beginmatchifis_eqtheneq_elimination_reffalsesortelseNonewith|Somer->destConstRefr|None->destConstRef(lookup_eliminatorenvind_spsort)endend|_->(* cannot occur since we checked that we are in presence of
Logic.eq or Jmeq just before *)assertfalseinProofview.tclUNITcelseletscheme_name=matchdep,lft2rgt,incclwith(* Non dependent case *)|false,Sometrue,true->rew_l2r_scheme_kind|false,Sometrue,false->rew_r2l_scheme_kind|false,_,false->rew_l2r_scheme_kind|false,_,true->rew_r2l_scheme_kind(* Dependent case *)|true,Sometrue,true->rew_l2r_dep_scheme_kind|true,Sometrue,false->rew_l2r_forward_dep_scheme_kind|true,_,true->rew_r2l_dep_scheme_kind|true,_,false->rew_r2l_forward_dep_scheme_kindinmatchEConstr.kindsigmahdcnclwith|Ind(ind,u)->find_schemescheme_nameind|_->assertfalseendlettype_of_clauseclsgl=matchclswith|None->Proofview.Goal.conclgl|Someid->pf_get_hyp_typidglletleibniz_rewrite_ebindings_clauseclslft2rgttacc((_,hdcncl,_)ast)lwith_evarsfrzevarsdep_proof_ok=Proofview.Goal.enterbeginfungl->letevd=Proofview.Goal.sigmaglinlettype_of_cls=type_of_clauseclsglinletdep=dep_proof_ok&&dependent_no_evarevdctype_of_clsinfind_elimlft2rgtdepclst>>=funelim->general_elim_clausewith_evarsfrzevarstacclsctl(matchlft2rgtwithNone->false|Someb->b)elimendletadjust_rewriting_directionargslft2rgt=matchargswith|[_]->(* equality to a constant, like in eq_true *)(* more natural to see -> as the rewriting to the constant *)ifnotlft2rgtthenuser_errPp.(str"Rewriting non-symmetric equality not allowed from right-to-left.");None|_->(* other equality *)Somelft2rgtletrewrite_side_tactacsidetac=side_tactac(Option.mapfstsidetac)(* Main function for dispatching which kind of rewriting it is about *)letgeneral_rewrite~where:cls~l2r:lft2rgtoccs~freeze:frzevars~dep:dep_proof_ok~with_evars?tac((c,l):constrwith_bindings)=ifnot(Locusops.is_all_occurrencesoccs)then(rewrite_side_tac(Hook.getforward_general_setoid_rewrite_clauseclslft2rgtoccs(c,l)~new_goals:[])tac)elseProofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletenv=Proofview.Goal.envglinletctype=get_type_ofenvsigmacinletrels,t=decompose_prod_declssigma(whd_betaiotazetaenvsigmactype)inmatchmatch_with_equality_typeenvsigmatwith|Some(hdcncl,args)->(* Fast path: direct leibniz-like rewrite *)letlft2rgt=adjust_rewriting_directionargslft2rgtinleibniz_rewrite_ebindings_clauseclslft2rgttacc(rels,hdcncl,args)lwith_evarsfrzevarsdep_proof_ok|None->Proofview.tclORELSEbeginrewrite_side_tac(Hook.getforward_general_setoid_rewrite_clauseclslft2rgtoccs(c,l)~new_goals:[])tacendbeginfunction|(e,info)->Proofview.tclEVARMAP>>=funsigma->letenv'=push_rel_contextrelsenvinletrels',t'=whd_decompose_prod_declsenv'sigmatin(* Search for underlying eq *)matchmatch_with_equality_typeenv'sigmat'with|Some(hdcncl,args)->letlft2rgt=adjust_rewriting_directionargslft2rgtinleibniz_rewrite_ebindings_clauseclslft2rgttacc(rels'@rels,hdcncl,args)lwith_evarsfrzevarsdep_proof_ok|None->Proofview.tclZERO~infoe(* error "The provided term does not end with an equality or a declared rewrite relation." *)endendletclear_for_rewrite_in_hypsidsc=letids=Id.Set.of_listidsinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglin(* Is this the right err? *)leterr=(Evarutil.OccurHypInSimpleClauseNone)inletsigma=tryEvarutil.check_and_clear_in_constrenvsigmaerridscwithEvarutil.ClearDependencyError(id,err,inglobal)->CErrors.user_errPp.(str"Cannot rewrite due to dependency on "++Id.printid++str".")inProofview.Unsafe.tclEVARSsigmaendletgeneral_rewrite_clausel2rwith_evars?tacccl=letoccs_of=occurrences_map(List.fold_left(funacc->functionArgArgx->x::acc|ArgVar_->acc)[])inmatchcl.onhypswith|Somel->(* If a precise list of locations is given, success is mandatory for
each of these locations. *)letrecdo_hyps=function|[]->Proofview.tclUNIT()|((occs,id),_)::l->tclTHENFIRST(general_rewrite~where:(Someid)~l2r(occs_ofoccs)~freeze:false~dep:true~with_evars?tacc)(do_hypsl)inlettac=ifcl.concl_occs==NoOccurrencesthendo_hypslelsetclTHENFIRST(general_rewrite~where:None~l2r(occs_ofcl.concl_occs)~freeze:false~dep:true~with_evars?tacc)(do_hypsl)inbeginmatchlwith|[]|[_]->(* don't clear when rewriting in 1 hyp *)tac|_->tclTHEN(clear_for_rewrite_in_hyps(List.map(fun((_,id),_)->id)l)(fstc))tacend|None->(* Otherwise, if we are told to rewrite in all hypothesis via the
syntax "* |-", we fail iff all the different rewrites fail *)letrecdo_hyps_atleastonce=function|[]->tclZEROMSG(Pp.str"Nothing to rewrite.")|id::l->tclIFTHENFIRSTTRYELSEMUST(tclTHEN(clear_for_rewrite_in_hyps[id](fstc))(general_rewrite~where:(Someid)~l2rAllOccurrences~freeze:false~dep:true~with_evars?tacc))(do_hyps_atleastoncel)inletdo_hyps=Proofview.Goal.enterbeginfungl->do_hyps_atleastonce(pf_ids_of_hypsgl)endinifcl.concl_occs==NoOccurrencesthendo_hypselsetclIFTHENFIRSTTRYELSEMUST(general_rewrite~where:None~l2r(occs_ofcl.concl_occs)~freeze:false~dep:true~with_evars?tacc)do_hypsletapply_special_clear_requestclear_flagf=Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletenv=Proofview.Goal.envglintrylet(sigma,(c,bl))=fenvsigmainletc=trySome(destVarsigmac)withDestKO->Noneinapply_clear_requestclear_flag(use_clear_hyp_by_default())cwithewhennoncriticale->tclIDTACendtypemulti=|Preciselyofint|UpToofint|RepeatStar|RepeatPlusletgeneral_multi_rewritewith_evarslcltac=letdo1l2rf=Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletenv=Proofview.Goal.envglinlet(sigma,c)=fenvsigmaintclWITHHOLESwith_evars(general_rewrite_clausel2rwith_evars?tacccl)sigmaendinletrecdoNl2rc=function|Preciselynwhenn<=0->Proofview.tclUNIT()|Precisely1->do1l2rc|Preciselyn->tclTHENFIRST(do1l2rc)(doNl2rc(Precisely(n-1)))|RepeatStar->tclREPEAT_MAIN(do1l2rc)|RepeatPlus->tclTHENFIRST(do1l2rc)(doNl2rcRepeatStar)|UpTonwhenn<=0->Proofview.tclUNIT()|UpTon->tclTHENFIRST(tclTRY(do1l2rc))(doNl2rc(UpTo(n-1)))inletrecloop=function|[]->Proofview.tclUNIT()|(l2r,m,clear_flag,c)::l->tclTHENFIRST(tclTHEN(doNl2rcm)(apply_special_clear_requestclear_flagc))(loopl)inlooplletrewriteLRc=general_rewrite~where:None~l2r:trueAllOccurrences~freeze:true~dep:true~with_evars:false(c,NoBindings)letrewriteRLc=general_rewrite~where:None~l2r:falseAllOccurrences~freeze:true~dep:true~with_evars:false(c,NoBindings)(* Replacing tactics *)letclasses_dirpath=DirPath.make(List.mapId.of_string["Classes";"Coq"])letinit_setoid()=ifis_dirpath_prefix_ofclasses_dirpath(Lib.cwd())then()elsecheck_required_library["Coq";"Setoids";"Setoid"]letcheck_setoidcl=letconcloccs=Locusops.occurrences_map(funx->x)cl.concl_occsinOption.fold_left(List.fold_left(funb((occ,_),_)->b||(not(Locusops.is_all_occurrences(Locusops.occurrences_map(funx->x)occ)))))(not(Locusops.is_all_occurrencesconcloccs)&&(concloccs<>NoOccurrences))cl.onhypsletreplace_coreclausel2req=ifcheck_setoidclausetheninit_setoid();tclTHENFIRST(assert_afterAnonymouseq)(onLastHypId(funid->tclTHEN(tclTRY(general_rewrite_clausel2rfalse(mkVarid,NoBindings)clause))(clear[id])))(* eq,sym_eq : equality on Type and its symmetry theorem
c1 c2 : c1 is to be replaced by c2
unsafe : If true, do not check that c1 and c2 are convertible
tac : Used to prove the equality c1 = c2
gl : goal *)letreplace_using_leibnizclausec1c2l2runsafetry_prove_eq_opt=Proofview.Goal.enterbeginfungl->letget_type_of=pf_applyget_type_ofglinlett1=get_type_ofc1andt2=get_type_ofc2inletevd=ifunsafethenSome(Tacmach.projectgl)elsetrySome(Evarconv.unify_delay(Proofview.Goal.envgl)(Tacmach.projectgl)t1t2)withEvarconv.UnableToUnify_->Noneinmatchevdwith|None->tclFAIL(str"Terms do not have convertible types")|Someevd->lete,sym=trylib_ref"core.eq.type",lib_ref"core.eq.sym"withNotFoundRef_->trylib_ref"core.identity.type",lib_ref"core.identity.sym"withNotFoundRef_->user_err(strbrk"Need a registration for either core.eq.type and core.eq.sym or core.identity.type and core.identity.sym.")inTacticals.pf_constr_of_globalsym>>=funsym->Tacticals.pf_constr_of_globale>>=fune->leteq=applist(e,[t1;c1;c2])inletsolve_tac=matchtry_prove_eq_optwith|None->tclFIRST[assumption;tclTHEN(applysym)assumption;Proofview.tclUNIT()]|Sometac->tclCOMPLETEtacintclTHENLAST(replace_coreclausel2req)solve_tacendletreplacec1c2=replace_using_leibnizonConclc2c1falsefalseNoneletreplace_byc1c2tac=replace_using_leibnizonConclc2c1falsefalse(Sometac)letreplace_in_clause_maybe_bydir_optc1c2cltac_opt=letc1,c2,dir=matchdir_optwith|None|Somefalse->c1,c2,false|Sometrue->c2,c1,trueinreplace_using_leibnizclc2c1dirfalsetac_opt(* End of Eduardo's code. The rest of this file could be improved
using the functions match_with_equation, etc that I defined
in Pattern.ml.
-- Eduardo (19/8/97)
*)(* Tactics for equality reasoning with the "eq" relation. This code
will work with any equivalence relation which is substitutive *)(* [find_positions t1 t2]
will find the positions in the two terms which are suitable for
discrimination, or for injection. Obviously, if there is a
position which is suitable for discrimination, then we want to
exploit it, and not bother with injection. So when we find a
position which is suitable for discrimination, we will just raise
an exception with that position.
So the algorithm goes like this:
if [t1] and [t2] start with the same constructor, then we can
continue to try to find positions in the arguments of [t1] and
[t2].
if [t1] and [t2] do not start with the same constructor, then we
have found a discrimination position
if one [t1] or [t2] do not start with a constructor and the two
terms are not already convertible, then we have found an injection
position.
A discriminating position consists of a constructor-path and a pair
of operators. The constructor-path tells us how to get down to the
place where the two operators, which must differ, can be found.
An injecting position has two terms instead of the two operators,
since these terms are different, but not manifestly so.
A constructor-path is a list of pairs of (operator * int), where
the int (based at 0) tells us which argument of the operator we
descended into.
*)exceptionDiscrFoundof(constructor*int)list*constructor*constructorletkeep_proof_equalities_for_injection=reffalselet()=declare_bool_option{optstage=Summary.Stage.Interp;optdepr=None;optkey=["Keep";"Proof";"Equalities"];optread=(fun()->!keep_proof_equalities_for_injection);optwrite=(funb->keep_proof_equalities_for_injection:=b)}letkeep_proof_equalities=function|None->!keep_proof_equalities_for_injection|Someflags->flags.keep_proof_equalitiesmoduleKeepEqualities=structtypet=inductivemoduleSet=Indset_envletencode_envr=Nametab.global_inductiverletsubstsubstobj=Mod_subst.subst_indsubstobjletprinterind=Nametab.pr_global_envId.Set.empty(GlobRef.IndRefind)letkey=["Keep";"Equalities"]lettitle="Prop-valued inductive types for which injection keeps equality proofs"letmember_messageindb=letb=ifbthenmt()elsestr"not "instr"Equality proofs over "++(printerind)++str" are "++b++str"kept by injection"endmoduleKeepEqualitiesTable=Goptions.MakeRefTable(KeepEqualities)letset_keep_equality=KeepEqualitiesTable.set(* [keep_proofs] is relevant for types in Prop with elimination in Type *)(* In particular, it is relevant for injection but not for discriminate *)letkeep_head_inductivesigmac=(* Note that we do not weak-head normalize c before checking it is an
applied inductive, because [get_sort_family_of] did not use to either.
As a matter of fact, if it reduces to an applied template inductive
type but is not syntactically equal to it, it will fail to project. *)let_,hd=EConstr.decompose_prodsigmacinlethd,_=EConstr.decompose_appsigmahdinmatchEConstr.kindsigmahdwith|Ind(ind,_)->KeepEqualitiesTable.activeind|_->falseletfind_positionsenvsigma~keep_proofs~no_discrt1t2=letprojectenvsortsposnt1t2=letty1=get_type_ofenvsigmat1inletkeep=ifkeep_head_inductivesigmaty1thentrueelselets=get_sort_family_ofenvsigmaty1inList.mem_fSorts.family_equalssortsinifkeepthen[(List.revposn,t1,t2)]else[]inletrecfindrecsortsposnt1t2=lethd1,args1=whd_all_stackenvsigmat1inlethd2,args2=whd_all_stackenvsigmat2inmatch(EConstr.kindsigmahd1,EConstr.kindsigmahd2)with|Construct((ind1,i1assp1),u1),Construct(sp2,_)whenInt.equal(List.lengthargs1)(constructor_nallargsenvsp1)->letsorts'=CList.intersectSorts.family_equalsorts(sorts_below(top_allowed_sortenv(fstsp1)))in(* both sides are fully applied constructors, so either we descend,
or we can discriminate here. *)ifEnviron.QConstruct.equalenvsp1sp2thenletnparams=inductive_nparamsenvind1inletparams1,rargs1=List.chopnparamsargs1inlet_,rargs2=List.chopnparamsargs2inlet(mib,mip)=lookup_mind_specifenvind1inletctxt=(get_constructor((ind1,u1),mib,mip,params1)i1).cs_argsinletadjusti=CVars.adjust_rel_to_rel_contextctxt(i+1)-1inList.flatten(List.map2_i(funi->findrecsorts'((sp1,adjusti)::posn))0rargs1rargs2)elseifList.mem_fSorts.family_equalInTypesorts'&¬no_discrthen(* see build_discriminator *)raise(DiscrFound(List.revposn,sp1,sp2))else(* if we cannot eliminate to Type, we cannot discriminate but we
may still try to project *)projectenvsortsposn(applist(hd1,args1))(applist(hd2,args2))|_->lett1_0=applist(hd1,args1)andt2_0=applist(hd2,args2)inifis_convenvsigmat1_0t2_0then[]elseprojectenvsortsposnt1_0t2_0intryletsorts=ifkeep_proofsthen[InSet;InType;InProp]else[InSet;InType]inInr(findrecsorts[]t1t2)withDiscrFound(path,c1,c2)->Inl(path,c1,c2)letuse_keep_proofs=function|None->!keep_proof_equalities_for_injection|Someb->b(* Once we have found a position, we need to project down to it. If
we are discriminating, then we need to produce False on one of the
branches of the discriminator, and True on the other one. So the
result type of the case-expressions is always Prop.
If we are injecting, then we need to discover the result-type.
This can be difficult, since the type of the two terms at the
injection-position can be different, and we need to find a
dependent sigma-type which generalizes them both.
We can get an approximation to the right type to choose by:
(0) Before beginning, we reserve a patvar for the default
value of the match, to be used in all the bogus branches.
(1) perform the case-splits, down to the site of the injection. At
each step, we have a term which is the "head" of the next
case-split. At the point when we actually reach the end of our
path, the "head" is the term to return. We compute its type, and
then, backwards, make a sigma-type with every free debruijn
reference in that type. We can be finer, and first do a S(TRONG)NF
on the type, so that we get the fewest number of references
possible.
(2) This gives us a closed type for the head, which we use for the
types of all the case-splits.
(3) Now, we can compute the type of one of T1, T2, and then unify
it with the type of the last component of the result-type, and this
will give us the bindings for the other arguments of the tuple.
*)(* The algorithm, then is to perform successive case-splits. We have
the result-type of the case-split, and also the type of that
result-type. We have a "direction" we want to follow, i.e. a
constructor-number, and in all other "directions", we want to juse
use the default-value.
After doing the case-split, we call the afterfun, with the updated
environment, to produce the term for the desired "direction".
The assumption is made here that the result-type is not manifestly
functional, so we can just use the length of the branch-type to
know how many lambda's to stick in.
*)(* [descend_then env sigma head dirn]
returns the number of products introduced, and the environment
which is active, in the body of the case-branch given by [dirn],
along with a continuation, which expects to be fed:
(1) the value of the body of the branch given by [dirn]
(2) the default-value
(3) the type of the default-value, which must also be the type of
the body of the [dirn] branch
the continuation then constructs the case-split.
*)letdescend_thenenvsigmaheaddirn=letIndType(indf,_)asindt=tryfind_rectypeenvsigma(get_type_ofenvsigmahead)withNot_found->user_errPp.(str"Cannot project on an inductive type derived from a dependency.")inlet(ind,_),_=(dest_ind_familyindf)inlet()=check_privacyenvindinlet(mib,mip)=lookup_mind_specifenvindinletcstr=get_constructorsenvindfinletdirn_nlams=cstr.(dirn-1).cs_nargsinletdirn_env=EConstr.push_rel_contextcstr.(dirn-1).cs_argsenvin(dirn_nlams,dirn_env,(funsigmadirnval(dfltval,resty)->letdeparsign=make_arity_signatureenvsigmatrueindfinletp=it_mkLambda_or_LetIn(lift(mip.mind_nrealargs+1)resty)deparsigninletbuild_branchi=letresult=ifInt.equalidirnthendirnvalelsedfltvalinletcs_args=cstr.(i-1).cs_argsinletargs=name_contextenvsigmacs_argsinit_mkLambda_or_LetInresultargsinletbrl=List.mapbuild_branch(List.interval1(Array.lengthmip.mind_consnames))inletrci=ERelevance.relevantin(* TODO relevance *)letci=make_case_infoenvindRegularStyleinInductiveops.make_case_or_projectenvsigmaindtci(p,rci)head(Array.of_listbrl)))(* Now we need to construct the discriminator, given a discriminable
position. This boils down to:
(1) If the position is directly beneath us, then we need to do a
case-split, with result-type Prop, and stick True and False into
the branches, as is convenient.
(2) If the position is not directly beneath us, then we need to
call descend_then, to descend one step, and then recursively
construct the discriminator.
*)letbuild_coq_False()=pf_constr_of_global(lib_ref"core.False.type")letbuild_coq_True()=pf_constr_of_global(lib_ref"core.True.type")letbuild_coq_I()=pf_constr_of_global(lib_ref"core.True.I")letrecbuild_discriminatorenvsigmatrue_0false_0posc=function|[]->letcty=get_type_ofenvsigmacinmake_selectorenvsigma~pos~special:true_0~default:(fstfalse_0)ccty|((sp,cnum),argnum)::l->let(cnum_nlams,cnum_env,kont)=descend_thenenvsigmaccnuminletnewc=mkRel(cnum_nlams-argnum)inletsubval=build_discriminatorcnum_envsigmatrue_0false_0posnewclinkontsigmasubvalfalse_0(* Note: discrimination could be more clever: if some elimination is
not allowed because of a large impredicative constructor in the
path (see allowed_sorts in find_positions), the positions could
still be discrimated by projecting first instead of putting the
discrimination combinator inside the projecting combinator. Example
of relevant situation:
Inductive t:Set := c : forall A:Set, A -> nat -> t.
Goal ~ c _ 0 0 = c _ 0 1. intro. discriminate H.
*)letgen_absurdityid=Proofview.Goal.enterbeginfungl->letenv=pf_envglinletsigma=projectglinlethyp_typ=pf_get_hyp_typidglinifis_empty_typeenvsigmahyp_typthensimplest_elim(mkVarid)elsetclZEROMSG(str"Not the negation of an equality.")end(* Precondition: eq is leibniz equality
returns ((eq_elim t t1 P i t2), absurd_term)
where P=[e:t]discriminator
absurd_term=False
*)letind_scheme_of_eqlbeqto_kind=(* use ind rather than case by compatibility *)letkind=Elimschemes.elim_scheme~dep:false~to_kindinfind_schemekind(destIndReflbeq.eq)>>=func->Proofview.tclUNIT(GlobRef.ConstRefc)letdiscrimination_pfe(t,t1,t2)discriminatorlbeqto_kind=build_coq_I()>>=funi->ind_scheme_of_eqlbeqto_kind>>=funeq_elim->pf_constr_of_globaleq_elim>>=funeq_elim->Proofview.tclEVARMAP>>=funsigma->Proofview.tclUNIT(applist(eq_elim,[t;t1;mkNamedLambdasigma(make_annoteERelevance.relevant)tdiscriminator;i;t2]))typeequality={eq_data:(coq_eq_data*(EConstr.t*EConstr.t*EConstr.t));(* equality data + A : Type, t1 : A, t2 : A *)eq_term:EConstr.t;(* term [M : R A t1 t2] where [R] is the equality from above *)eq_evar:Proofview_monad.goal_with_statelist;(* List of implicit hypotheses on which the data above depends. *)}leteq_baseid=Id.of_string"e"letdiscr_positionsenvsigma{eq_data=(lbeq,(t,t1,t2));eq_term=v;eq_evar=evs}cpathdirn=build_coq_True()>>=funtrue_0->build_coq_False()>>=funfalse_0->letfalse_ty=Retyping.get_type_ofenvsigmafalse_0inletfalse_kind=Retyping.get_sort_family_ofenvsigmafalse_0inlete=next_ident_awayeq_baseid(vars_of_envenv)inlete_env=push_named(Context.Named.Declaration.LocalAssum(make_annoteERelevance.relevant,t))envinletdiscriminator=tryProofview.tclUNIT(build_discriminatore_envsigmatrue_0(false_0,false_ty)dirn(mkVare)cpath)withUserError_asex->let_,info=Exninfo.captureexinProofview.tclZERO~infoexindiscriminator>>=fundiscriminator->discrimination_pfe(t,t1,t2)discriminatorlbeqfalse_kind>>=funpf->(* pf : eq t t1 t2 -> False *)letpf=EConstr.mkApp(pf,[|v|])intclTHENS(assert_afterAnonymousfalse_0)[onLastHypIdgen_absurdity;Tactics.exact_checkpf<*>Proofview.Unsafe.tclNEWGOALSevs]letdiscrEqeq=let{eq_data=(_,(_,t1,t2))}=eqinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinmatchfind_positionsenvsigma~keep_proofs:false~no_discr:falset1t2with|Inr_->letinfo=Exninfo.reify()intclZEROMSG~info(str"Not a discriminable equality.")|Inl(cpath,(_,dirn),_)->discr_positionsenvsigmaeqcpathdirnendletmake_clausewith_evarsenvsigmatlbindc=letsigma',eq_clause=EClause.make_evar_clauseenvsigmatinletsigma'=EClause.solve_evar_clauseenvsigma'falseeq_clauselbindcinlet()=ifnotwith_evarsthenEClause.check_evar_clauseenvsigmasigma'eq_clauseinsigma',eq_clauseletonEqualitywith_evarstac(c,lbindc)=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletstate=Proofview.Goal.stateglinlett=Retyping.get_type_ofenvsigmacinlett'=trysnd(Tacred.reduce_to_quantified_indenvsigmat)withUserError_->tinletsigma,eq_clause=make_clausewith_evarsenvsigmat'lbindcinletfilterh=ifh.EClause.hole_depsthenNoneelsetrySome(Proofview_monad.goal_with_state(fst@@destEvarsigmah.EClause.hole_evar)state)withDestKO->Noneinletgoals=List.map_filterfiltereq_clause.EClause.cl_holesinletcl_args=Array.map_of_list(funh->h.EClause.hole_evar)eq_clause.EClause.cl_holesinlet(eq,u,eq_args)=find_this_eq_data_decomposeenvsigmaeq_clause.cl_conclinleteq={eq_data=(eq,eq_args);eq_term=mkApp(c,cl_args);eq_evar=goals}inProofview.Unsafe.tclEVARSsigma<*>taceqendletonNegatedEqualitywith_evarstac=Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletccl=Proofview.Goal.conclglinletenv=Proofview.Goal.envglinmatchEConstr.kindsigma(hnf_constr0envsigmaccl)with|Prod(na,t,u)->letu=nf_betaiota(push_rel_assum(na,t)env)sigmauinifis_empty_typeenvsigmauthentclTHENintrof(onLastHypId(funid->onEqualitywith_evarstac(mkVarid,NoBindings)))elsetclZEROMSG(str"Not a negated primitive equality.")|_->letinfo=Exninfo.reify()intclZEROMSG~info(str"Not a negated primitive equality.")endletdiscrSimpleClausewith_evars=function|None->onNegatedEqualitywith_evarsdiscrEq|Someid->onEqualitywith_evarsdiscrEq(mkVarid,NoBindings)letdiscrwith_evars=onEqualitywith_evarsdiscrEqletdiscrClausewith_evars=onClause(discrSimpleClausewith_evars)letdiscrEverywherewith_evars=tclTHEN(Proofview.tclUNIT())(* Delay the interpretation of side-effect *)(tclTHEN(tclREPEATintrof)(tryAllHyps(funid->tclCOMPLETE(discrwith_evars(mkVarid,NoBindings)))))letdiscr_tacwith_evars=function|None->discrEverywherewith_evars|Somec->onInductionArg(funclear_flag->discrwith_evars)cletdiscrConcl=discrClausefalseonConclletdiscrHypid=discrClausefalse(onHypid)(* The problem is to build a destructor (a generalization of the
predecessor) which, when applied to a term made of constructors
(say [Ci(e1,Cj(e2,Ck(...,term,...),...),...)]), returns a given
subterm of the term (say [term]).
Let [typ] be the type of [term]. If [term] has no dependencies in
the [e1], [e2], etc, then all is simple. If not, then we need to
encapsulated the dependencies into a dependent tuple in such a way
that the destructor has not a dependent type and rewriting can then
be applied. The destructor has the form
[e]Cases e of
| ...
| Ci (x1,x2,...) =>
Cases x2 of
| ...
| Cj (y1,y2,...) =>
Cases y2 of
| ...
| Ck (...,z,...) => z
| ... end
| ... end
| ... end
and the dependencies is expressed by the fact that [z] has a type
dependent in the x1, y1, ...
Assume [z] is typed as follows: env |- z:zty
If [zty] has no dependencies, this is simple. Otherwise, assume
[zty] has free (de Bruijn) variables in,...i1 then the role of
[make_iterated_tuple env sigma (term,typ) (z,zty)] is to build the
tuple
[existT [xn]Pn Rel(in) .. (existT [x2]P2 Rel(i2) (existT [x1]P1 Rel(i1) z))]
where P1 is zty[i1/x1], P2 is {x1 | P1[i2/x2]} etc.
*)letrecbuild_injrecenvsigmadefaultc=function|[]->letcty=get_type_ofenvsigmacinletsigma,{telescope_type;telescope_value},dfltval=make_iterated_tupleenvsigma~defaultcctyinsigma,(telescope_value,telescope_type,dfltval)|((sp,cnum),argnum)::l->trylet(cnum_nlams,cnum_env,kont)=descend_thenenvsigmaccnuminletnewc=mkRel(cnum_nlams-argnum)inletsigma,(subval,tuplety,dfltval)=build_injreccnum_envsigmadefaultnewclinletres=kontsigmasubval(dfltval,tuplety)insigma,(res,tuplety,dfltval)withUserError_->failwith"caught"letbuild_injectorenvsigmadfltccpath=letsigma,(injcode,resty,_)=build_injrecenvsigmadfltccpathinsigma,(injcode,resty)leteq_dec_scheme_kind_name=ref(fun_->failwith"eq_dec_scheme undefined")letset_eq_dec_scheme_kindk=eq_dec_scheme_kind_name:=(fun_->k)letwarn_inject_no_eqdep_dec=CWarnings.create~name:"injection-missing-eqdep-dec"~category:CWarnings.CoreCategories.tacticsPp.(fun(env,ind)->str"The equality scheme for"++spc()++Printer.pr_inductiveenvind++spc()++str"could not be used as Coq.Logic.Eqdep_dec has not been required.")letinject_if_homogenous_dependent_pairty=Proofview.Goal.enterbeginfungl->tryletenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinleteq,u,(t,t1,t2)=pf_applyfind_this_eq_data_decomposegltyin(* fetch the informations of the pair *)letsigTconstr=Coqlib.(lib_ref"core.sigT.type")inletexistTconstr=Coqlib.lib_ref"core.sigT.intro"in(* check whether the equality deals with dep pairs or not *)leteqTypeDest=fst(decompose_appsigmat)inifnot(isRefXenvsigmasigTconstreqTypeDest)thenraise_notraceExit;lethd1,ar1=decompose_appsigmat1andhd2,ar2=decompose_appsigmat2inifnot(isRefXenvsigmaexistTconstrhd1)thenraise_notraceExit;ifnot(isRefXenvsigmaexistTconstrhd2)thenraise_notraceExit;let(ind,_),_=trypf_applyfind_mrectypeglar1.(0)withNot_found->raise_notraceExitin(* check if the user has declared the dec principle *)(* and compare the fst arguments of the dep pair *)(* Note: should work even if not an inductive type, but the table only *)(* knows inductive types *)ifnot(Option.has_some(Ind_tables.lookup_scheme(!eq_dec_scheme_kind_name())ind)&&pf_applyis_convglar1.(2)ar2.(2))thenraise_notraceExit;letinj2=matchlib_ref_opt"core.eqdep_dec.inj_pair2"with|None->warn_inject_no_eqdep_dec(env,ind);raise_notraceExit|Somev->vinletnew_eq_args=[|pf_get_type_ofglar1.(3);ar1.(3);ar2.(3)|]infind_scheme(!eq_dec_scheme_kind_name())ind>>=func->letc=ifGlobal.is_polymorphic(ConstRefc)thenCErrors.anomalyPp.(str"Unexpected univ poly in inject_if_homogenous_dependent_pair")elseUnsafeMonomorphic.mkConstcin(* cut with the good equality and prove the requested goal *)tclTHENLIST[intro;onLastHyp(funhyp->Tacticals.pf_constr_of_globalCoqlib.(lib_ref"core.eq.type")>>=funceq->tclTHENS(cut(mkApp(ceq,new_eq_args)))[clear[destVarsigmahyp];Tacticals.pf_constr_of_globalinj2>>=funinj2->Tactics.exact_check(mkApp(inj2,[|ar1.(0);c;ar1.(1);ar1.(2);ar1.(3);ar2.(3);hyp|]))])]withExit->Proofview.tclUNIT()end(* Given t1=t2 Inj calculates the whd normal forms of t1 and t2 and it
expands then only when the whdnf has a constructor of an inductive type
in hd position, otherwise delta expansion is not done *)letsimplify_argsenvsigmat=(* Quick hack to reduce in arguments of eq only *)matchdecompose_appsigmatwith|eq,[|t;c1;c2|]->applist(eq,[t;simplenvsigmac1;simplenvsigmac2])|eq,[|t1;c1;t2;c2|]->applist(eq,[t1;simplenvsigmac1;t2;simplenvsigmac2])|_->tletinject_at_positionsenvsigmal2reqposnstac=let{eq_data=(eq,(t,t1,t2));eq_term=v;eq_evar=evs}=eqinlete=next_ident_awayeq_baseid(vars_of_envenv)inlete_env=push_named(LocalAssum(make_annoteERelevance.relevant,t))envinletevdref=refsigmainletfilter(cpath,t1',t2')=try(* arbitrarily take t1' as the injector default value *)letsigma,(injbody,resty)=build_injectore_env!evdreft1'(mkVare)cpathinletinjfun=mkNamedLambdasigma(make_annoteERelevance.relevant)tinjbodyinletsigma,congr=Evd.fresh_globalenvsigmaeq.congrin(* pf : eq t t1 t2 -> eq resty (injfun t1) (injfun t2) *)letmkc=Retyping.get_judgment_ofenvsigmacinletargs=Array.mapmk[|t;resty;injfun;t1;t2|]inletsigma,pf=Typing.judge_of_applyenvsigma(mkcongr)argsinlet{Environ.uj_val=pf;Environ.uj_type=pf_typ}=pfinletpf_typ=Vars.subst1mkProp(pi3@@destProdsigmapf_typ)inletpf=mkApp(pf,[|v|])inletty=simplify_argsenvsigmapf_typinevdref:=sigma;Some(pf,ty)withFailure_->Noneinletinjectors=List.map_filterfilterposnsinifList.is_emptyinjectorsthentclZEROMSG(str"Failed to decompose the equality.")elseletmap(pf,ty)=tclTHENS(cutty)[inject_if_homogenous_dependent_pairty;Tactics.exact_checkpf<*>Proofview.Unsafe.tclNEWGOALSevs;]inProofview.Unsafe.tclEVARS!evdref<*>Tacticals.tclTHENFIRST(Tacticals.tclMAPmap(ifl2rthenList.revinjectorselseinjectors))(tac(List.lengthinjectors))exceptionNothingToInjectlet()=CErrors.register_handler(function|NothingToInject->Some(Pp.str"Nothing to inject.")|_->None)letinjEqThenkeep_proofstacl2reql=Proofview.Goal.enterbeginfungl->let{eq_data=(eq,(t,t1,t2))}=eqlinletsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletid=trySome(destVarsigmaeql.eq_term)withDestKO->Noneinmatchfind_positionsenvsigma~keep_proofs~no_discr:truet1t2with|Inl_->assertfalse|Inr[]->letsuggestion=ifkeep_proofsthen""else" You can try to use option Set Keep Proof Equalities."intclZEROMSG(strbrk("No information can be deduced from this equality and the injectivity of constructors. This may be because the terms are convertible, or due to pattern matching restrictions in the sort Prop."^suggestion))|Inr[([],_,_)]->Proofview.tclZERONothingToInject|Inrposns->inject_at_positionsenvsigmal2reqlposns(tacid)endletget_previous_hyp_positionidgl=letenv,sigma=Proofview.Goal.(envgl,sigmagl)inletrecauxdest=function|[]->raise(RefinerError(env,sigma,NoSuchHypid))|d::right->lethyp=Context.Named.Declaration.get_iddinifId.equalhypidthendestelseaux(MoveAfterhyp)rightinauxMoveLast(Proofview.Goal.hypsgl)letinjEqflags?(injection_in_context=injection_in_context_flag())with_evarsclear_flagipats=(* Decide which compatibility mode to use *)letipats_style,l2r,dft_clear_flag,bounded_intro=matchipatswith|Nonewheninjection_in_context->Some[],true,true,true|None->None,false,false,false|_->letb=use_injection_pattern_l2r_orderflagsinipats,b,b,bin(* Built the post tactic depending on compatibility mode *)letpost_tacidn=matchipats_stylewith|Someipats->Proofview.Goal.enterbeginfungl->letdestopt=matchidwith|Someid->get_previous_hyp_positionidgl|None->MoveLastinletclear_tac=tclTRY(apply_clear_requestclear_flagdft_clear_flagid)in(* Try should be removal if dependency were treated *)letintro_tac=ifbounded_introthenintro_patterns_bound_towith_evarsndestoptipatselseintro_patterns_towith_evarsdestoptipatsintclTHENclear_tacintro_tacend|None->tclIDTACininjEqThen(keep_proof_equalitiesflags)post_tacl2rletinjflags?injection_in_contextipatswith_evarsclear_flag=onEqualitywith_evars(injEqflags?injection_in_contextwith_evarsclear_flagipats)letinjClauseflags?injection_in_contextipatswith_evars=function|None->onNegatedEqualitywith_evars(injEqflags?injection_in_contextwith_evarsNoneipats)|Somec->onInductionArg(injflags?injection_in_contextipatswith_evars)cletsimpleInjClauseflagswith_evars=function|None->onNegatedEqualitywith_evars(injEqflags~injection_in_context:falsewith_evarsNoneNone)|Somec->onInductionArg(funclear_flag->onEqualitywith_evars(injEqflags~injection_in_context:falsewith_evarsclear_flagNone))cletinjConclflags?injection_in_context()=injClauseflags?injection_in_contextNonefalseNoneletinjHypflags?injection_in_contextclear_flagid=injClauseflags?injection_in_contextNonefalse(Some(clear_flag,ElimOnIdentCAst.(makeid)))letdecompEqThenkeep_proofsntaceq=let{eq_data=(_,(_,t1,t2)asu)}=eqinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletido=trySome(destVarsigmaeq.eq_term)withDestKO->Noneinmatchfind_positionsenvsigma~keep_proofs~no_discr:falset1t2with|Inl(cpath,(_,dirn),_)->discr_positionsenvsigmaeqcpathdirn|Inr[]->(* Change: do not fail, simplify clear this trivial hyp *)ntacido0|Inrposns->inject_at_positionsenvsigmatrueeqposns(ntacido)endletdEqThen0~keep_proofswith_evarsntac=function|None->onNegatedEqualitywith_evars(decompEqThen(use_keep_proofskeep_proofs)(ntacNone))|Somec->onInductionArg(funclear_flag->onEqualitywith_evars(decompEqThen(use_keep_proofskeep_proofs)(ntacclear_flag)))cletdEq~keep_proofswith_evars=dEqThen0~keep_proofswith_evars(funclear_flagidox->(apply_clear_requestclear_flag(use_clear_hyp_by_default())ido))letdEqThen~keep_proofswith_evarsntacwhere=dEqThen0~keep_proofswith_evars(fun__n->ntacn)whereletintro_decomp_eqtac(eq,_,data)(c,t)=Proofview.Goal.enterbeginfungl->leteq={eq_data=(eq,data);eq_term=c;eq_evar=[]}indecompEqThen!keep_proof_equalities_for_injection(fun_->tac)eqendlet()=declare_intro_decomp_eqintro_decomp_eq(* [subst_tuple_term dep_pair B]
Given that dep_pair looks like:
(existT e1 (existT e2 ... (existT en en+1) ... ))
of type {x1:T1 & {x2:T2(x1) & ... {xn:Tn(x1..xn-1) & en+1 } } }
and B might contain instances of the ei, we will return the term:
([x1:ty1]...[xn+1:tyn+1]B
(projT1 (mkRel 1))
(projT1 (projT2 (mkRel 1)))
...
(projT1 (projT2^n (mkRel 1)))
(projT2 (projT2^n (mkRel 1)))
That is, we will abstract out the terms e1...en+1 of types
t1 (=_beta T1), ..., tn+1 (=_beta Tn+1(e1..en)) as usual, but
will then produce a term in which the abstraction is on a single
term - the debruijn index [mkRel 1], which will be of the same type
as dep_pair (note that the abstracted body may not be typable!).
ALGORITHM for abstraction:
We have a list of terms, [e1]...[en+1], which we want to abstract
out of [B]. For each term [ei], going backwards from [n+1], we
just do a [subst_term], and then do a lambda-abstraction to the
type of the [ei].
*)letdecomp_tupleenvsigmac=letrecdecomprecaccuex=matchfind_sigma_data_decomposeenvsigmaexwith|({proj1;proj2}),(u,a,b,car,cdr)->decomprec((proj1,proj2,u,a,b,car,cdr)::accu)cdr|exceptionConstr_matching.PatternMatchingFailure->List.revaccuindecomprec[]cletmake_tuple_projsdata=letfoldaccu(p1,p2,u,a,b,_,_)=letproj=applist(mkConstU(destConstRefp1,u),[a;b;accu])inletaccu=applist(mkConstU(destConstRefp2,u),[a;b;accu])inaccu,projinletlast,projs=List.fold_left_mapfold(mkRel1)datainprojs@[last]letmake_tuple_argssigmaargtypdata=letfold_(_,_,_,a,b,car,cdr)=lettyp=beta_applistsigma(b,[car])in(cdr,typ),(car,a)inletlast,args=List.fold_left_mapfold(arg,typ)datainargs@[last]letsubst_tuple_termenvsigmadep_pair1dep_pair2body=lettyp=get_type_ofenvsigmadep_pair1in(* We find all possible decompositions *)letdata1=decomp_tupleenvsigmadep_pair1inletdata2=decomp_tupleenvsigmadep_pair2in(* We adjust to the shortest decomposition *)letn=min(List.lengthdata1)(List.lengthdata2)inletdata1=List.firstnndata1inletdata2=List.firstnndata2in(* We rewrite dep_pair1 ... *)letproj_list=make_tuple_projsdata1inlete1_list=make_tuple_argssigmadep_pair1typdata1in(* ... and use dep_pair2 to compute the expected goal *)lete2_list=make_tuple_argssigmadep_pair2typdata2in(* We build the expected goal *)letfold(e,t)body=lambda_createenvsigma(ERelevance.relevant,t,subst_termsigmaebody)inletabst_B=List.fold_rightfolde1_listbodyinletctx,abst_B=decompose_lambda_n_assumsigma(List.lengthe1_list)abst_Bin(* Retype the body, it might be ill-typed if it depends on the abstracted subterms *)letsigma,_=Typing.type_of(push_rel_contextctxenv)sigmaabst_Binletsigma=(* FIXME: this should be enforced before. We only have to check the last
projection, since all previous ones mention a prefix of the subtypes. *)letenv=push_rel(Rel.Declaration.LocalAssum(anonR,typ))envinletsigma,_=Typing.type_ofenvsigma(List.lastproj_list)insigmainletpred_body=Vars.substl(List.revproj_list)abst_Binletbody=mkApp(lambda_createenvsigma(ERelevance.relevant,typ,pred_body),[|dep_pair1|])inletexpected_goal=Vars.substl(List.rev_mapfste2_list)abst_Bin(* Simulate now the normalisation treatment made by Logic.mk_refgoals *)letexpected_goal=nf_betaiotaenvsigmaexpected_goalin(sigma,(body,expected_goal))(* Like "replace" but decompose dependent equalities *)(* i.e. if equality is "exists t v = exists u w", and goal is "phi(t,u)", *)(* then it uses the predicate "\x.phi(proj1_sig x,proj2_sig x)", and so *)(* on for further iterated sigma-tuples *)letcutSubstInConcll2reqn=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet(lbeq,u,(t,e1,e2))=pf_applyfind_eq_data_decomposegleqninlettyp=pf_conclglinlet(e1,e2)=ifl2rthen(e1,e2)else(e2,e1)inlet(sigma,(typ,expected))=subst_tuple_termenvsigmae1e2typintclTHEN(Proofview.Unsafe.tclEVARSsigma)(tclTHENFIRST(tclTHENLIST[(change_concltyp);(* Put in pattern form *)(replace_coreonConcll2reqn)])(change_conclexpected))(* Put in normalized form *)endletcutSubstInHypl2reqnid=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet(lbeq,u,(t,e1,e2))=pf_applyfind_eq_data_decomposegleqninlettyp=pf_get_hyp_typidglinlet(e1,e2)=ifl2rthen(e1,e2)else(e2,e1)inlet(sigma,(typ,expected))=subst_tuple_termenvsigmae1e2typintclTHEN(Proofview.Unsafe.tclEVARSsigma)(tclTHENFIRST(tclTHENLIST[(change_in_hyp~check:trueNone(make_change_argtyp)(id,InHypTypeOnly));(replace_core(onHypid)l2reqn)])(change_in_hyp~check:trueNone(make_change_argexpected)(id,InHypTypeOnly)))endlettry_rewritetac=Proofview.tclORELSEtacbeginfunction(e,info)->matchewith|Constr_matching.PatternMatchingFailure->tclZEROMSG~info(str"Not a primitive equality here.")|e->(* XXX: absorbing anomalies?? *)tclZEROMSG~info(strbrk"Cannot find a well-typed generalization of the goal that makes the proof progress.")endletcutSubstClausel2reqncls=matchclswith|None->cutSubstInConcll2reqn|Someid->cutSubstInHypl2reqnidletsubstClausel2rccls=Proofview.Goal.enterbeginfungl->leteq=pf_applyget_type_ofglcintclTHENS(cutSubstClausel2reqcls)[Proofview.tclUNIT();exact_no_checkc]endletrewriteClausel2rccls=try_rewrite(substClausel2rccls)letrewriteInHypl2rcid=rewriteClausel2rc(Someid)letrewriteInConcll2rc=rewriteClausel2rcNone(* Naming scheme for rewrite tactics
give equality give proof of equality
/ cutSubstClause substClause
raw | cutSubstInHyp substInHyp
\ cutSubstInConcl substInConcl
raw = raise typing error or PatternMatchingFailure
user = raise user error specific to rewrite
*)(**********************************************************************)(* Substitutions tactics (JCF) *)letrestrict_to_eq_and_identityenveq=(* compatibility *)letis_refb=matchCoqlib.lib_ref_optbwith|None->false|Someb->Environ.QGlobRef.equalenveqbinifnot(List.existsis_ref["core.eq.type";"core.identity.type"])thenraiseConstr_matching.PatternMatchingFailureexceptionFoundHypof(Id.t*constr*bool)(* tests whether hyp [c] is [x = t] or [t = x], [x] not occurring in [t] *)letis_eq_xglxd=letid=NamedDecl.get_iddintryletis_varidc=matchEConstr.kind(projectgl)cwith|Varid'->Id.equalidid'|_->falseinletc=pf_nf_evargl(NamedDecl.get_typed)inlet(_,lhs,rhs)=pi3(pf_applyfind_eq_data_decomposeglc)inif(is_varxlhs)&¬(local_occur_var(projectgl)xrhs)thenraise(FoundHyp(id,rhs,true));if(is_varxrhs)&¬(local_occur_var(projectgl)xlhs)thenraise(FoundHyp(id,lhs,false))withConstr_matching.PatternMatchingFailure->()exceptionFoundDepInGlobalofId.toption*GlobRef.tlettest_non_indirectly_dependent_section_variableglx=letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinlethyps=Proofview.Goal.hypsglinletconcl=Proofview.Goal.conclglinList.iter(fundecl->NamedDecl.iter_constr(func->matchoccur_var_indirectlyenvsigmaxcwith|Somegr->raise(FoundDepInGlobal(Some(NamedDecl.get_iddecl),gr))|None->())decl)hyps;matchoccur_var_indirectlyenvsigmaxconclwith|Somegr->raise(FoundDepInGlobal(None,gr))|None->()letcheck_non_indirectly_dependent_section_variableglx=trytest_non_indirectly_dependent_section_variableglxwithFoundDepInGlobal(pos,gr)->letwhere=matchposwith|Someid->str"hypothesis "++Id.printid|None->str"the conclusion of the goal"inuser_err(strbrk"Section variable "++Id.printx++strbrk" occurs implicitly in global declaration "++Printer.pr_globalgr++strbrk" present in "++where++strbrk".")letis_non_indirectly_dependent_section_variableglz=trytest_non_indirectly_dependent_section_variableglz;truewithFoundDepInGlobal(pos,gr)->false(* Rewrite "hyp:x=rhs" or "hyp:rhs=x" (if dir=false) everywhere and
erase hyp and x; proceed by generalizing all dep hyps *)letsubst_onedep_proof_okx(hyp,rhs,dir)=Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinlethyps=Proofview.Goal.hypsglinletconcl=Proofview.Goal.conclglin(* The set of hypotheses using x *)letdephyps=List.rev(pi3(List.fold_right(fundcl(dest,deps,allhyps)->letid=NamedDecl.get_iddclinifnot(Id.equalidhyp)&&List.exists(funy->local_occur_var_in_declsigmaydcl)depsthen(dest,id::deps,(dest,id)::allhyps)else(MoveBeforeid,deps,allhyps))hyps(MoveBeforex,[x],[])))in(* In practice, no dep hyps before x, so MoveBefore x is good enough *)(* Decides if x appears in conclusion *)letdepconcl=local_occur_varsigmaxconclinletneed_rewrite=not(List.is_emptydephyps)||depconclintclTHENLIST((ifneed_rewritethen[Generalize.revert(List.mapsnddephyps);general_rewrite~where:None~l2r:dirAtLeastOneOccurrence~freeze:true~dep:dep_proof_ok~with_evars:false(mkVarhyp,NoBindings);(tclMAP(fun(dest,id)->intro_move(Someid)dest)dephyps)]else[Proofview.tclUNIT()])@[tclTRY(clear[x;hyp])])end(* Look for an hypothesis hyp of the form "x=rhs" or "rhs=x", rewrite
it everywhere, and erase hyp and x; proceed by generalizing all dep hyps *)letsubst_one_vardep_proof_okx=Proofview.Goal.enterbeginfungl->letdecl=pf_get_hypxglin(* If x has a body, simply replace x with body and clear x *)ifis_local_defdeclthentclTHEN(unfold_bodyx)(clear[x])else(* Find a non-recursive definition for x *)letres=try(* [is_eq_x] ensures nf_evar on its side *)lethyps=Proofview.Goal.hypsglinlettesthyp_=is_eq_xglxhypinContext.Named.fold_outsidetest~init:()hyps;user_err(str"Cannot find any non-recursive equality over "++Id.printx++str".")withFoundHypres->resinifis_section_variable(Global.env())xthencheck_non_indirectly_dependent_section_variableglx;subst_onedep_proof_okxresendletsubst_gendep_proof_okids=tclMAP(subst_one_vardep_proof_ok)ids(* For every x, look for an hypothesis hyp of the form "x=rhs" or "rhs=x",
rewrite it everywhere, and erase hyp and x; proceed by generalizing
all dep hyps *)letsubst=subst_gentruetypesubst_tactic_flags={only_leibniz:bool;rewrite_dependent_proof:bool}letdefault_subst_tactic_flags={only_leibniz=false;rewrite_dependent_proof=true}letwarn_deprecated_simple_subst=CWarnings.create~name:"deprecated-simple-subst"~category:Deprecation.Version.v8_8(fun()->strbrk"\"simple subst\" is deprecated")letsubst_all?(flags=default_subst_tactic_flags)()=let()=ifflags.only_leibniz||notflags.rewrite_dependent_proofthenwarn_deprecated_simple_subst()in(* Find hypotheses to treat in linear time *)letprocesshyp=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=projectglinletc=pf_get_hyphypgl|>NamedDecl.get_typeintryletlbeq,u,(_,x,y)=pf_applyfind_eq_data_decomposeglcinifflags.only_leibnizthenrestrict_to_eq_and_identityenvlbeq.eq;matchEConstr.kindsigmax,EConstr.kindsigmaywith|Varx,VarywhenId.equalxy->Proofview.tclUNIT()|Varx',_whennot(Termops.local_occur_varsigmax'y)&¬(is_evaluableenv(EvalVarRefx'))&&is_non_indirectly_dependent_section_variableglx'->subst_oneflags.rewrite_dependent_proofx'(hyp,y,true)|_,Vary'whennot(Termops.local_occur_varsigmay'x)&¬(is_evaluableenv(EvalVarRefy'))&&is_non_indirectly_dependent_section_variablegly'->subst_oneflags.rewrite_dependent_proofy'(hyp,x,false)|_->Proofview.tclUNIT()withConstr_matching.PatternMatchingFailure->Proofview.tclUNIT()endinProofview.Goal.enterbeginfungl->tclMAPprocess(List.rev(List.mapNamedDecl.get_id(Proofview.Goal.hypsgl)))end(* Rewrite the first assumption for which a condition holds
and gives the direction of the rewrite *)letcond_eq_term_leftctgl=trylet(_,x,_)=pi3(pf_applyfind_eq_data_decomposeglt)inifpf_conv_xglcxthentrueelsefailwith"not convertible"withConstr_matching.PatternMatchingFailure->failwith"not an equality"letcond_eq_term_rightctgl=trylet(_,_,x)=pi3(pf_applyfind_eq_data_decomposeglt)inifpf_conv_xglcxthenfalseelsefailwith"not convertible"withConstr_matching.PatternMatchingFailure->failwith"not an equality"letcond_eq_termctgl=trylet(_,x,y)=pi3(pf_applyfind_eq_data_decomposeglt)inifpf_conv_xglcxthentrueelseifpf_conv_xglcythenfalseelsefailwith"not convertible"withConstr_matching.PatternMatchingFailure->failwith"not an equality"letrewrite_assumption_condcond_eq_termcl=letrecarechypsgl=matchhypswith|[]->user_errPp.(str"No such assumption.")|hyp::rest->letid=NamedDecl.get_idhypinbegintryletdir=cond_eq_term(NamedDecl.get_typehyp)glingeneral_rewrite_clausedirfalse(mkVarid,NoBindings)clwith|Failure_|UserError_->arecrestglendinProofview.Goal.enterbeginfungl->lethyps=Proofview.Goal.hypsglinarechypsglend(* Generalize "subst x" to substitution of subterm appearing as an
equation in the context, but not clearing the hypothesis *)letreplace_termdir_optc=letcond_eq_fun=matchdir_optwith|None->cond_eq_termc|Sometrue->cond_eq_term_leftc|Somefalse->cond_eq_term_rightcinrewrite_assumption_condcond_eq_fun(* Declare rewriting tactic for intro patterns "<-" and "->" *)let()=letgmrl2rwith_evarstacc=general_rewrite_clausel2rwith_evarstaccinHook.setTactics.general_rewrite_clausegmrlet()=Hook.setTactics.subst_onesubst_one