123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430(************************************************************************)(* * 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=VarsopenPpopenCErrorsopenUtilopenNamesopenNameopsopenConstropenContextopenTermopsopenEnvironopenEConstropenVarsopenFind_subtermopenNamegenopenDeclarationsopenInductiveopsopenReductionopsopenEvdopenTacredopenGenredexpropenTacmachopenLogicopenClenvopenTacticalsopenHipatternopenCoqlibopenEvarutilopenIndrecopenPretype_errorsopenUnificationopenLocusopenLocusopsopenTactypesopenProofview.NotationsopenContext.Named.DeclarationmoduleRelDecl=Context.Rel.DeclarationmoduleNamedDecl=Context.Named.DeclarationlettclEVARS=Proofview.Unsafe.tclEVARSlettclEVARSTHENsigmat=Proofview.tclTHEN(tclEVARSsigma)tletinj_with_occurrencese=(AllOccurrences,e)lettyp_ofenvsigmac=letopenRetypingintryget_type_of~lax:trueenvsigmacwithRetypeErrore->user_err(print_retype_errore)openGoptionsletclear_hyp_by_default=reffalseletuse_clear_hyp_by_default()=!clear_hyp_by_defaultlet()=declare_bool_option{optstage=Summary.Stage.Interp;optdepr=None;optkey=["Default";"Clearing";"Used";"Hypotheses"];optread=(fun()->!clear_hyp_by_default);optwrite=(funb->clear_hyp_by_default:=b)}(*********************************************)(* Errors *)(*********************************************)exceptionIntroAlreadyDeclaredofId.texceptionClearDependencyofenv*evar_map*Id.toption*Evarutil.clear_dependency_error*GlobRef.toptionexceptionReplacingDependencyofenv*evar_map*Id.t*Evarutil.clear_dependency_error*GlobRef.toptionexceptionAlreadyUsedofId.texceptionUsedTwiceofId.texceptionVariableHasNoValueofId.texceptionConvertIncompatibleTypesofenv*evar_map*constr*constrexceptionConvertNotATypeexceptionNotConvertibleexceptionNotUnfoldableexceptionNoQuantifiedHypothesisofquantified_hypothesis*boolexceptionCannotFindInstanceofId.texceptionNothingToRewriteofId.texceptionIllFormedEliminationTypeexceptionSchemeDontApplyexceptionUnableToApplyLemmaofenv*evar_map*constr*constrexceptionDependsOnBodyofId.tlist*Id.Set.t*Id.toptionexceptionNeedFullyAppliedArgumentexceptionNotRightNumberConstructorsofintexceptionNotEnoughConstructorsexceptionConstructorNumberedFromOneexceptionNoConstructorsexceptionUnexpectedExtraPatternofintoption*delayed_open_constrintro_pattern_exprexceptionNotAnInductionSchemeofstringexceptionNotAnInductionSchemeLetInexceptionCannotFindInductiveArgumentexceptionMentionConclusionDependentOnofId.texceptionDontKnowWhatToDoWithofintro_pattern_naming_exprexceptionOneIntroPatternExpectedexceptionKeepAndClearModifierOnlyForHypothesesexceptionFixpointOnNonInductiveTypeexceptionNotEnoughProductsexceptionFixpointSameMutualInductiveTypeexceptionAllMethodsInCoinductiveTypeexceptionReplacementIllTypedofexnexceptionUnsupportedWithClauseexceptionUnsupportedEqnClauseexceptionUnsupportedInClauseofboolexceptionDontKnowWhereToFindArgumentexceptionMultipleAsAndUsingClauseOnlyListexceptionNotEnoughPremisesexceptionNeedDependentProductleterror?loce=Loc.raise?loceletclear_in_global_msg=function|None->mt()|Someref->str" implicitly in "++Printer.pr_globalrefletclear_dependency_msgenvsigmaiderringlobal=letppidupper=functionSomeid->Id.printid|None->str"This variable"inletppid=functionSomeid->Id.printid|None->str"this variable"inletpp=clear_in_global_msginglobalinmatcherrwith|Evarutil.OccurHypInSimpleClauseNone->ppidupperid++str" is used"++pp++str" in conclusion."|Evarutil.OccurHypInSimpleClause(Someid')->ppidupperid++strbrk" is used"++pp++str" in hypothesis "++Id.printid'++str"."|Evarutil.EvarTypingBreakev->str"Cannot remove "++ppidid++strbrk" without breaking the typing of "++Printer.pr_existentialenvsigmaev++str"."|Evarutil.NoCandidatesLeftev->str"Cannot remove "++ppidid++str" as it would leave the existential "++Printer.pr_existential_keyenvsigmaev++str" without candidates."letreplacing_dependency_msgenvsigmaiderringlobal=letpp=clear_in_global_msginglobalinmatcherrwith|Evarutil.OccurHypInSimpleClauseNone->str"Cannot change "++Id.printid++str", it is used"++pp++str" in conclusion."|Evarutil.OccurHypInSimpleClause(Someid')->str"Cannot change "++Id.printid++strbrk", it is used"++pp++str" in hypothesis "++Id.printid'++str"."|Evarutil.EvarTypingBreakev->str"Cannot change "++Id.printid++strbrk" without breaking the typing of "++Printer.pr_existentialenvsigmaev++str"."|Evarutil.NoCandidatesLeftev->str"Cannot change "++Id.printid++str" as it would leave the existential "++Printer.pr_existential_keyenvsigmaev++str" without candidates."letmsg_quantified_hypothesis=function|NamedHypid->str"quantified hypothesis named "++Id.printid.CAst.v|AnonHypn->pr_nthn++str" non dependent hypothesis"letexplain_unexpected_extra_patternboundpat=letnb=Option.getboundinlets1,s2,s3=matchpatwith|IntroNaming(IntroIdentifier_)->"name",(String.pluralnb" introduction pattern"),"no"|_->"introduction pattern","","none"instr"Unexpected "++strs1++str" ("++(ifInt.equalnb0then(strs3++strs2)else(str"at most "++intnb++strs2))++spc()++str(ifInt.equalnb1then"was"else"were")++strbrk" expected in the branch)."exceptionUnhandledletwrap_unhandledfe=trySome(fe)withUnhandled->Nonelettactic_interp_error_handler=function|IntroAlreadyDeclaredid->Id.printid++str" is already declared."|ClearDependency(env,sigma,id,err,inglobal)->clear_dependency_msgenvsigmaiderringlobal|ReplacingDependency(env,sigma,id,err,inglobal)->replacing_dependency_msgenvsigmaiderringlobal|AlreadyUsedid->Id.printid++str" is already used."|UsedTwiceid->Id.printid++str" is used twice."|VariableHasNoValueid->Id.printid++str" is not a defined hypothesis."|ConvertIncompatibleTypes(env,sigma,t1,t2)->str"The first term has type"++spc()++quote(Termops.Internal.print_constr_envenvsigmat1)++spc()++strbrk"while the second term has incompatible type"++spc()++quote(Termops.Internal.print_constr_envenvsigmat2)++str"."|ConvertNotAType->str"Not a type."|NotConvertible->str"Not convertible."|NotUnfoldable->str"Cannot unfold a non-constant."|NoQuantifiedHypothesis(id,red)->str"No "++msg_quantified_hypothesisid++strbrk" in current goal"++(ifredthenstrbrk" even after head-reduction"elsemt())++str"."|CannotFindInstanceid->str"Cannot find an instance for "++Id.printid++str"."|NothingToRewriteid->str"Nothing to rewrite in "++Id.printid++str"."|IllFormedEliminationType->str"The type of elimination clause is not well-formed."|SchemeDontApply->str"Scheme cannot be applied."|UnableToApplyLemma(env,sigma,thm,t)->str"Unable to apply lemma of type"++brk(1,1)++quote(Printer.pr_leconstr_envenvsigmathm)++spc()++str"on hypothesis of type"++brk(1,1)++quote(Printer.pr_leconstr_envenvsigmat)++str"."|DependsOnBody(idl,ids,where)->letidl=List.filter(funid->Id.Set.memidids)idlinleton_the_bodies=function|[]->assertfalse|[id]->str" depends on the body of "++Id.printid|l->str" depends on the bodies of "++pr_sequenceId.printlin(matchwherewith|None->str"Conclusion"++on_the_bodiesidl|Someid->str"Hypothesis "++Id.printid++on_the_bodiesidl)|NeedFullyAppliedArgument->str"Need a fully applied argument."|NotRightNumberConstructorsn->str"Not an inductive goal with "++intn++str(String.pluraln" constructor")++str"."|NotEnoughConstructors->str"Not enough constructors."|ConstructorNumberedFromOne->str"The constructors are numbered starting from 1."|NoConstructors->str"The type has no constructors."|UnexpectedExtraPattern(bound,pat)->explain_unexpected_extra_patternboundpat|NotAnInductionSchemes->lets=ifnot(String.is_emptys)thens^" "elsesinstr"Cannot recognize "++strs++str"an induction scheme."|NotAnInductionSchemeLetIn->str"Strange letin, cannot recognize an induction scheme."|CannotFindInductiveArgument->str"Cannot find inductive argument of elimination scheme."|MentionConclusionDependentOnid->str"Conclusion must be mentioned: it depends on "++Id.printid++str"."|DontKnowWhatToDoWithid->str"Do not know what to do with "++Miscprint.pr_intro_pattern_namingid|OneIntroPatternExpected->str"Introduction pattern for one hypothesis expected."|KeepAndClearModifierOnlyForHypotheses->str"keep/clear modifiers apply only to hypothesis names."|FixpointOnNonInductiveType->str"Cannot do a fixpoint on a non inductive type."|NotEnoughProducts->str"Not enough products."|FixpointSameMutualInductiveType->str"Fixpoints should be on the same mutual inductive declaration."|AllMethodsInCoinductiveType->str"All methods must construct elements in coinductive types."|ReplacementIllTypede->str"Replacement would lead to an ill-typed term:"++spc()++CErrors.printe|UnsupportedEqnClause->str"'eqn' clause not supported here."|UnsupportedWithClause->str"'with' clause not supported here."|UnsupportedInClauseb->str(ifbthen"'in' clause not supported here."else"'eqn' clause not supported here.")|DontKnowWhereToFindArgument->str"Don't know where to find some argument."|MultipleAsAndUsingClauseOnlyList->str"'as' clause with multiple arguments and 'using' clause can only occur last."|NotEnoughPremises->str"Applied theorem does not have enough premises."|NeedDependentProduct->str"Needs a non-dependent product."|_->raiseUnhandledlet_=CErrors.register_handler(wrap_unhandledtactic_interp_error_handler)leterror_clear_dependencyenvsigmaiderringlobal=error(ClearDependency(env,sigma,Someid,err,inglobal))leterror_replacing_dependencyenvsigmaiderringlobal=error(ReplacingDependency(env,sigma,id,err,inglobal))(*********************************************)(* Tactics *)(*********************************************)(******************************************)(* Primitive tactics *)(******************************************)(** This tactic creates a partial proof realizing the introduction rule, but
does not check anything. *)letunsafe_introenvdeclb=Refine.refine~typecheck:falsebeginfunsigma->letctx=named_context_valenvinletnctx=push_named_context_valdeclctxinletinst=EConstr.identity_subst_val(named_context_valenv)inletninst=SList.cons(mkRel1)instinletnb=subst1(mkVar(NamedDecl.get_iddecl))binlet(sigma,ev)=new_pure_evarnctxsigmanb~principal:truein(sigma,mkLambda_or_LetIn(NamedDecl.to_rel_decldecl)(mkEvar(ev,ninst)))endletintroductionid=Proofview.Goal.enterbeginfungl->letconcl=Proofview.Goal.conclglinletsigma=Tacmach.projectglinlethyps=named_context_val(Proofview.Goal.envgl)inletenv=Proofview.Goal.envglinlet()=ifmem_named_context_validhypsthenerror(IntroAlreadyDeclaredid)inletopenContext.Named.DeclarationinmatchEConstr.kindsigmaconclwith|Prod(id0,t,b)->unsafe_introenv(LocalAssum({id0withbinder_name=id},t))b|LetIn(id0,c,t,b)->unsafe_introenv(LocalDef({id0withbinder_name=id},c,t))b|_->raise(RefinerError(env,sigma,IntroNeedsProduct))end(* Not sure if being able to disable [cast] is useful. Uses seem picked somewhat randomly. *)letconvert_concl~cast~checktyk=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletconclty=Proofview.Goal.conclglinRefine.refine~typecheck:falsebeginfunsigma->letsigma=ifcheckthenbeginletsigma,_=Typing.type_ofenvsigmatyinmatchReductionops.infer_convenvsigmatyconcltywith|None->errorNotConvertible|Somesigma->sigmaendelsesigmainlet(sigma,x)=Evarutil.new_evarenvsigma~principal:truetyinletans=ifnotcastthenxelsemkCast(x,k,conclty)in(sigma,ans)endendletconvert_hyp~check~reorderd=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletty=Proofview.Goal.conclglinletsign=convert_hyp~check~reorderenvsigmadinletenv=reset_with_named_contextsignenvinRefine.refine~typecheck:falsebeginfunsigma->Evarutil.new_evarenvsigma~principal:truetyendendletconvert_genpbxy=Proofview.Goal.enterbeginfungl->matchTacmach.pf_apply(Reductionops.infer_conv~pb)glxywith|Somesigma->Proofview.Unsafe.tclEVARSsigma|None->letinfo=Exninfo.reify()inTacticals.tclFAIL~info(str"Not convertible")|exceptione->let_,info=Exninfo.captureein(* FIXME: Sometimes an anomaly is raised from conversion *)Tacticals.tclFAIL~info(str"Not convertible")endletconvertxy=convert_genConversion.CONVxyletconvert_leqxy=convert_genConversion.CUMULxy(* This tactic enables users to remove hypotheses from the signature.
* Some care is taken to prevent them from removing variables that are
* subsequently used in other hypotheses or in the conclusion of the
* goal. *)letclear_genfail=function|[]->Proofview.tclUNIT()|ids->Proofview.Goal.enterbeginfungl->letids=List.fold_rightId.Set.addidsId.Set.emptyin(* clear_hyps_in_evi does not require nf terms *)letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglinlet(sigma,hyps,concl)=tryclear_hyps_in_evienvsigma(named_context_valenv)conclidswithEvarutil.ClearDependencyError(id,err,inglobal)->failenvsigmaiderringlobalinletenv=reset_with_named_contexthypsenvinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Refine.refine~typecheck:falsebeginfunsigma->Evarutil.new_evarenvsigma~principal:trueconclend)endletclearids=clear_generror_clear_dependencyidsletclear_for_replacingids=clear_generror_replacing_dependencyidsletapply_clear_requestclear_flagdftc=letdoclear=matchclear_flagwith|None->ifdftthencelseNone|Sometrue->beginmatchcwith|None->errorKeepAndClearModifierOnlyForHypotheses|Someid->Someidend|Somefalse->Noneinmatchdoclearwith|None->Proofview.tclUNIT()|Someid->clear[id](* Moving hypotheses *)letmove_hypiddest=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletty=Proofview.Goal.conclglinletsign=named_context_valenvinletsign'=move_hyp_in_named_contextenvsigmaiddestsigninletenv=reset_with_named_contextsign'envinRefine.refine~typecheck:falsebeginfunsigma->Evarutil.new_evarenvsigma~principal:truetyendend(* Renaming hypotheses *)letrename_hyprepl=letfoldaccu(src,dst)=matchaccuwith|None->None|Some(srcs,dsts)->ifId.Set.memsrcsrcsthenNoneelseifId.Set.memdstdststhenNoneelseletsrcs=Id.Set.addsrcsrcsinletdsts=Id.Set.adddstdstsinSome(srcs,dsts)inletinit=Some(Id.Set.empty,Id.Set.empty)inletdom=List.fold_leftfoldinitreplinmatchdomwith|None->letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"Not a one-to-one name mapping")|Some(src,dst)->Proofview.Goal.enterbeginfungl->letconcl=Proofview.Goal.conclglinletenv=Proofview.Goal.envglinletsign=named_context_valenvinletsigma=Proofview.Goal.sigmaglin(* Check that we do not mess variables *)letvars=ids_of_named_context_valsigninlet()=ifnot(Id.Set.subsetsrcvars)thenlethyp=Id.Set.choose(Id.Set.diffsrcvars)inraise(RefinerError(env,sigma,NoSuchHyphyp))inletmods=Id.Set.diffvarssrcinlet()=tryletelt=Id.Set.choose(Id.Set.interdstmods)inerror(AlreadyUsedelt)withNot_found->()in(* All is well *)letmake_subst(src,dst)=(src,mkVardst)inletsubst=List.mapmake_substreplinletsubstc=Vars.replace_varssigmasubstcinletreplaceid=tryList.assoc_fId.equalidreplwithNot_found->idinletmapdecl=decl|>NamedDecl.map_idreplace|>NamedDecl.map_constrsubstinletohyps=named_context_of_valsigninletnhyps=List.mapmapohypsinletnconcl=substconclinletnctx=val_of_named_contextnhypsinletfoldodeclndeclaccu=ifId.equal(NamedDecl.get_idodecl)(NamedDecl.get_idndecl)thenSList.defaultaccuelseSList.cons(mkVar@@NamedDecl.get_idodecl)accuinletinstance=List.fold_right2foldohypsnhypsSList.emptyinRefine.refine~typecheck:falsebeginfunsigma->letsigma,ev=Evarutil.new_pure_evarnctxsigmanconcl~principal:trueinsigma,mkEvar(ev,instance)endend(**************************************************************)(* Fresh names *)(**************************************************************)letfresh_id_in_envavoididenv=letavoid'=ids_of_named_context_val(named_context_valenv)inletavoid=ifId.Set.is_emptyavoidthenavoid'elseId.Set.unionavoid'avoidinnext_ident_away_in_goal(Global.env())idavoidletnew_fresh_idavoididgl=fresh_id_in_envavoidid(Proofview.Goal.envgl)letid_of_name_with_defaultid=function|Anonymous->id|Nameid->idletdefault_id_of_sortsigmas=ifESorts.is_smallsigmasthendefault_small_identelsedefault_type_identletdefault_idenvsigmadecl=letopenContext.Rel.Declarationinmatchdeclwith|LocalAssum(name,t)->letdft=default_id_of_sortsigma(Retyping.get_sort_ofenvsigmat)inid_of_name_with_defaultdftname.binder_name|LocalDef(name,b,_)->id_of_name_using_hdcharenvsigmabname.binder_name(* Non primitive introduction tactics are treated by intro_then_gen
There is possibly renaming, with possibly names to avoid and
possibly a move to do after the introduction *)typename_flag=|NamingAvoidofId.Set.t|NamingBasedOnofId.t*Id.Set.t|NamingMustBeoflidentletnaming_of_name=function|Anonymous->NamingAvoidId.Set.empty|Nameid->NamingMustBe(CAst.makeid)letfind_namemayrepldeclnaminggl=matchnamingwith|NamingAvoididl->(* this case must be compatible with [find_intro_names] below. *)letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinnew_fresh_ididl(default_idenvsigmadecl)gl|NamingBasedOn(id,idl)->new_fresh_ididlidgl|NamingMustBe{CAst.loc;v=id}->(* When name is given, we allow to hide a global name *)letids_of_hyps=Tacmach.pf_ids_set_of_hypsglinifnotmayrepl&&Id.Set.memidids_of_hypsthenerror?loc(AlreadyUsedid);id(**************************************************************)(* Computing position of hypotheses for replacing *)(**************************************************************)letget_next_hyp_positionenvsigmaid=letrecaux=function|[]->error_no_such_hypothesisenvsigmaid|decl::right->ifId.equal(NamedDecl.get_iddecl)idthenmatchrightwithdecl::_->MoveBefore(NamedDecl.get_iddecl)|[]->MoveFirstelseauxrightinauxletget_previous_hyp_positionenvsigmaid=letrecauxdest=function|[]->error_no_such_hypothesisenvsigmaid|decl::right->lethyp=NamedDecl.get_iddeclinifId.equalhypidthendestelseaux(MoveAfterhyp)rightinauxMoveLast(**************************************************************)(* Cut rule *)(**************************************************************)letclear_hyps2envsigmaidssigntcl=tryletsigma=Evd.clear_metassigmainEvarutil.clear_hyps2_in_evienvsigmasigntclidswithEvarutil.ClearDependencyError(id,err,inglobal)->error_replacing_dependencyenvsigmaiderringloballetinternal_cut?(check=true)replaceidt=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglinletsign=named_context_valenvinletr=Retyping.relevance_of_typeenvsigmatinletenv',t,concl,sigma=ifreplacethenletnexthyp=get_next_hyp_positionenvsigmaid(named_context_of_valsign)inletsigma,sign',t,concl=clear_hyps2envsigma(Id.Set.singletonid)signtconclinletsign'=insert_decl_in_named_contextenvsigma(LocalAssum(make_annotidr,t))nexthypsign'inEnviron.reset_with_named_contextsign'env,t,concl,sigmaelse(ifcheck&&mem_named_context_validsignthenerror(IntroAlreadyDeclaredid);push_named(LocalAssum(make_annotidr,t))env,t,concl,sigma)inletnf_t=nf_betaiotaenvsigmatinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Refine.refine~typecheck:falsebeginfunsigma->let(sigma,ev)=Evarutil.new_evarenvsigmanf_tinlet(sigma,ev')=Evarutil.new_evar~principal:trueenv'sigmaconclinletterm=mkLetIn(make_annot(Nameid)r,ev,t,EConstr.Vars.subst_varsigmaidev')in(sigma,term)end)endletassert_before_then_genbnamingttac=letopenContext.Rel.DeclarationinProofview.Goal.enterbeginfungl->letid=find_nameb(LocalAssum(make_annotAnonymousSorts.Relevant,t))namingglinTacticals.tclTHENLAST(internal_cutbidt)(tacid)endletassert_before_genbnamingt=assert_before_then_genbnamingt(fun_->Proofview.tclUNIT())letassert_beforena=assert_before_genfalse(naming_of_namena)letassert_before_replacingid=assert_before_gentrue(NamingMustBe(CAst.makeid))letreplace_error_optionerrtac=matcherrwith|None->tac|Some(e,info)->Proofview.tclORELSEtac(fun_->Proofview.tclZERO~infoe)letassert_after_then_genbnamingttac=letopenContext.Rel.DeclarationinProofview.Goal.enterbeginfungl->letid=find_nameb(LocalAssum(make_annotAnonymousSorts.Relevant,t))namingglinTacticals.tclTHENFIRST(internal_cutbidt<*>Proofview.cycle1)(tacid)endletassert_after_genbnamingt=assert_after_then_genbnamingt(fun_->(Proofview.tclUNIT()))letassert_afterna=assert_after_genfalse(naming_of_namena)letassert_after_replacingid=assert_after_gentrue(NamingMustBe(CAst.makeid))(**************************************************************)(* Fixpoints and CoFixpoints *)(**************************************************************)letrecmk_holesenvsigma=function|[]->(sigma,[])|arg::rem->let(sigma,arg)=Evarutil.new_evarenvsigmaarginlet(sigma,rem)=mk_holesenvsigmaremin(sigma,arg::rem)letreccheck_mutindenvsigmakcl=matchEConstr.kindsigma(strip_outer_castsigmacl)with|Prod(na,c1,b)->ifInt.equalk1thentrylet((sp,_),u),_=find_inductiveenvsigmac1in(sp,u)withNot_found->errorFixpointOnNonInductiveTypeelseletopenContext.Rel.Declarationincheck_mutind(push_rel(LocalAssum(na,c1))env)sigma(predk)b|LetIn(na,c1,t,b)->letopenContext.Rel.Declarationincheck_mutind(push_rel(LocalDef(na,c1,t))env)sigmakb|_->errorNotEnoughProducts(* Refine as a fixpoint *)letmutual_fixfnrestj=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglinlet(sp,u)=check_mutindenvsigmanconclinletfirsts,lasts=List.chopjrestinletall=firsts@(f,n,concl)::lastsinletrecmk_signsign=function|[]->sign|(f,n,ar)::oth->letopenContext.Named.Declarationinlet(sp',u')=check_mutindenvsigmanarinifnot(QMutInd.equalenvspsp')thenerrorFixpointSameMutualInductiveType;ifmem_named_context_valfsignthenerror(IntroAlreadyDeclaredf);mk_sign(push_named_context_val(LocalAssum(make_annotfSorts.Relevant,ar))sign)othinletnenv=reset_with_named_context(mk_sign(named_context_valenv)all)envinRefine.refine~typecheck:falsebeginfunsigma->let(sigma,evs)=mk_holesnenvsigma(List.mappi3all)inletids=List.mappi1allinletevs=List.map(Vars.subst_varssigma(List.revids))evsinletindxs=Array.of_list(List.map(funn->n-1)(List.mappi2all))in(* TODO relevance *)letfunnames=Array.of_list(List.map(funi->make_annot(Namei)Sorts.Relevant)ids)inlettyparray=Array.of_list(List.mappi3all)inletbodies=Array.of_listevsinletoterm=mkFix((indxs,0),(funnames,typarray,bodies))in(sigma,oterm)endendletfixidn=mutual_fixidn[]0letreccheck_is_mutcoindenvsigmacl=letb=whd_allenvsigmaclinmatchEConstr.kindsigmabwith|Prod(na,c1,b)->letopenContext.Rel.Declarationincheck_is_mutcoind(push_rel(LocalAssum(na,c1))env)sigmab|_->trylet_=find_coinductiveenvsigmabin()withNot_found->errorAllMethodsInCoinductiveType(* Refine as a cofixpoint *)letmutual_cofixfothersj=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglinletfirsts,lasts=List.chopjothersinletall=firsts@(f,concl)::lastsinList.iter(fun(_,c)->check_is_mutcoindenvsigmac)all;letrecmk_signsign=function|[]->sign|(f,ar)::oth->letopenContext.Named.Declarationinifmem_named_context_valfsignthenerror(AlreadyUsedf);mk_sign(push_named_context_val(LocalAssum(make_annotfSorts.Relevant,ar))sign)othinletnenv=reset_with_named_context(mk_sign(named_context_valenv)all)envinRefine.refine~typecheck:falsebeginfunsigma->let(ids,types)=List.splitallinlet(sigma,evs)=mk_holesnenvsigmatypesinletevs=List.map(Vars.subst_varssigma(List.revids))evsin(* TODO relevance *)letfunnames=Array.of_list(List.map(funi->make_annot(Namei)Sorts.Relevant)ids)inlettyparray=Array.of_listtypesinletbodies=Array.of_listevsinletoterm=mkCoFix(0,(funnames,typarray,bodies))in(sigma,oterm)endendletcofixid=mutual_cofixid[]0(**************************************************************)(* Reduction and conversion tactics *)(**************************************************************)typetactic_reduction=Reductionops.reduction_functiontypee_tactic_reduction=Reductionops.e_reduction_functionlete_pf_change_decl(redfun:bool->e_reduction_function)whereenvsigmadecl=letopenContext.Named.Declarationinmatchdeclwith|LocalAssum(id,ty)->ifwhere==InHypValueOnlythenerror(VariableHasNoValueid.binder_name);let(sigma,ty')=redfunfalseenvsigmatyin(sigma,LocalAssum(id,ty'))|LocalDef(id,b,ty)->let(sigma,b')=ifwhere!=InHypTypeOnlythenredfuntrueenvsigmabelse(sigma,b)inlet(sigma,ty')=ifwhere!=InHypValueOnlythenredfunfalseenvsigmatyelse(sigma,ty)in(sigma,LocalDef(id,b',ty'))letbind_change_occurrencesoccs=function|None->None|Somec->Some(Redexpr.out_with_occurrences(occs,c))(* The following two tactics apply an arbitrary
reduction function either to the conclusion or to a
certain hypothesis *)(** Tactic reduction modulo evars (for universes essentially) *)lete_change_in_concl~cast~check(redfun,sty)=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinlet(sigma,c')=redfun(Tacmach.pf_envgl)sigma(Tacmach.pf_conclgl)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(convert_concl~cast~checkc'sty)endlete_change_in_hyp~check~reorderredfun(id,where)=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinlethyp=Tacmach.pf_get_hypidglinlet(sigma,c)=e_pf_change_declredfunwhere(Proofview.Goal.envgl)sigmahypinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(convert_hyp~check~reorderc)endtypehyp_conversion=|AnyHypConv(** Arbitrary conversion *)|StableHypConv(** Does not introduce new dependencies on variables *)|LocalHypConv(** Same as above plus no dependence on the named environment *)lete_change_in_hyps~check~reorderfargs=matchargswith|[]->Proofview.tclUNIT()|_::_->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinlet(env,sigma)=matchreorderwith|LocalHypConv->(* If the reduction function is known not to depend on the named
context, then we can perform it in parallel. *)letfoldaccuarg=let(id,redfun)=farginletold=tryId.Map.findidaccuwithNot_found->[]inId.Map.addid(redfun::old)accuinletreds=List.fold_leftfoldId.Map.emptyargsinletevdref=refsigmainletmapd=letid=NamedDecl.get_iddinmatchId.Map.findidredswith|reds->letd=EConstr.of_named_decldinletfoldredfun(sigma,d)=redfunenvsigmadinlet(sigma,d)=List.fold_rightfoldreds(sigma,d)inlet()=evdref:=sigmainEConstr.Unsafe.to_named_decld|exceptionNot_found->dinletsign=Environ.map_named_valmap(Environ.named_context_valenv)inletenv=reset_with_named_contextsignenvin(env,!evdref)|StableHypConv|AnyHypConv->letreorder=reorder==AnyHypConvinletfold(env,sigma)arg=let(id,redfun)=farginlethyp=trylookup_namedidenvwithNot_found->raise(RefinerError(env,sigma,NoSuchHypid))inlet(sigma,d)=redfunenvsigmahypinletsign=Logic.convert_hyp~check~reorderenvsigmadinletenv=reset_with_named_contextsignenvin(env,sigma)inList.fold_leftfold(env,sigma)argsinletty=Proofview.Goal.conclglinProofview.Unsafe.tclEVARSsigma<*>Refine.refine~typecheck:falsebeginfunsigma->Evarutil.new_evarenvsigma~principal:truetyendendlete_reduct_in_concl=e_change_in_conclletreduct_in_concl~cast~check(redfun,sty)=letredfunenvsigmac=(sigma,redfunenvsigmac)ine_change_in_concl~cast~check(redfun,sty)lete_reduct_in_hyp~check~reorderredfun(id,where)=letredfun_envsigmac=redfunenvsigmacine_change_in_hyp~check~reorderredfun(id,where)letreduct_in_hyp~check~reorderredfun(id,where)=letredfun_envsigmac=(sigma,redfunenvsigmac)ine_change_in_hyp~check~reorderredfun(id,where)lete_reduct_option~checkredfun=function|Someid->e_reduct_in_hyp~check~reorder:check(fstredfun)id|None->e_change_in_concl~cast:true~checkredfunletreduct_option~check(redfun,sty)where=letredfunenvsigmac=(sigma,redfunenvsigmac)ine_reduct_option~check(redfun,sty)wheretypechange_arg=Ltac_pretype.patvar_map->env->evar_map->evar_map*EConstr.constrletmake_change_argcpatsenvsigma=(sigma,replace_varssigma(Id.Map.bindingspats)c)letcheck_typesenvsigmamayneedglobalcheckdeepnewcorigc=lett1=Retyping.get_type_ofenvsigmanewcinifdeepthenbeginlett2=Retyping.get_type_ofenvsigmaorigcinletsigma,t2=Evarsolve.refresh_universes~onlyalg:true(Somefalse)envsigmat2inmatchinfer_conv~pb:Conversion.CUMULenvsigmat1t2with|None->ifisSortsigma(whd_allenvsigmat1)&&isSortsigma(whd_allenvsigmat2)then(mayneedglobalcheck:=true;sigma)elseerror(ConvertIncompatibleTypes(env,sigma,t2,t1))|Somesigma->sigmaendelseifnot(isSortsigma(whd_allenvsigmat1))thenerrorConvertNotATypeelsesigma(* Now we introduce different instances of the previous tacticals *)letchange_and_checkcv_pbmayneedglobalcheckdeeptenvsigmac=let(sigma,t')=tenvsigmainletsigma=check_typesenvsigmamayneedglobalcheckdeept'cinmatchinfer_conv~pb:cv_pbenvsigmat'cwith|None->errorNotConvertible|Somesigma->(sigma,t')(* Use cumulativity only if changing the conclusion not a subterm *)letchange_on_subterm~checkcv_pbdeeptwhereenvsigmac=letmayneedglobalcheck=reffalseinlet(sigma,c)=matchwherewith|None->ifcheckthenchange_and_checkcv_pbmayneedglobalcheckdeep(tId.Map.empty)envsigmacelsetId.Map.emptyenvsigma|Someoccl->e_contextuallyfalseoccl(funsubst->ifcheckthenchange_and_checkConversion.CONVmayneedglobalchecktrue(tsubst)elsefunenvsigma_c->tsubstenvsigma)envsigmacinletsigma=if!mayneedglobalcheckthenbegintryfst(Typing.type_ofenvsigmac)withewhennoncriticale->error(ReplacementIllTypede)endelsesigmain(sigma,c)letchange_in_concl~checkocclt=(* No need to check in e_change_in_concl, the check is done in change_on_subterm *)e_change_in_concl~cast:false~check:false((change_on_subterm~checkConversion.CUMULfalsetoccl),DEFAULTcast)letchange_in_hyp~checkoccltid=(* Same as above *)e_change_in_hyp~check:false~reorder:check(funx->change_on_subterm~checkConversion.CONVxtoccl)idletconcrete_clause_ofenum_hypscl=matchcl.onhypswith|None->letfid=(id,AllOccurrences,InHyp)inList.mapf(enum_hyps())|Somel->List.map(fun((occs,id),w)->(id,occs,w))lletchange~checkchgccls=Proofview.Goal.enterbeginfungl->lethyps=concrete_clause_of(fun()->Tacmach.pf_ids_of_hypsgl)clsinbeginmatchcls.concl_occswith|NoOccurrences->Proofview.tclUNIT()|occs->change_in_concl~check(bind_change_occurrencesoccschg)cend<*>letf(id,occs,where)=letoccl=bind_change_occurrencesoccschginletredfundeepenvsigmat=change_on_subterm~checkConversion.CONVdeepcocclenvsigmatinletredfunenvsigmad=e_pf_change_declredfunwhereenvsigmadin(id,redfun)inletreorder=ifcheckthenAnyHypConvelseStableHypConvin(* Don't check, we do it already in [change_on_subterm] *)e_change_in_hyps~check:false~reorderfhypsendletchange_conclt=change_in_concl~check:trueNone(make_change_argt)(* Pour usage interne (le niveau User est pris en compte par reduce) *)letred_in_concl=reduct_in_concl~cast:true~check:false(red_product,DEFAULTcast)letred_in_hyp=reduct_in_hyp~check:false~reorder:falsered_productletred_option=reduct_option~check:false(red_product,DEFAULTcast)lethnf_in_concl=reduct_in_concl~cast:true~check:false(hnf_constr,DEFAULTcast)lethnf_in_hyp=reduct_in_hyp~check:false~reorder:falsehnf_constrlethnf_option=reduct_option~check:false(hnf_constr,DEFAULTcast)letsimpl_in_concl=reduct_in_concl~cast:true~check:false(simpl,DEFAULTcast)letsimpl_in_hyp=reduct_in_hyp~check:false~reorder:falsesimplletsimpl_option=reduct_option~check:false(simpl,DEFAULTcast)letnormalise_in_concl=reduct_in_concl~cast:true~check:false(compute,DEFAULTcast)letnormalise_in_hyp=reduct_in_hyp~check:false~reorder:falsecomputeletnormalise_option=reduct_option~check:false(compute,DEFAULTcast)letnormalise_vm_in_concl=reduct_in_concl~cast:true~check:false(Redexpr.cbv_vm,VMcast)letunfold_in_conclloccname=reduct_in_concl~cast:true~check:false(unfoldnloccname,DEFAULTcast)letunfold_in_hyploccname=reduct_in_hyp~check:false~reorder:false(unfoldnloccname)letunfold_optionloccname=reduct_option~check:false(unfoldnloccname,DEFAULTcast)letpattern_optionl=e_reduct_option~check:false(pattern_occsl,DEFAULTcast)(* The main reduction function *)letis_local_flagenvflags=ifflags.rDeltathenfalseelseletcheck=function|EvalVarRef_->false|EvalConstRefc->Id.Set.is_empty(Environ.vars_of_globalenv(GlobRef.ConstRefc))inList.for_allcheckflags.rConstletis_local_unfoldenvflags=letcheck(_,c)=matchcwith|EvalVarRef_->false|EvalConstRefc->Id.Set.is_empty(Environ.vars_of_globalenv(GlobRef.ConstRefc))inList.for_allcheckflagsletreduceredexpcl=lettraceenvsigma=letopenPrinterinletpr=((fune->pr_econstr_enve),(fune->pr_leconstr_enve),pr_evaluable_reference,pr_constr_pattern_env)inPp.(hov2(Ppred.pr_red_expr_envenvsigmaprstrredexp))inProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlethyps=concrete_clause_of(fun()->Tacmach.pf_ids_of_hypsgl)clinletnbcl=(ifcl.concl_occs=NoOccurrencesthen0else1)+List.lengthhypsinletcheck=matchredexpwithFold_|Pattern_->true|_->falseinletreorder=matchredexpwith|Fold_|Pattern_->AnyHypConv|Simpl(flags,_)|Cbvflags|Cbnflags|Lazyflags->ifis_local_flagenvflagsthenLocalHypConvelseStableHypConv|Unfoldflags->ifis_local_unfoldenvflagsthenLocalHypConvelseStableHypConv|Red_|Hnf|CbvVm_|CbvNative_->StableHypConv|ExtraRedExpr_->StableHypConv(* Should we be that lenient ?*)inletredexp=Redexpr.eval_red_exprenvredexpinProofview.Trace.name_tactic(fun()->traceenvsigma)beginbeginmatchcl.concl_occswith|NoOccurrences->Proofview.tclUNIT()|occs->letredfun=Redexpr.reduction_of_red_expr_val~occs:(occs,nbcl)redexpine_change_in_concl~cast:true~checkredfunend<*>letf(id,occs,where)=let(redfun,_)=Redexpr.reduction_of_red_expr_val~occs:(occs,nbcl)redexpinletredfun_envsigmac=redfunenvsigmacinletredfunenvsigmad=e_pf_change_declredfunwhereenvsigmadin(id,redfun)ine_change_in_hyps~check~reorderfhypsendend(* Unfolding occurrences of a constant *)letunfold_constr=function|GlobRef.ConstRefsp->unfold_in_concl[AllOccurrences,EvalConstRefsp]|GlobRef.VarRefid->unfold_in_concl[AllOccurrences,EvalVarRefid]|_->errorNotUnfoldable(*******************************************)(* Introduction tactics *)(*******************************************)(* Returns the names that would be created by intros, without doing
intros. This function is supposed to be compatible with an
iteration of [find_name] above. As [default_id] checks the sort of
the type to build hyp names, we maintain an environment to be able
to type dependent hyps. *)letfind_intro_namesenv0sigmactxt=let_,res,_=List.fold_right(fundeclacc->letenv,idl,avoid=accinletname=fresh_id_in_envavoid(default_idenvsigmadecl)env0inletnewenv=push_reldeclenvin(newenv,name::idl,Id.Set.addnameavoid))ctxt(env0,[],Id.Set.empty)inList.revresletbuild_intro_taciddesttac=matchdestwith|MoveLast->Tacticals.tclTHEN(introductionid)(tacid)|dest->Tacticals.tclTHENLIST[introductionid;move_hypiddest;tacid]letrecintro_then_genname_flagmove_flag~force~deptac=letopenContext.Rel.DeclarationinProofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletenv=Tacmach.pf_envglinletconcl=Proofview.Goal.conclglinmatchEConstr.kindsigmaconclwith|Prod(name,t,u)whennotdep||not(noccurnsigma1u)->letname=find_namefalse(LocalAssum(name,t))name_flagglinbuild_intro_tacnamemove_flagtac|LetIn(name,b,t,u)whennotdep||not(noccurnsigma1u)->letname=find_namefalse(LocalDef(name,b,t))name_flagglinbuild_intro_tacnamemove_flagtac|Evarevwhenforce->letsigma,t=Evardefine.define_evar_as_productenvsigmaevinTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(intro_then_genname_flagmove_flag~force~deptac)|_->beginifnotforcethenletinfo=Exninfo.reify()inProofview.tclZERO~info(RefinerError(env,sigma,IntroNeedsProduct))(* Note: red_in_concl includes betaiotazeta and this was like *)(* this since at least V6.3 (a pity *)(* that intro do betaiotazeta only when reduction is needed; and *)(* probably also a pity that intro does zeta *)elseProofview.tclUNIT()end<*>Proofview.tclORELSE(Tacticals.tclTHENhnf_in_concl(intro_then_genname_flagmove_flag~force:false~deptac))beginfunction(e,info)->matchewith|RefinerError(env,sigma,IntroNeedsProduct)->Tacticals.tclZEROMSG~info(str"No product even after head-reduction.")|e->Proofview.tclZERO~infoeendendletdrop_intro_name(_:Id.t)=Proofview.tclUNIT()letintro_gennm~force~dep=intro_then_gennm~force~depdrop_intro_nameletintro_mustbe_forceid=intro_gen(NamingMustBe(CAst.makeid))MoveLast~force:true~dep:falseletintro_using_thenid=intro_then_gen(NamingBasedOn(id,Id.Set.empty))MoveLast~force:false~dep:falseletintro_usingid=intro_using_theniddrop_intro_nameletintro_then=intro_then_gen(NamingAvoidId.Set.empty)MoveLast~force:false~dep:falseletintro=intro_thendrop_intro_nameletintrof=intro_gen(NamingAvoidId.Set.empty)MoveLast~force:true~dep:falseletintro_avoidingl=intro_gen(NamingAvoidl)MoveLast~force:false~dep:falseletintro_move_avoididoptavoidhto=matchidoptwith|None->intro_gen(NamingAvoidavoid)hto~force:true~dep:false|Someid->intro_gen(NamingMustBe(CAst.makeid))hto~force:true~dep:falseletintro_moveidopthto=intro_move_avoididoptId.Set.emptyhto(**** Multiple introduction tactics ****)letrecintros_using=function|[]->Proofview.tclUNIT()|str::l->Tacticals.tclTHEN(intro_usingstr)(intros_usingl)letrecintros_mustbe_force=function|[]->Proofview.tclUNIT()|str::l->Tacticals.tclTHEN(intro_mustbe_forcestr)(intros_mustbe_forcel)letrecintros_using_then_helpertacacc=function|[]->tac(List.revacc)|str::l->intro_using_thenstr(funstr'->intros_using_then_helpertac(str'::acc)l)letintros_using_thenltac=intros_using_then_helpertac[]lletis_overboundboundn=matchboundwithNone->false|Somep->n>=pletintro_forthcoming_last_then_genavoiddep_flagboundntac=letopenRelDeclinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletconcl=Proofview.Goal.conclglinletavoid=letavoid'=ids_of_named_context_val(named_context_valenv)inifId.Set.is_emptyavoidthenavoid'elseId.Set.unionavoid'avoidinletrecdecomposeenvavoidnconclsubstdeclsndecls=letdecl=ifis_overboundboundnthenNoneelsematchEConstr.kindsigmaconclwith|Prod(na,t,u)whennotdep_flag||not(noccurnsigma1u)->Some(LocalAssum(na,t),u)|LetIn(na,b,t,u)whennotdep_flag||not(noccurnsigma1u)->Some(LocalDef(na,b,t),u)|_->Noneinmatchdeclwith|None->ndecls,decls,Vars.esubstVars.lift_substituendsubstconcl|Some(decl,concl)->letid=default_idenvsigmadeclinletid=next_ident_away_in_goal(Global.env())idavoidinletavoid=Id.Set.addidavoidinletenv=EConstr.push_reldeclenvinletndecl=NamedDecl.of_rel_decl(fun_->id)declinletndecl=NamedDecl.map_constr(func->Vars.esubstVars.lift_substituendsubstc)ndeclinletsubst=Esubst.subs_cons(make_substituend@@mkVarid)substindecomposeenvavoid(n+1)conclsubst(decl::decls)(ndecl::ndecls)inlet(ndecls,decls,nconcl)=decomposeenvavoidnconcl(Esubst.subs_id0)[][]inletids=List.mapNamedDecl.get_idndeclsinifList.is_emptyidsthentac[]elseRefine.refine~typecheck:falsebeginfunsigma->letctx=named_context_valenvinletnctx=List.fold_rightpush_named_context_valndeclsctxinletinst=SList.defaultn(List.length@@Environ.named_contextenv)SList.emptyinletrels=List.init(List.lengthdecls)(funi->mkRel(i+1))inletninst=List.fold_right(funcaccu->SList.conscaccu)relsinstinlet(sigma,ev)=new_pure_evarnctxsigmanconcl~principal:truein(sigma,it_mkLambda_or_LetIn(mkEvar(ev,ninst))decls)end<*>tacidsendletintros=intro_forthcoming_last_then_genId.Set.emptyfalseNone0(fun_->tclIDTAC)letintro_forthcoming_then_genavoidmove_flag~depboundntac=matchmove_flagwith|MoveLast->(* Fast path *)intro_forthcoming_last_then_genavoiddepboundntac|MoveFirst|MoveAfter_|MoveBefore_->letrecauxnids=(* Note: we always use the bound when there is one for "*" and "**" *)ifnot(is_overboundboundn)thenProofview.tclORELSEbeginintro_then_gen(NamingAvoidavoid)move_flag~force:false~dep(funid->aux(n+1)(id::ids))endbeginfunction(e,info)->matchewith|RefinerError(env,sigma,IntroNeedsProduct)->tacids|e->Proofview.tclZERO~infoeendelsetacidsinauxn[]letintro_replacingid=Proofview.Goal.enterbeginfungl->letenv,sigma=Proofview.Goal.(envgl,sigmagl)inlethyps=Proofview.Goal.hypsglinletnext_hyp=get_next_hyp_positionenvsigmaidhypsinTacticals.tclTHENLIST[clear_for_replacing[id];introductionid;move_hypidnext_hyp;]end(* We have e.g. [x, y, y', x', y'' |- forall y y' y'', G] and want to
reintroduce y, y,' y''. Note that we have to clear y, y' and y''
before introducing y because y' or y'' can e.g. depend on old y. *)(* This version assumes that replacement is actually possible *)(* (ids given in the introduction order) *)(* We keep a sub-optimality in cleaing for compatibility with *)(* the behavior of inversion *)letintros_possibly_replacingids=letsuboptimal=trueinProofview.Goal.enterbeginfungl->letenv,sigma=Proofview.Goal.(envgl,sigmagl)inlethyps=Proofview.Goal.hypsglinletposl=List.map(funid->(id,get_next_hyp_positionenvsigmaidhyps))idsinTacticals.tclTHEN(Tacticals.tclMAP(funid->Tacticals.tclTRY(clear_for_replacing[id]))(ifsuboptimalthenidselseList.revids))(Tacticals.tclMAP(fun(id,pos)->Tacticals.tclORELSE(intro_move(Someid)pos)(intro_usingid))posl)end(* This version assumes that replacement is actually possible *)letintros_replacingids=Proofview.Goal.enterbeginfungl->lethyps=Proofview.Goal.hypsglinletenv,sigma=Proofview.Goal.(envgl,sigmagl)inletposl=List.map(funid->(id,get_next_hyp_positionenvsigmaidhyps))idsinTacticals.tclTHEN(clear_for_replacingids)(Tacticals.tclMAP(fun(id,pos)->intro_move(Someid)pos)posl)end(* The standard for implementing Automatic Introduction *)letauto_intros_tacids=letfoldused=function|Nameid->Id.Set.addidused|Anonymous->usedinletavoid=NamingAvoid(List.fold_leftfoldId.Set.emptyids)inletnaming=function|Nameid->NamingMustBeCAst.(makeid)|Anonymous->avoidinTacticals.tclMAP(funname->intro_gen(namingname)MoveLast~force:true~dep:false)(List.revids)(* User-level introduction tactics *)letlookup_hypothesis_as_renamedenvsigmaccl=function|AnonHypn->Detyping.lookup_index_as_renamedenvsigmaccln|NamedHypid->Detyping.lookup_name_as_displayedenvsigmacclid.CAst.vletlookup_hypothesis_as_renamed_genredhgl=letenv=Proofview.Goal.envglinletrecauxccl=matchlookup_hypothesis_as_renamedenv(Tacmach.projectgl)cclhwith|Nonewhenred->let(redfun,_)=Redexpr.reduction_of_red_exprenv(Redtrue)inlet(_,c)=redfunenv(Proofview.Goal.sigmagl)cclinauxc|x->xintryaux(Proofview.Goal.conclgl)withRedelimination->Noneletis_quantified_hypothesisidgl=matchlookup_hypothesis_as_renamed_genfalse(NamedHyp(CAst.makeid))glwith|Some_->true|None->falseletwarn_deprecated_intros_until_0=CWarnings.create~name:"deprecated-intros-until-0"~category:CWarnings.CoreCategories.tactics(fun()->strbrk"\"intros until 0\" is deprecated, use \"intros *\"; instead of \"induction 0\" and \"destruct 0\" use explicitly a name.\"")letdepth_of_quantified_hypothesisredhgl=ifh=AnonHyp0thenwarn_deprecated_intros_until_0();matchlookup_hypothesis_as_renamed_genredhglwith|Somedepth->depth|None->error(NoQuantifiedHypothesis(h,red))letintros_until_genredh=Proofview.Goal.enterbeginfungl->letn=depth_of_quantified_hypothesisredhglinTacticals.tclDOn(ifredthenintrofelseintro)endletintros_until_idid=intros_until_genfalse(NamedHyp(CAst.makeid))letintros_until_n_genredn=intros_until_genred(AnonHypn)letintros_until=intros_until_gentrueletintros_until_n=intros_until_n_gentruelettclCHECKVARid=Proofview.Goal.enterbeginfungl->let_=Tacmach.pf_get_hypidglinProofview.tclUNIT()endlettry_intros_until_id_checkid=Tacticals.tclORELSE(intros_until_idid)(tclCHECKVARid)lettry_intros_untiltac=function|NamedHyp{CAst.v=id}->Tacticals.tclTHEN(try_intros_until_id_checkid)(tacid)|AnonHypn->Tacticals.tclTHEN(intros_until_nn)(Tacticals.onLastHypIdtac)letrecintros_move=function|[]->Proofview.tclUNIT()|(hyp,destopt)::rest->Tacticals.tclTHEN(intro_gen(NamingMustBe(CAst.makehyp))destopt~force:false~dep:false)(intros_moverest)(* Apply a tactic on a quantified hypothesis, an hypothesis in context
or a term with bindings *)lettactic_infer_flagswith_evar=Pretyping.{use_coercions=true;use_typeclasses=UseTC;solve_unification_constraints=true;fail_evar=notwith_evar;expand_evars=true;program_mode=false;polymorphic=false;}typeevars_flag=bool(* true = pose evars false = fail on evars *)typerec_flag=bool(* true = recursive false = not recursive *)typeadvanced_flag=bool(* true = advanced false = basic *)typeclear_flag=booloption(* true = clear hyp, false = keep hyp, None = use default *)type'acore_destruction_arg=|ElimOnConstrof'a|ElimOnIdentoflident|ElimOnAnonHypofinttype'adestruction_arg=clear_flag*'acore_destruction_argletonOpenInductionArgenvsigmatac=function|clear_flag,ElimOnConstrf->let(sigma',cbl)=fenvsigmainTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma')(tacclear_flag(Somesigma,cbl))|clear_flag,ElimOnAnonHypn->Tacticals.tclTHEN(intros_until_nn)(Tacticals.onLastHyp(func->Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglintacclear_flag(Somesigma,(c,NoBindings))end))|clear_flag,ElimOnIdent{CAst.v=id}->(* A quantified hypothesis *)Tacticals.tclTHEN(try_intros_until_id_checkid)(Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglintacclear_flag(Somesigma,(mkVarid,NoBindings))end)letonInductionArgtac=function|clear_flag,ElimOnConstrcbl->tacclear_flagcbl|clear_flag,ElimOnAnonHypn->Tacticals.tclTHEN(intros_until_nn)(Tacticals.onLastHyp(func->tacclear_flag(c,NoBindings)))|clear_flag,ElimOnIdent{CAst.v=id}->(* A quantified hypothesis *)Tacticals.tclTHEN(try_intros_until_id_checkid)(tacclear_flag(mkVarid,NoBindings))letmap_destruction_argfsigma=function|clear_flag,ElimOnConstrg->letsigma,x=fsigmagin(sigma,(clear_flag,ElimOnConstrx))|clear_flag,ElimOnAnonHypnasx->(sigma,x)|clear_flag,ElimOnIdentidasx->(sigma,x)letfinish_evar_resolution?(flags=Pretyping.all_and_fail_flags)envcurrent_sigma(pending,c)=letsigma=Pretyping.solve_remaining_evarsflagsenvcurrent_sigma?initial:pendingin(sigma,nf_evarsigmac)letfinish_delayed_evar_resolutionwith_evarsenvsigmaf=let(sigma',(c,lbind))=fenvsigmainletflags=tactic_infer_flagswith_evarsinlet(sigma',c)=finish_evar_resolution~flagsenvsigma'(Somesigma,c)in(sigma',(c,lbind))letwith_no_bindings(c,lbind)=iflbind!=NoBindingsthenerrorUnsupportedWithClause;cletforce_destruction_argwith_evarsenvsigmac=map_destruction_arg(finish_delayed_evar_resolutionwith_evarsenv)sigmac(****************************************)(* tactic "cut" (actually modus ponens) *)(****************************************)letcutc=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletconcl=Proofview.Goal.conclglin(* Backward compat: ensure that [c] is well-typed. Plus we need to
know the relevance *)matchTyping.sort_ofenvsigmacwith|exceptionewhennoncriticale->let_,info=Exninfo.captureeinTacticals.tclZEROMSG~info(str"Not a proposition or a type.")|sigma,s->letr=ESorts.relevance_of_sortsigmasinletid=next_name_away_with_default"H"Anonymous(Tacmach.pf_ids_set_of_hypsgl)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Refine.refine~typecheck:falsebeginfunh->let(h,f)=Evarutil.new_evar~principal:trueenvh(mkArrowcrconcl)inlet(h,x)=Evarutil.new_evarenvhcinletf=mkLetIn(make_annot(Nameid)r,x,c,mkApp(f,[|mkRel1|]))in(h,f)end)endletcheck_unresolved_evars_of_metassigmaclenv=(* This checks that Metas turned into Evars by *)(* Refiner.pose_all_metas_as_evars are resolved *)Metamap.iter(funmvb->matchbwith|Clval(na,(c,_),_)->(matchConstr.kind(EConstr.Unsafe.to_constrc.rebus)with|Evar(evk,_)whenEvd.is_undefined(clenv_evdclenv)evk&¬(Evd.memsigmaevk)->letid=matchnawithNameid->id|_->anomaly(Pp.str"unnamed dependent meta.")inerror(CannotFindInstanceid)|_->())|_->())(meta_list(clenv_evdclenv))letdo_replaceid=function|NamingMustBe{CAst.v=id'}whenOption.equalId.equalid(Someid')->true|_->false(* For a clenv expressing some lemma [C[?1:T1,...,?n:Tn] : P] and some
goal [G], [clenv_refine_in] returns [n+1] subgoals, the [n] last
ones (resp [n] first ones if [sidecond_first] is [true]) being the
[Ti] and the first one (resp last one) being [G] whose hypothesis
[id] is replaced by P using the proof given by [tac] *)letclenv_refine_inwith_evarstargetidreplaceenvsigma0clenv=letclenv=Clenv.clenv_pose_dependent_evars~with_evarsclenvinletevd=Typeclasses.resolve_typeclasses~fail:(notwith_evars)env(clenv_evdclenv)inletclenv=Clenv.update_clenv_evdclenvevdinletnew_hyp_typ=clenv_typeclenvinifnotwith_evarsthencheck_unresolved_evars_of_metassigma0clenv;let[@ocaml.warning"-3"]exact_tac=Clenv.Internal.refinerclenvinletnaming=NamingMustBe(CAst.maketargetid)inProofview.Unsafe.tclEVARS(clear_metasevd)<*>Proofview.Goal.enterbeginfungl->letid=find_namereplace(LocalAssum(make_annotAnonymousSorts.Relevant,new_hyp_typ))namingglinTacticals.tclTHENLAST(internal_cutreplaceidnew_hyp_typ<*>Proofview.cycle1)exact_tacend(********************************************)(* Elimination tactics *)(********************************************)letnth_argic=matchiwith|None->List.lastc|Somei->List.nthciletindex_of_ind_argsigmat=letrecauxijt=matchEConstr.kindsigmatwith|LetIn(_,_,_,t)->auxijt|Prod(_,t,u)->(* heuristic *)ifisIndsigma(fst(decompose_appsigmat))thenaux(Somej)(j+1)uelseauxi(j+1)u|_->matchiwith|Somei->i|None->errorCannotFindInductiveArgumentinauxNone0t(*
* Elimination tactic with bindings and using an arbitrary
* elimination constant called elimc. This constant should end
* with a clause (x:I)(P .. ), where P is a bound variable.
* The term c is of type t, which is a product ending with a type
* matching I, lbindc are the expected terms for c arguments
*)typeeliminator=|ElimConstantof(Constant.t*EInstance.t)(* Constant generated by a scheme function *)|ElimClauseofEConstr.constrwith_bindings(* Arbitrary expression provided by the user *)letgeneral_elim_clause0with_evarsflags(submetas,c,ty)elim=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletclause,bindings,index=matchelimwith|ElimConstantcst->letelimc=mkConstUcstinletelimt=Retyping.get_type_ofenvsigmaelimcinleti=index_of_ind_argsigmaelimtin(elimc,elimt),NoBindings,Somei|ElimClause(elimc,lbindelimc)->letelimt=Retyping.get_type_ofenvsigmaelimcin(elimc,elimt),lbindelimc,Noneinletelimclause=make_clenv_bindingenvsigmaclausebindingsinletindmv=trynth_argindex(clenv_argumentselimclause)withFailure_|Invalid_argument_->errorIllFormedEliminationTypeinletelimclause=clenv_instantiate~flags~submetasindmvelimclause(c,ty)inClenv.res_pfelimclause~with_evars~with_classes:true~flagsendletgeneral_elim_clause_in0with_evarsflagsid(submetas,c,ty)elim=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletelimc=mkConstUeliminletelimt=Retyping.get_type_ofenvsigmaelimcinleti=index_of_ind_argsigmaelimtinletelimclause=mk_clenv_fromenvsigma(elimc,elimt)inletindmv=trynth_arg(Somei)(clenv_argumentselimclause)withFailure_|Invalid_argument_->errorIllFormedEliminationTypein(* Assumes that the metas of [c] are part of [sigma] already *)lethypmv=matchList.removeInt.equalindmv(clenv_independentelimclause)with|[a]->a|_->errorIllFormedEliminationTypeinletelimclause=clenv_instantiate~flags~submetasindmvelimclause(c,ty)inlethyp=mkVaridinlethyp_typ=Retyping.get_type_ofenvsigmahypinletelimclause=tryclenv_instantiate~flagshypmvelimclause(hyp,hyp_typ)withPretypeError(env,evd,NoOccurrenceFound(op,_))->(* Set the hypothesis name in the message *)raise(PretypeError(env,evd,NoOccurrenceFound(op,Someid)))inletnew_hyp_typ=clenv_typeelimclauseinifEConstr.eq_constrsigmahyp_typnew_hyp_typthenerror(NothingToRewriteid);clenv_refine_inwith_evarsidtrueenvsigmaelimclauseendletgeneral_elimwith_evarsclear_flag(c,lbindc)elim=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletct=Retyping.get_type_ofenvsigmacinletid=trySome(destVarsigmac)withDestKO->Noneinlett=trysnd(reduce_to_quantified_indenvsigmact)withUserError_->ctinletindclause=make_clenv_bindingenvsigma(c,t)lbindcinletflags=elim_flags()inletmetas=Evd.meta_list(clenv_evdindclause)inletsubmetas=List.map(funmv->mv,Metamap.findmvmetas)(clenv_argumentsindclause)inProofview.Unsafe.tclEVARS(Evd.clear_metas(clenv_evdindclause))<*>Tacticals.tclTHEN(general_elim_clause0with_evarsflags(submetas,c,clenv_typeindclause)elim)(apply_clear_requestclear_flag(use_clear_hyp_by_default())id)endletgeneral_elim_clausewith_evarsflagswhereargelim=Proofview.tclENV>>=funenv->Proofview.tclEVARMAP>>=funsigma->let(sigma,(elim,u))=Evd.fresh_constant_instanceenvsigmaeliminProofview.Unsafe.tclEVARSsigma<*>matchwherewith|None->general_elim_clause0with_evarsflagsarg(ElimConstant(elim,EInstance.makeu))|Someid->general_elim_clause_in0with_evarsflagsidarg(elim,EInstance.makeu)(* Case analysis tactics *)letgeneral_case_analysis_in_contextwith_evarsclear_flag(c,lbindc)=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletconcl=Proofview.Goal.conclglinletstate=Proofview.Goal.stateglinletct=Retyping.get_type_ofenvsigmacinlet(mind,_),t=reduce_to_quantified_indenvsigmactinletdep=ifdependentsigmacconclthentrueelsedefault_case_analysis_dependenceenvmindinletid=trySome(destVarsigmac)withDestKO->Noneinletindclause=make_clenv_bindingenvsigma(c,t)lbindcinletindclause=Clenv.clenv_pose_dependent_evars~with_evars:trueindclauseinletargtype=clenv_typeindclausein(* Guaranteed to be meta-free *)lettac=Proofview.tclEVARMAP>>=funsigma->letsigma=Evd.push_future_goalssigmainlet(sigma,ev)=Evarutil.new_evarenvsigmaargtypeinlet_,sigma=Evd.pop_future_goalssigmainletevk,_=destEvarsigmaevinletindclause=Clenv.update_clenv_evdindclause(meta_merge(meta_list@@Clenv.clenv_evdindclause)sigma)inProofview.Unsafe.tclEVARSsigma<*>Proofview.Unsafe.tclNEWGOALS~before:true[Proofview.goal_with_stateevkstate]<*>Proofview.tclDISPATCH[Clenv.res_pf~with_evars:trueindclause;tclIDTAC]<*>Proofview.tclEXTEND[]tclIDTAC[Clenv.case_pf~with_evars~dep(ev,argtype)]inletsigma=Evd.clear_metas(clenv_evdindclause)inTacticals.tclTHENLIST[Tacticals.tclWITHHOLESwith_evarstacsigma;apply_clear_requestclear_flag(use_clear_hyp_by_default())id;]endletgeneral_case_analysiswith_evarsclear_flag(c,lbindcascx)=Proofview.tclEVARMAP>>=funsigma->matchEConstr.kindsigmacwith|Varidwhenlbindc==NoBindings->Tacticals.tclTHEN(try_intros_until_id_checkid)(general_case_analysis_in_contextwith_evarsclear_flagcx)|_->general_case_analysis_in_contextwith_evarsclear_flagcxletsimplest_casec=general_case_analysisfalseNone(c,NoBindings)letsimplest_ecasec=general_case_analysistrueNone(c,NoBindings)(* Elimination tactic with bindings but using the default elimination
* constant associated with the type. *)exceptionIsNonrecletis_nonrecenvmind=(Environ.lookup_mind(fstmind)env).mind_finite==Declarations.BiFiniteletfind_ind_eliminatorenvsigmainds=letc=lookup_eliminatorenvindsinletsigma,c=EConstr.fresh_globalenvsigmacinsigma,destConstsigmacletdefault_elimwith_evarsclear_flag(c,_ascx)=Proofview.tclORELSE(Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletconcl=Proofview.Goal.conclglinletsigma,t=Typing.type_ofenvsigmacinlet(ind,u)=eval_to_quantified_indenvsigmatinifis_nonrecenvindthenraiseIsNonrec;letsigma,elim=find_ind_eliminatorenvsigmaind(Retyping.get_sort_family_ofenvsigmaconcl)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(general_elimwith_evarsclear_flagcx(ElimConstantelim))end)beginfunction(e,info)->matchewith|IsNonrec->(* For records, induction principles aren't there by default
anymore. Instead, we do a case analysis. *)general_case_analysiswith_evarsclear_flagcx|e->Proofview.tclZERO~infoeendletelim_in_contextwith_evarsclear_flagc=function|Someelim->general_elimwith_evarsclear_flagc(ElimClauseelim)|None->default_elimwith_evarsclear_flagcletelimwith_evarsclear_flag(c,lbindcascx)elim=Proofview.tclEVARMAP>>=funsigma->matchEConstr.kindsigmacwith|Varidwhenlbindc==NoBindings->Tacticals.tclTHEN(try_intros_until_id_checkid)(elim_in_contextwith_evarsclear_flagcxelim)|_->elim_in_contextwith_evarsclear_flagcxelim(* The simplest elimination tactic, with no substitutions at all. *)letsimplest_elimc=default_elimfalseNone(c,NoBindings)(* Elimination in hypothesis *)(* Typically, elimclause := (eq_ind ?x ?P ?H ?y ?Heq : ?P ?y)
indclause : forall ..., hyps -> a=b (to take place of ?Heq)
id : phi(a) (to take place of ?H)
and the result is to overwrite id with the proof of phi(b)
but this generalizes to any elimination scheme with one constructor
(e.g. it could replace id:A->B->C by id:C, knowing A/\B)
*)(* Apply a tactic below the products of the conclusion of a lemma *)typeconjunction_status=|DefinedRecordofConstant.toptionlist|NotADefinedRecordUseSchemeletmake_projectionenvsigmaparamscstrsigneliminc(ind,u)=letopenContext.Rel.Declarationinletelim=matchelimwith|NotADefinedRecordUseScheme->(* bugs: goes from right to left when i increases! *)letcs_args=List.map(fund->map_rel_declEConstr.of_constrd)cstr.cs_argsinletdecl=List.nthcs_argsiinlett=RelDecl.get_typedeclinletb=matchdeclwithLocalAssum_->mkRel(i+1)|LocalDef(_,b,_)->binif(* excludes dependent projection types *)noccur_betweensigma1(n-i-1)t(* to avoid surprising unifications, excludes flexible
projection types or lambda which will be instantiated by Meta/Evar *)&¬(isEvarsigma(fst(whd_betaiota_stackenvsigmat)))&&(not(isRelsigmat))thenlet(_,mip)=Inductive.lookup_mind_specifenvindinlett=lift(i+1-n)tinletksort=Retyping.get_sort_family_of(push_rel_contextsignenv)sigmatinifSorts.family_leqksortmip.mind_kelimthenletarity=List.firstnmip.mind_nrealdeclsmip.mind_arity_ctxtinletmknasctx=Array.of_list(List.rev_mapget_annotctx)inletci=Inductiveops.make_case_infoenvind(get_relevancedecl)RegularStyleinletbr=[|mknascs_args,b|]inletargs=Context.Rel.instancemkRel0signinletpnas=Array.append(mknasarity)[|make_annotAnonymousmip.mind_relevance|]inletp=(pnas,lift(Array.lengthpnas)t)inletc=mkCase(ci,u,Array.of_listparams,p,NoInvert,mkApp(c,args),br)inSome(it_mkLambda_or_LetIncsign,it_mkProd_or_LetIntsign)elseNoneelseNone|DefinedRecordl->(* goes from left to right when i increases! *)matchList.nthliwith|Someproj->letargs=Context.Rel.instancemkRel0signinletproj=matchStructures.PrimitiveProjections.find_optprojwith|Someproj->mkProj(Projection.makeprojfalse,mkApp(c,args))|None->mkApp(mkConstU(proj,u),Array.append(Array.of_listparams)[|mkApp(c,args)|])inletapp=it_mkLambda_or_LetInprojsigninlett=Retyping.get_type_ofenvsigmaappinSome(app,t)|None->Noneinelimletdescend_in_conjunctionsavoidtac(err,info)c=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglintrylett=Retyping.get_type_ofenvsigmacinlet((ind,u),t)=reduce_to_quantified_indenvsigmatinletsign,ccl=EConstr.decompose_prod_declssigmatinmatchmatch_with_tupleenvsigmacclwith|Some(_,_,isrec)->(* At this point, ind is known to be an index-free one-constructor
inductive type, potentially recursive. *)letn=(constructors_nrealargsenvind).(0)inletIndType(indf,_)=find_rectypeenvsigmacclinlet(_,inst),params=dest_ind_familyindfinletparams=List.mapEConstr.of_constrparamsinletcstr=(get_constructorsenvindf).(0)inletelim=tryDefinedRecord(Structures.Structure.find_projectionsind)withNot_found->NotADefinedRecordUseSchemeinletor_tact1t2e=Proofview.tclORELSE(t1e)t2inList.fold_rightor_tac(List.initn(funi(err,info)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinmatchmake_projectionenvsigmaparamscstrsigneliminc(ind,u)with|None->Proofview.tclZERO~infoerr|Some(p,pt)->Tacticals.tclTHENS(Proofview.tclORELSE(assert_before_genfalse(NamingAvoidavoid)pt)(fun_->Proofview.tclZERO~infoerr))[Proofview.tclORELSE(Refine.refine~typecheck:false(funh->(h,p)))(fun_->Proofview.tclZERO~infoerr);(* Might be ill-typed due to forbidden elimination. *)Tacticals.onLastHypId(tac(err,info)(notisrec))]end))(fun(err,info)->Proofview.tclZERO~infoerr)(err,info)|None->Proofview.tclZERO~infoerrwithRefinerError_|UserError_->Proofview.tclZERO~infoerrend(****************************************************)(* Resolution tactics *)(****************************************************)letgeneral_apply?(with_classes=true)?(respect_opaque=false)with_deltawith_destructwith_evarsclear_flag{CAst.loc;v=(c,lbind:EConstr.constrwith_bindings)}=Proofview.Goal.enterbeginfungl->letconcl=Proofview.Goal.conclglinletsigma=Tacmach.projectglinletid=trySome(destVarsigmac)withDestKO->Nonein(* The actual type of the theorem. It will be matched against the
goal. If this fails, then the head constant will be unfolded step by
step. *)letconcl_nprod=nb_prod_modulo_zetasigmaconclinletrectry_main_applywith_destructc=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletts=ifrespect_opaquethenConv_oracle.get_transp_state(oracleenv)elseTransparentState.fullinletflags=ifwith_deltathendefault_unify_flags()elsedefault_no_delta_unify_flagstsinletthm_ty=nf_betaiotaenvsigma(Retyping.get_type_ofenvsigmac)inletsigma,thm_ty=Evarsolve.refresh_universes~onlyalg:trueNoneenvsigmathm_tyinlettry_applythm_tynprod=tryletn=nb_prod_modulo_zetasigmathm_ty-nprodinifn<0thenerrorNotEnoughPremises;letclause=make_clenv_binding_applyenvsigma(Somen)(c,thm_ty)lbindinClenv.res_pfclause~with_classes~with_evars~flagswithexnwhennoncriticalexn->letexn,info=Exninfo.captureexninProofview.tclZERO~infoexninletrectry_red_applythm_ty(exn0,info)=try(* Try to head-reduce the conclusion of the theorem *)letred_thm=try_red_productenvsigmathm_tyinProofview.tclORELSE(try_applyred_thmconcl_nprod)(fun_->try_red_applyred_thm(exn0,info))withRedeliminationasexn->(* Last chance: if the head is a variable, apply may try
second order unification *)letexn,info=Exninfo.captureexninletinfo=Option.cata(funloc->Loc.add_locinfoloc)infolocinlettac=ifwith_destructthenProofview.tclORELSE(descend_in_conjunctionsId.Set.empty(fun_bid->Tacticals.tclTHEN(try_main_applyb(mkVarid))(clear[id]))(exn0,info)c)(fun_->Proofview.tclZERO~infoexn0)elseProofview.tclZERO~infoexn0inifnot(Int.equalconcl_nprod0)thenTacticals.tclORELSE0(try_applythm_ty0)tacelsetacinProofview.tclORELSE(try_applythm_tyconcl_nprod)(try_red_applythm_ty)endinTacticals.tclTHEN(try_main_applywith_destructc)(apply_clear_requestclear_flag(use_clear_hyp_by_default())id)endletrecapply_with_bindings_gen?with_classesbe=function|[]->Proofview.tclUNIT()|[k,cb]->general_apply?with_classesbbekcb|(k,cb)::cbl->Tacticals.tclTHENLAST(general_apply?with_classesbbekcb)(apply_with_bindings_gen?with_classesbecbl)letapply_with_delayed_bindings_genbel=letonek{CAst.loc;v=f}=Proofview.Goal.enterbeginfungl->letsigma=Tacmach.projectglinletenv=Proofview.Goal.envglinlet(sigma,cb)=fenvsigmainTacticals.tclWITHHOLESe(general_apply~respect_opaque:(notb)bbekCAst.(make?loccb))sigmaendinletrecaux=function|[]->Proofview.tclUNIT()|[k,f]->onekf|(k,f)::cbl->Tacticals.tclTHENLAST(onekf)(auxcbl)inauxlletapply_with_bindingscb=apply_with_bindings_genfalsefalse[None,(CAst.makecb)]leteapply_with_bindings?with_classescb=apply_with_bindings_gen?with_classesfalsetrue[None,(CAst.makecb)]letapplyc=apply_with_bindings_genfalsefalse[None,(CAst.make(c,NoBindings))]leteapply?with_classesc=apply_with_bindings_gen?with_classesfalsetrue[None,(CAst.make(c,NoBindings))]letapply_list=function|c::l->apply_with_bindings(c,ImplicitBindingsl)|_->assertfalse(* [apply_in hyp c] replaces
hyp : forall y1, ti -> t hyp : rho(u)
======================== with ============ and the =======
goal goal rho(ti)
assuming that [c] has type [forall x1..xn -> t' -> u] for some [t]
unifiable with [t'] with unifier [rho]
*)exceptionUnableToApplyletprogress_with_clauseenvflags(id,t)clausemvs=letinnerclause=mk_clenv_from_nenv(clenv_evdclause)0(mkVarid,t)inifList.is_emptymvsthenraiseUnableToApply;letfmv=letrecfindinnerclause=letmetas=Evd.meta_list(clenv_evdinnerclause)inletsubmetas=List.map(funmv->mv,Metamap.findmvmetas)(clenv_argumentsinnerclause)intrySome(clenv_instantiatemv~flags~submetasclause(mkVarid,clenv_typeinnerclause))withewhennoncriticale->matchclenv_push_prodinnerclausewith|Some(_,_,innerclause)->findinnerclause|None->NoneinfindinnerclauseinmatchList.find_mapfmvswith|Somev->v|None->raiseUnableToApplyletapply_in_once_mainflags(id,t)envsigma(loc,d,lbind)=letthm=nf_betaiotaenvsigma(Retyping.get_type_ofenvsigmad)inletrecauxclausemvs=tryprogress_with_clauseenvflags(id,t)clausemvswithewhenCErrors.noncriticale->lete'=Exninfo.captureeinmatchclenv_push_prodclausewith|Some(mv,dep,clause)->auxclause(ifdepthen[]else[mv])|None->matchewith|UnableToApply->error?loc(UnableToApplyLemma(env,sigma,thm,t))|_->Exninfo.iraisee'inletclenv=make_clenv_bindingenvsigma(d,thm)lbindinletmvs=List.rev(clenv_independentclenv)inauxclenvmvsletapply_in_once?(respect_opaque=false)with_deltawith_destructwith_evarsnamingid(clear_flag,{CAst.loc;v=d,lbind})tac=letopenContext.Rel.DeclarationinProofview.Goal.enterbeginfungl->lett'=Tacmach.pf_get_hyp_typidglinlettargetid=find_nametrue(LocalAssum(make_annotAnonymousSorts.Relevant,t'))namingglinletreplace=Id.equalidtargetidinletrecaux?erridstoclearwith_destructc=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletidc=trySome(destVar(Tacmach.projectgl)c)withDestKO->Noneinletts=ifrespect_opaquethenConv_oracle.get_transp_state(oracleenv)elseTransparentState.fullinletflags=ifwith_deltathendefault_unify_flags()elsedefault_no_delta_unify_flagstsintryletclause=apply_in_once_mainflags(id,t')envsigma(loc,c,lbind)inletcleartac=apply_clear_requestclear_flagfalseidc<*>clearidstoclearinletrefine=Tacticals.tclTHENFIRST(clenv_refine_inwith_evarstargetidreplaceenvsigmaclause)cleartacinTacticals.tclTHENFIRST(replace_error_optionerrrefine)(tactargetid)withewhenwith_destruct&&CErrors.noncriticale->leterr=Option.default(Exninfo.capturee)errin(descend_in_conjunctions(Id.Set.singletontargetid)(funerrbid->aux~err(id::idstoclear)b(mkVarid))errc)endinaux[]with_destructdendletapply_in_delayed_once?(respect_opaque=false)with_deltawith_destructwith_evarsnamingid(clear_flag,{CAst.loc;v=f})tac=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinlet(sigma,c)=fenvsigmainTacticals.tclWITHHOLESwith_evars(apply_in_once~respect_opaquewith_deltawith_destructwith_evarsnamingid(clear_flag,CAst.(make?locc))tac)sigmaend(* A useful resolution tactic which, if c:A->B, transforms |- C into
|- B -> C and |- A
-------------------
Gamma |- c : A -> B Gamma |- ?2 : A
----------------------------------------
Gamma |- B Gamma |- ?1 : B -> C
-----------------------------------------------------
Gamma |- ? : C
Ltac lapply c :=
let ty := check c in
match eval hnf in ty with
?A -> ?B => cut B; [ idtac | apply c ]
end.
*)letcut_and_applyc=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletconcl=Proofview.Goal.conclglinletsigma,t=Typing.type_ofenvsigmacinmatchEConstr.kindsigma(hnf_constrenvsigmat)with|Prod(_,c1,c2)whenVars.noccurnsigma1c2->Proofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Refine.refine~typecheck:falsebeginfunsigma->lettyp=mkProd(make_annotAnonymousSorts.Relevant,c2,concl)inlet(sigma,f)=Evarutil.new_evarenvsigmatypinlet(sigma,x)=Evarutil.new_evarenvsigmac1in(sigma,mkApp(f,[|mkApp(c,[|x|])|]))end)|_->errorNeedDependentProductend(********************************************************************)(* Exact tactics *)(********************************************************************)letexact_no_checkc=Refine.refine~typecheck:false(funh->(h,c))letexact_checkc=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglin(* We do not need to normalize the goal because we just check convertibility *)letconcl=Proofview.Goal.conclglinletenv=Proofview.Goal.envglinletsigma,ct=Typing.type_ofenvsigmacinTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Tacticals.tclTHEN(convert_leqctconcl)(exact_no_checkc))endletcast_no_checkcastc=Proofview.Goal.enterbeginfungl->letconcl=Proofview.Goal.conclglinexact_no_check(mkCast(c,cast,concl))endletvm_cast_no_checkc=cast_no_checkVMcastcletnative_cast_no_checkc=cast_no_checkNATIVEcastcletexact_proofc=letopenTacmachinProofview.Goal.enterbeginfungl->Refine.refine~typecheck:falsebeginfunsigma->let(c,ctx)=Constrintern.interp_casted_constr(pf_envgl)sigmac(pf_conclgl)inletsigma=Evd.set_universe_contextsigmactxin(sigma,c)endendletassumption=letrecarecglonly_eq=function|[]->ifonly_eqthenlethyps=Proofview.Goal.hypsglinarecglfalsehypselseletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"No such assumption.")|decl::rest->lett=NamedDecl.get_typedeclinletconcl=Proofview.Goal.conclglinletsigma=Tacmach.projectglinletans=ifonly_eqthenifEConstr.eq_constrsigmatconclthenSomesigmaelseNoneelseletenv=Proofview.Goal.envglininfer_convenvsigmatconclinmatchanswith|Somesigma->(Proofview.Unsafe.tclEVARSsigma)<*>exact_no_check(mkVar(NamedDecl.get_iddecl))|None->arecglonly_eqrestinletassumption_tacgl=lethyps=Proofview.Goal.hypsglinarecgltruehypsinProofview.Goal.enterassumption_tac(*****************************************************************)(* Modification of a local context *)(*****************************************************************)letcheck_is_typeenvsigmaidlidsty=tryletsigma,_=Typing.sort_ofenvsigmatyinsigmawithewhenCErrors.noncriticale->raise(DependsOnBody(idl,ids,None))letcheck_declenvsigmaidlidsdecl=letopenContext.Named.Declarationinletty=NamedDecl.get_typedeclintryletsigma,_=Typing.sort_ofenvsigmatyinletsigma=matchdeclwith|LocalAssum_->sigma|LocalDef(_,c,_)->Typing.checkenvsigmactyinsigmawithewhenCErrors.noncriticale->letid=NamedDecl.get_iddeclinraise(DependsOnBody(idl,ids,Someid))letclear_bodyidl=letopenContext.Named.DeclarationinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletconcl=Proofview.Goal.conclglinletsigma=Tacmach.projectglinletctx=named_contextenvinletids=Id.Set.of_listidlin(* We assume the context to respect dependencies *)letrecfoldidsctx=ifId.Set.is_emptyidsthenletbase_env=reset_contextenvinletenv=push_named_contextctxbase_envinenv,sigma,Id.Set.emptyelsematchctxwith|[]->assertfalse|decl::ctx->letdecl,ids,found=matchdeclwith|LocalAssum(id,t)->let()=ifId.Set.memid.binder_nameidsthenerror(VariableHasNoValueid.binder_name)indecl,ids,false|LocalDef(id,_,t)asdecl->ifId.Set.memid.binder_nameidsthenLocalAssum(id,t),Id.Set.removeid.binder_nameids,trueelsedecl,ids,falseinletenv,sigma,ids=foldidsctxinifId.Set.exists(funid->occur_var_in_declenvsigmaiddecl)idsthenletsigma=check_declenvsigmaidlidsdeclin(* can sigma really change? *)letids=Id.Set.add(get_iddecl)idsinpush_nameddeclenv,sigma,Id.Set.add(get_iddecl)idselsepush_nameddeclenv,sigma,iffoundthenId.Set.add(get_iddecl)idselseidsintryletenv,sigma,ids=foldidsctxinletsigma=ifId.Set.exists(funid->occur_varenvsigmaidconcl)idsthencheck_is_typeenvsigmaidlidsconclelsesigmainProofview.Unsafe.tclEVARSsigma<*>Refine.refine~typecheck:falsebeginfunsigma->Evarutil.new_evarenvsigma~principal:trueconclendwithDependsOnBody_asexn->let_,info=Exninfo.captureexninProofview.tclZERO~infoexnendletclear_wildcardsids=letclear_wildcards_msg?locenvsigma_iderringlobal=user_err?loc(clear_dependency_msgenvsigmaNoneerringlobal)inTacticals.tclMAP(fun{CAst.loc;v=id}->clear_gen(clear_wildcards_msg?loc)[id])ids(* Takes a list of booleans, and introduces all the variables
* quantified in the goal which are associated with a value
* true in the boolean list. *)letrecintros_clearing=function|[]->Proofview.tclUNIT()|(false::tl)->Tacticals.tclTHENintro(intros_clearingtl)|(true::tl)->Tacticals.tclTHENLIST[intro;Tacticals.onLastHypId(funid->clear[id]);intros_clearingtl](* Keeping only a few hypotheses *)letkeephyps=Proofview.Goal.enterbeginfungl->Proofview.tclENV>>=funenv->letccl=Proofview.Goal.conclglinletsigma=Tacmach.projectglinletcl,_=fold_named_context_reverse(fun(clear,keep)decl->letdecl=map_named_declEConstr.of_constrdeclinlethyp=NamedDecl.get_iddeclinifId.List.memhyphyps||List.exists(occur_var_in_declenvsigmahyp)keep||occur_varenvsigmahypcclthen(clear,decl::keep)else(hyp::clear,keep))~init:([],[])(Proofview.Goal.envgl)inclearclend(*********************************)(* Basic generalization tactics *)(*********************************)(* Given a type [T] convertible to [forall x1..xn:A1..An(x1..xn-1), G(x1..xn)]
and [a1..an:A1..An(a1..an-1)] such that the goal is [G(a1..an)],
this generalizes [hyps |- goal] into [hyps |- T] *)letapply_type~typechecknewclargs=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinRefine.refine~typecheckbeginfunsigma->letnewcl=nf_betaiotaenvsigmanewcl(* As in former Logic.refine *)inlet(sigma,ev)=Evarutil.new_evarenvsigma~principal:truenewclin(sigma,applist(ev,args))endend(* Given a context [hyps] with domain [x1..xn], possibly with let-ins,
and well-typed in the current goal, [bring_hyps hyps] generalizes
[ctxt |- G(x1..xn] into [ctxt |- forall hyps, G(x1..xn)] *)letbring_hypshyps=ifList.is_emptyhypsthenTacticals.tclIDTACelselethyps=List.revhypsinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletconcl=Tacmach.pf_conclglinletnewcl=it_mkNamedProd_or_LetInsigmaconclhypsinletargs=Context.Named.instancemkVarhypsinRefine.refine~typecheck:falsebeginfunsigma->let(sigma,ev)=Evarutil.new_evarenvsigma~principal:truenewclin(sigma,mkApp(ev,args))endendletreverthyps=Proofview.Goal.enterbeginfungl->letctx=List.map(funid->Tacmach.pf_get_hypidgl)hypsin(bring_hypsctx)<*>(clearhyps)end(************************)(* Introduction tactics *)(************************)letcheck_number_of_constructorsexpctdnumoptinconstr=ifInt.equali0thenerrorConstructorNumberedFromOne;beginmatchexpctdnumoptwith|Somenwhennot(Int.equalnnconstr)->error(NotRightNumberConstructorsn)|_->()end;ifi>nconstrthenerrorNotEnoughConstructorsletconstructor_corewith_evarscstrlbind=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinlet(sigma,(cons,u))=Evd.fresh_constructor_instanceenvsigmacstrinletcons=mkConstructU(cons,EInstance.makeu)inletapply_tac=general_applytruefalsewith_evarsNone(CAst.make(cons,lbind))inTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)apply_tacendletconstructor_tacwith_evarsexpctdnumoptilbind=Proofview.Goal.enterbeginfungl->letcl=Tacmach.pf_conclglinletenv=Proofview.Goal.envglinlet((ind,_),redcl)=Tacmach.pf_applyTacred.reduce_to_quantified_indglclinletnconstr=Array.length(snd(Inductive.lookup_mind_specifenvind)).mind_consnamesincheck_number_of_constructorsexpctdnumoptinconstr;Tacticals.tclTHENLIST[convert_concl~cast:false~check:falseredclDEFAULTcast;intros;constructor_corewith_evars(ind,i)lbind]endletone_constructorilbind=constructor_tacfalseNoneilbind(* Try to apply the constructor of the inductive definition followed by
a tactic t given as an argument.
Should be generalize in Constructor (Fun c : I -> tactic)
*)letany_constructorwith_evarstacopt=letone_constr=lettaccstr=constructor_corewith_evarscstrNoBindingsinmatchtacoptwith|None->tac|Somet->funcstr->Tacticals.tclTHEN(taccstr)tinletrecany_constrindni()=ifInt.equalinthenone_constr(ind,i)elseTacticals.tclORD(one_constr(ind,i))(any_constrindn(i+1))inProofview.Goal.enterbeginfungl->letcl=Tacmach.pf_conclglinletenv=Proofview.Goal.envglinlet(ind,_),redcl=Tacmach.pf_applyTacred.reduce_to_quantified_indglclinletnconstr=Array.length(snd(Inductive.lookup_mind_specifenvind)).mind_consnamesinifInt.equalnconstr0thenerrorNoConstructors;Tacticals.tclTHENLIST[convert_concl~cast:false~check:falseredclDEFAULTcast;intros;any_constrindnconstr1()]endletleft_with_bindingswith_evars=constructor_tacwith_evars(Some2)1letright_with_bindingswith_evars=constructor_tacwith_evars(Some2)2letsplit_with_bindingswith_evarsl=Tacticals.tclMAP(constructor_tacwith_evars(Some1)1)lletsplit_with_delayed_bindingswith_evarsbl=Tacticals.tclMAPDELAYEDWITHHOLESwith_evarsbl(constructor_tacwith_evars(Some1)1)letleft=left_with_bindingsfalseletsimplest_left=leftNoBindingsletright=right_with_bindingsfalseletsimplest_right=rightNoBindingsletsplit=constructor_tacfalse(Some1)1letsimplest_split=splitNoBindings(*****************************)(* Decomposing introductions *)(*****************************)(* Rewriting function for rewriting one hypothesis at the time *)let(forward_general_rewrite_clause,general_rewrite_clause)=Hook.make()(* Rewriting function for substitution (x=t) everywhere at the same time *)let(forward_subst_one,subst_one)=Hook.make()letintro_decomp_eq_function=ref(fun_->failwith"Not implemented")letdeclare_intro_decomp_eqf=intro_decomp_eq_function:=fletmy_find_eq_data_decomposeenvsigmat=trySome(find_eq_data_decomposeenvsigmat)withewhenis_anomalye(* Hack in case equality is not yet defined... one day, maybe,
known equalities will be dynamically registered *)->None|Constr_matching.PatternMatchingFailure->Noneletintro_decomp_eq?loclthintacid=Proofview.Goal.enterbeginfungl->letc=mkVaridinletenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet{uj_type=t}=Typing.judge_of_variableenvidinlet_,t=reduce_to_atomic_indenvsigmatinmatchmy_find_eq_data_decomposeenvsigmatwith|Some(eq,u,eq_args)->!intro_decomp_eq_function(funn->tac((CAst.makeid)::thin)(Somen)l)(eq,t,eq_args)(c,t)|None->letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"Not a primitive equality here.")endletintro_or_and_pattern?locwith_evarsllthintacid=Proofview.Goal.enterbeginfungl->letc=mkVaridinletenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet{uj_type=t}=Typing.judge_of_variableenvidinletind=eval_to_quantified_indenvsigmatinletbranchsigns=compute_constructor_signaturesenv~rec_flag:falseindinletnv_with_let=Array.mapList.lengthbranchsignsinletll=fix_empty_or_and_pattern(Array.lengthbranchsigns)llinletll=get_and_check_or_and_pattern?locllbranchsignsinletcase=ifwith_evarsthensimplest_ecaseelsesimplest_caseinTacticals.tclTHENLASTn(Tacticals.tclTHEN(casec)(clear[id]))(Array.map2(funnl->tacthin(Somen)l)nv_with_letll)endletrewrite_hyp_thenwith_evarsthinl2ridtac=letrew_onl2r=Hook.getforward_general_rewrite_clausel2rwith_evars(mkVarid,NoBindings)inletsubst_onl2rxrhs=Hook.getforward_subst_onetruex(id,rhs,l2r)inletclear_var_and_eqid'=clear[id';id]inletearly_clearid'thin=List.filter(fun{CAst.v=id}->not(Id.equalidid'))thininProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinlet{uj_type=t}=Typing.judge_of_variableenvidinlett=whd_allenvsigmatinleteqtac,thin=matchmatch_with_equality_typeenvsigmatwith|Some(hdcncl,[_;lhs;rhs])->ifl2r&&isVarsigmalhs&¬(occur_varenvsigma(destVarsigmalhs)rhs)thenletid'=destVarsigmalhsinsubst_onl2rid'rhs,early_clearid'thinelseifnotl2r&&isVarsigmarhs&¬(occur_varenvsigma(destVarsigmarhs)lhs)thenletid'=destVarsigmarhsinsubst_onl2rid'lhs,early_clearid'thinelseTacticals.tclTHEN(rew_onl2ronConcl)(clear[id]),thin|Some(hdcncl,[c])->letl2r=notl2rin(* equality of the form eq_true *)ifisVarsigmacthenletid'=destVarsigmacinTacticals.tclTHEN(rew_onl2rallHypsAndConcl)(clear_var_and_eqid'),early_clearid'thinelseTacticals.tclTHEN(rew_onl2ronConcl)(clear[id]),thin|_->Tacticals.tclTHEN(rew_onl2ronConcl)(clear[id]),thinin(* Skip the side conditions of the rewriting step *)Tacticals.tclTHENFIRSTeqtac(tacthin)endletreccollect_intro_names=letopenCAstinfunction|{v=IntroForthcoming_}::l->collect_intro_namesl|{v=IntroNaming(IntroIdentifierid)}::l->letids1,ids2=collect_intro_nameslinId.Set.addidids1,ids2|{v=IntroAction(IntroOrAndPatternl)}::l'->letll=matchlwithIntroAndPatternl->[l]|IntroOrPatternll->llinletfold(ids1',ids2')l=letids1,ids2=collect_intro_names(l@l')inId.Set.unionids1ids1',Id.Set.unionids2ids2'inList.fold_leftfold(Id.Set.empty,Id.Set.empty)ll|{v=IntroAction(IntroInjectionl)}::l'->collect_intro_names(l@l')|{v=IntroAction(IntroApplyOn(c,pat))}::l'->collect_intro_names(pat::l')|{v=IntroNaming(IntroFreshid)}::l->letids1,ids2=collect_intro_nameslinids1,Id.Set.addidids2|{v=(IntroNamingIntroAnonymous|IntroAction(IntroWildcard|IntroRewrite_))}::l->collect_intro_namesl|[]->Id.Set.empty,Id.Set.emptyletexplicit_intro_namesl=fst(collect_intro_namesl)letexplicit_all_intro_namesl=letids1,ids2=collect_intro_nameslinId.Set.unionids1ids2letreccheck_name_unicityenvokseen=letopenCAstinfunction|{v=IntroForthcoming_}::l->check_name_unicityenvokseenl|{loc;v=IntroNaming(IntroIdentifierid)}::l->(tryignore(ifList.mem_fId.equalidokthenraiseNot_foundelselookup_namedidenv);error?loc(AlreadyUsedid)withNot_found->ifList.mem_fId.equalidseenthenerror?loc(UsedTwiceid)elsecheck_name_unicityenvok(id::seen)l)|{v=IntroAction(IntroOrAndPatternl)}::l'->letll=matchlwithIntroAndPatternl->[l]|IntroOrPatternll->llinList.iter(funl->check_name_unicityenvokseen(l@l'))ll|{v=IntroAction(IntroInjectionl)}::l'->check_name_unicityenvokseen(l@l')|{v=IntroAction(IntroApplyOn(c,pat))}::l'->check_name_unicityenvokseen(pat::l')|{v=(IntroNaming(IntroAnonymous|IntroFresh_)|IntroAction(IntroWildcard|IntroRewrite_))}::l->check_name_unicityenvokseenl|[]->()letfresh_wildids=letrecauxs=ifId.Set.exists(funid->String.is_prefixs(Id.to_stringid))idsthenaux(s^"'")elseId.of_stringsinaux"_H"letmake_naming?locavoidl=function|IntroIdentifierid->NamingMustBe(CAst.make?locid)|IntroAnonymous->NamingAvoid(Id.Set.unionavoid(explicit_intro_namesl))|IntroFreshid->NamingBasedOn(id,Id.Set.unionavoid(explicit_intro_namesl))letrecmake_naming_actionavoidl=function(* In theory, we could use a tmp id like "wild_id" for all actions
but we prefer to avoid it to avoid this kind of "ugly" names *)|IntroWildcard->NamingBasedOn(fresh_wild(Id.Set.unionavoid(explicit_all_intro_namesl)),Id.Set.empty)|IntroApplyOn(_,{CAst.v=pat;loc})->make_naming_patternavoid?loclpat|(IntroOrAndPattern_|IntroInjection_|IntroRewrite_)aspat->NamingAvoid(Id.Set.unionavoid(explicit_intro_names((CAst.make@@IntroActionpat)::l)))andmake_naming_pattern?locavoidl=function|IntroNamingpat->make_naming?locavoidlpat|IntroActionpat->make_naming_actionavoidlpat|IntroForthcoming_->NamingAvoid(Id.Set.unionavoid(explicit_intro_namesl))letprepare_naming?locpat=make_naming?locId.Set.empty[]patletfit_boundn=function|None->true|Somen'->n=n'letexceed_boundn=function|None->false|Somen'->n>=n'(* We delay thinning until the completion of the whole intros tactic
to ensure that dependent hypotheses are cleared in the right
dependency order (see BZ#1000); we use fresh names, not used in
the tactic, for the hyps to clear *)(* In [intro_patterns_core b avoid ids thin destopt bound n tac patl]:
[b]: compatibility flag, if false at toplevel, do not complete incomplete
trailing toplevel or_and patterns (as in "intros []", see
[bracketing_last_or_and_intro_pattern])
[avoid]: names to avoid when creating an internal name
[ids]: collect introduced names for possible use by the [tac] continuation
[thin]: collect names to erase at the end
[destopt]: position in the context where to introduce the hypotheses
[bound]: number of pending intros to do in the current or-and pattern,
with remembering of [b] flag if at toplevel
[n]: number of introduction done in the current or-and pattern
[tac]: continuation tactic
[patl]: introduction patterns to interpret
*)letrecintro_patterns_corewith_evarsavoididsthindestoptboundntac=function|[]whenfit_boundnbound->tacidsthin|[]->(* Behave as IntroAnonymous *)intro_patterns_corewith_evarsavoididsthindestoptboundntac[CAst.make@@IntroNamingIntroAnonymous]|{CAst.loc;v=pat}::l->ifexceed_boundnboundthenerror?loc(UnexpectedExtraPattern(bound,pat))elsematchpatwith|IntroForthcomingonlydeps->letnaming=Id.Set.unionavoid(explicit_intro_namesl)inintro_forthcoming_then_gennamingdestopt~dep:onlydepsboundn(funids->intro_patterns_corewith_evarsavoididsthindestoptbound(n+List.lengthids)tacl)|IntroActionpat->letnaming=make_naming_actionavoidlpatinintro_then_gennamingdestopt~force:true~dep:false(intro_pattern_action?locwith_evarspatthindestopt(funthinbound'->intro_patterns_corewith_evarsavoididsthindestoptbound'0(funidsthin->intro_patterns_corewith_evarsavoididsthindestoptbound(n+1)tacl)))|IntroNamingpat->letnaming=make_namingavoidlpatinintro_then_gennamingdestopt~force:true~dep:false(funid->intro_patterns_corewith_evarsavoid(id::ids)thindestoptbound(n+1)tacl)andintro_pattern_action?locwith_evarspatthindestopttacid=matchpatwith|IntroWildcard->tac(CAst.(make?locid)::thin)None[]|IntroOrAndPatternll->intro_or_and_pattern?locwith_evarsllthintacid|IntroInjectionl'->intro_decomp_eq?locl'thintacid|IntroRewritel2r->rewrite_hyp_thenwith_evarsthinl2rid(funthin->tacthinNone[])|IntroApplyOn({CAst.loc=loc';v=f},{CAst.loc;v=pat})->letnaming=NamingMustBe(CAst.make?locid)inlettac_ipat=prepare_action?locwith_evarsdestoptpatinletfenvsigma=let(sigma,c)=fenvsigmain(sigma,(c,NoBindings))inapply_in_delayed_oncetruetruewith_evarsnamingid(None,CAst.make?loc:loc'f)(funid->Tacticals.tclTHENLIST[tac_ipatid;tacthinNone[]])andprepare_action?locwith_evarsdestopt=function|IntroNamingipat->(fun_->Proofview.tclUNIT())|IntroActionipat->(lettacthinbound=intro_patterns_corewith_evarsId.Set.empty[]thindestoptbound0(fun_l->clear_wildcardsl)infunid->intro_pattern_action?locwith_evarsipat[]destopttacid)|IntroForthcoming_->error?locOneIntroPatternExpectedletintro_patterns_head_corewith_evarsdestoptboundpat=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglincheck_name_unicityenv[][]pat;intro_patterns_corewith_evarsId.Set.empty[][]destoptbound0(fun_l->clear_wildcardsl)patendletintro_patterns_bound_towith_evarsndestopt=intro_patterns_head_corewith_evarsdestopt(Somen)letintro_patterns_towith_evarsdestopt=intro_patterns_head_corewith_evarsdestoptNoneletintro_pattern_towith_evarsdestoptpat=intro_patterns_towith_evarsdestopt[CAst.makepat]letintro_patternswith_evars=intro_patterns_towith_evarsMoveLast(* Implements "intros" *)letintros_patternswith_evars=function|[]->intros|l->intro_patterns_towith_evarsMoveLastl(**************************)(* Forward reasoning *)(**************************)letprepare_intros_optwith_evarsdftdestoptipat=letnaming,loc,ipat=matchipatwith|None->letpat=IntroNamingdftinmake_naming_patternId.Set.empty[]pat,None,pat|Some{CAst.loc;v=(IntroNamingpatasipat)}->(* "apply ... in H as id" needs to use id and H is kept iff id<>H *)prepare_naming?locpat,loc,ipat|Some{CAst.loc;v=(IntroActionpatasipat)}->(* "apply ... in H as pat" reuses H so that old H is always cleared *)(matchdftwithIntroIdentifier_->prepare_naming?locdft|_->make_naming_actionId.Set.empty[]pat),loc,ipat|Some{CAst.loc;v=(IntroForthcoming_)}->assertfalseinnaming,prepare_action?locwith_evarsdestoptipatletipat_of_name=function|Anonymous->None|Nameid->Some(CAst.make@@IntroNaming(IntroIdentifierid))lethead_identsigmac=letc=fst(decompose_appsigma(snd(decompose_lambda_declssigmac)))inifisVarsigmacthenSome(destVarsigmac)elseNone(* apply in as *)letgeneral_apply_in?(respect_opaque=false)with_deltawith_destructwith_evarsidlemmasipatthen_tac=lettac(naming,lemma)tacid=apply_in_delayed_once~respect_opaquewith_deltawith_destructwith_evarsnamingidlemmatacinProofview.Goal.enterbeginfungl->letdestopt=ifwith_evarsthenMoveLast(* evars would depend on the whole context *)else(letenv,sigma=Proofview.Goal.(envgl,sigmagl)inget_previous_hyp_positionenvsigmaid(Proofview.Goal.hypsgl))inletnaming,ipat_tac=prepare_intros_optwith_evars(IntroIdentifierid)destoptipatinletlemmas_target,last_lemma_target=letlast,first=List.sep_lastlemmasinList.map(funlem->(NamingMustBe(CAst.makeid),lem))first,(naming,last)in(* We chain apply_in_once, ending with an intro pattern *)List.fold_righttaclemmas_target(taclast_lemma_target(funid->Tacticals.tclTHEN(ipat_tacid)then_tac))idend(*
if sidecond_first then
(* Skip the side conditions of the applied lemma *)
Tacticals.tclTHENLAST (tclMAPLAST tac lemmas_target) (ipat_tac id)
else
Tacticals.tclTHENFIRST (tclMAPFIRST tac lemmas_target) (ipat_tac id)
*)letapply_insimplewith_evarsidlemmasipat=letlemmas=List.map(fun(k,{CAst.loc;v=l})->k,CAst.make?loc(fun_sigma->(sigma,l)))lemmasingeneral_apply_insimplesimplewith_evarsidlemmasipatTacticals.tclIDTACletapply_delayed_insimplewith_evarsidlemmasipatthen_tac=general_apply_in~respect_opaque:truesimplesimplewith_evarsidlemmasipatthen_tac(*****************************)(* Tactics abstracting terms *)(*****************************)(* Implementation without generalisation: abbrev will be lost in hyps in *)(* in the extracted proof *)letdecode_hyp=function|None->MoveLast|Someid->MoveAfterid(* [letin_tac b (... abstract over c ...) gl] transforms
[...x1:T1(c),...,x2:T2(c),... |- G(c)] into
[...x:T;Heqx:(x=c);x1:T1(x),...,x2:T2(x),... |- G(x)] if [b] is false or
[...x:=c:T;x1:T1(x),...,x2:T2(x),... |- G(x)] if [b] is true
*)letletin_tac_genwith_eq(id,depdecls,lastlhyp,ccl,c)ty=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinlet(sigma,t)=matchtywith|Somet->(sigma,t)|None->lett=typ_ofenvsigmacinEvarsolve.refresh_universes~onlyalg:true(Somefalse)envsigmatinletrel=Retyping.relevance_of_termenvsigmacinlet(sigma,(newcl,eq_tac))=matchwith_eqwith|Some(lr,{CAst.loc;v=ido})->letheq=matchidowith|IntroAnonymous->new_fresh_id(Id.Set.singletonid)(add_prefix"Heq"id)gl|IntroFreshheq_base->new_fresh_id(Id.Set.singletonid)heq_basegl|IntroIdentifierid->idinleteqdata=build_coq_eq_data()inletargs=iflrthen[mkVarid;c]else[c;mkVarid]inlet(sigma,eq)=Evd.fresh_globalenvsigmaeqdata.eqinlet(sigma,refl)=Evd.fresh_globalenvsigmaeqdata.reflinletsigma,eq=Typing.checked_applistenvsigmaeq[t]inleteq=applist(eq,args)inletrefl=applist(refl,[t;mkVarid])inletterm=mkNamedLetInsigma(make_annotidrel)ct(mkLetIn(make_annot(Nameheq)Sorts.Relevant,refl,eq,ccl))inletans=term,Tacticals.tclTHENLIST[intro_gen(NamingMustBeCAst.(make?locheq))(decode_hyplastlhyp)~force:true~dep:false;clear_body[heq;id]]in(sigma,ans)|None->(sigma,(mkNamedLetInsigma(make_annotidrel)ctccl,Proofview.tclUNIT()))inTacticals.tclTHENLIST[Proofview.Unsafe.tclEVARSsigma;convert_concl~cast:false~check:falsenewclDEFAULTcast;intro_gen(NamingMustBe(CAst.makeid))(decode_hyplastlhyp)~force:true~dep:false;Tacticals.tclMAP(convert_hyp~check:false~reorder:false)depdecls;eq_tac]endletinsert_beforedeclslasthypenv=matchlasthypwith|None->push_named_contextdeclsenv|Someid->Environ.fold_named_context(fun_denv->letd=map_named_declEConstr.of_constrdinletenv=ifId.equalid(NamedDecl.get_idd)thenpush_named_contextdeclsenvelseenvinpush_nameddenv)~init:(reset_contextenv)envletmk_eq_nameenvid{CAst.loc;v=ido}=matchidowith|IntroAnonymous->fresh_id_in_env(Id.Set.singletonid)(add_prefix"Heq"id)env|IntroFreshheq_base->fresh_id_in_env(Id.Set.singletonid)heq_baseenv|IntroIdentifierid->ifList.memid(ids_of_named_context(named_contextenv))thenerror(AlreadyUsedid);id(* unsafe *)letmkletin_goalenvsigmawith_eqdep(id,lastlhyp,ccl,c)ty=letopenContext.Named.Declarationinlett=matchtywithSomet->t|_->typ_ofenvsigmacinletr=Retyping.relevance_of_typeenvsigmatinletdecl=ifdepthenLocalDef(make_annotidr,c,t)elseLocalAssum(make_annotidr,t)inmatchwith_eqwith|Some(lr,heq)->leteqdata=build_coq_eq_data()inletargs=iflrthen[mkVarid;c]else[c;mkVarid]inlet(sigma,eq)=Evd.fresh_globalenvsigmaeqdata.eqinlet(sigma,refl)=Evd.fresh_globalenvsigmaeqdata.reflin(* NB we are not in the right env for [id] so we only check the partial application.
This is enough to produce the desired univ constraint between univ of eq and univ of t *)letsigma,eq=Typing.checked_applistenvsigmaeq[t]inleteq=applist(eq,args)inletrefl=applist(refl,[t;mkVarid])inletnewenv=insert_before[LocalAssum(make_annotheqSorts.Relevant,eq);decl]lastlhypenvinlet(sigma,x)=new_evarnewenvsigma~principal:truecclin(sigma,mkNamedLetInsigma(make_annotidr)ct(mkNamedLetInsigma(make_annotheqSorts.Relevant)refleqx))|None->letnewenv=insert_before[decl]lastlhypenvinlet(sigma,x)=new_evarnewenvsigma~principal:truecclin(sigma,mkNamedLetInsigma(make_annotidr)ctx)letpose_tacnac=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinlethyps=named_context_valenvinletconcl=Proofview.Goal.conclglinlett=typ_ofenvsigmacinletrel=Retyping.relevance_of_termenvsigmacinlet(sigma,t)=Evarsolve.refresh_universes~onlyalg:true(Somefalse)envsigmatinletid=matchnawith|Nameid->let()=ifmem_named_context_validhypsthenerror(IntroAlreadyDeclaredid)inid|Anonymous->letid=id_of_name_using_hdcharenvsigmatAnonymousinnext_ident_away_in_goal(Global.env())id(ids_of_named_context_valhyps)inProofview.Unsafe.tclEVARSsigma<*>Refine.refine~typecheck:falsebeginfunsigma->letid=make_annotidrelinletnhyps=EConstr.push_named_context_val(NamedDecl.LocalDef(id,c,t))hypsinlet(sigma,ev)=Evarutil.new_pure_evarnhypssigmaconclinletinst=EConstr.identity_subst_valhypsinletbody=mkEvar(ev,SList.cons(mkRel1)inst)in(sigma,mkLetIn(map_annotName.mk_nameid,c,t,body))endendletletin_tacwith_eqidctyoccs=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletccl=Proofview.Goal.conclglinletabs=AbstractExact(id,c,ty,occs,true)inlet(id,_,depdecls,lastlhyp,ccl,res)=make_abstractionenvsigmacclabsin(* We keep the original term to match but record the potential side-effects
of unifying universes. *)let(sigma,c)=matchreswith|None->(sigma,c)|Some(sigma,_)->(sigma,c)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(letin_tac_genwith_eq(id,depdecls,lastlhyp,ccl,c)ty)endletletin_pat_tacwith_evarswith_eqidcoccs=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletccl=Proofview.Goal.conclglinletcheckt=trueinletabs=AbstractPattern(false,check,id,c,occs,false)inlet(id,_,depdecls,lastlhyp,ccl,res)=make_abstractionenvsigmacclabsinlet(sigma,c)=matchreswith|None->finish_evar_resolution~flags:(tactic_infer_flagswith_evars)envsigmac|Someres->resinProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(letin_tac_genwith_eq(id,depdecls,lastlhyp,ccl,c)None)end(* Tactics "pose proof" (usetac=None) and "assert"/"enough" (otherwise) *)letforwardbusetacipatc=matchusetacwith|None->Proofview.Goal.enterbeginfungl->lett=Tacmach.pf_get_type_ofglcinletsigma=Tacmach.projectglinlethd=head_identsigmacinletassert_as=letnaming,tac=prepare_intros_optfalseIntroAnonymousMoveLastipatinletrepl=do_replacehdnaminginifreplthenassert_before_gentruenamingtelseassert_before_then_genfalsenamingttacinTacticals.tclTHENFIRSTassert_as(exact_no_checkc)end|Sometac->lettac=matchtacwith|None->Tacticals.tclIDTAC|Sometac->Tacticals.tclCOMPLETEtacinletnaming,assert_tac=prepare_intros_optfalseIntroAnonymousMoveLastipatinifbthenTacticals.tclTHENFIRST(assert_before_then_genfalsenamingcassert_tac)tacelseTacticals.tclTHENS3PARTS(assert_after_then_genfalsenamingcassert_tac)[||]tac[|Tacticals.tclIDTAC|]letpose_proofnac=forwardtrueNone(ipat_of_namena)cletassert_bynattac=forwardtrue(Some(Sometac))(ipat_of_namena)tletenough_bynattac=forwardfalse(Some(Sometac))(ipat_of_namena)t(***************************)(* Generalization tactics *)(***************************)(* Compute a name for a generalization *)letgeneralized_nameenvsigmactidscl=function|Nameidasna->ifId.List.memididsthenerror(AlreadyUsedid);na|Anonymous->matchEConstr.kindsigmacwith|Varid->(* Keep the name even if not occurring: may be used by intros later *)Nameid|_->ifnoccurnsigma1clthenAnonymouselse(* On ne s'etait pas casse la tete : on avait pris pour nom de
variable la premiere lettre du type, meme si "c" avait ete une
constante dont on aurait pu prendre directement le nom *)named_hdenvsigmatAnonymous(* Abstract over [c] in [forall x1:A1(c)..xi:Ai(c).T(c)] producing
[forall x, x1:A1(x1), .., xi:Ai(x). T(x)] with all [c] abtracted in [Ai]
but only those at [occs] in [T] *)letgeneralize_goal_genenvsigmaidsi((occs,c,b),na)tcl=letopenContext.Rel.Declarationinletdecls,cl=decompose_prod_n_declssigmaiclinletdummy_prod=it_mkProd_or_LetInmkPropdeclsinletnewdecls,_=letarity=Array.length(snd(EConstr.decompose_appsigmac))inletcache=refInt.Map.emptyinleteqsigmakt=letc=tryInt.Map.findk!cachewithNot_found->letc=EConstr.Vars.liftkcinlet()=cache:=Int.Map.addkc!cacheincin(* We use a nounivs equality because generalize morally takes a pattern as
argument, so we have to ignore freshly generated sorts. *)EConstr.eq_constr_nounivssigmactindecompose_prod_n_declssigmai(replace_term_gensigmaeqarity(mkRel1)dummy_prod)inletcl',sigma'=subst_closed_term_occenvsigma(AtOccsoccs)c(it_mkProd_or_LetInclnewdecls)inletna=generalized_nameenvsigmactidscl'nainletr=Retyping.relevance_of_typeenvsigmatinletdecl=matchbwith|None->LocalAssum(make_annotnar,t)|Someb->LocalDef(make_annotnar,b,t)inmkProd_or_LetIndeclcl',sigma'letgeneralize_goalgli((occs,c,b),naaso)(cl,sigma)=letopenTacmachinletenv=pf_envglinletids=pf_ids_of_hypsglinletsigma,t=Typing.type_ofenvsigmacingeneralize_goal_genenvsigmaidsiotclletgeneralize_dep?(with_let=false)c=letopenTacmachinletopenTacticalsinProofview.Goal.enterbeginfungl->letenv=pf_envglinletsign=named_context_valenvinletsigma=projectglinletinit_ids=ids_of_named_context(Global.named_context())inletseek(d:named_declaration)(toquant:named_context)=ifList.exists(fund'->occur_var_in_declenvsigma(NamedDecl.get_idd')d)toquant||dependent_in_declsigmacdthend::toquantelsetoquantinletto_quantify=Context.Named.fold_outsideseek(named_context_of_valsign)~init:[]inletqhyps=List.mapNamedDecl.get_idto_quantifyinlettothin=List.filter(funid->not(Id.List.memidinit_ids))qhypsinlettothin'=matchEConstr.kindsigmacwith|Varidwhenmem_named_context_validsign&¬(Id.List.memidinit_ids)->tothin@[id]|_->tothininletcl'=it_mkNamedProd_or_LetInsigma(pf_conclgl)to_quantifyinletis_var,body=matchEConstr.kindsigmacwith|Varid->letbody=NamedDecl.get_value(pf_get_hypidgl)inletis_var=Option.is_emptybody&¬(List.memidinit_ids)inifwith_letthenis_var,bodyelseis_var,None|_->false,Noneinletcl'',evd=generalize_goalgl0((AllOccurrences,c,body),Anonymous)(cl',projectgl)in(* Check that the generalization is indeed well-typed *)letevd=(* No need to retype for variables, term is statically well-typed *)ifis_varthenevdelsefst(Typing.type_ofenvevdcl'')inletargs=Array.to_list(Context.Named.instancemkVarto_quantify)intclTHENLIST[Proofview.Unsafe.tclEVARSevd;apply_type~typecheck:falsecl''(ifOption.is_emptybodythenc::argselseargs);cleartothin']end(** *)letgeneralize_gen_letlconstr=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletnewcl,evd=List.fold_right_i(generalize_goalgl)0lconstr(Tacmach.pf_conclgl,Tacmach.projectgl)inlet(evd,_)=Typing.type_ofenvevdnewclinletmap((_,c,b),_)=ifOption.is_emptybthenSomecelseNoneinProofview.tclTHEN(Proofview.Unsafe.tclEVARSevd)(apply_type~typecheck:falsenewcl(List.map_filtermaplconstr))endletnew_generalize_gen_letlconstr=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletconcl=Proofview.Goal.conclglinletenv=Proofview.Goal.envglinletids=Tacmach.pf_ids_of_hypsglinletnewcl,sigma,args=List.fold_right_i(funi((_,c,b),_aso)(cl,sigma,args)->letsigma,t=Typing.type_ofenvsigmacinletargs=ifOption.is_emptybthenc::argselseargsinletcl,sigma=generalize_goal_genenvsigmaidsiotclin(cl,sigma,args))0lconstr(concl,sigma,[])inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)(Refine.refine~typecheck:falsebeginfunsigma->let(sigma,ev)=Evarutil.new_evarenvsigma~principal:truenewclin(sigma,applist(ev,args))end)endletgeneralize_genlconstr=generalize_gen_let(List.map(fun(occs_c,na)->let(occs,c)=Redexpr.out_with_occurrencesoccs_cin(occs,c,None),na)lconstr)letnew_generalize_genlconstr=new_generalize_gen_let(List.map(fun((occs,c),na)->(occs,c,None),na)lconstr)letgeneralizel=new_generalize_gen_let(List.map(func->((AllOccurrences,c,None),Anonymous))l)(* Faudra-t-il une version avec plusieurs args de generalize_dep ?
Cela peut-être troublant de faire "Generalize Dependent H n" dans
"n:nat; H:n=n |- P(n)" et d'échouer parce que H a disparu après la
généralisation dépendante par n.
let quantify lconstr =
List.fold_right
(fun com tac -> tclTHEN tac (tactic_com generalize_dep c))
lconstr
tclIDTAC
*)(* Instantiating some arguments (whatever their position) of an hypothesis
or any term, leaving other arguments quantified. If operating on an
hypothesis of the goal, the new hypothesis replaces it.
(c,lbind) are supposed to be of the form
((H t1 t2 ... tm) , someBindings)
whete t1..tn are user given args, to which someBinding must be combined.
The goal is to pose a proof with body
(fun y1...yp => H t1 t2 ... tm u1 ... uq)
where yi are the remaining arguments of H that lbind could not
solve, ui are a mix of inferred args and yi. The overall effect
is to remove from H as much quantification as possible given
lbind. *)moduleSpecialize:sigvalunify_bindings:evar_map->(evar_map->int->'a->evar_map)->types->'abindings->evar_map(* Unfortunate small code duplication with EClause *)end=structtypehole={hole_evar:int;hole_deps:bool;hole_name:Name.t;}letmake_evar_clausesigmat=letopenEConstrinletopenVarsinletrecclrecholesnt=matchEConstr.kindsigmatwith|Cast(t,_,_)->clrecholesnt|Prod(na,t1,t2)->letdep=not(noccurnsigma1t2)inlethole={hole_evar=n;hole_deps=dep;hole_name=na.binder_name;}inclrec(hole::holes)(n+1)t2|LetIn(na,b,_,t)->clrecholesn(subst1bt)|_->holesinletholes=clrec[]0tinList.revholesletevar_with_nameholes({CAst.v=id}aslid)=letmaph=matchh.hole_namewith|Anonymous->None|Nameid'->ifId.equalidid'thenSomehelseNoneinlethole=List.map_filtermapholesinmatchholewith|[]->letfoldhaccu=matchh.hole_namewith|Anonymous->accu|Nameid->id::accuinletmvl=List.fold_rightfoldholes[]inEClause.explain_no_such_bound_variablemvllid|h::_->h.hole_evarletevar_of_binderholes=function|NamedHyps->evar_with_nameholess|AnonHypn->tryletnondeps=List.filter(funhole->nothole.hole_deps)holesinleth=List.nthnondeps(predn)inh.hole_evarwithewhenCErrors.noncriticale->user_errPp.(str"No such binder.")letsolve_evar_clausesigmaunifyholes=function|NoBindings->sigma|ImplicitBindingslargs->letmaph=ifh.hole_depsthenSomeh.hole_evarelseNoneinletevs=List.map_filtermapholesinletlen=List.lengthevsinifInt.equallen(List.lengthlargs)thenletfoldsigmaevarg=unifysigmaevarginletsigma=List.fold_left2foldsigmaevslargsinsigmaelseEClause.error_not_right_number_missing_argumentslen|ExplicitBindingslbind->let()=EClause.check_bindingslbindinletfoldsigma{CAst.v=(binder,c)}=letev=evar_of_binderholesbinderinunifysigmaevcinletsigma=List.fold_leftfoldsigmalbindinsigmaletunify_bindingssigmaunifyty=letholes=make_evar_clausesigmatyinsolve_evar_clausesigmaunifyholesendletspecialize(c,lbind)ipat=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlettyp_of_c=Retyping.get_type_ofenvsigmacinletsigma,term,typ=matchlbindwith|NoBindings->sigma,c,typ_of_c|ExplicitBindings_|ImplicitBindings_->letctx,ty=decompose_prod_declssigmatyp_of_cin(* Create a new context where variables mentioned further in the telescope
are turned into evars that live in the telescope context. This allows
instantiating each evar with the original variable as a default value.
For instance, on Γ := [x : A, y : B{x}, z : C{x, y}] it produces evars
[x : A ⊢ ?X : A]
[x : A, y : B{?X{x}} ⊢ ?Y : B{?X{x}}]
[x : A, y : B{?X{x}}, z : C{?X{x}, ?Y{x, y}} ⊢ ?Z : C{?X{x}, ?Y{x, y}}]
and returns the context
Δ := [x : A, y : B{?X{x}}, z : C{?X{x}, ?Y{x, y}}]
together with a substitution [?X, ?Y, ?Z] : Γ ⊢ Δ.
*)letopenRelDeclinletrecinstantiatesigmaenvsubstaccu=function|[]->sigma,subst,rel_contextenv,List.revaccu|LocalAssum(na,t)::decls->lett=Vars.esubstVars.lift_substituendsubsttinletenv=push_rel(LocalAssum(na,t))envinletsigma,ev=Evarutil.new_evarenvsigma(lift1t)inletsubst=Esubst.subs_cons(Vars.make_substituendev)(Esubst.subs_shft(1,subst))ininstantiatesigmaenvsubst((env,ev)::accu)decls|LocalDef(na,b,t)::decls->letb=Vars.esubstVars.lift_substituendsubstbinlett=Vars.esubstVars.lift_substituendsubsttinletenv=push_rel(LocalDef(na,b,t))envinletsubst=Esubst.subs_liftsubstininstantiatesigmaenvsubstaccudeclsinletsigma,subst,nctx,holes=instantiatesigmaenv(Esubst.subs_id0)[](List.revctx)inletnty=Vars.esubstVars.lift_substituendsubsttyin(* Solve holes with the provided bindings *)letunifysigmanc=letenv,ev=List.nthholesninEvarconv.unifyenvsigmaCONVevcinletsigma=Specialize.unify_bindingssigmaunifytyp_of_clbindin(* Instantiate unsolved holes with their default value *)letfoldsigma(env,ev)=ifisEvarsigmaevthenEvarconv.unifyenvsigmaCONVev(mkRel1)elsesigmainletsigma=List.fold_leftfoldsigmaholesin(* Requantify the proof term and its type *)letargs=Context.Rel.instance_listmkRel0ctxinletnc=applist(c,List.map(func->Vars.esubstVars.lift_substituendsubstc)args)inletrecrebuildrelsctxcty=matchctxwith|[]->c,ty|decl::ctx->letlifts=Int.Set.fold(funnaccu->Int.Set.add(n-1)accu)sInt.Set.emptyinletc,ty,rels=(* We always keep let bindings *)ifRelDecl.is_local_defdecl||Int.Set.mem1relsthenletrels=lift(Int.Set.remove1rels)inletrels=RelDecl.fold_constr(funcaccu->Int.Set.unionaccu(free_relssigmac))declrelsinmkLambda_or_LetIndeclc,mkProd_or_LetIndeclty,relselsesubst1mkProp(* dummy *)c,subst1mkPropty,liftrelsinrebuildrelsctxctyinletrels=Int.Set.union(free_relssigmanc)(free_relssigmanty)inletnc,nty=rebuildrelsnctxncntyinsigma,nc,ntyinlettac=matchEConstr.kindsigma(fst(EConstr.decompose_appsigma(snd(EConstr.decompose_lambda_declssigmac))))with|VaridwhenId.List.memid(Tacmach.pf_ids_of_hypsgl)->(* Like assert (id:=id args) but with the concept of specialization *)letipat=matchipatwithNone->Some(CAst.make(IntroNaming(IntroIdentifierid)))|_->ipatinletnaming,tac=prepare_intros_optfalseIntroAnonymousMoveLastipatinletrepl=do_replace(Someid)namingin(* "specialize H ... as H", "specialize H ...": do not clear (cleared implicitly at replacing time) *)(* "specialize H ... as H'", "specialize H ... as ?H": keep a copy by convention *)(* "specialize H ... as any_other_pattern": clear *)letdoclear=matchipatwith|Some{CAst.v=IntroNaming(IntroIdentifier_|IntroFresh_)}->false|_->trueinlettac=ifdoclearthenfunid'->Tacticals.tclTHEN(clear[id])(tacid')elsetacinTacticals.tclTHENFIRST(assert_before_then_genreplnamingtyptac)(exact_no_checkterm)|_->matchipatwith|None->(* Like generalize with extra support for "with" bindings *)(* even though the "with" bindings forces full application *)(* TODO: add intro to be more homogeneous. It will break
scripts but will be easy to fix *)(Tacticals.tclTHENLAST(cuttyp)(exact_no_checkterm))|Some_asipat->(* Like pose proof with extra support for "with" bindings *)(* even though the "with" bindings forces full application *)letnaming,tac=prepare_intros_optfalseIntroAnonymousMoveLastipatinTacticals.tclTHENFIRST(assert_before_then_genfalsenamingtyptac)(exact_no_checkterm)inTacticals.tclTHEN(Proofview.Unsafe.tclEVARSsigma)tacend(*****************************)(* Ad hoc unfold *)(*****************************)(* The two following functions should already exist, but found nowhere *)(* Unfolds x by its definition everywhere *)letunfold_bodyx=letopenContext.Named.DeclarationinProofview.Goal.enterbeginfungl->(* We normalize the given hypothesis immediately. *)letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletxval=matchEnviron.lookup_namedxenvwith|LocalAssum_->error(VariableHasNoValuex)|LocalDef(_,xval,_)->xvalinletxval=EConstr.of_constrxvalinTacticals.afterHypxbeginfunaft->lethl=List.fold_right(fundeclcl->(NamedDecl.get_iddecl,InHyp)::cl)aft[]inletrfun__c=replace_varssigma[x,xval]cinletreducthh=reduct_in_hyp~check:false~reorder:falserfunhinletreductc=reduct_in_concl~cast:false~check:false(rfun,DEFAULTcast)inTacticals.tclTHENLIST[Tacticals.tclMAPreducthhl;reductc]endendletwarn_cannot_remove_as_expected=CWarnings.create~name:"cannot-remove-as-expected"~category:CWarnings.CoreCategories.tactics(fun(id,inglobal)->letpp=matchinglobalwith|None->mt()|Someref->str", it is used implicitly in "++Printer.pr_globalrefinstr"Cannot remove "++Id.printid++pp++str".")letclear_for_destructids=Proofview.tclORELSE(clear_gen(funenvsigmaiderringlobal->raise(ClearDependencyError(id,err,inglobal)))ids)(function|ClearDependencyError(id,err,inglobal),_->warn_cannot_remove_as_expected(id,inglobal);Proofview.tclUNIT()|e->Exninfo.iraisee)(* Either unfold and clear if defined or simply clear if not a definition *)letexpand_hypid=Tacticals.tclTRY(unfold_bodyid)<*>clear_for_destruct[id](*****************************)(* High-level induction *)(*****************************)(*
* A "natural" induction tactic
*
- [H0:T0, ..., Hi:Ti, hyp0:P->I(args), Hi+1:Ti+1, ..., Hn:Tn |-G] is the goal
- [hyp0] is the induction hypothesis
- we extract from [args] the variables which are not rigid parameters
of the inductive type, this is [indvars] (other terms are forgotten);
- we look for all hyps depending of [hyp0] or one of [indvars]:
this is [dephyps] of types [deptyps] respectively
- [statuslist] tells for each hyps in [dephyps] after which other hyp
fixed in the context they must be moved (when induction is done)
- [hyp0succ] is the name of the hyp fixed in the context after which to
move the subterms of [hyp0succ] in the i-th branch where it is supposed
to be the i-th constructor of the inductive type.
Strategy: (cf in [induction_with_atomization_of_ind_arg])
- requantify and clear all [dephyps]
- apply induction on [hyp0]
- clear those of [indvars] that are variables and [hyp0]
- in the i-th subgoal, intro the arguments of the i-th constructor
of the inductive type after [hyp0succ] (done in
[induct_discharge]) let the induction hypotheses on top of the
hyps because they may depend on variables between [hyp0] and the
top. A counterpart is that the dep hyps programmed to be intro-ed
on top must now be intro-ed after the induction hypotheses
- move each of [dephyps] at the right place following the
[statuslist]
*)letwarn_unused_intro_pattern=CWarnings.create~name:"unused-intro-pattern"~category:CWarnings.CoreCategories.tactics(fun(env,sigma,names)->strbrk"Unused introduction "++str(String.plural(List.lengthnames)"pattern")++str": "++prlist_with_sepspc(Miscprint.pr_intro_pattern(func->Printer.pr_econstr_envenvsigma(snd(cenvsigma))))names)letcheck_unused_namesenvsigmanames=ifnot(List.is_emptynames)thenwarn_unused_intro_pattern(env,sigma,names)letintropattern_of_nameglavoid=function|Anonymous->IntroNamingIntroAnonymous|Nameid->IntroNaming(IntroIdentifier(new_fresh_idavoididgl))letrecconsume_patternavoidnaisdepgl=letopenCAstinfunction|[]->((CAst.make@@intropattern_of_nameglavoidna),[])|{loc;v=IntroForthcomingtrue}::nameswhennotisdep->consume_patternavoidnaisdepglnames|{loc;v=IntroForthcoming_}::namesasfullpat->(CAst.make?loc@@intropattern_of_nameglavoidna,fullpat)|{loc;v=IntroNamingIntroAnonymous}::names->(CAst.make?loc@@intropattern_of_nameglavoidna,names)|{loc;v=IntroNaming(IntroFreshid')}::names->(CAst.make?loc@@IntroNaming(IntroIdentifier(new_fresh_idavoidid'gl)),names)|pat::names->(pat,names)letre_intro_dependent_hypotheses(lstatus,rstatus)(_,tophyp)=lettophyp=matchtophypwithNone->MoveLast|Somehyp->MoveAfterhypinletnewlstatus=(* if some IH has taken place at the top of hyps *)List.map(function(hyp,MoveLast)->(hyp,tophyp)|x->x)lstatusinTacticals.tclTHEN(intros_moverstatus)(intros_movenewlstatus)letdest_intro_patternswith_evarsavoidthindestpattac=intro_patterns_corewith_evarsavoid[]thindestNone0tacpatletsafe_dest_intro_patternswith_evarsavoidthindestpattac=Proofview.tclORELSE(dest_intro_patternswith_evarsavoidthindestpattac)beginfunction(e,info)->matchewith|CannotMoveHyp_->(* May happen e.g. with "destruct x using s" with an hypothesis
which is morally an induction hypothesis to be "MoveLast" if
known as such but which is considered instead as a subterm of
a constructor to be move at the place of x. *)dest_intro_patternswith_evarsavoidthinMoveLastpattac|e->Proofview.tclZERO~infoeendtypeelim_arg_kind=RecArg|IndArg|OtherArgtypebranch_argument={ba_kind:elim_arg_kind;ba_assum:bool;ba_dep:bool;ba_name:Id.t;}typerecarg_position=|AfterFixedPositionofId.toption(* None = top of context *)letupdate_dest(recargdests,tophypasdests)=function|[]->dests|hyp::_->(matchrecargdestswith|AfterFixedPositionNone->AfterFixedPosition(Somehyp)|x->x),(matchtophypwithNone->Somehyp|x->x)letget_recarg_dest(recargdests,tophyp)=matchrecargdestswith|AfterFixedPositionNone->MoveLast|AfterFixedPosition(Someid)->MoveAfterid(* Current policy re-introduces recursive arguments of destructed
variable at the place of the original variable while induction
hypothesese are introduced at the top of the context. Since in the
general case of an inductive scheme, the induction hypotheses can
arrive just after the recursive arguments (e.g. as in "forall
t1:tree, P t1 -> forall t2:tree, P t2 -> P (node t1 t2)", we need
to update the position for t2 after "P t1" is introduced if ever t2
had to be introduced at the top of the context).
*)letinduct_dischargewith_evarsdestsavoid'tac(avoid,ra)names=letavoid=Id.Set.unionavoid'(Id.Set.unionavoid(explicit_intro_namesnames))inletrecpeel_tacradestsnamesthin=matchrawith|({ba_kind=RecArg}asrarg)::({ba_kind=IndArg}asiarg)::ra'->Proofview.Goal.enterbeginfungl->let(recpat,names)=matchnameswith|[{CAst.loc;v=IntroNaming(IntroIdentifierid)}aspat]->letid'=new_fresh_idavoid(add_prefix"IH"id)glin(pat,[CAst.make@@IntroNaming(IntroIdentifierid')])|_->consume_patternavoid(Namerarg.ba_name)rarg.ba_depglnamesinletdest=get_recarg_destdestsindest_intro_patternswith_evarsavoidthindest[recpat](funidsthin->Proofview.Goal.enterbeginfungl->let(hyprec,names)=consume_patternavoid(Nameiarg.ba_name)iarg.ba_depglnamesindest_intro_patternswith_evarsavoidthinMoveLast[hyprec](funids'thin->peel_tacra'(update_destdestsids')namesthin)end)end|({ba_kind=IndArg}asiarg)::ra'->Proofview.Goal.enterbeginfungl->(* Rem: does not happen in Coq schemes, only in user-defined schemes *)letpat,names=consume_patternavoid(Nameiarg.ba_name)iarg.ba_depglnamesindest_intro_patternswith_evarsavoidthinMoveLast[pat](funidsthin->peel_tacra'(update_destdestsids)namesthin)end|({ba_kind=RecArg}asrarg)::ra'->Proofview.Goal.enterbeginfungl->let(pat,names)=consume_patternavoid(Namerarg.ba_name)rarg.ba_depglnamesinletdest=get_recarg_destdestsindest_intro_patternswith_evarsavoidthindest[pat](funidsthin->peel_tacra'destsnamesthin)end|({ba_kind=OtherArg}asoarg)::ra'->Proofview.Goal.enterbeginfungl->let(pat,names)=consume_patternavoidAnonymousoarg.ba_depglnamesinletdest=get_recarg_destdestsinsafe_dest_intro_patternswith_evarsavoidthindest[pat](funidsthin->peel_tacra'destsnamesthin)end|[]->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglincheck_unused_namesenvsigmanames;Tacticals.tclTHEN(clear_wildcardsthin)(tacdests)endinpeel_tacra(dests,None)names[](* - le recalcul de indtyp à chaque itération de atomize_one est pour ne pas
s'embêter à regarder si un letin_tac ne fait pas des
substitutions aussi sur l'argument voisin *)letexpand_projectionsenvsigmac=letrecauxenvc=matchEConstr.kindsigmacwith|Proj(p,c)->Retyping.expand_projectionenvsigmap(auxenvc)[]|_->map_constr_with_full_bindersenvsigmapush_relauxenvcinauxenvc(* Marche pas... faut prendre en compte l'occurrence précise... *)letatomize_param_of_indenvsigma(hd,params,indices)=letparams'=List.map(expand_projectionsenvsigma)paramsin(* le gl est important pour ne pas préévaluer *)letrecatomize_oneaccuargsargs'avoid=function|[]->lett=applist(hd,params@args)in(List.revaccu,avoid,t)|c::rem->matchEConstr.kindsigmacwith|Varidwhennot(List.exists(func->occur_varenvsigmaidc)args')&¬(List.exists(func->occur_varenvsigmaidc)params')->(* Based on the knowledge given by the user, all
constraints on the variable are generalizable in the
current environment so that it is clearable after destruction *)atomize_oneaccu(c::args)(c::args')(Id.Set.addidavoid)rem|_->letc'=expand_projectionsenvsigmacinletdependentt=dependentsigmactinifList.existsdependentparams'||List.existsdependentargs'then(* This is a case where the argument is constrained in a
way which would require some kind of inversion; we
follow the (old) discipline of not generalizing over
this term, since we don't try to invert the
constraint anyway. *)atomize_oneaccu(c::args)(c'::args')avoidremelse(* We reason blindly on the term and do as if it were
generalizable, ignoring the constraints coming from
its structure *)letid=matchEConstr.kindsigmacwith|Varid->id|_->letty=Retyping.get_type_ofenvsigmacinid_of_name_using_hdcharenvsigmatyAnonymousinletx=fresh_id_in_envavoididenvinletaccu=(x,c)::accuinatomize_oneaccu(mkVarx::args)(mkVarx::args')(Id.Set.addxavoid)reminatomize_one[][][]Id.Set.empty(List.revindices)(* [cook_sign] builds the lists [beforetoclear] (preceding the
ind. var.) and [aftertoclear] (coming after the ind. var.) of hyps
that must be erased, the lists of hyps to be generalize [decldeps] on the
goal together with the places [(lstatus,rstatus)] where to re-intro
them after induction. To know where to re-intro the dep hyp, we
remember the name of the hypothesis [lhyp] after which (if the dep
hyp is more recent than [hyp0]) or [rhyp] before which (if older
than [hyp0]) its equivalent must be moved when the induction has
been applied. Since computation of dependencies and [rhyp] is from
more ancient (on the right) to more recent hyp (on the left) but
the computation of [lhyp] progresses from the other way, [cook_hyp]
is in two passes (an alternative would have been to write an
higher-order algorithm). We use references to reduce
the accumulation of arguments.
To summarize, the situation looks like this
Goal(n,x) -| H6:(Q n); x:A; H5:True; H4:(le O n); H3:(P n); H2:True; n:nat
Left Right
Induction hypothesis is H4 ([hyp0])
Variable parameters of (le O n) is the singleton list with "n" ([indvars])
The dependent hyps are H3 and H6 ([dephyps])
For H3 the memorized places are H5 ([lhyp]) and H2 ([rhyp])
because these names are among the hyp which are fixed through the induction
For H6 the neighbours are None ([lhyp]) and H5 ([rhyp])
For H3, because on the right of H4, we remember rhyp (here H2)
For H6, because on the left of H4, we remember lhyp (here None)
For H4, we remember lhyp (here H5)
The right neighbour is then translated into the left neighbour
because move_hyp tactic needs the name of the hyp _after_ which we
move the hyp to move.
But, say in the 2nd subgoal of the hypotheses, the goal will be
(m:nat)((P m)->(Q m)->(Goal m)) -> (P Sm)-> (Q Sm)-> (Goal Sm)
^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^
both go where H4 was goes where goes where
H3 was H6 was
We have to intro and move m and the recursive hyp first, but then
where to move H3 ??? Only the hyp on its right is relevant, but we
have to translate it into the name of the hyp on the left
Note: this case where some hyp(s) in [dephyps] has(have) the same
left neighbour as [hyp0] is the only problematic case with right
neighbours. For the other cases (e.g. an hyp H1:(R n) between n and H2
would have posed no problem. But for uniformity, we decided to use
the right hyp for all hyps on the right of H4.
Other solutions are welcome
PC 9 fev 06: Adapted to accept multi argument principle with no
main arg hyp. hyp0 is now optional, meaning that it is possible
that there is no main induction hypotheses. In this case, we
consider the last "parameter" (in [indvars]) as the limit between
"left" and "right", BUT it must be included in indhyps.
Other solutions are still welcome
*)exceptionShuntofId.tmove_locationletcook_signhyp0_optinhypsindvarsenvsigma=(* First phase from L to R: get [toclear], [decldep] and [statuslist]
for the hypotheses before (= more ancient than) hyp0 (see above) *)lettoclear=ref[]inletavoid=refId.Set.emptyinletdecldeps=ref[]inletldeps=ref[]inletrstatus=ref[]inletlstatus=ref[]inletbefore=reftrueinletmaindep=reffalseinletseek_depsenvdeclrhyp=letdecl=map_named_declEConstr.of_constrdeclinlethyp=NamedDecl.get_iddeclinif(matchhyp0_optwithSomehyp0->Id.equalhyphyp0|_->false)thenbeginbefore:=false;(* Note that if there was no main induction hypotheses, then hyp
is one of indvars too *)toclear:=hyp::!toclear;MoveFirst(* fake value *)endelseifId.Set.memhypindvarsthenbegin(* The variables in indvars are such that they don't occur any
more after generalization, so declare them to clear. *)toclear:=hyp::!toclear;rhypendelseletdephyp0=not!before&&List.is_emptyinhyps&&(Option.cata(funid->occur_var_in_declenvsigmaiddecl)falsehyp0_opt)inletdepother=List.is_emptyinhyps&&(Id.Set.exists(funid->occur_var_in_declenvsigmaiddecl)indvars||List.exists(fundecl'->occur_var_in_declenvsigma(NamedDecl.get_iddecl')decl)!decldeps)inifnot(List.is_emptyinhyps)&&Id.List.memhypinhyps||dephyp0||depotherthenbegindecldeps:=decl::!decldeps;avoid:=Id.Set.addhyp!avoid;maindep:=dephyp0||!maindep;if!beforethenbegintoclear:=hyp::!toclear;rstatus:=(hyp,rhyp)::!rstatusendelsebegintoclear:=hyp::!toclear;ldeps:=hyp::!ldeps(* status computed in 2nd phase *)end;MoveBeforehypendelseMoveBeforehypinlet_=fold_named_contextseek_depsenv~init:MoveFirstin(* 2nd phase from R to L: get left hyp of [hyp0] and [lhyps] *)letcompute_lstatuslhypdecl=lethyp=NamedDecl.get_iddeclinif(matchhyp0_optwithSomehyp0->Id.equalhyphyp0|_->false)thenraise(Shuntlhyp);ifId.List.memhyp!ldepsthenbeginlstatus:=(hyp,lhyp)::!lstatus;lhypendelseifId.List.memhyp!toclearthenlhypelseMoveAfterhypintrylet_=fold_named_context_reversecompute_lstatus~init:MoveLastenvinraise(ShuntMoveLast)(* ?? FIXME *)withShuntlhyp0->letlhyp0=matchlhyp0with|MoveLast->None|MoveAfterhyp->Somehyp|_->assertfalseinletstatuslists=(!lstatus,List.rev!rstatus)inletrecargdests=AfterFixedPosition(ifOption.is_emptyhyp0_optthenNoneelselhyp0)in(statuslists,recargdests,!toclear,!decldeps,!avoid,!maindep)(*
The general form of an induction principle is the following:
forall prm1 prm2 ... prmp, (induction parameters)
forall Q1...,(Qi:Ti_1 -> Ti_2 ->...-> Ti_ni),...Qq, (predicates)
branch1, branch2, ... , branchr, (branches of the principle)
forall (x1:Ti_1) (x2:Ti_2) ... (xni:Ti_ni), (induction arguments)
(HI: I prm1..prmp x1...xni) (optional main induction arg)
-> (Qi x1...xni HI (f prm1...prmp x1...xni)).(conclusion)
^^ ^^^^^^^^^^^^^^^^^^^^^^^^
optional optional argument added if
even if HI principle generated by functional
present above induction, only if HI does not exist
[indarg] [farg]
HI is not present when the induction principle does not come directly from an
inductive type (like when it is generated by functional induction for
example). HI is present otherwise BUT may not appear in the conclusion
(dependent principle). HI and (f...) cannot be both present.
Principles taken from functional induction have the final (f...).*)(* [rel_contexts] and [rel_declaration] actually contain triples, and
lists are actually in reverse order to fit [compose_prod]. *)typeelim_scheme={elimt:types;indref:GlobRef.toption;params:rel_context;(* (prm1,tprm1);(prm2,tprm2)...(prmp,tprmp) *)nparams:int;(* number of parameters *)predicates:rel_context;(* (Qq, (Tq_1 -> Tq_2 ->...-> Tq_nq)), (Q1,...) *)npredicates:int;(* Number of predicates *)branches:rel_context;(* branchr,...,branch1 *)nbranches:int;(* Number of branches *)args:rel_context;(* (xni, Ti_ni) ... (x1, Ti_1) *)nargs:int;(* number of arguments *)indarg:rel_declarationoption;(* Some (H,I prm1..prmp x1...xni)
if HI is in premisses, None otherwise *)concl:types;(* Qi x1...xni HI (f...), HI and (f...)
are optional and mutually exclusive *)indarg_in_concl:bool;(* true if HI appears at the end of conclusion *)farg_in_concl:bool;(* true if (f...) appears at the end of conclusion *)}letempty_scheme={elimt=mkProp;indref=None;params=[];nparams=0;predicates=[];npredicates=0;branches=[];nbranches=0;args=[];nargs=0;indarg=None;concl=mkProp;indarg_in_concl=false;farg_in_concl=false;}letmake_basenid=ifInt.equaln0||Int.equaln1thenidelse(* This extends the name to accept new digits if it already ends with *)(* digits *)Id.of_string(atompart_of_id(make_ident(Id.to_stringid)(Some0)))(* Builds two different names from an optional inductive type and a
number, also deals with a list of names to avoid. If the inductive
type is None, then hyprecname is IHi where i is a number. *)letmake_up_namesnind_optcname=letis_hyp=String.equal(atompart_of_idcname)"H"inletbase=Id.to_string(make_basencname)inletind_prefix="IH"inletbase_ind=ifis_hypthenmatchind_optwith|None->Id.of_stringind_prefix|Someind_id->add_prefixind_prefix(Nametab.basename_of_globalind_id)elseadd_prefixind_prefixcnameinlethyprecname=make_basenbase_indinletavoid=ifInt.equaln1(* Only one recursive argument *)||Int.equaln0thenId.Set.emptyelse(* Forbid to use cname, cname0, hyprecname and hyprecname0 *)(* in order to get names such as f1, f2, ... *)letavoid=Id.Set.add(make_ident(Id.to_stringhyprecname)None)(Id.Set.singleton(make_ident(Id.to_stringhyprecname)(Some0)))inifnot(String.equal(atompart_of_idcname)"H")thenId.Set.add(make_identbase(Some0))(Id.Set.add(make_identbaseNone)avoid)elseavoidinId.of_stringbase,hyprecname,avoidleterror_ind_schemes=error(NotAnInductionSchemes)letcoq_eqenvsigma=Evd.fresh_globalenvsigmaCoqlib.(lib_ref"core.eq.type")letcoq_eq_reflenvsigma=Evd.fresh_globalenvsigmaCoqlib.(lib_ref"core.eq.refl")letcoq_heq_ref=lazy(Coqlib.lib_ref"core.JMeq.type")letcoq_heqenvsigma=Evd.fresh_globalenvsigma(Lazy.forcecoq_heq_ref)letcoq_heq_reflenvsigma=Evd.fresh_globalenvsigma(Coqlib.lib_ref"core.JMeq.refl")(* let coq_heq_refl = lazy (glob (lib_ref "core.JMeq.refl")) *)letmkEqenvsigmatxy=letsigma,eq=coq_eqenvsigmainsigma,mkApp(eq,[|t;x;y|])letmkReflenvsigmatx=letsigma,refl=coq_eq_reflenvsigmainsigma,mkApp(refl,[|t;x|])letmkHEqenvsigmatxuy=letsigma,c=coq_heqenvsigmainsigma,mkApp(c,[|t;x;u;y|])letmkHReflenvsigmatx=letsigma,c=coq_heq_reflenvsigmainsigma,mkApp(c,[|t;x|])letlift_togethernnl=letl',_=List.fold_right(funx(acc,n)->(liftnx::acc,succn))l([],n)inl'letlift_listl=List.map(lift1)lletids_of_constrenvsigma?(all=false)varsc=letrecauxvarsc=matchEConstr.kindsigmacwith|Varid->Id.Set.addidvars|App(f,args)->(matchEConstr.kindsigmafwith|Construct((ind,_),_)|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinArray.fold_left_from(ifallthen0elsemib.Declarations.mind_nparams)auxvarsargs|_->EConstr.foldsigmaauxvarsc)|_->EConstr.foldsigmaauxvarscinauxvarscletdecompose_indappenvsigmafargs=matchEConstr.kindsigmafwith|Construct((ind,_),_)|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinletfirst=mib.Declarations.mind_nparams_recinletpars,args=Array.chopfirstargsinmkApp(f,pars),args|_->f,argsletmk_term_eqhomogeneousenvsigmatytty't'=ifhomogeneousthenletsigma,eq=mkEqenvsigmatytt'inletsigma,refl=mkReflenvsigmaty't'insigma,(eq,refl)elseletsigma,heq=mkHEqenvsigmatytty't'inletsigma,hrefl=mkHReflenvsigmaty't'insigma,(heq,hrefl)letmake_abstract_generalizeenvidtypconcldepctxbodyceqsargsrefls=letopenContext.Rel.DeclarationinRefine.refine~typecheck:truebeginfunsigma->leteqslen=List.lengtheqsin(* Abstract by the "generalized" hypothesis equality proof if necessary. *)letsigma,abshypeq,abshypt=ifdepthenletty=lift1cinlethomogeneous=Reductionops.is_convenvsigmatytypinletsigma,(eq,refl)=mk_term_eqhomogeneous(push_rel_contextctxenv)sigmaty(mkRel1)typ(mkVarid)insigma,mkProd(make_annotAnonymousSorts.Relevant,eq,lift1concl),[|refl|]elsesigma,concl,[||]in(* Abstract by equalities *)leteqs=lift_togethern1eqsin(* lift together and past genarg *)letabseqs=it_mkProd_or_LetIn(lifteqslenabshypeq)(List.map(funx->LocalAssum(make_annotAnonymousSorts.Relevant,x))eqs)inletr=Sorts.Relevantin(* TODO relevance *)letdecl=matchbodywith|None->LocalAssum(make_annot(Nameid)r,c)|Somebody->LocalDef(make_annot(Nameid)r,body,c)in(* Abstract by the "generalized" hypothesis. *)letgenarg=mkProd_or_LetIndeclabseqsin(* Abstract by the extension of the context *)letgenctyp=it_mkProd_or_LetIngenargctxin(* The goal will become this product. *)let(sigma,genc)=Evarutil.new_evarenvsigma~principal:truegenctypin(* Apply the old arguments giving the proper instantiation of the hyp *)letinstc=mkApp(genc,Array.of_listargs)in(* Then apply to the original instantiated hyp. *)letinstc=Option.cata(fun_->instc)(mkApp(instc,[|mkVarid|]))bodyin(* Apply the reflexivity proofs on the indices. *)letappeqs=mkApp(instc,Array.of_listrefls)in(* Finally, apply the reflexivity proof for the original hyp, to get a term of type gl again. *)(sigma,mkApp(appeqs,abshypt))endlethyps_of_varsenvsigmasignnogenhyps=ifId.Set.is_emptyhypsthen[]elselet(_,lh)=Context.Named.fold_inside(fun(hs,hl)d->letx=NamedDecl.get_iddinifId.Set.memxnogenthen(hs,hl)elseifId.Set.memxhsthen(hs,x::hl)elseletxvars=global_vars_set_of_declenvsigmadinifnot(Id.Set.is_empty(Id.Set.diffxvarshs))then(Id.Set.addxhs,x::hl)else(hs,hl))~init:(hyps,[])signinlhexceptionSeenletlinearenvsigmavarsargs=letseen=refvarsintryArray.iter(funi->letrels=ids_of_constrenvsigma~all:trueId.Set.emptyiinletseen'=Id.Set.fold(funidacc->ifId.Set.memidaccthenraiseSeenelseId.Set.addidacc)rels!seeninseen:=seen')args;truewithSeen->falseletis_defined_variableenvid=env|>lookup_namedid|>is_local_defletabstract_argsglgeneralize_varsdepiddefinedfargs=letopenContext.Rel.Declarationinletsigma=Tacmach.projectglinletenv=Tacmach.pf_envglinletconcl=Tacmach.pf_conclglinlethyps=Proofview.Goal.hypsglinletdep=dep||local_occur_varsigmaidconclinletavoid=refId.Set.emptyinletget_idname=letid=fresh_id_in_env!avoid(matchnamewithNamen->n|Anonymous->Id.of_string"gen_x")envinavoid:=Id.Set.addid!avoid;idin(* Build application generalized w.r.t. the argument plus the necessary eqs.
From env |- c : forall G, T and args : G we build
(T[G'], G' : ctx, env ; G' |- args' : G, eqs := G'_i = G_i, refls : G' = G, vars to generalize)
eqs are not lifted w.r.t. each other yet. (* will be needed when going to dependent indexes *)
*)letaux(sigma,prod,ctx,ctxenv,c,args,eqs,refls,nongenvars,vars)arg=letname,ty_relevance,ty,arity=letrel,c=Reductionops.hnf_decompose_prod_n_declsenvsigma1prodinletdecl=List.hdrelinRelDecl.get_namedecl,RelDecl.get_relevancedecl,RelDecl.get_typedecl,cinletargty=Retyping.get_type_ofenvsigmaarginletsigma,ty=Evarsolve.refresh_universes(Sometrue)envsigmatyinletlenctx=List.lengthctxinletliftargty=liftlenctxargtyinletleq=constr_cmpctxenvsigmaConversion.CUMULliftargtytyinmatchEConstr.kindsigmaargwith|Varidwhennot(is_defined_variableenvid)&&leq&¬(Id.Set.memidnongenvars)->(sigma,subst1argarity,ctx,ctxenv,mkApp(c,[|arg|]),args,eqs,refls,Id.Set.addidnongenvars,Id.Set.removeidvars)|_->letname=get_idnameinletdecl=LocalAssum(make_annot(Namename)ty_relevance,ty)inletctx=decl::ctxinletc'=mkApp(lift1c,[|mkRel1|])inletargs=arg::argsinletliftarg=lift(List.lengthctx)arginletsigma,eq,refl=ifleqthenletsigma,eq=mkEqenvsigma(lift1ty)(mkRel1)liftarginletsigma,refl=mkReflenvsigma(lift(-lenctx)ty)arginsigma,eq,reflelseletsigma,eq=mkHEqenvsigma(lift1ty)(mkRel1)liftargtyliftarginletsigma,refl=mkHReflenvsigmaargtyarginsigma,eq,reflinleteqs=eq::lift_listeqsinletrefls=refl::reflsinletargvars=ids_of_constrenvsigmavarsargin(sigma,arity,ctx,push_reldeclctxenv,c',args,eqs,refls,nongenvars,Id.Set.unionargvarsvars)inletf',args'=decompose_indappenvsigmafargsinletdogen,f',args'=letparvars=ids_of_constrenvsigma~all:trueId.Set.emptyf'inifnot(linearenvsigmaparvarsargs')thentrue,f,argselsematchArray.findi(funix->not(isVarsigmax)||is_defined_variableenv(destVarsigmax))args'with|None->false,f',args'|Somenonvar->letbefore,after=Array.chopnonvarargs'intrue,mkApp(f',before),afterinifdogenthenlettyf'=Retyping.get_type_ofenvsigmaf'inletsigma,arity,ctx,ctxenv,c',args,eqs,refls,nogen,vars=Array.fold_leftaux(sigma,tyf',[],env,f',[],[],[],Id.Set.empty,Id.Set.empty)args'inletargs,refls=List.revargs,List.revreflsinletvars=ifgeneralize_varsthenletnogen=Id.Set.addidnogeninhyps_of_varsenvsigmahypsnogenvarselse[]inletbody,c'=ifdefinedthenSomec',Retyping.get_type_ofctxenvsigmac'elseNone,c'inlettyp=Tacmach.pf_get_hyp_typidglinlettac=make_abstract_generalizeenvidtypconcldepctxbodyc'eqsargsreflsinlettac=Proofview.Unsafe.tclEVARSsigma<*>tacinSome(tac,dep,succ(List.lengthctx),vars)elseNoneletabstract_generalize?(generalize_vars=true)?(force_dep=false)id=letopenContext.Named.DeclarationinProofview.Goal.enterbeginfungl->Coqlib.(check_required_libraryjmeq_module_name);letsigma=Tacmach.projectglinlet(f,args,def,id,oldid)=letoldid=Tacmach.pf_get_new_ididglinmatchTacmach.pf_get_hypidglwith|LocalAssum(_,t)->letf,args=decompose_app_listsigmatin(f,args,false,id,oldid)|LocalDef(_,t,_)->letf,args=decompose_app_listsigmatin(f,args,true,id,oldid)inifList.is_emptyargsthenProofview.tclUNIT()elseletargs=Array.of_listargsinletnewc=abstract_argsglgeneralize_varsforce_depiddeffargsinmatchnewcwith|None->Proofview.tclUNIT()|Some(tac,dep,n,vars)->lettac=ifdepthenTacticals.tclTHENLIST[tac;rename_hyp[(id,oldid)];Tacticals.tclDOnintro;generalize_dep~with_let:true(mkVaroldid)]elseTacticals.tclTHENLIST[tac;clear[id];Tacticals.tclDOnintro]inifList.is_emptyvarsthentacelseTacticals.tclTHENtac(Tacticals.tclFIRST[revertvars;Tacticals.tclMAP(funid->Tacticals.tclTRY(generalize_dep~with_let:true(mkVarid)))vars])endletcompare_upto_variablessigmaxy=letreccomparexy=if(isVarsigmax||isRelsigmax)&&(isVarsigmay||isRelsigmay)thentrueelsecompare_constrsigmacomparexyincomparexyletspecialize_eqsid=letopenContext.Rel.DeclarationinProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletty=Tacmach.pf_get_hyp_typidglinletevars=ref(Proofview.Goal.sigmagl)inletunifenvevarsc1c2=compare_upto_variables!evarsc1c2&&(matchEvarconv.unify_delayenv!evarsc1c2with|sigma->evars:=sigma;true|exceptionEvarconv.UnableToUnify_->false)inletrecauxin_eqsctxaccty=matchEConstr.kind!evarstywith|Prod(na,t,b)->(matchEConstr.kind!evarstwith|App(eq,[|eqty;x;y|])whenis_lib_refenv!evars"core.eq.type"eq->letc=ifnoccur_between!evars1(List.lengthctx)xthenyelsexinletpt=mkApp(eq,[|eqty;c;c|])inletind=destInd!evarseqinletp=mkApp(mkConstructUi(ind,0),[|eqty;c|])inifunif(push_rel_contextctxenv)evarspttthenauxtruectx(mkApp(acc,[|p|]))(subst1pb)elseacc,in_eqs,ctx,ty|App(heq,[|eqty;x;eqty';y|])whenisRefXenv!evars(Lazy.forcecoq_heq_ref)heq->leteqt,c=ifnoccur_between!evars1(List.lengthctx)xtheneqty',yelseeqty,xinletpt=mkApp(heq,[|eqt;c;eqt;c|])inletind=destInd!evarsheqinletp=mkApp(mkConstructUi(ind,0),[|eqt;c|])inifunif(push_rel_contextctxenv)evarspttthenauxtruectx(mkApp(acc,[|p|]))(subst1pb)elseacc,in_eqs,ctx,ty|_->ifin_eqsthenacc,in_eqs,ctx,tyelseletsigma,e=Evarutil.new_evar(push_rel_contextctxenv)!evarstinevars:=sigma;auxfalse(LocalDef(na,e,t)::ctx)(mkApp(lift1acc,[|mkRel1|]))b)|t->acc,in_eqs,ctx,tyinletacc,worked,ctx,ty=auxfalse[](mkVarid)tyinletctx'=nf_rel_context_evar!evarsctxinletctx''=List.map(function|LocalDef(n,k,t)whenisEvar!evarsk->LocalAssum(n,t)|decl->decl)ctx'inletty'=it_mkProd_or_LetIntyctx''inletacc'=it_mkLambda_or_LetInaccctx''inletty'=Tacred.whd_simplenv!evarsty'andacc'=Tacred.whd_simplenv!evarsacc'inletty'=Evarutil.nf_evar!evarsty'inifworkedthenTacticals.tclTHENFIRST(internal_cuttrueidty')(exact_no_check((* refresh_universes_strict *)acc'))elseletinfo=Exninfo.reify()inTacticals.tclFAIL~info(str"Nothing to do in hypothesis "++Id.printid)endletspecialize_eqsid=Proofview.Goal.enterbeginfungl->letmsg=str"Specialization not allowed on dependent hypotheses"inProofview.tclOR(clear[id])(fun(_,info)->Tacticals.tclZEROMSG~infomsg)>>=fun()->specialize_eqsidendletoccur_relsigmanc=letres=not(noccurnsigmanc)inres(* This function splits the products of the induction scheme [elimt] into four
parts:
- branches, easily detectable (they are not referred by rels in the subterm)
- what was found before branches (acc1) that is: parameters and predicates
- what was found after branches (acc3) that is: args and indarg if any
if there is no branch, we try to fill in acc3 with args/indargs.
We also return the conclusion.
*)letdecompose_paramspred_branch_argssigmaelimt=letopenContext.Rel.Declarationinletreccut_noccurelimtacc2=matchEConstr.kindsigmaelimtwith|Prod(nme,tpe,elimt')->lethd_tpe,_=decompose_appsigma(snd(decompose_prod_declssigmatpe))inifnot(occur_relsigma1elimt')&&isRelsigmahd_tpethencut_noccurelimt'(LocalAssum(nme,tpe)::acc2)elseletacc3,ccl=decompose_prod_declssigmaelimtinacc2,acc3,ccl|App(_,_)|Rel_->acc2,[],elimt|_->error_ind_scheme""inletreccut_occurelimtacc1=matchEConstr.kindsigmaelimtwith|Prod(nme,tpe,c)whenoccur_relsigma1c->cut_occurc(LocalAssum(nme,tpe)::acc1)|Prod(nme,tpe,c)->letacc2,acc3,ccl=cut_noccurelimt[]inacc1,acc2,acc3,ccl|App(_,_)|Rel_->acc1,[],[],elimt|_->error_ind_scheme""inletacc1,acc2,acc3,ccl=cut_occurelimt[]in(* Particular treatment when dealing with a dependent empty type elim scheme:
if there is no branch, then acc1 contains all hyps which is wrong (acc1
should contain parameters and predicate only). This happens for an empty
type (See for example Empty_set_ind, as False would actually be ok). Then
we must find the predicate of the conclusion to separate params_pred from
args. We suppose there is only one predicate here. *)matchacc2with|[]->lethyps,ccl=decompose_prod_declssigmaelimtinlethd_ccl_pred,_=decompose_appsigmacclinbeginmatchEConstr.kindsigmahd_ccl_predwith|Reli->letacc3,acc1=List.chop(i-1)hypsinacc1,[],acc3,ccl|_->error_ind_scheme""end|_->acc1,acc2,acc3,cclletexchange_hd_appsigmasubst_hdt=lethd,args=decompose_appsigmatinmkApp(subst_hd,args)(* Builds an elim_scheme from its type and calling form (const+binding). We
first separate branches. We obtain branches, hyps before (params + preds),
hyps after (args <+ indarg if present>) and conclusion. Then we proceed as
follows:
- separate parameters and predicates in params_preds. For that we build:
forall (x1:Ti_1)(xni:Ti_ni) (HI:I prm1..prmp x1...xni), DUMMY x1...xni HI/farg
^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^
optional opt
Free rels appearing in this term are parameters (branches should not
appear, and the only predicate would have been Qi but we replaced it by
DUMMY). We guess this heuristic catches all params. TODO: generalize to
the case where args are merged with branches (?) and/or where several
predicates are cited in the conclusion.
- finish to fill in the elim_scheme: indarg/farg/args and finally indref. *)letcompute_elim_sigsigmaelimt=letopenContext.Rel.Declarationinletparams_preds,branches,args_indargs,conclusion=decompose_paramspred_branch_argssigmaelimtinletccl=exchange_hd_appsigma(mkVar(Id.of_string"__QI_DUMMY__"))conclusioninletconcl_with_args=it_mkProd_or_LetIncclargs_indargsinletnparams=Int.Set.cardinal(free_relssigmaconcl_with_args)inletpreds,params=List.chop(List.lengthparams_preds-nparams)params_predsin(* A first approximation, further analysis will tweak it *)letres=ref{empty_schemewith(* This fields are ok: *)elimt=elimt;concl=conclusion;predicates=preds;npredicates=List.lengthpreds;branches=branches;nbranches=List.lengthbranches;farg_in_concl=isAppsigmaccl&&isAppsigma(last_argsigmaccl);params=params;nparams=nparams;(* all other fields are unsure at this point. Including these:*)args=args_indargs;nargs=List.lengthargs_indargs;}intry(* Order of tests below is important. Each of them exits if successful. *)(* 1- First see if (f x...) is in the conclusion. *)if!res.farg_in_conclthenbeginres:={!reswithindarg=None;indarg_in_concl=false;farg_in_concl=true};raise_notraceExitend;(* 2- If no args_indargs (=!res.nargs at this point) then no indarg *)ifInt.equal!res.nargs0thenraise_notraceExit;(* 3- Look at last arg: is it the indarg? *)ignore(matchList.hdargs_indargswith|LocalDef(hiname,_,hi)->error_ind_scheme""|LocalAssum(hiname,hi)->lethi_ind,hi_args=decompose_appsigmahiinlethi_is_ind=(* hi est d'un type globalisable *)matchEConstr.kindsigmahi_indwith|Ind(mind,_)->true|Var_->true|Const_->true|Construct_->true|_->falseinlethi_args_enough=(* hi a le bon nbre d'arguments *)Int.equal(Array.lengthhi_args)(List.lengthparams+!res.nargs-1)in(* FIXME: Ces deux tests ne sont pas suffisants. *)ifnot(hi_is_ind&&hi_args_enough)thenraise_notraceExit(* No indarg *)else(* Last arg is the indarg *)res:={!reswithindarg=Some(List.hd!res.args);indarg_in_concl=occur_relsigma1ccl;args=List.tl!res.args;nargs=!res.nargs-1;};raise_notraceExit);raise_notraceExit(* exit anyway *)withExit->(* Ending by computing indref: *)match!res.indargwith|None->!res(* No indref *)|Some(LocalDef_)->error_ind_scheme""|Some(LocalAssum(_,ind))->letindhd,indargs=decompose_appsigmaindintry{!reswithindref=Some(fst(destRefsigmaindhd))}withDestKO->errorCannotFindInductiveArgumentletcompute_scheme_signatureevdschemenames_infoind_type_guess=letopenContext.Rel.Declarationinletf,l=decompose_appevdscheme.conclin(* Vérifier que les arguments de Qi sont bien les xi. *)letcond,check_concl=matchscheme.indargwith|Some(LocalDef_)->errorNotAnInductionSchemeLetIn|None->(* Non standard scheme *)letcondhd=EConstr.eq_constrevdhdind_type_guess&¬scheme.farg_in_conclin(cond,fun__->())|Some(LocalAssum(_,ind))->(* Standard scheme from an inductive type *)letindhd,indargs=decompose_app_listevdindinletcondhd=EConstr.eq_constrevdhdindhdinletcheck_conclis_predp=(* Check again conclusion *)letccl_arg_ok=is_pred(p+scheme.nargs+1)f==IndArginletind_is_ok=List.equal(func1c2->EConstr.eq_constrevdc1c2)(List.lastnscheme.nargsindargs)(Context.Rel.instance_listmkRel0scheme.args)inifnot(ccl_arg_ok&&ind_is_ok)thenerror_ind_scheme"the conclusion of"in(cond,check_concl)inletis_prednc=lethd=fst(decompose_appevdc)inmatchEConstr.kindevdhdwith|Relqwhenn<q&&q<=n+scheme.npredicates->IndArg|_whencondhd->RecArg|_->OtherArginletreccheck_branchpc=matchEConstr.kindevdcwith|Prod(_,t,c)->(is_predpt,true,not(Vars.noccurnevd1c))::check_branch(p+1)c|LetIn(_,_,_,c)->(OtherArg,false,not(Vars.noccurnevd1c))::check_branch(p+1)c|_whenis_predpc==IndArg->[]|_->raise_notraceExitinletrecfind_branchesplbrch=matchlbrchwith|LocalAssum(_,t)::brs->beginmatchcheck_branchptwith|lchck_brch->letn=List.count(fun(b,_,_)->b==RecArg)lchck_brchinletrecvarname,hyprecname,avoid=make_up_namesnscheme.indrefnames_infoinletmap(b,is_assum,dep)={ba_kind=b;ba_assum=is_assum;ba_dep=dep;ba_name=ifb==IndArgthenhyprecnameelserecvarname;}inletnamesign=List.mapmaplchck_brchin(avoid,namesign)::find_branches(p+1)brs|exceptionExit->error_ind_scheme"the branches of"end|LocalDef_::_->error_ind_scheme"the branches of"|[]->check_conclis_predp;[]inArray.of_list(find_branches0(List.revscheme.branches))letcompute_case_signatureenvminddepnames_info=letindref=GlobRef.IndRefmindinletreccheck_branchc=matchConstr.kindcwith|Prod(_,t,c)->lethd,_=Constr.decompose_apptin(* no recursive call in case analysis *)letarg=ifConstr.isRefXindrefhdthenRecArgelseOtherArgin(arg,true,not(CVars.noccurn1c))::check_branchc|LetIn(_,_,_,c)->(OtherArg,false,not(CVars.noccurn1c))::check_branchc|_->[]inlet(mib,mip)=Inductive.lookup_mind_specifenvmindinletfind_branchesk=let(ctx,typ)=mip.mind_nf_lc.(k)inletargctx=List.firstnmip.mind_consnrealdecls.(k)ctxinlet_,args=Constr.decompose_apptypinlet_,indices=Array.chopmib.mind_nparamsargsinletbase=ifdepthenArray.appendindices(Context.Rel.instanceConstr.mkRel0argctx)elseindicesinletbase=Constr.mkApp(Constr.mkProp,base)in(* only used for noccurn *)letlchck_brch=check_branch(Term.it_mkProd_or_LetInbaseargctx)inletn=List.count(fun(b,_,_)->b==RecArg)lchck_brchinletrecvarname,hyprecname,avoid=make_up_namesn(Someindref)names_infoinletmap(b,is_assum,dep)={ba_kind=b;ba_assum=is_assum;ba_dep=dep;ba_name=recvarname;}inletnamesign=List.mapmaplchck_brchin(avoid,namesign)inArray.init(Array.lengthmip.mind_consnames)find_branchesleterror_cannot_recognizeind=user_errPp.(str"Cannot recognize a statement based on "++Nametab.pr_global_envId.Set.empty(IndRefind)++str".")letguess_elim_shapeenvsigmaisrecshyp0=lettmptyp0=Typing.type_of_variableenvhyp0inlet(mind,u),typ=Tacred.reduce_to_atomic_indenvsigmatmptyp0inletis_elim=isrec&¬(is_nonrecenvmind)inletnparams=ifis_elimthenletgr=lookup_eliminatorenvmindsinletsigma,ind=Evd.fresh_globalenvsigmagrinletelimt=Retyping.get_type_ofenvsigmaindinletscheme=compute_elim_sigsigmaelimtinlet()=matchscheme.indrefwith|None->error_cannot_recognizemind|Someref->ifQGlobRef.equalenvref(IndRefmind)then()elseerror_cannot_recognizemindinscheme.nparamselseletmib=Environ.lookup_mind(fstmind)envinmib.mind_nparamsinlethd,args=decompose_app_listsigmatypinlet(params,indices)=List.chopnparamsargsinis_elim,(mind,u),(hd,params,indices)letgiven_elimenvsigmahyp0(elimc,lbindase)=lettmptyp0=Typing.type_of_variableenvhyp0inletind_type_guess,_=decompose_appsigma(snd(decompose_prodsigmatmptyp0))inletsigma,elimt=Typing.type_ofenvsigmaelimcinsigma,(e,elimt),ind_type_guesstypescheme_signature=(Id.Set.t*branch_argumentlist)arraytypeeliminator_source=|CaseOverofId.t*(inductive*EInstance.t)|ElimOverofbool*Id.t*(inductive*EInstance.t)|ElimUsingofId.t*(Evd.econstrwith_bindings*EConstr.types*scheme_signature)|ElimUsingListof(Evd.econstrwith_bindings*EConstr.types*scheme_signature)*Id.tlist*Id.tlist*EConstr.tlistletfind_induction_typeenvsigmaisrecelimhyp0sort=matchelimwith|None->letis_elim,ind,typ=guess_elim_shapeenvsigmaisrecsorthyp0in(* We drop the scheme and elimc/elimt waiting to know if it is dependent, this
needs no update to sigma at this point. *)letelim=ifis_elimthenElimOver(isrec,hyp0,ind)elseCaseOver(hyp0,ind)insigma,typ,elim|Some(elimc,lbindase)->letsigma,elimt=Typing.type_ofenvsigmaelimcinletscheme=compute_elim_sigsigmaelimtinlet()=ifOption.is_emptyscheme.indargthenerrorCannotFindInductiveArgumentinletref=matchscheme.indrefwith|None->error_ind_scheme""|Someref->refinlettmptyp0=Typing.type_of_variableenvhyp0inletindtyp=reduce_to_atomic_refenvsigmareftmptyp0inlethd,args=decompose_app_listsigmaindtypinletindsign=compute_scheme_signaturesigmaschemehyp0hdinlet(params,indices)=List.chopscheme.nparamsargsinsigma,(hd,params,indices),ElimUsing(hyp0,(e,elimt,indsign))letis_functional_inductionelimcgl=letsigma=Tacmach.projectglinletscheme=compute_elim_sigsigma(Tacmach.pf_get_type_ofgl(fstelimc))in(* The test is not safe: with non-functional induction on non-standard
induction scheme, this may fail *)Option.is_emptyscheme.indarg(* Instantiate all meta variables of elimclause using lid, some elts
of lid are parameters (first ones), the other are
arguments. Returns the clause obtained. *)letrecolle_clenviparamsargselimclausegl=letlindmv=Array.of_list(clenv_argumentselimclause)inletk=matchiwithNone->Array.lengthlindmv-List.lengthargs|Somei->iin(* parameters correspond to first elts of lid. *)letclauses_params=List.mapi(funiid->id,lindmv.(i))paramsinletclauses_args=List.mapi(funiid->id,lindmv.(k+i))argsinletclauses=clauses_params@clauses_argsin(* iteration of clenv_instantiate with all infos we have. *)List.fold_right(funeacc->letx,i=einlety=pf_get_hyp_typxglinletelimclause'=clenv_instantiateiacc(mkVarx,y)inelimclause')(List.revclauses)elimclause(* Unification of the goal and the principle applied to meta variables:
(elimc ?i ?j ?k...?l). This solves partly meta variables (and may
produce new ones). Then refine with the resulting term with holes.
*)letinduction_tacwith_evarsparamsindvars(elim,elimt)=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinletclause,bindings,index=matchelimwith|ElimConstantc->leti=index_of_ind_argsigmaelimtin(mkConstUc,elimt),NoBindings,Somei|ElimClause(elimc,lbindelimc)->(elimc,elimt),lbindelimc,Nonein(* elimclause contains this: (elimc ?i ?j ?k...?l) *)letelimclause=make_clenv_bindingenvsigmaclausebindingsin(* elimclause' is built from elimclause by instantiating all args and params. *)letelimclause=recolle_clenvindexparamsindvarselimclauseglinClenv.res_pf~with_evars~flags:(elim_flags())elimclauseendletdestruct_tacwith_evarsindvardep=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletty=Typing.type_of_variableenvindvarinClenv.case_pf~with_evars~dep(mkVarindvar,ty)end(* Apply induction "in place" taking into account dependent
hypotheses from the context, replacing the main hypothesis on which
induction applies with the induction hypotheses *)letapply_induction_in_contextwith_evarsinhypselimindvarsnames=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletconcl=Tacmach.pf_conclglinlethyp0=matchelimwith|ElimUsing(hyp0,_)|ElimOver(_,hyp0,_)|CaseOver(hyp0,_)->Somehyp0|ElimUsingList_->Noneinletstatuslists,lhyp0,toclear,deps,avoid,dep_in_hyps=cook_signhyp0inhypsindvarsenvsigmainlettmpcl=it_mkNamedProd_or_LetInsigmaconcldepsinlets=Retyping.get_sort_family_ofenvsigmatmpclinletdeps_cstr=List.fold_left(funadecl->ifNamedDecl.is_local_assumdeclthen(mkVar(NamedDecl.get_iddecl))::aelsea)[]depsin(* Wait the last moment to guess the eliminator so as to know if we
need a dependent one or not *)let(sigma,isrec,induct_tac,indsign)=matchelimwith|CaseOver(id,(mind,u))->letdep_in_concl=occur_varenvsigmaidconclinletdep=dep_in_hyps||dep_in_conclinletdep=dep||default_case_analysis_dependenceenvmindinletindsign=compute_case_signatureenvminddepidinlettac=destruct_tacwith_evarsiddepinsigma,false,tac,indsign|ElimOver(isrec,id,(mind,u))->letsigma,ind=find_ind_eliminatorenvsigmamindsin(* FIXME: we should store this instead of recomputing it *)letelimt=Retyping.get_type_ofenvsigma(mkConstUind)inletscheme=compute_elim_sigsigmaelimtinletindsign=compute_scheme_signaturesigmaschemeid(mkIndU(mind,u))inlettac=induction_tacwith_evars[][id](ElimConstantind,elimt)insigma,isrec,tac,indsign|ElimUsing(hyp0,(elim,elimt,indsign))->lettac=induction_tacwith_evars[][hyp0](ElimClauseelim,elimt)insigma,(* bugged, should be computed *)true,tac,indsign|ElimUsingList((elim,elimt,indsign),params,realindvars,patts)->lettac=Tacticals.tclTHENLIST[(* pattern to make the predicate appear. *)reduce(Pattern(List.mapinj_with_occurrencespatts))onConcl;(* Induction by "refine (indscheme ?i ?j ?k...)" + resolution of all
possible holes using arguments given by the user (but the
functional one). *)(* FIXME: Tester ca avec un principe dependant et non-dependant *)induction_tacwith_evarsparamsrealindvars(ElimClauseelim,elimt);]insigma,(* bugged, should be computed *)true,tac,indsigninletbranchletsigns=letfba=ba.ba_assuminArray.map(fun(_,l)->List.mapfl)indsigninletnames=compute_induction_namestruebranchletsignsnamesinlet()=Array.iter(check_name_unicityenvtoclear[])namesinlettac=(ifisrecthenTacticals.tclTHENFIRSTnelseTacticals.tclTHENLASTn)(Tacticals.tclTHENLIST[(* Generalize dependent hyps (but not args) *)ifdeps=[]thenProofview.tclUNIT()elseapply_type~typecheck:falsetmpcldeps_cstr;(* side-conditions in elim (resp case) schemes come last (resp first) *)induct_tac;Tacticals.tclMAPexpand_hyptoclear;])(Array.map2(induct_dischargewith_evarslhyp0avoid(re_intro_dependent_hypothesesstatuslists))indsignnames)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)tacendletinduction_with_atomization_of_ind_argisrecwith_evarselimnameshyp0inhyps=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinletsort=Tacticals.elimination_sort_of_goalglinletsigma,ty,elim_info=find_induction_typeenvsigmaisrecelimhyp0sortinletletins,avoid,t=atomize_param_of_indenvsigmatyinletletins=tclMAP(fun(na,c)->letin_tacNone(Namena)cNoneallHypsAndConcl)letinsinTacticals.tclTHENLIST[Proofview.Unsafe.tclEVARSsigma;letins;change_in_hyp~check:falseNone(make_change_argt)(hyp0,InHypTypeOnly);apply_induction_in_contextwith_evarsinhypselim_infoavoidnames]endletmsg_not_right_number_induction_argumentsscheme=str"Not the right number of induction arguments (expected "++pr_enum(funx->x)[ifscheme.farg_in_conclthenstr"the function name"elsemt();ifscheme.nparams!=0thenintscheme.nparams++str(String.pluralscheme.nparams" parameter")elsemt();ifscheme.nargs!=0thenintscheme.nargs++str(String.pluralscheme.nargs" argument")elsemt()]++str")."(* Induction on a list of induction arguments. Analyze the elim
scheme (which is mandatory for multiple ind args), check that all
parameters and arguments are given (mandatory too).
Main differences with induction_from_context is that there is no
main induction argument. On the other hand, all args and params
must be given, so we help a bit the unifier by making the "pattern"
by hand before calling induction_tac *)letinduction_without_atomizationisrecwith_evarselimnameslid=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlethyp0=List.hdlidin(* Check that the elimination scheme has a form similar to the
elimination schemes built by Coq. Schemes may have the standard
form computed from an inductive type OR (feb. 2006) a non standard
form. That is: with no main induction argument and with an optional
extra final argument of the form (f x y ...) in the conclusion. In
the non standard case, naming of generated hypos is slightly
different. *)let(sigma,(elimc,elimt),ind_type_guess)=given_elimenvsigmahyp0eliminletscheme=compute_elim_sigsigmaelimtinletindsign=compute_scheme_signaturesigmaschemehyp0ind_type_guessinletnargs_indarg_farg=scheme.nargs+(ifscheme.farg_in_conclthen1else0)inifnot(Int.equal(List.lengthlid)(scheme.nparams+nargs_indarg_farg))thenletinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(msg_not_right_number_induction_argumentsscheme)elseletindvars,lid_params=List.chopnargs_indarg_farglidin(* terms to patternify we must patternify indarg or farg if present in concl *)letrealindvars=List.rev(ifscheme.farg_in_conclthenList.tlindvarselseindvars)inletlidcstr=List.mapmkVar(List.revindvars)inletparams=List.revlid_paramsinletindvars=(* Temporary hack for compatibility, while waiting for better
analysis of the form of induction schemes: a scheme like
gt_wf_rec was taken as a functional scheme with no parameters,
but by chance, because of the addition of at least hyp0 for
cook_sign, it behaved as if there was a real induction arg. *)ifList.is_emptyindvarsthenId.Set.singleton(List.hdlid_params)elseId.Set.of_listindvarsinletelim=ElimUsingList((elimc,scheme.elimt,indsign),params,realindvars,lidcstr)inapply_induction_in_contextwith_evars[]elimindvarsnamesend(* assume that no occurrences are selected *)letclear_unselected_contextidinhypscls=Proofview.Goal.enterbeginfungl->ifoccur_var(Tacmach.pf_envgl)(Tacmach.projectgl)id(Tacmach.pf_conclgl)&&cls.concl_occs==NoOccurrencesthenerror(MentionConclusionDependentOnid);matchcls.onhypswith|Somehyps->letto_erased=letid'=NamedDecl.get_iddinifId.List.memid'inhypsthen(* if selected, do not erase *)Noneelse(* erase if not selected and dependent on id or selected hyps *)lettestid=occur_var_in_decl(Tacmach.pf_envgl)(Tacmach.projectgl)iddinifList.existstest(id::inhyps)thenSomeid'elseNoneinletids=List.map_filterto_erase(Proofview.Goal.hypsgl)inclearids|None->Proofview.tclUNIT()endletuse_bindingsenvsigmaelimmust_be_closed(c,lbind)typ=lettyp=(* Normalize [typ] until the induction reference becomes plainly visible *)matchelimwith|None->(* w/o an scheme, the term has to be applied at least until
obtaining an inductive type (even though the arity might be
known only by pattern-matching, as in the case of a term of
the form "nat_rect ?A ?o ?s n", with ?A to be inferred by
matching. *)letsign,t=hnf_decompose_prodenvsigmatypinit_mkProdtsign|Some(elimc,_)->(* Otherwise, we compute the induction reference of the scheme
and go looking for that. *)letsigma,elimt=Typing.type_ofenvsigmaelimcinletscheme=compute_elim_sigsigmaelimtinmatchscheme.indrefwith|None->errorCannotFindInductiveArgument|Someindref->Tacred.reduce_to_quantified_ref~allow_failure:trueenvsigmaindreftypinletrecfind_clausetyp=tryletindclause=make_clenv_bindingenvsigma(c,typ)lbindinifmust_be_closed&&occur_meta(clenv_evdindclause)(clenv_valueindclause)thenerrorNeedFullyAppliedArgument;(* We lose the possibility of coercions in with-bindings *)letsigma,term=pose_all_metas_as_evarsenv(clenv_evdindclause)(clenv_valueindclause)inletsigma,typ=pose_all_metas_as_evarsenvsigma(clenv_typeindclause)insigma,term,typwithewhennoncriticale->tryfind_clause(try_red_productenvsigmatyp)withRedelimination->raiseeinfind_clausetypletcheck_expected_typeenvsigma(elimc,bl)elimt=(* Compute the expected template type of the term in case a using
clause is given *)letsign,_=hnf_decompose_prodenvsigmaelimtinletn=List.lengthsigninifn==0thenerrorSchemeDontApply;letsigma,cl=EClause.make_evar_clauseenvsigma~len:(n-1)elimtinletsigma=EClause.solve_evar_clauseenvsigmatrueclblinlet(_,u,_)=destProdsigma(whd_allenvsigmacl.cl_concl)infunt->matchEvarconv.unify_leq_delayenvsigmatuwith|_sigma->true|exceptionEvarconv.UnableToUnify_->falseletcheck_enough_appliedenvsigmaelim=(* A heuristic to decide whether the induction arg is enough applied *)matchelimwith|None->(* No eliminator given *)funu->lett,_=decompose_appsigma(whd_allenvsigmau)inisIndsigmat|Someelimc->letelimt=Retyping.get_type_ofenvsigma(fstelimc)inletscheme=compute_elim_sigsigmaelimtinmatchscheme.indrefwith|None->(* in the absence of information, do not assume it may be
partially applied *)fun_->true|Some_->(* Last argument is supposed to be the induction argument *)check_expected_typeenvsigmaelimcelimtletguard_no_unifiable=Proofview.guard_no_unifiable>>=function|None->Proofview.tclUNIT()|Somel->Proofview.tclENV>>=functionenv->Proofview.tclEVARMAP>>=functionsigma->letinfo=Exninfo.reify()inProofview.tclZERO~info(RefinerError(env,sigma,UnresolvedBindingsl))letpose_induction_arg_thenisrecwith_evars(is_arg_pure_hyp,from_prefix)elimid((pending,(c0,lbind)),(eqname,names))t0inhypsclstac=Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Proofview.Goal.envglinletccl=Proofview.Goal.conclglinletcheck=check_enough_appliedenvsigmaeliminletsigma',c,_=use_bindingsenvsigmaelimfalse(c0,lbind)t0inletabs=AbstractPattern(from_prefix,check,Nameid,(pending,c),cls,false)inlet(id,sign,_,lastlhyp,ccl,res)=make_abstractionenvsigma'cclabsinmatchreswith|None->(* pattern not found *)letwith_eq=Option.map(funeq->(false,mk_eq_nameenvideq))eqnameinletinhyps=ifList.is_emptyinhypstheninhypselseOption.fold_left(funinhyps(_,heq)->heq::inhyps)inhypswith_eqin(* we restart using bindings after having tried type-class
resolution etc. on the term given by the user *)letflags=tactic_infer_flags(with_evars&&(* do not give a success semantics to edestruct on an open term yet *)false)inlet(sigma,c0)=finish_evar_resolution~flagsenvsigma(pending,c0)inlettac=(ifisrecthen(* Historically, induction has side conditions last *)Tacticals.tclTHENFIRSTelse(* and destruct has side conditions first *)Tacticals.tclTHENLAST)(Tacticals.tclTHENLIST[Refine.refine~typecheck:falsebeginfunsigma->letb=notwith_evars&&with_eq!=Noneinletsigma,c,t=use_bindingsenvsigmaelimb(c0,lbind)t0inmkletin_goalenvsigmawith_eqfalse(id,lastlhyp,ccl,c)(Somet)end;ifwith_evarsthenProofview.shelve_unifiableelseguard_no_unifiable;ifis_arg_pure_hypthenProofview.tclEVARMAP>>=funsigma->Tacticals.tclTRY(clear[destVarsigmac0])elseProofview.tclUNIT();ifisrecthenProofview.cycle(-1)elseProofview.tclUNIT()])(tacinhyps)inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma)tac|Some(sigma',c)->(* pattern found *)(* TODO: if ind has predicate parameters, use JMeq instead of eq *)letenv=reset_with_named_contextsignenvinletwith_eq=Option.map(funeq->(false,mk_eq_nameenvideq))eqnameinletinhyps=ifList.is_emptyinhypstheninhypselseOption.fold_left(funinhyps(_,heq)->heq::inhyps)inhypswith_eqinlettac=Tacticals.tclTHENLIST[Refine.refine~typecheck:falsebeginfunsigma->mkletin_goalenvsigmawith_eqtrue(id,lastlhyp,ccl,c)Noneend;(tacinhyps)]inProofview.tclTHEN(Proofview.Unsafe.tclEVARSsigma')tacendlethas_generic_occurrences_but_goalclsidenvsigmaccl=clause_with_generic_context_selectioncls&&(* TODO: whd_evar of goal *)(cls.concl_occs!=NoOccurrences||not(occur_varenvsigmaidccl))letinduction_genclear_flagisrecwith_evarselim((_pending,(c,lbind)),(eqname,names)asarg)cls=letinhyps=matchclswith|Some{onhyps=Somehyps}->List.map(fun((_,id),_)->id)hyps|_->[]inProofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletevd=Proofview.Goal.sigmaglinletccl=Proofview.Goal.conclglinletcls=Option.defaultallHypsAndConclclsinlett=typ_ofenvevdcinletis_arg_pure_hyp=isVarevdc&¬(mem_named_context_val(destVarevdc)(Global.named_context_val()))&&lbind==NoBindings&¬with_evars&&Option.is_emptyeqname&&clear_flag==None&&has_generic_occurrences_but_goalcls(destVarevdc)envevdcclinletenough_applied=check_enough_appliedenvevdelimtinifis_arg_pure_hyp&&enough_appliedthen(* First case: induction on a variable already in an inductive type and
with maximal abstraction over the variable.
This is a situation where the induction argument is a
clearable variable of the goal w/o occurrence selection
and w/o equality kept: no need to generalize *)letid=destVarevdcinTacticals.tclTHEN(clear_unselected_contextidinhypscls)(induction_with_atomization_of_ind_argisrecwith_evarselimnamesidinhyps)else(* Otherwise, we look for the pattern, possibly adding missing arguments and
declaring the induction argument as a new local variable *)letid=(* Type not the right one if partially applied but anyway for internal use*)letavoid=matcheqnamewith|Some{CAst.v=IntroIdentifierid}->Id.Set.singletonid|_->Id.Set.emptyinletx=id_of_name_using_hdcharenvevdtAnonymousinnew_fresh_idavoidxglinletinfo_arg=(is_arg_pure_hyp,notenough_applied)inpose_induction_arg_thenisrecwith_evarsinfo_argelimidargtinhypscls(induction_with_atomization_of_ind_argisrecwith_evarselimnamesid)end(* Induction on a list of arguments. First make induction arguments
atomic (using letins), then do induction. The specificity here is
that all arguments and parameters of the scheme are given
(mandatory for the moment), so we don't need to deal with
parameters of the inductive type as in induction_gen. *)letinduction_gen_lisrecwith_evarselimnameslc=letnewlc=ref[]inletlc=List.map(function|(c,None)->c|(c,Some{CAst.loc;v=eqname})->error?loc(DontKnowWhatToDoWitheqname))lcinletrecatomize_listl=matchlwith|[]->Proofview.tclUNIT()|c::l'->Proofview.tclEVARMAP>>=funsigma->matchEConstr.kindsigmacwith|Varidwhennot(mem_named_context_valid(Global.named_context_val()))&¬with_evars->let()=newlc:=id::!newlcinatomize_listl'|_->Proofview.Goal.enterbeginfungl->letsigma,t=pf_applyTyping.type_ofglcinletx=id_of_name_using_hdchar(Proofview.Goal.envgl)sigmatAnonymousinletid=new_fresh_idId.Set.emptyxglinletnewl'=List.map(funr->replace_termsigmac(mkVarid)r)l'inlet()=newlc:=id::!newlcinTacticals.tclTHENLIST[tclEVARSsigma;letin_tacNone(Nameid)cNoneallHypsAndConcl;atomize_listnewl';]endinTacticals.tclTHENLIST[(atomize_listlc);(Proofview.tclUNIT()>>=fun()->(* ensure newlc has been computed *)induction_without_atomizationisrecwith_evarselimnames!newlc)](* Induction either over a term, over a quantified premisse, or over
several quantified premisses (like with functional induction
principles).
TODO: really unify induction with one and induction with several
args *)letinduction_destructisrecwith_evars(lc,elim)=matchlcwith|[]->assertfalse(* ensured by syntax, but if called inside caml? *)|[c,(eqname,namesasallnames),cls]->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinmatchelimwith|Someelimwhenis_functional_inductionelimgl->(* Standard induction on non-standard induction schemes *)(* will be removable when is_functional_induction will be more clever *)ifnot(Option.is_emptycls)thenerror(UnsupportedInClausetrue);let_,c=force_destruction_argfalseenvsigmacinonInductionArg(fun_clear_flagc->induction_gen_lisrecwith_evarselimnames[with_no_bindingsc,eqname])c|_->(* standard induction *)onOpenInductionArgenvsigma(funclear_flagc->induction_genclear_flagisrecwith_evarselim(c,allnames)cls)cend|_->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinmatchelimwith|None->(* Several arguments, without "using" clause *)(* TODO: Do as if the arguments after the first one were called with *)(* "destruct", but selecting occurrences on the initial copy of *)(* the goal *)let(a,b,cl)=List.hdlcinletl=List.tllcin(* TODO *)Tacticals.tclTHEN(onOpenInductionArgenvsigma(funclear_flaga->induction_genclear_flagisrecwith_evarsNone(a,b)cl)a)(Tacticals.tclMAP(fun(a,b,cl)->Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinonOpenInductionArgenvsigma(funclear_flaga->induction_genclear_flagfalsewith_evarsNone(a,b)cl)aend)l)|Someelim->(* Several induction hyps with induction scheme *)letlc=List.map(on_pi1(func->snd(force_destruction_argfalseenvsigmac)))lcinletnewlc=List.map(fun(x,(eqn,names),cls)->ifcls!=NonethenerrorUnsupportedEqnClause;matchxwith(* FIXME: should we deal with ElimOnIdent? *)|_clear_flag,ElimOnConstrx->ifeqn<>Nonethenerror(UnsupportedInClausefalse);(with_no_bindingsx,names)|_->errorDontKnowWhereToFindArgument)lcin(* Check that "as", if any, is given only on the last argument *)letnames,rest=List.sep_last(List.mapsndnewlc)inifList.exists(funn->not(Option.is_emptyn))restthenerrorMultipleAsAndUsingClauseOnlyList;letnewlc=List.map(fun(x,_)->(x,None))newlcininduction_gen_lisrecwith_evarselimnamesnewlcendletinductionevclrcle=induction_genclrtrueeve((None,(c,NoBindings)),(None,l))Noneletdestructevclrcle=induction_genclrfalseeve((None,(c,NoBindings)),(None,l))None(*
* Eliminations giving the type instead of the proof.
* These tactics use the default elimination constant and
* no substitutions at all.
* May be they should be integrated into Elim ...
*)letelim_typet=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinlet(ind,t)=Tacmach.pf_applyreduce_to_atomic_indgltinletevd,elimc=Tacmach.pf_applyfind_ind_eliminatorgl(fstind)(Tacticals.elimination_sort_of_goalgl)inletelimc=mkConstUelimcinletelimt=Retyping.get_type_ofenvevdelimcinletclause=mk_clenv_fromenvevd(elimc,elimt)inletmv=List.last(clenv_argumentsclause)inletclause'=(* t is inductive, then CUMUL or CONV is irrelevant *)clenv_unify_meta_type~flags:(elim_flags())Conversion.CUMULtmvclauseinProofview.tclTHEN(Proofview.Unsafe.tclEVARSevd)(Clenv.res_pfclause'~flags:(elim_flags())~with_evars:false)endletcase_typet=assert_before_then_genfalse(NamingAvoidId.Set.empty)tbeginfunid->Proofview.Goal.enterbeginfungl->letsigma=Proofview.Goal.sigmaglinletenv=Tacmach.pf_envglinlet((ind,u),t)=reduce_to_atomic_indenvsigmatinletdep=default_case_analysis_dependenceenvindintclTHENLIST[Clenv.case_pf~with_evars:false~dep(mkVarid,t);clear[id];]endendletexfalso=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglinlet(sigma,f)=Evd.fresh_globalenvsigma(Coqlib.lib_ref"core.False.type")inProofview.Unsafe.tclEVARSsigma<*>elim_typefend(************************************************)(* Tactics related with logic connectives *)(************************************************)(* Reflexivity tactics *)let(forward_setoid_reflexivity,setoid_reflexivity)=Hook.make()letmaybe_betadeltaiota_conclallowredgl=letconcl=Tacmach.pf_conclglinletsigma=Tacmach.projectglinifnotallowredthenconclelseletenv=Proofview.Goal.envglinwhd_allenvsigmaconclletreflexivity_redallowred=Proofview.Goal.enterbeginfungl->(* PL: usual reflexivity don't perform any reduction when searching
for an equality, but we may need to do some when called back from
inside setoid_reflexivity (see Optimize cases in setoid_replace.ml). *)letenv=Tacmach.pf_envglinletsigma=Tacmach.projectglinletconcl=maybe_betadeltaiota_conclallowredglinmatchmatch_with_equality_typeenvsigmaconclwith|None->letinfo=Exninfo.reify()inProofview.tclZERO~infoNoEquationFound|Some_->one_constructor1NoBindingsendletreflexivity=Proofview.tclORELSE(reflexivity_redfalse)beginfunction(e,info)->matchewith|NoEquationFound->Hook.getforward_setoid_reflexivity|e->Proofview.tclZERO~infoeendletintros_reflexivity=(Tacticals.tclTHENintrosreflexivity)(* Symmetry tactics *)(* This tactic first tries to apply a constant named sym_eq, where eq
is the name of the equality predicate. If this constant is not
defined and the conclusion is a=b, it solves the goal doing (Cut
b=a;Intro H;Case H;Constructor 1) *)let(forward_setoid_symmetry,setoid_symmetry)=Hook.make()(* This is probably not very useful any longer *)letprove_symmetryhdcncleq_kind=letsymc=matcheq_kindwith|MonomorphicLeibnizEq(c1,c2)->mkApp(hdcncl,[|c2;c1|])|PolymorphicLeibnizEq(typ,c1,c2)->mkApp(hdcncl,[|typ;c2;c1|])|HeterogenousEq(t1,c1,t2,c2)->mkApp(hdcncl,[|t2;c2;t1;c1|])inTacticals.tclTHENFIRST(cutsymc)(Tacticals.tclTHENLIST[intro;Tacticals.onLastHypsimplest_case;one_constructor1NoBindings])letmatch_with_equationc=Proofview.tclEVARMAP>>=funsigma->Proofview.tclENV>>=funenv->tryletres=match_with_equationenvsigmacinProofview.tclUNITreswithNoEquationFoundasexn->let_,info=Exninfo.captureexninProofview.tclZERO~infoNoEquationFoundletsymmetry_redallowred=Proofview.Goal.enterbeginfungl->(* PL: usual symmetry don't perform any reduction when searching
for an equality, but we may need to do some when called back from
inside setoid_reflexivity (see Optimize cases in setoid_replace.ml). *)letconcl=maybe_betadeltaiota_conclallowredglinmatch_with_equationconcl>>=funwith_eqn->matchwith_eqnwith|Someeq_data,_,_->Tacticals.tclTHEN(convert_concl~cast:false~check:falseconclDEFAULTcast)(Tacticals.pf_constr_of_globaleq_data.sym>>=apply)|None,eq,eq_kind->prove_symmetryeqeq_kindendletsymmetry=Proofview.tclORELSE(symmetry_redfalse)beginfunction(e,info)->matchewith|NoEquationFound->Hook.getforward_setoid_symmetry|e->Proofview.tclZERO~infoeendlet(forward_setoid_symmetry_in,setoid_symmetry_in)=Hook.make()letsymmetry_inid=Proofview.Goal.enterbeginfungl->letsigma,ctype=Tacmach.pf_type_ofgl(mkVarid)inletsign,t=decompose_prod_declssigmactypeintclEVARSTHENsigma(Proofview.tclORELSEbeginmatch_with_equationt>>=fun(_,hdcncl,eq)->letsymccl=matcheqwith|MonomorphicLeibnizEq(c1,c2)->mkApp(hdcncl,[|c2;c1|])|PolymorphicLeibnizEq(typ,c1,c2)->mkApp(hdcncl,[|typ;c2;c1|])|HeterogenousEq(t1,c1,t2,c2)->mkApp(hdcncl,[|t2;c2;t1;c1|])inTacticals.tclTHENS(cut(EConstr.it_mkProd_or_LetInsymcclsign))[intro_replacingid;Tacticals.tclTHENLIST[intros;symmetry;apply(mkVarid);assumption]]endbeginfunction(e,info)->matchewith|NoEquationFound->Hook.getforward_setoid_symmetry_inid|e->Proofview.tclZERO~infoeend)endletintros_symmetry=Tacticals.onClause(function|None->Tacticals.tclTHENintrossymmetry|Someid->symmetry_inid)(* Transitivity tactics *)(* This tactic first tries to apply a constant named eq_trans, where eq
is the name of the equality predicate. If this constant is not
defined and the conclusion is a=b, it solves the goal doing
Cut x1=x2;
[Cut x2=x3; [Intros e1 e2; Case e2;Assumption
| Idtac]
| Idtac]
--Eduardo (19/8/97)
*)let(forward_setoid_transitivity,setoid_transitivity)=Hook.make()(* This is probably not very useful any longer *)letprove_transitivityhdcncleq_kindt=Proofview.Goal.enterbeginfungl->leteq1,eq2=matcheq_kindwith|MonomorphicLeibnizEq(c1,c2)->mkApp(hdcncl,[|c1;t|]),mkApp(hdcncl,[|t;c2|])|PolymorphicLeibnizEq(typ,c1,c2)->mkApp(hdcncl,[|typ;c1;t|]),mkApp(hdcncl,[|typ;t;c2|])|HeterogenousEq(typ1,c1,typ2,c2)->letenv=Proofview.Goal.envglinletsigma=Tacmach.projectglinlettypt=Retyping.get_type_ofenvsigmatinmkApp(hdcncl,[|typ1;c1;typt;t|]),mkApp(hdcncl,[|typt;t;typ2;c2|])inTacticals.tclTHENFIRST(cuteq2)(Tacticals.tclTHENFIRST(cuteq1)(Tacticals.tclTHENLIST[Tacticals.tclDO2intro;Tacticals.onLastHypsimplest_case;assumption]))endlettransitivity_redallowredt=Proofview.Goal.enterbeginfungl->(* PL: usual transitivity don't perform any reduction when searching
for an equality, but we may need to do some when called back from
inside setoid_reflexivity (see Optimize cases in setoid_replace.ml). *)letconcl=maybe_betadeltaiota_conclallowredglinmatch_with_equationconcl>>=funwith_eqn->matchwith_eqnwith|Someeq_data,_,_->Tacticals.tclTHEN(convert_concl~cast:false~check:falseconclDEFAULTcast)(matchtwith|None->Tacticals.pf_constr_of_globaleq_data.trans>>=eapply|Somet->Tacticals.pf_constr_of_globaleq_data.trans>>=funtrans->apply_list[trans;t])|None,eq,eq_kind->matchtwith|None->letinfo=Exninfo.reify()inTacticals.tclZEROMSG~info(str"etransitivity not supported for this relation.")|Somet->prove_transitivityeqeq_kindtendlettransitivity_gent=Proofview.tclORELSE(transitivity_redfalset)beginfunction(e,info)->matchewith|NoEquationFound->Hook.getforward_setoid_transitivityt|e->Proofview.tclZERO~infoeendletetransitivity=transitivity_genNonelettransitivityt=transitivity_gen(Somet)letintros_transitivityn=Tacticals.tclTHENintros(transitivity_genn)letconstr_eq~strictxy=letfail~info=Tacticals.tclFAIL~info(str"Not equal")inletfail_universes~info=Tacticals.tclFAIL~info(str"Not equal (due to universes)")inProofview.Goal.enterbeginfungl->letenv=Tacmach.pf_envglinletevd=Tacmach.projectglinmatchEConstr.eq_constr_universesenvevdxywith|Somecsts->ifstrictthenifUnivProblem.Set.check(Evd.universesevd)cststhenProofview.tclUNIT()elseletinfo=Exninfo.reify()infail_universes~infoelseletcsts=UnivProblem.Set.forcecstsinbeginmatchEvd.add_universe_constraintsevdcstswith|evd->Proofview.Unsafe.tclEVARSevd|exception(UGraph.UniverseInconsistency_ase)->let_,info=Exninfo.captureeinfail_universes~infoend|None->letinfo=Exninfo.reify()infail~infoendletunify?(state=TransparentState.full)xy=Proofview.Goal.enterbeginfungl->letenv=Proofview.Goal.envglinletsigma=Proofview.Goal.sigmaglintryletcore_flags={(default_unify_flags()).core_unify_flagswithmodulo_delta=state;modulo_conv_on_closed_terms=Somestate}in(* What to do on merge and subterm flags?? *)letflags={(default_unify_flags())withcore_unify_flags=core_flags;merge_unify_flags=core_flags;subterm_unify_flags={core_flagswithmodulo_delta=TransparentState.empty}}inletsigma=w_unify(Tacmach.pf_envgl)sigmaConversion.CONV~flagsxyinProofview.Unsafe.tclEVARSsigmawithewhennoncriticale->lete,info=Exninfo.captureeinProofview.tclZERO~info(PretypeError(env,sigma,CannotUnify(x,y,None)))end(** [tclWRAPFINALLY before tac finally] runs [before] before each
entry-point of [tac] and passes the result of [before] to
[finally], which is then run at each exit-point of [tac],
regardless of whether it succeeds or fails. Said another way, if
[tac] succeeds, then it behaves as [before >>= fun v -> tac >>= fun
ret -> finally v <*> tclUNIT ret]; otherwise, if [tac] fails with
[e], it behaves as [before >>= fun v -> finally v <*> tclZERO
e]. Note that if [tac] succeeds [n] times before finally failing,
[before] and [finally] are both run [n+1] times (once around each
succuess, and once more around the final failure). *)(* We should probably export this somewhere, but it's not clear
where. As per
https://github.com/coq/coq/pull/12197#discussion_r418480525 and
https://gitter.im/coq/coq?at=5ead5c35347bd616304e83ef, we don't
export it from Proofview, because it seems somehow not primitive
enough. We don't export it from this file because it is more of a
tactical than a tactic. But we also don't export it from Tacticals
because all of the non-New tacticals there operate on `tactic`, not
`Proofview.tactic`, and all of the `New` tacticals that deal with
multi-success things are focussing, i.e., apply their arguments on
each goal separately (and it even says so in the comment on `New`),
whereas it's important that `tclWRAPFINALLY` doesn't introduce
extra focussing. *)letrectclWRAPFINALLYbeforetacfinally=letopenProofviewinletopenProofview.Notationsinbefore>>=funv->tclCASEtac>>=function|Fail(e,info)->finallyv>>=fun()->tclZERO~infoe|Next(ret,tac')->tclOR(finallyv>>=fun()->tclUNITret)(fune->tclWRAPFINALLYbefore(tac'e)finally)letwith_set_strategylvl_qlk=letglob_keyr=matchrwith|GlobRef.ConstRefsp->ConstKeysp|GlobRef.VarRefid->VarKeyid|_->user_errPp.(str"cannot set an inductive type or a constructor as transparent")inletkl=List.concat(List.map(fun(lvl,ql)->List.map(funq->(lvl,glob_keyq))ql)lvl_ql)intclWRAPFINALLY(Proofview.tclENV>>=funenv->letorig_kl=List.map(fun(_lvl,k)->(Conv_oracle.get_strategy(Environ.oracleenv)k,k))klin(* Because the global env might be desynchronized from the
proof-local env, we need to update the global env to have this
tactic play nicely with abstract.
TODO: When abstract no longer depends on Global, delete this
let orig_kl_global = ... in *)letorig_kl_global=List.map(fun(_lvl,k)->(Conv_oracle.get_strategy(Environ.oracle(Global.env()))k,k))klinletenv=List.fold_left(funenv(lvl,k)->Environ.set_oracleenv(Conv_oracle.set_strategy(Environ.oracleenv)klvl))envklinProofview.Unsafe.tclSETENVenv<*>(* TODO: When abstract no longer depends on Global, remove this
[Proofview.tclLIFT] block *)Proofview.tclLIFT(Proofview.NonLogical.make(fun()->List.iter(fun(lvl,k)->Global.set_strategyklvl)kl))<*>Proofview.tclUNIT(orig_kl,orig_kl_global))k(fun(orig_kl,orig_kl_global)->(* TODO: When abstract no longer depends on Global, remove this
[Proofview.tclLIFT] block *)Proofview.tclLIFT(Proofview.NonLogical.make(fun()->List.iter(fun(lvl,k)->Global.set_strategyklvl)orig_kl_global))<*>Proofview.tclENV>>=funenv->letenv=List.fold_left(funenv(lvl,k)->Environ.set_oracleenv(Conv_oracle.set_strategy(Environ.oracleenv)klvl))envorig_klinProofview.Unsafe.tclSETENVenv)moduleSimple=struct(** Simplified version of some of the above tactics *)letintrox=intro_move(Somex)MoveLastletapplyc=apply_with_bindings_genfalsefalse[None,(CAst.make(c,NoBindings))]leteapplyc=apply_with_bindings_genfalsetrue[None,(CAst.make(c,NoBindings))]letelimc=elimfalseNone(c,NoBindings)Noneletcasec=general_case_analysisfalseNone(c,NoBindings)letapply_inidc=apply_infalsefalseid[None,(CAst.make(c,NoBindings))]Noneend(** Tacticals defined directly in term of Proofview *)letreduce_after_refine=(* For backward compatibility reasons, we do not contract let-ins, but we unfold them. *)letredfunenvt=letopenCClosureinletflags=RedFlags.red_add_transparentallnoletTransparentState.emptyinclos_norm_flagsflagsenvtinreduct_in_concl~cast:false~check:false(redfun,DEFAULTcast)letrefine~typecheckc=Refine.refine~typecheckc<*>reduce_after_refine