123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814(************************************************************************)(* * 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) *)(************************************************************************)(* enable in case of stm problems *)(* let stm_debug () = CDebug.(get_flag misc) *)letstm_debug=reffalseletstm_pr_errs=Format.eprintf"%s] %s\n%!"(Spawned.process_id())sletstm_pp_errpp=Format.eprintf"%s] @[%a@]\n%!"(Spawned.process_id())Pp.pp_withppletstm_prerr_endlines=if!stm_debugthenbeginstm_pr_err(s())endelse()letstm_pperr_endlines=if!stm_debugthenbeginstm_pp_err(s())endelse()letstm_prerr_debugs=ifCDebug.(get_flagmisc)thenbeginstm_pr_err(s())endelse()openPpopenCErrorsopenNamesopenFeedbackopenVernacexpropenVernacextendmodulePG_compat=Vernacstate.Declare[@@ocaml.warning"-3"]letis_vtkeep=functionVtKeep_->true|_->falseletget_vtkeep=functionVtKeepx->x|_->assertfalsemoduleAsyncOpts=structtypecache=Forcetypeasync_proofs=APoff|APonLazy|APontypetac_error_filter=FNone|FOnlyofstringlist|FAlltypestm_opt={async_proofs_n_workers:int;async_proofs_n_tacworkers:int;async_proofs_cache:cacheoption;async_proofs_mode:async_proofs;async_proofs_private_flags:stringoption;async_proofs_never_reopen_branch:bool;async_proofs_tac_error_resilience:tac_error_filter;async_proofs_cmd_error_resilience:bool;async_proofs_delegation_threshold:float;async_proofs_worker_priority:CoqworkmgrApi.priority;}letdefault_opts={async_proofs_n_workers=1;async_proofs_n_tacworkers=2;async_proofs_cache=None;async_proofs_mode=APoff;async_proofs_private_flags=None;async_proofs_never_reopen_branch=false;async_proofs_tac_error_resilience=FOnly["curly"];async_proofs_cmd_error_resilience=true;async_proofs_delegation_threshold=0.03;async_proofs_worker_priority=CoqworkmgrApi.Low;}letcur_opt:stm_optoptionref=refNoneletset_cur_opto=assert(Option.is_empty!cur_opt);cur_opt:=Someoletcur_opt()=match!cur_optwith|Someo->o|None->anomalyPp.(str"Incorrect stm init: accessing options before they are set.")endopenAsyncOptsletasync_proofs_is_masteropt=opt.async_proofs_mode=APon&&!Flags.async_proofs_worker_id="master"letexecution_error?locstate_idmsg=feedback~id:state_id(Message(Error,loc,msg))moduleHooks=structletstate_computed=ref(fun~doc:_state_id~in_cache->feedback~id:state_idProcessed)letstate_ready=ref(fun~doc:_state_id->())letforward_feedback=letm=Mutex.create()inref(function|{doc_id=did;span_id=id;route;contents}->CThread.with_lockm~scope:(fun()->feedback~did~id~routecontents))letunreachable_state=ref(fun~doc:___->())letdocument_add=ref(fun__->())letdocument_edit=ref(fun_->())letsentence_exec=ref(fun_->())endletasync_proofs_workers_extra_env=ref[||]typeaast={verbose:bool;indentation:int;strlen:int;mutableexpr:vernac_control;(* mutable: Proof using hinted by aux file *)}letpr_ast{expr;indentation}=Pp.(intindentation++str" "++Ppvernac.pr_vernacexpr)(* Commands piercing opaque (probably should be using the vernactypes system instead) *)letmay_pierce_opaque=function|VernacSynPure(VernacPrint_)->true|VernacSynterp(VernacExtend({ext_plugin="coq-core.plugins.extraction"},_))->true|_->falsetypedepth=inttype'branchbranch_type_gen=|Master|Proofofdepth|EditofStateid.t*Stateid.t*Vernacextend.vernac_qed_type*'branchmoduleKind=structtype'at='abranch_type_genletmaster=MasterendmoduleVcs_=Vcs.Make(Stateid.Self)(Kind)typefuture_proof=Declare.Proof.closed_proof_outputoptionFuture.computationtypebranch_type=Vcs_.Branch.tKind.t(* TODO 8.7 : split commands and tactics, since this type is too messy now *)typecqueue=MainQueue|SkipQueuetypecmd_t={ctac:bool;(* is a tactic *)ceff:bool;(* is a side-effecting command in the middle of a proof *)cast:aast;cids:Names.Id.tlist;cblock:proof_block_nameoption;cqueue:cqueue;cancel_switch:AsyncTaskQueue.cancel_switch;}typefork_t=aast*Vcs_.Branch.t*opacity_guarantee*Names.Id.tlisttypeqed_t={qast:aast;keep:vernac_qed_type;mutablefproof:(future_proofoption*AsyncTaskQueue.cancel_switch)option;brname:Vcs_.Branch.t;brinfo:Vcs_.branch_info}typeseff_t=ReplayCommandofaast|CherryPickEnvtypealias_t=Stateid.t*aasttypetransaction=|Cmdofcmd_t|Forkoffork_t|Qedofqed_t|Sideffofseff_t|Aliasofalias_t|Nooptypestep=|SCmdofcmd_t|SForkoffork_t*Stateid.toption|SQedofqed_t*Stateid.t|SSideffofseff_t*Stateid.t|SAliasofalias_ttypevisit={step:step;next:Stateid.t}letmkTransTaccastcblockcqueue=Cmd{ctac=true;cast;cblock;cqueue;cids=[];ceff=false;cancel_switch=reffalse}letmkTransCmdcastcidsceffcqueue=Cmd{ctac=false;cast;cblock=None;cqueue;cids;ceff;cancel_switch=reffalse}typecached_state=|EmptyState|ParsingStateofPcoq.frozen_t|FullStateofVernacstate.t|ErrorStateofPcoq.frozen_toption*Exninfo.iexntypebranch=Vcs_.Branch.t*Vcs_.branch_infotypebackup={mine:branch;others:branchlist}moduleDynBlockData:Dyn.S=Dyn.Make()(* Clusters of nodes implemented as Dag properties. While Dag and Vcs impose
* no constraint on properties, here we impose boxes to be non overlapping.
* Such invariant makes sense for the current kinds of boxes (proof blocks and
* entire proofs) but may make no sense and dropped/refined in the future.
* Such invariant is useful to detect broken proof block detection code *)typebox=|ProofTaskofpt|ProofBlockofstatic_block_declaration*proof_block_nameandpt={(* TODO: inline records in OCaml 4.03 *)lemma:Stateid.t;qed:Stateid.t;}andstatic_block_declaration={block_start:Stateid.t;block_stop:Stateid.t;dynamic_switch:Stateid.t;carry_on_data:DynBlockData.t;}(* Functions that work on a Vcs with a specific branch type *)moduleVcs_aux:sigvalproof_nesting:('t,'i,'c)Vcs_.t->intvalfind_proof_at_depth:('t,'i,'c)Vcs_.t->int->Vcs_.Branch.t*Vcs_.branch_infoexceptionExpiredvalvisit:(transaction,'i,'c)Vcs_.t->Vcs_.Dag.node->visitend=struct(* {{{ *)letproof_nestingvcs=List.fold_leftmax0(CList.map_filter(function|{Vcs_.kind=Proofn}->Somen|{Vcs_.kind=Edit_}->Some1|_->None)(List.map(Vcs_.get_branchvcs)(Vcs_.branchesvcs)))letfind_proof_at_depthvcspl=tryList.find(function|_,{Vcs_.kind=Proofn}->Int.equalnpl|_,{Vcs_.kind=Edit_}->anomaly(Pp.str"find_proof_at_depth")|_->false)(List.map(funh->h,Vcs_.get_branchvcsh)(Vcs_.branchesvcs))withNot_found->failwith"find_proof_at_depth"exceptionExpiredletvisitvcsid=ifStateid.equalidStateid.initialthenanomaly(Pp.str"Visiting the initial state id.")elseifStateid.equalidStateid.dummythenanomaly(Pp.str"Visiting the dummy state id.")elsetrymatchVcs_.Dag.from_node(Vcs_.dagvcs)idwith|[n,Cmdx]->{step=SCmdx;next=n}|[n,Aliasx]->{step=SAliasx;next=n}|[n,Forkx]->{step=SFork(x,None);next=n}|[n,Forkx;p,Noop]->{step=SFork(x,Somep);next=n}|[p,Noop;n,Forkx]->{step=SFork(x,Somep);next=n}|[n,Qedx;p,Noop]|[p,Noop;n,Qedx]->{step=SQed(x,p);next=n}|[n,SideffCherryPickEnv;p,Noop]|[p,Noop;n,SideffCherryPickEnv]->{step=SSideff(CherryPickEnv,p);next=n}|[n,Sideff(ReplayCommandx);p,Noop]|[p,Noop;n,Sideff(ReplayCommandx)]->{step=SSideff(ReplayCommandx,p);next=n}|[n,Sideff(ReplayCommandx)]->{step=SSideff(ReplayCommandx,Stateid.dummy);next=n}|_->anomaly(Pp.str("Malformed VCS at node "^Stateid.to_stringid^"."))withNot_found->raiseExpiredend(* }}} *)(*************************** THE DOCUMENT *************************************)(******************************************************************************)(* The main document type associated to a VCS *)typestm_doc_type=|VoDocofstring|VosDocofstring|InteractiveofCoqargs.top(* Dummy until we land the functional interp patch + fixed start_library *)typedoc=intletdummy_doc:doc=0(* Imperative wrap around VCS to obtain _the_ VCS that is the
* representation of the document Coq is currently processing *)moduleVCS:sigexceptionExpiredmoduleBranch:(moduletypeofVcs_.Branchwithtypet=Vcs_.Branch.t)typeid=Stateid.ttypebranch_info=Vcs_.branch_info={kind:branch_type;root:id;pos:id;}typevcs=(transaction,state_info,box)Vcs_.tandstate_info={(* TODO: Make this record private to VCS *)mutablen_reached:int;(* debug cache: how many times was computed *)mutablen_goals:int;(* open goals: indentation *)mutablestate:cached_state;(* state value *)mutableproof_mode:Pvernac.proof_modeoption;mutablevcs_backup:vcsoption*backupoption;}valinit:stm_doc_type->id->Pcoq.frozen_t->doc(* val get_type : unit -> stm_doc_type *)valset_ldir:Names.DirPath.t->unitvalget_ldir:unit->Names.DirPath.tvalis_interactive:unit->boolvalis_vos_doc:unit->boolvalcurrent_branch:unit->Branch.tvalcheckout:Branch.t->unitvalbranches:unit->Branch.tlistvalget_branch:Branch.t->branch_infovalget_branch_pos:Branch.t->idvalnew_node:?id:Stateid.t->Pvernac.proof_modeoption->unit->idvalmerge:id->ours:transaction->?into:Branch.t->Branch.t->unitvalrewrite_merge:id->ours:transaction->at:id->Branch.t->unitvaldelete_branch:Branch.t->unitvalcommit:id->transaction->unitvalmk_branch_name:aast->Branch.tvaledit_branch:Branch.tvalbranch:?root:id->?pos:id->Branch.t->branch_type->unitvalreset_branch:Branch.t->id->unitvalreachable:id->Stateid.Set.tvalcur_tip:unit->idvalget_info:id->state_infovalreached:id->unitvalgoals:id->int->unitvalset_state:id->cached_state->unitvalget_state:id->cached_statevalset_parsing_state:id->Pcoq.frozen_t->unitvalget_parsing_state:id->Pcoq.frozen_toptionvalget_proof_mode:id->Pvernac.proof_modeoption(* cuts from start -> stop, raising Expired if some nodes are not there *)valslice:block_start:id->block_stop:id->vcsvalnodes_in_slice:block_start:id->block_stop:id->Stateid.tlistvalcreate_proof_task_box:idlist->qed:id->block_start:id->unitvalcreate_proof_block:static_block_declaration->string->unitvalbox_of:id->boxlistvaldelete_boxes_of:id->unitvalproof_task_box_of:id->ptoptionvalproof_nesting:unit->intvalcheckout_shallowest_proof_branch:unit->unitvalpropagate_sideff:action:seff_t->Stateid.tlistvalpropagate_qed:unit->unitvalgc:unit->unitvalvisit:id->visitvalprint:?now:bool->unit->unitvalbackup:unit->vcsvalrestore:vcs->unitend=struct(* {{{ *)includeVcs_exceptionExpired=Vcs_aux.ExpiredopenPrintftypevcs=(transaction,state_info,box)tandstate_info={(* TODO: Make this record private to VCS *)mutablen_reached:int;(* debug cache: how many times was computed *)mutablen_goals:int;(* open goals: indentation *)mutablestate:cached_state;(* state value *)mutableproof_mode:Pvernac.proof_modeoption;mutablevcs_backup:vcsoption*backupoption;}letdefault_infoproof_mode={n_reached=0;n_goals=0;state=EmptyState;proof_mode;vcs_backup=(None,None);}letprint_dagvcs()=(* Due to threading, be wary that this will be called from the
toplevel with we_are_parsing set to true, as indeed, the
toplevel is waiting for input . What a race! XD
In case you are hitting the race enable stm_debug.
*)if!stm_debugthenFlags.in_synterp_phase:=false;letfname="stm_"^Str.global_replace(Str.regexp" ")"_"(Spawned.process_id())inletstring_of_transaction=function|Cmd{cast=t}|Fork(t,_,_,_)->(tryPp.string_of_ppcmds(pr_astt)withewhenCErrors.noncriticale->"ERR")|Sideff(ReplayCommandt)->sprintf"Sideff(%s)"(tryPp.string_of_ppcmds(pr_astt)withewhenCErrors.noncriticale->"ERR")|SideffCherryPickEnv->"EnvChange"|Noop->" "|Alias(id,_)->sprintf"Alias(%s)"(Stateid.to_stringid)|Qed{qast}->Pp.string_of_ppcmds(pr_astqast)inletis_greenid=matchget_infovcsidwith|Some{state=FullState_}->true|_->falseinletis_redid=matchget_infovcsidwith|Some{state=ErrorState_}->true|_->falseinlethead=current_branchvcsinletheads=List.map(funx->x,(get_branchvcsx).pos)(branchesvcs)inletgraph=dagvcsinletquotes=Str.global_replace(Str.regexp"\n")"<BR/>"(Str.global_replace(Str.regexp"<")"<"(Str.global_replace(Str.regexp">")">"(Str.global_replace(Str.regexp"\"")"""(Str.global_replace(Str.regexp"&")"&"(String.subs0(min(String.lengths)20))))))inletfname_dot,fname_ps=letf="/tmp/"^Filename.basenamefnameinf^".dot",f^".pdf"inletnodeid="s"^Stateid.to_stringidinletedgetr=sprintf"<<FONT POINT-SIZE=\"12\" FACE=\"sans\">%s</FONT>>"(quote(string_of_transactiontr))inletnode_infoid=matchget_infovcsidwith|None->""|Someinfo->sprintf"<<FONT POINT-SIZE=\"12\">%s</FONT>"(Stateid.to_stringid)^sprintf" <FONT POINT-SIZE=\"11\">r:%d g:%d</FONT>>"info.n_reachedinfo.n_goalsinletcolorid=ifis_redidthen"red"elseifis_greenidthen"green"else"white"inletnodefmtocid=fprintfoc"%s [label=%s,style=filled,fillcolor=%s];\n"(nodeid)(node_infoid)(colorid)inletids=refStateid.Set.emptyinletboxes=ref[]in(* Fill in *)Dag.itergraph(funfrom__l->ids:=Stateid.Set.addfrom!ids;List.iter(funbox->boxes:=box::!boxes)(Dag.property_ofgraphfrom);List.iter(fun(dest,_)->ids:=Stateid.Set.adddest!ids;List.iter(funbox->boxes:=box::!boxes)(Dag.property_ofgraphdest))l);boxes:=CList.sort_uniquizeDag.Property.compare!boxes;letoc=open_outfname_dotinoutput_stringoc"digraph states {\n";Dag.itergraph(funfromcf_l->List.iter(fun(dest,trans)->fprintfoc"%s -> %s [xlabel=%s,labelfloat=true];\n"(nodefrom)(nodedest)(edgetrans))l);letcontainsb1b2=Stateid.Set.subset(Dag.Property.having_itb2)(Dag.Property.having_itb1)inletsame_box=Dag.Property.equalinletouterboxesboxes=List.filter(funb->not(List.exists(funb1->not(same_boxb1b)&&containsb1b)boxes))boxesinletrecrec_printb=boxes:=CList.removesame_boxb!boxes;letsub_boxes=List.filter(containsb)(outerboxes!boxes)infprintfoc"subgraph cluster_%s {\n"(Dag.Property.to_stringb);List.iterrec_printsub_boxes;Stateid.Set.iter(funid->ifStateid.Set.memid!idsthenbeginids:=Stateid.Set.removeid!ids;nodefmtocidend)(Dag.Property.having_itb);matchDag.Property.databwith|ProofBlock({dynamic_switch=id},lbl)->fprintfoc"label=\"%s (test:%s)\";\n"lbl(Stateid.to_stringid);fprintfoc"color=red; }\n"|ProofTask_->fprintfoc"color=blue; }\n"inList.iterrec_print(outerboxes!boxes);Stateid.Set.iter(nodefmtoc)!ids;List.iteri(funi(b,id)->letshape=ifBranch.equalheadbthen"box3d"else"box"infprintfoc"b%d -> %s;\n"i(nodeid);fprintfoc"b%d [shape=%s,label=\"%s\"];\n"ishape(Branch.to_stringb);)heads;output_stringoc"}\n";close_outoc;ignore(Sys.command("dot -Tpdf -Gcharset=latin1 "^fname_dot^" -o"^fname_ps))letvcs:vcsref=ref(emptyStateid.dummy)letdoc_type=ref(Interactive(Coqargs.TopLogical(Names.DirPath.make[])))letldir=refNames.DirPath.emptyletinitdtidps=doc_type:=dt;vcs:=emptyid;letinfo={(default_infoNone)withstate=ParsingStateps}invcs:=set_info!vcsidinfo;dummy_docletset_ldirld=ldir:=ldletget_ldir()=!ldir(* let get_type () = !doc_type *)letis_interactive()=match!doc_typewith|Interactive_->true|_->falseletis_vos_doc()=match!doc_typewith|VosDoc_->true|_->falseletcurrent_branch()=current_branch!vcsletcheckouthead=vcs:=checkout!vcsheadletbranches()=branches!vcsletget_branchhead=get_branch!vcsheadletget_branch_poshead=(get_branchhead).posletnew_node?(id=Stateid.fresh())proof_mode()=assert(Vcs_.get_info!vcsid=None);vcs:=set_info!vcsid(default_infoproof_mode);idletmergeid~ours?intobranch=vcs:=merge!vcsid~ours~theirs:Noop?intobranchletdelete_branchbranch=vcs:=delete_branch!vcsbranchletreset_branchbranchid=vcs:=reset_branch!vcsbranchidletcommitidt=vcs:=commit!vcsidtletrewrite_mergeid~ours~atbranch=vcs:=rewrite_merge!vcsid~ours~theirs:Noop~atbranchletreachableid=reachable!vcsidletmk_branch_name{expr=x}=Branch.make(matchx.CAst.v.Vernacexpr.exprwith|VernacSynPure(VernacDefinition(_,({CAst.v=Namei},_),_))->Id.to_stringi|VernacSynPure(VernacStartTheoremProof(_,[({CAst.v=i},_),_]))->Id.to_stringi|VernacSynPure(VernacInstance(({CAst.v=Namei},_),_,_,_,_))->Id.to_stringi|_->"branch")letedit_branch=Branch.make"edit"letbranch?root?posnamekind=vcs:=branch!vcs?root?posnamekindletget_infoid=matchget_info!vcsidwith|Somex->x|None->raiseVcs_aux.Expiredletset_stateids=letinfo=get_infoidininfo.state<-s;letis_full_state_valid=matchswith|FullState_->true|EmptyState|ErrorState_|ParsingState_->falseinifasync_proofs_is_master(cur_opt())&&is_full_state_validthen!Hooks.state_ready~doc:dummy_doc(* XXX should be taken in input *)idletget_stateid=(get_infoid).stateletget_parsing_stateid=stm_pperr_endline(fun()->str"retrieve parsing state state "++str(Stateid.to_stringid)++str" }}}");match(get_infoid).statewith|FullStates->SomeVernacstate.(Synterp.parsings.synterp)|ParsingStates->Somes|ErrorState(s,_)->s|EmptyState->Noneletset_parsing_stateidps=letinfo=get_infoidinletnew_state=matchinfo.statewith|FullStates->assertfalse|ParsingStates->assertfalse|ErrorState_->assertfalse|EmptyState->ParsingStatepsininfo.state<-new_stateletget_proof_modeid=(get_infoid).proof_modeletreachedid=letinfo=get_infoidininfo.n_reached<-info.n_reached+1letgoalsidn=(get_infoid).n_goals<-nletcur_tip()=get_branch_pos(current_branch())letproof_nesting()=Vcs_aux.proof_nesting!vcsletcheckout_shallowest_proof_branch()=ifList.memedit_branch(Vcs_.branches!vcs)thenbegincheckoutedit_branchendelseletpl=proof_nesting()intryletbranch=fst@@Vcs_aux.find_proof_at_depth!vcsplincheckoutbranchwithFailure_->checkoutBranch.master(* copies the transaction on every open branch *)letpropagate_sideff~action=List.map(funb->checkoutb;letproof_mode=get_proof_mode@@get_branch_posbinletid=new_nodeproof_mode()inmergeid~ours:(Sideffaction)~into:bBranch.master;id)(List.filter(funb->not(Branch.equalbBranch.master))(branches()))letpropagate_qed()=List.iter(funb->checkoutb;letproof_mode=get_proof_mode@@get_branch_posbinletid=new_nodeproof_mode()inletparsing=Option.get@@get_parsing_state(get_branch_posb)inmergeid~ours:(SideffCherryPickEnv)~into:bBranch.master;set_parsing_stateidparsing)(List.filter(funb->not(Branch.equalbBranch.master))(branches()))letvisitid=Vcs_aux.visit!vcsidletnodes_in_slice~block_start~block_stop=letrecauxid=ifStateid.equalidblock_startthen[]elsematchvisitidwith|{next=n;step=SCmdx}->(id,Cmdx)::auxn|{next=n;step=SAliasx}->(id,Aliasx)::auxn|{next=n;step=SSideff(ReplayCommandx,_)}->(id,Sideff(ReplayCommandx))::auxn|_->anomalyPp.(str("Cannot slice from "^Stateid.to_stringblock_start^" to "^Stateid.to_stringblock_stop^"."))inauxblock_stop(* [slice] copies a slice of the DAG, keeping only the last known valid state.
When it copies a state, it drops the libobjects and keeps only the structure. *)letslice~block_start~block_stop=letl=nodes_in_slice~block_start~block_stopinletcopy_infovid=letinfo=get_infoidinVcs_.set_infovid{infowithstate=EmptyState;vcs_backup=None,None}inletmake_shallow=function|FullStatest->FullState(Vernacstate.Stm.make_shallowst)|x->xinletcopy_info_w_statevid=letinfo=get_infoidinVcs_.set_infovid{infowithstate=make_shallowinfo.state;vcs_backup=None,None}inletcopy_proof_blockesv=letnodes=Vcs_.Dag.all_nodes(Vcs_.dagv)inletprops=Stateid.Set.fold(funnpl->Vcs_.property_of!vcsn@pl)nodes[]inletprops=CList.sort_uniquizeVcs_.Dag.Property.comparepropsinList.fold_left(funvp->Vcs_.create_propertyv(Stateid.Set.elements(Vcs_.Dag.Property.having_itp))(Vcs_.Dag.Property.datap))vpropsinletv=Vcs_.emptyblock_startinletv=copy_infovblock_startinletv=List.fold_right(fun(id,tr)v->letv=Vcs_.commitvidtrinletv=copy_infovidinv)lvin(* Stm should have reached the beginning of proof *)assert(matchget_stateblock_startwithFullState_->true|_->false);(* We put in the new dag the most recent state known to master *)letrecfillid=matchget_stateidwith|EmptyState|ErrorState_|ParsingState_->fill(Vcs_aux.visitvid).next|FullState_->copy_info_w_statevidinletv=fillblock_stopin(* We put in the new dag the first state (since Qed shall run on it,
* see check_task_aux) *)letv=copy_info_w_statevblock_startincopy_proof_blockesvletnodes_in_slice~block_start~block_stop=List.rev(List.mapfst(nodes_in_slice~block_start~block_stop))lettopo_invariantl=letall=List.fold_rightStateid.Set.addlStateid.Set.emptyinList.for_all(funx->letprops=property_of!vcsxinletsets=List.mapDag.Property.having_itpropsinList.for_all(funs->Stateid.Set.(subsetsall||subsetalls))sets)lletcreate_proof_task_boxl~qed~block_start:lemma=ifnot(topo_invariantl)thenanomalyPp.(str"overlapping boxes.");vcs:=create_property!vcsl(ProofTask{qed;lemma})letcreate_proof_block({block_start;block_stop}asdecl)name=letl=nodes_in_slice~block_start~block_stopinifnot(topo_invariantl)thenanomalyPp.(str"overlapping boxes.");vcs:=create_property!vcsl(ProofBlock(decl,name))letbox_ofid=List.mapDag.Property.data(property_of!vcsid)letdelete_boxes_ofid=List.iter(funx->vcs:=delete_property!vcsx)(property_of!vcsid)letproof_task_box_ofid=matchCList.map_filter(functionProofTaskx->Somex|_->None)(box_ofid)with|[]->None|[x]->Somex|_->anomalyPp.(str"node with more than 1 proof task box.")letgc()=letold_vcs=!vcsinletnew_vcs,erased_nodes=gcold_vcsinStateid.Set.iter(funid->match(Vcs_aux.visitold_vcsid).stepwith|SQed({fproof=Some(_,cancel_switch)},_)|SCmd{cancel_switch}->cancel_switch:=true|_->())erased_nodes;vcs:=new_vcsmoduleNB:sig(* Non blocking Sys.command *)valcommand:now:bool->(unit->unit)->unitend=structletm=Mutex.create()letc=Condition.create()letjob=refNoneletworker=refNoneletset_last_jobj=CThread.with_lockm~scope:(fun()->job:=Somej;Condition.signalc)letget_last_job()=CThread.with_lockm~scope:(fun()->whileOption.is_empty!jobdoCondition.waitcm;done;match!jobwith|None->assertfalse|Somex->job:=None;x)letrun_command()=trywhiletruedoget_last_job()()donewithe->()(* No failure *)letcommand~nowjob=ifnowthenjob()elsebeginset_last_jobjob;ifOption.is_empty!workerthenworker:=Some(CThread.createrun_command())endendletprint?(now=false)()=ifCDebug.(get_flagmisc)thenNB.command~now(print_dag!vcs)letbackup()=!vcsletrestorev=vcs:=vend(* }}} *)typestate=ValidofVernacstate.toption|Expired|Errorofexnletstate_of_id~docid=trymatchVCS.get_stateidwith|FullStates->Valid(Somes)|ErrorState(_,(e,_))->Errore|EmptyState|ParsingState_->ValidNonewithVCS.Expired->Expiredlet()=Stateid.set_is_valid(fun~docid->state_of_id~docid<>Expired)(****** A cache: fills in the nodes of the VCS document with their value ******)moduleState:sigtypetvalfreeze:unit->tvalunfreeze:t->unit(** The function is from unit, so it uses the current state to define
a new one. I.e. one may been to install the right state before
defining a new one.
Warning: an optimization in installed_cached requires that state
modifying functions are always executed using this wrapper. *)valdefine:doc:doc->?safe_id:Stateid.t->?redefine:bool->?cache:bool->?feedback_processed:bool->(unit->unit)->Stateid.t->unitvalinstall_cached:Stateid.t->unit(* val install_parsing_state : Stateid.t -> unit *)valis_cached:?cache:bool->Stateid.t->boolvalis_cached_and_valid:?cache:bool->Stateid.t->boolvalexn_on:Stateid.t->valid:Stateid.t->Exninfo.iexn->Exninfo.iexn(* to send states across worker/master *)valget_cached:Stateid.t->Vernacstate.ttypepartial_state=|FullofVernacstate.t|ProofOnlyofStateid.t*Vernacstate.Stm.pstatevalassign:Stateid.t->partial_state->unit(* Handlers for initial state, prior to document creation. *)valregister_root_state:unit->unitvalrestore_root_state:unit->unitvalpurify:('a->'b)->'a->'bend=struct(* {{{ *)typet={id:Stateid.t;vernac_state:Vernacstate.t}(* cur_id holds Stateid.dummy in case the last attempt to define a state
* failed, so the global state may contain garbage *)letcur_id=refStateid.dummyletfreeze()={id=!cur_id;vernac_state=Vernacstate.freeze_full_state()}letunfreezest=Vernacstate.unfreeze_full_statest.vernac_state;cur_id:=st.idletinvalidate_cur_state()=cur_id:=Stateid.dummytypepartial_state=|FullofVernacstate.t|ProofOnlyofStateid.t*Vernacstate.Stm.pstateletcache_stateid=VCS.set_stateid(FullState(Vernacstate.freeze_full_state()))letfreeze_invalididiexn=letps=VCS.get_parsing_stateidinVCS.set_stateid(ErrorState(ps,iexn))letis_cached?(cache=false)idonly_valid=ifStateid.equalid!cur_idthentrymatchVCS.get_infoidwith|({state=EmptyState}|{state=ParsingState_})whencache->cache_stateid;true|_->truewithVCS.Expired->falseelsetrymatchVCS.get_stateidwith|EmptyState|ParsingState_->false|FullState_->true|ErrorState_->notonly_validwithVCS.Expired->falseletis_cached_and_valid?cacheid=is_cached?cacheidtrueletis_cached?cacheid=is_cached?cacheidfalseletinstall_cachedid=matchVCS.get_stateidwith|FullStates->Vernacstate.unfreeze_full_states;cur_id:=id|ErrorState(_,ie)->Exninfo.iraiseie|EmptyState|ParsingState_->(* coqc has a 1 slot cache and only for valid states *)if(VCS.is_interactive())||not(Stateid.equalid!cur_id)thenanomalyPp.(str"installing a non cached state.")(*
let install_parsing_state id =
if not (Stateid.equal id !cur_id) then begin
Vernacstate.Parser.install @@ VCS.get_parsing_state id
end
*)letget_cachedid=trymatchVCS.get_stateidwith|FullStates->s|_->anomalyPp.(str"not a cached state.")withVCS.Expired->anomalyPp.(str"not a cached state (expired).")letassignidwhat=ifVCS.get_stateid<>EmptyStatethen()elsetrymatchwhatwith|Fulls->lets=tryletprev=(VCS.visitid).nextinifis_cached_and_validprevthenletopenVernacstatein{swithinterp={s.interpwithlemmas=PG_compat.copy_terminators~src:((get_cachedprev).interp.lemmas)~tgt:s.interp.lemmas}}elseswithVCS.Expired->sinVCS.set_stateid(FullStates)|ProofOnly(ontop,pstate)->ifis_cached_and_validontopthenlets=get_cachedontopinlets=Vernacstate.Stm.set_pstatespstateinVCS.set_stateid(FullStates)withVCS.Expired->()letexn_onid~valid(e,info)=matchStateid.getinfowith|Some_->(e,info)|None->letloc=Loc.get_locinfoinexecution_error?locid(iprint(e,info));(e,Stateid.addinfo~validid)(* [define] puts the system in state [id] calling [f ()] *)(* [safe_id] is the last known valid state before execution *)letdefine~doc?safe_id?(redefine=false)?(cache=false)?(feedback_processed=true)fid=feedback~id:id(ProcessingIn!Flags.async_proofs_worker_id);letstr_id=Stateid.to_stringidinifis_cachedid&¬redefinethenanomalyPp.(str"defining state "++strstr_id++str" twice.");trystm_prerr_endline(fun()->"defining "^str_id^" (cache="^ifcachethen"Y)"else"N)");f();ifcachethencache_stateid;stm_prerr_endline(fun()->"setting cur id to "^str_id);cur_id:=id;iffeedback_processedthen!Hooks.state_computed~docid~in_cache:false;VCS.reachedid;ifPG_compat.there_are_pending_proofs()thenVCS.goalsid(PG_compat.get_open_goals())withe->let(e,info)=Exninfo.captureeinletgood_id=!cur_idininvalidate_cur_state();VCS.reachedid;letie=matchStateid.getinfo,safe_idwith|None,None->(exn_onid~valid:good_id(e,info))|None,Somegood_id->(exn_onid~valid:good_id(e,info))|Some_,None->(e,info)|Some(_,at),Someid->(e,Stateid.addinfo~valid:idat)inifcachethenfreeze_invalididie;!Hooks.unreachable_state~docidie;Exninfo.iraiseieletinit_state=refNoneletregister_root_state()=init_state:=Some(Vernacstate.freeze_full_state())letrestore_root_state()=cur_id:=Stateid.dummy;Vernacstate.unfreeze_full_state(Option.get!init_state)(* Protect against state changes *)letpurifyfx=letst=freeze()intryletres=fxinVernacstate.Interp.invalidate_cache();unfreezest;reswithe->lete=Exninfo.captureeinVernacstate.Interp.invalidate_cache();unfreezest;Exninfo.iraiseeend(* }}} *)(* Wrapper for the proof-closing special path for Qed *)letstm_qed_delay_proof?route~proof~id~st~loc~controlpending:Vernacstate.Interp.t=set_id_for_feedback?routedummy_docid;Vernacinterp.interp_qed_delayed_proof~proof~st~control(CAst.make?locpending)(* Wrapper for Vernacentries.interp to set the feedback id *)(* It is currently called 19 times, this number should be certainly
reduced... *)letstm_vernac_interp?routeidst{verbose;expr}:Vernacstate.t=(* The Stm will gain the capability to interpret commmads affecting
the whole document state, such as backtrack, etc... so we start
to design the stm command interpreter now *)set_id_for_feedback?routedummy_docid;Aux_file.record_in_aux_set_at?loc:expr.CAst.loc();(* We need to check if a command should be filtered from
* vernac_entries, as it cannot handle it. This should go away in
* future refactorings.
*)letis_filtered_command=functionVernacSynPure(VernacResetName_|VernacResetInitial|VernacBack_|VernacRestart|VernacUndo_|VernacUndoTo_|VernacAbortAll)->true|_->falsein(* XXX unsupported attributes *)letcmd=expr.CAst.v.exprinifis_filtered_commandcmdthen(stm_pperr_endlinePp.(fun()->str"ignoring "++Ppvernac.pr_vernacexpr);st)elsebeginstm_pperr_endlinePp.(fun()->str"interpreting "++Ppvernac.pr_vernacexpr);Vernacinterp.(interp~intern:fs_intern?verbosely:(Someverbose)~stexpr)end(****************************** CRUFT *****************************************)(******************************************************************************)(* The backtrack module simulates the classic behavior of a linear document *)moduleBacktrack:sigvalrecord:unit->unit(* we could navigate the dag, but this ways easy *)valbranches_of:Stateid.t->backup(* Returns the state that the command should backtract to *)valundo_vernac_classifier:vernac_control->doc:doc->Stateid.tvalget_prev_proof:doc:doc->Stateid.t->Proof.toptionvalget_proof:doc:doc->Stateid.t->Proof.toptionend=struct(* {{{ *)letrecord()=List.iter(funcurrent_branch->letmine=current_branch,VCS.get_branchcurrent_branchinletinfo=VCS.get_info(VCS.get_branch_poscurrent_branch)inletothers=CList.map_filter(funb->ifVcs_.Branch.equalbcurrent_branchthenNoneelseSome(b,VCS.get_branchb))(VCS.branches())inletbackup=iffstinfo.vcs_backup<>Nonethenfstinfo.vcs_backupelseSome(VCS.backup())inletbranches=ifsndinfo.vcs_backup<>Nonethensndinfo.vcs_backupelseSome{mine;others}ininfo.vcs_backup<-backup,branches)[VCS.current_branch();VCS.Branch.master]letbranches_ofid=letinfo=VCS.get_infoidinmatchinfo.vcs_backupwith|_,None->anomalyPp.(str"Backtrack.branches_of "++str(Stateid.to_stringid)++str": a state with no vcs_backup.")|_,Somex->xtype('a,'b)cont=Stopof'a|Contof'bletrecfold_untilfaccid=letnextacc=ifid=Stateid.initialthenraiseNot_foundelsefold_untilfacc(VCS.visitid).nextinletinfo=VCS.get_infoidinmatchinfo.vcs_backupwith|None,_->nextacc|Somevcs,_->letids,tactic,undo=ifid=Stateid.initial||id=Stateid.dummythen[],false,0elsematchVCS.visitidwith|{step=SFork((_,_,_,l),_)}->l,false,0|{step=SCmd{cids=l;ctac}}->l,ctac,0|{step=SAlias(_,{expr})}whennot(Vernacprop.has_query_controlexpr)->beginmatchexpr.CAst.v.exprwith|VernacSynPure(VernacUndon)->[],false,n|_->[],false,0end|_->[],false,0inmatchfacc(id,vcs,ids,tactic,undo)with|Stopx->x|Contacc->nextaccletundo_costly_in_batch_mode=CWarnings.create~name:"undo-batch-mode"Pp.(funv->str"Command"++spc()++quote(Ppvernac.pr_vernacv)++strbrk(" is not recommended in batch mode. In particular, going back in the document"^" is not efficient in batch mode due to Coq not caching previous states for memory optimization reasons."^" If your use is intentional, you may want to disable this warning and pass"^" the \"-async-proofs-cache force\" option to Coq."))letback_tacticn(id,_,_,tactic,undo)=letvalue=(iftacticthen1else0)-undoinifInt.equaln0thenStopidelseCont(n-value)letget_proof~docid=matchstate_of_id~docidwith|Valid(Somevstate)->Option.map(Vernacstate.LemmaStack.with_top~f:Declare.Proof.get)vstate.Vernacstate.interp.lemmas|_->Noneletundo_vernac_classifierv~doc=ifnot(VCS.is_interactive())&&(cur_opt()).async_proofs_cache<>SomeForcethenundo_costly_in_batch_modev;trymatchv.CAst.v.exprwith|VernacSynPureVernacResetInitial->Stateid.initial|VernacSynPure(VernacResetName{CAst.v=name})->letid=VCS.cur_tip()in(tryletoid=fold_until(funb(id,_,label,_,_)->ifbthenStopidelseCont(List.memnamelabel))falseidinoidwithNot_found->id)|VernacSynPure(VernacBackn)->letid=VCS.cur_tip()inletoid=fold_until(funn(id,_,_,_,_)->ifInt.equaln0thenStopidelseCont(n-1))nidinoid|VernacSynPure(VernacUndon)->letid=VCS.cur_tip()inletoid=fold_untilback_tacticnidinoid|VernacSynPure(VernacUndoTo_|VernacRestartase)->letm=matchewithVernacUndoTom->m|_->0inletid=VCS.cur_tip()inletvcs=match(VCS.get_infoid).vcs_backupwith|None,_->anomalyPp.(str"Backtrack: tip with no vcs_backup.")|Somevcs,_->vcsinletcb,_=tryVcs_aux.find_proof_at_depthvcs(Vcs_aux.proof_nestingvcs)withFailure_->raisePG_compat.NoCurrentProofinletn=fold_until(funn(_,vcs,_,_,_)->ifList.memcb(Vcs_.branchesvcs)thenCont(n+1)elseStopn)0idinletoid=fold_until(funn(id,_,_,_,_)->ifInt.equaln0thenStopidelseCont(n-1))(n-m-1)idinoid|VernacSynPureVernacAbortAll->letid=VCS.cur_tip()inletoid=fold_until(fun()(id,vcs,_,_,_)->matchVcs_.branchesvcswith[_]->Stopid|_->Cont())()idinoid|_->anomalyPp.(str"incorrect VtMeta classification")with|Not_found->CErrors.user_errPp.(str"Cannot undo.")letget_prev_proof~docid=tryletnp=get_proof~docidinmatchnpwith|None->None|Somecp->letrecsearchdid=letdid=fold_untilback_tactic1didinmatchget_proof~docdidwith|None->None|Somerv->ifEvar.Set.equal(Proof.all_goalsrv)(Proof.all_goalscp)thensearchdidelseSomervinsearchidwithNot_found|PG_compat.NoCurrentProof->Noneend(* }}} *)letget_prev_proof=Backtrack.get_prev_proofletget_proof=Backtrack.get_prooflethints=refAux_file.empty_aux_fileletset_compilation_hintsfile=hints:=Aux_file.load_aux_file_forfileletget_hint_ctxloc=lets=Aux_file.get?loc!hints"context_used"inletids=List.mapNames.Id.of_string(Str.split(Str.regexp" ")s)inletids=List.map(funid->CAst.makeid)idsinmatchidswith|[]->SsEmpty|x::xs->List.fold_left(funax->SsUnion(SsSinglx,a))(SsSinglx)xsletget_hint_bp_timeproof_name=tryfloat_of_string(Aux_file.get!hintsproof_name)withNot_found->1.0letrecord_pb_time?locproof_nametime=letproof_build_time=Printf.sprintf"%.3f"timeinAux_file.record_in_aux_at?loc"proof_build_time"proof_build_time;ifproof_name<>""thenbeginAux_file.record_in_aux_atproof_nameproof_build_time;hints:=Aux_file.set!hintsproof_nameproof_build_timeend(****************** proof structure for error recovery ************************)(******************************************************************************)typedocument_node={indentation:int;ast:Vernacexpr.vernac_control;id:Stateid.t;}typedocument_view={entry_point:document_node;prev_node:document_node->document_nodeoption;}typestatic_block_detection=document_view->static_block_declarationoptiontyperecovery_action={base_state:Stateid.t;goals_to_admit:Evar.tlist;recovery_command:Vernacexpr.vernac_controloption;}typeblock_classification=ValidBlockofrecovery_action|Leakstypedynamic_block_error_recovery=doc->static_block_declaration->block_classificationletproof_block_delimiters=ref[]letregister_proof_block_delimiternamestaticdynamic=ifList.mem_assocname!proof_block_delimitersthenCErrors.user_errPp.(str"Duplicate block delimiter "++strname++str".");proof_block_delimiters:=(name,(static,dynamic))::!proof_block_delimitersletmk_doc_nodeid=function|{step=SCmd{ctac;cast={indentation;expr}};next}whenctac->Some{indentation;ast=expr;id}|{step=SSideff(ReplayCommand{indentation;expr},_);next}->Some{indentation;ast=expr;id}|_->Noneletprev_node{id}=letid=(VCS.visitid).nextinmk_doc_nodeid(VCS.visitid)letcur_nodeid=mk_doc_nodeid(VCS.visitid)letis_block_name_enabledname=match(cur_opt()).async_proofs_tac_error_resiliencewith|FNone->false|FAll->true|FOnlyl->List.memnamelletdetect_proof_blockidname=letname=matchnamewithNone->"indent"|Somex->xinifis_block_name_enabledname&&(async_proofs_is_master(cur_opt())||Flags.async_proofs_is_worker())then(matchcur_nodeidwith|None->()|Someentry_point->tryletstatic,_=List.assocname!proof_block_delimitersinbeginmatchstatic{prev_node;entry_point}with|None->()|Some({block_start;block_stop}asdecl)->VCS.create_proof_blockdeclnameendwithNot_found->CErrors.user_errPp.(str"Unknown proof block delimiter "++strname++str"."))(****************************** THE SCHEDULER *********************************)(******************************************************************************)(* Unused module warning doesn't understand [module rec] *)[@@@ocaml.warning"-60"]modulerecProofTask:sigtypecompetence=Stateid.tlisttypetask_build_proof={t_exn_info:Stateid.exn_info;t_start:Stateid.t;t_stop:Stateid.t;t_drop:bool;t_states:competence;t_assign:Declare.Proof.closed_proof_outputoptionFuture.assignment->unit;t_loc:Loc.toption;t_uuid:Future.UUID.t;t_name:string}typetask=|BuildProofoftask_build_proof|StatesofStateid.tlisttyperequest=|ReqBuildProofof(Future.UUID.t,VCS.vcs)Stateid.request*bool*competence|ReqStatesofStateid.tlistincludeAsyncTaskQueue.Taskwithtypetask:=taskandtypecompetence:=competenceandtyperequest:=requestvalbuild_proof_here:doc:doc->?loc:Loc.t->drop_pt:bool->Stateid.exn_info->Stateid.t->Declare.Proof.closed_proof_outputoptionFuture.computationend=struct(* {{{ *)letforward_feedbackmsg=!Hooks.forward_feedbackmsgtypecompetence=Stateid.tlisttypetask_build_proof={t_exn_info:Stateid.exn_info;t_start:Stateid.t;t_stop:Stateid.t;t_drop:bool;t_states:competence;t_assign:Declare.Proof.closed_proof_outputoptionFuture.assignment->unit;t_loc:Loc.toption;t_uuid:Future.UUID.t;t_name:string}typetask=|BuildProofoftask_build_proof|StatesofStateid.tlisttypeworker_status=Fresh|Oldofcompetencetyperequest=|ReqBuildProofof(Future.UUID.t,VCS.vcs)Stateid.request*bool*competence|ReqStatesofStateid.tlisttypeerror={e_error_at:Stateid.t;e_safe_id:Stateid.t;e_msg:Pp.t;e_loc:Loc.toption;e_safe_states:Stateid.tlist}typeresponse=|RespBuiltProofofDeclare.Proof.closed_proof_outputoption*float|RespErroroferror|RespStatesof(Stateid.t*State.partial_state)listletname="proof"letextra_env()=!async_proofs_workers_extra_envlettask_matchaget=matchage,twith|Fresh,BuildProof{t_states}->true|Oldmy_states,Statesl->List.for_all(funx->CList.mem_fStateid.equalxmy_states)l|_->falseletname_of_task=function|BuildProoft->"proof: "^t.t_name|Statesl->"states: "^String.concat","(List.mapStateid.to_stringl)letname_of_request=function|ReqBuildProof(r,_,_)->"proof: "^r.Stateid.name|ReqStatesl->"states: "^String.concat","(List.mapStateid.to_stringl)letrequest_of_taskage=function|Statesl->Some(ReqStatesl)|BuildProof{t_exn_info;t_start;t_stop;t_loc;t_uuid;t_name;t_states;t_drop}->assert(age=Fresh);trySome(ReqBuildProof({Stateid.exn_info=t_exn_info;stop=t_stop;document=VCS.slice~block_start:t_start~block_stop:t_stop;loc=t_loc;uuid=t_uuid;name=t_name},t_drop,t_states))withVCS.Expired->Noneletuse_response(s:worker_status)tr=matchs,t,rwith|Oldc,States_,RespStatesl->List.iter(fun(id,s)->State.assignids)l;`End|Fresh,BuildProof{t_assign;t_loc;t_name;t_states;t_drop},RespBuiltProof(pl,time)->feedback(InProgress~-1);t_assign(`Valpl);record_pb_time?loc:t_loct_nametime;ift_dropthen`Stay(t_states,[Statest_states])else`End|Fresh,BuildProof{t_assign;t_loc;t_name;t_states},RespError{e_error_at;e_safe_id=valid;e_loc;e_msg;e_safe_states}->feedback(InProgress~-1);letinfo=matche_locwith|Someloc->Loc.add_locExninfo.nullloc|None->Exninfo.nullinletinfo=Stateid.add~validinfoe_error_atinlete=(AsyncTaskQueue.RemoteExceptione_msg,info)int_assign(`Exne);`Stay(t_states,[Statese_safe_states])|_->assertfalseleton_task_cancellation_or_expiration_or_slave_death=function|None->()|Some(States_)->()|Some(BuildProof{t_start=start;t_assign})->lets="Worker dies or task expired"inletinfo=Stateid.add~valid:startExninfo.nullstartinlete=(AsyncTaskQueue.RemoteException(Pp.strbrks),info)int_assign(`Exne);execution_errorstart(Pp.strbrks);feedback(InProgress~-1)letbuild_proof_here_fun~doc?loc~drop_pt~ideop=letwall_clock1=Unix.gettimeofday()inReach.known_state~doc~cache:(VCS.is_interactive())eop;letwall_clock2=Unix.gettimeofday()inAux_file.record_in_aux_at?loc"proof_build_time"(Printf.sprintf"%.3f"(wall_clock2-.wall_clock1));letp=ifdrop_ptthenNoneelseSome(PG_compat.return_proof())inifdrop_ptthenfeedback~idComplete;pletbuild_proof_here~doc?loc~drop_ptexn_infoeop=Future.create~fix_exn:(Someexn_info)(fun()->build_proof_here_fun~doc?loc~drop_pt~id:exn_info.Stateid.ideop)letperform_buildp{Stateid.exn_info;stop;document;loc}dropmy_states=tryVCS.restoredocument;VCS.print();letproof,time=letwall_clock=Unix.gettimeofday()inletproof=trybuild_proof_here_fun~doc:dummy_doc(* XXX should be document *)?loc~drop_pt:drop~id:exn_info.Stateid.idstopwithexn->letiexn=Exninfo.captureexninletiexn=State.exn_onexn_info.Stateid.id~valid:exn_info.Stateid.validiexninExninfo.iraiseiexninproof,Unix.gettimeofday()-.wall_clockin(* We typecheck the proof with the kernel (in the worker) to spot
* the few errors tactics don't catch, like the "fix" tactic building
* a bad fixpoint *)(* STATE: We use the current installed imperative state *)letst=State.freeze()inlet()=matchproofwith|None->(* drop *)()|Someproof->(* Unfortunately close_future_proof and friends are not pure so we need
to set the state manually here *)State.unfreezest;letpobject=PG_compat.close_future_proof~feedback_id:stop(Future.from_valproof)inletst=Vernacstate.freeze_full_state()inletopaque=Opaqueintrylet_pstate=stm_qed_delay_proof~st~id:stop~proof:pobject~loc~control:[](Proved(opaque,None))in()withexn->(* If [stm_qed_delay_proof] fails above we need to use the
exn callback in the same way than build_proof_here;
actually [fix_exn] there does more more than just
modifying exn info, it also updates the STM state *)letiexn=Exninfo.captureexninletiexn=State.exn_onexn_info.Stateid.id~valid:exn_info.Stateid.validiexninExninfo.iraiseiexnin(* STATE: Restore the state XXX: handle exn *)State.unfreezest;RespBuiltProof(proof,time)with|ewhenCErrors.noncriticale||e=Stack_overflow->lete,info=Exninfo.captureein(* This can happen if the proof is broken. The error has also been
* signalled as a feedback, hence we can silently recover *)lete_error_at,e_safe_id=matchStateid.getinfowith|Some(safe,err)->err,safe|None->Stateid.dummy,Stateid.dummyinlete_msg=iprint(e,info)instm_pperr_endlinePp.(fun()->str"failed with the following exception: "++fnl()++e_msg);lete_safe_states=List.filterState.is_cached_and_validmy_statesinRespError{e_error_at;e_safe_id;e_msg;e_loc=Loc.get_locinfo;e_safe_states}letperform_statesquery=ifquery=[]then[]elseletis_tace=matchVernac_classifier.classify_vernacewith|VtProofStep_->true|_->falseinletinitial=letrecauxid=trymatchVCS.visitidwith{next}->auxnextwithVCS.Expired->idinaux(List.hdquery)inletget_stateseenid=letprev=trylet{next=prev;step}=VCS.visitidinifState.is_cached_and_validprev&&List.memprevseenthenSome(prev,State.get_cachedprev,step)elseNonewithVCS.Expired->Noneinletthis=ifState.is_cached_and_valididthenSome(State.get_cachedid)elseNoneinmatchprev,thiswith|_,None->None|Some(prev,o,SCmd{cast={expr}}),Somenwhenis_tacexpr&&Vernacstate.Stm.same_envon->(* A pure tactic *)Some(id,State.ProofOnly(prev,Vernacstate.Stm.pstaten))|Some_,Somes->ifCDebug.(get_flagmisc)thenmsg_debug(Pp.str"STM: sending back a fat state");Some(id,State.Fulls)|_,Somes->Some(id,State.Fulls)inletrecauxseen=function|[]->[]|id::rest->matchget_stateseenidwith|None->auxseenrest|Somestuff->stuff::aux(id::seen)restinaux[initial]queryletperform=function|ReqBuildProof(bp,drop,states)->perform_buildpbpdropstates|ReqStatessl->RespStates(perform_statessl)leton_marshal_errors=function|States_->msg_warningPp.(strbrk("Marshalling error: "^s^". "^"The system state could not be sent to the master process."))|BuildProof{t_exn_info;t_stop;t_assign;t_loc;t_drop=drop_pt}->msg_warningPp.(strbrk("Marshalling error: "^s^". "^"The system state could not be sent to the worker process. "^"Falling back to local, lazy, evaluation."));t_assign(`Comp(fun()->build_proof_here_fun~doc:dummy_doc(* XXX should be stored in a closure, it is the same doc that was used to generate the task *)?loc:t_loc~drop_pt~id:t_exn_info.Stateid.idt_stop));feedback(InProgress~-1)end(* }}} *)(* Slave processes (if initialized, otherwise local lazy evaluation) *)andSlaves:sig(* (eventually) remote calls *)valbuild_proof:doc:doc->?loc:Loc.t->drop_pt:bool->exn_info:Stateid.exn_info->block_start:Stateid.t->block_stop:Stateid.t->name:string->unit->future_proof*AsyncTaskQueue.cancel_switch(* blocking function that waits for the task queue to be empty *)valwait_all_done:unit->unit(* initialize the whole machinery (optional) *)valinit:CoqworkmgrApi.priority->unittype'atasks=(('a,VCS.vcs)Stateid.request*bool)listvaldump_snapshot:unit->Future.UUID.ttasksvalcancel_worker:WorkerPool.worker_id->unitvalreset_task_queue:unit->unitend=struct(* {{{ *)moduleTaskQueue=AsyncTaskQueue.MakeQueue(ProofTask)()letqueue=refNoneletinitpriority=ifasync_proofs_is_master(cur_opt())thenqueue:=Some(TaskQueue.create(cur_opt()).async_proofs_n_workerspriority)elsequeue:=Some(TaskQueue.create0priority)letbuild_proof~doc?loc~drop_pt~exn_info~block_start~block_stop~name:pname()=letcancel_switch=reffalseinletn_workers=TaskQueue.n_workers(Option.get!queue)inifInt.equaln_workers0&¬(VCS.is_vos_doc())thenProofTask.build_proof_here~doc?loc~drop_ptexn_infoblock_stop,cancel_switchelseletf,t_assign=Future.create_delegate~name:pname(Someexn_info)inlett_uuid=Future.uuidfinifn_workers>0thenfeedback(InProgress1);lettask=ProofTask.(BuildProof{t_exn_info=exn_info;t_start=block_start;t_stop=block_stop;t_assign;t_drop=drop_pt;t_loc=loc;t_uuid;t_name=pname;t_states=VCS.nodes_in_slice~block_start~block_stop})inTaskQueue.enqueue_task(Option.get!queue)task~cancel_switch;f,cancel_switchletwait_all_done()=TaskQueue.join(Option.get!queue)letcancel_workern=TaskQueue.cancel_worker(Option.get!queue)n(* For external users this name is nicer than request *)type'atasks=(('a,VCS.vcs)Stateid.request*bool)listletdump_snapshot()=lettasks=TaskQueue.snapshot(Option.get!queue)inletreqs=CList.map_filterProofTask.(funx->matchrequest_of_taskFreshxwith|Some(ReqBuildProof(r,b,_))->Some(r,b)|_->None)tasksinstm_prerr_endline(fun()->Printf.sprintf"dumping %d tasks\n"(List.lengthreqs));reqsletreset_task_queue()=TaskQueue.clear(Option.get!queue)end(* }}} *)andQueryTask:sigtypetask={t_where:Stateid.t;t_for:Stateid.t;t_what:aast}includeAsyncTaskQueue.Taskwithtypetask:=taskend=struct(* {{{ *)typetask={t_where:Stateid.t;t_for:Stateid.t;t_what:aast}typerequest={r_where:Stateid.t;r_for:Stateid.t;r_what:aast;r_doc:VCS.vcs}typeresponse=unitletname="query"letextra_env_=[||]typecompetence=unittypeworker_status=Fresh|Oldofcompetencelettask_match__=trueletrequest_of_task_{t_where;t_what;t_for}=trySome{r_where=t_where;r_for=t_for;r_doc=VCS.slice~block_start:t_where~block_stop:t_where;r_what=t_what}withVCS.Expired->Noneletuse_response___=`Endleton_marshal_error__=stm_pr_err("Fatal marshal error in query");flush_all();exit1leton_task_cancellation_or_expiration_or_slave_death_=()letforward_feedbackmsg=!Hooks.forward_feedbackmsgletperform{r_where;r_doc;r_what;r_for}=VCS.restorer_doc;VCS.print();Reach.known_state~doc:dummy_doc(* XXX should be r_doc *)~cache:falser_where;(* STATE *)letst=Vernacstate.freeze_full_state()intry(* STATE SPEC:
* - start: r_where
* - end : after execution of r_what
*)ignore(stm_vernac_interpr_forst{r_whatwithverbose=true});feedback~id:r_forProcessedwithewhenCErrors.noncriticale->lete=Exninfo.captureeinletmsg=iprinteinfeedback~id:r_for(Message(Error,None,msg))letname_of_task{t_what}=string_of_ppcmds(pr_astt_what)letname_of_request{r_what}=string_of_ppcmds(pr_astr_what)end(* }}} *)andQuery:sigvalinit:CoqworkmgrApi.priority->unitend=struct(* {{{ *)moduleTaskQueue=AsyncTaskQueue.MakeQueue(QueryTask)()letqueue=refNoneletinitpriority=queue:=Some(TaskQueue.create0priority)end(* }}} *)(* Runs all transactions needed to reach a state *)andReach:sigvalknown_state:doc:doc->?redefine_qed:bool->cache:bool->Stateid.t->unitend=struct(* {{{ *)letasync_policy()=ifAttributes.is_universe_polymorphism()thenfalse(* FIXME this makes no sense, it is the default value of the attribute *)elseifVCS.is_interactive()then(async_proofs_is_master(cur_opt())||(cur_opt()).async_proofs_mode=APonLazy)else(VCS.is_vos_doc()||(cur_opt()).async_proofs_mode<>APoff)letdelegatename=get_hint_bp_timename>=(cur_opt()).async_proofs_delegation_threshold||VCS.is_vos_doc()typereason=|Aborted|Alias|AlreadyEvaluated|Doesn'tGuaranteeOpacity|Immediate|MutualProofs|NestedProof|NoPU_NoHint_NoES|Policy|Print|Transparent|Unknowntypesync_kind=|Syncofstring*reason|ASyncofStateid.t*Stateid.tlist*string*bool|MaybeASyncofStateid.t*Stateid.tlist*string*boolletcollect_proofkeepcurhdbrkindid=stm_prerr_endline(fun()->"Collecting proof ending at "^Stateid.to_stringid);letno_name=""inletname=function|[]->no_name|id::_->Names.Id.to_stringidinletloc=(sndcur).expr.CAst.locinletis_defined_expr=function|VernacSynPure(VernacEndProof(Proved(Transparent,_)))->true|_->falseinletis_defined=function|_,{expr=e}->is_defined_expre.CAst.v.expr&&(not(Vernacprop.has_query_controle))inlethas_default_proof_using=Option.has_some(Proof_using.get_default_proof_using())inletproof_using_ast=function|VernacSynPure(VernacProof(_,Some_))->true|VernacSynPure(VernacProof(_,None))->has_default_proof_using|_->falseinletproof_using_ast=function|Some(_,v)whenproof_using_astv.expr.CAst.v.expr&&(not(Vernacprop.has_query_controlv.expr))->Somev|_->Noneinlethas_proof_usingx=proof_using_astx<>Noneinletproof_no_using=function|VernacSynPure(VernacProof(t,None))->ifhas_default_proof_usingthenNoneelset|_->assertfalseinletproof_no_using=function|Some(_,v)->proof_no_usingv.expr.CAst.v.expr,v|_->assertfalseinlethas_proof_no_using=function|VernacSynPure(VernacProof(_,None))->nothas_default_proof_using|_->falseinlethas_proof_no_using=function|Some(_,v)->has_proof_no_usingv.expr.CAst.v.expr&&(not(Vernacprop.has_query_controlv.expr))|_->falseinlettoo_complex_to_delegate=functionVernacSynterp(VernacDeclareModule_|VernacDefineModule_|VernacDeclareModuleType_|VernacInclude_|VernacRequire_|VernacImport_)->true|ast->may_pierce_opaqueastinletparent=functionSome(p,_)->p|None->assertfalseinletis_empty=functionASync(_,[],_,_)|MaybeASync(_,[],_,_)->true|_->falseinletreccollectlastaccnid=letview=VCS.visitidinmatchview.stepwith|(SSideff(ReplayCommandx,_)|SCmd{cast=x})whentoo_complex_to_delegatex.expr.CAst.v.expr->Sync(no_name,Print)|SCmd{cast=x}->collect(Some(id,x))(id::accn)view.next|SSideff(ReplayCommandx,_)->collect(Some(id,x))(id::accn)view.next(* An Alias could jump everywhere... we hope we can ignore it*)|SAlias_->Sync(no_name,Alias)|SFork((_,_,_,_::_::_),_)->Sync(no_name,MutualProofs)|SFork((_,_,Doesn'tGuaranteeOpacity,_),_)->Sync(no_name,Doesn'tGuaranteeOpacity)|SFork((_,hd',GuaranteesOpacity,ids),_)whenhas_proof_usinglast->assert(VCS.Branch.equalhdhd'||VCS.Branch.equalhdVCS.edit_branch);letname=nameidsinASync(parentlast,accn,name,delegatename)|SFork((_,hd',GuaranteesOpacity,ids),_)whenhas_proof_no_usinglast&¬(State.is_cached_and_valid(parentlast))&&VCS.is_vos_doc()->assert(VCS.Branch.equalhdhd'||VCS.Branch.equalhdVCS.edit_branch);(tryletname,hint=nameids,get_hint_ctxlocinlett,v=proof_no_usinglastinv.expr<-CAst.map(fun_->{control=[];attrs=[];expr=VernacSynPure(VernacProof(t,Somehint))})v.expr;ASync(parentlast,accn,name,delegatename)withNot_found->letname=nameidsinMaybeASync(parentlast,accn,name,delegatename))|SFork((_,hd',GuaranteesOpacity,ids),_)->assert(VCS.Branch.equalhdhd'||VCS.Branch.equalhdVCS.edit_branch);letname=nameidsinMaybeASync(parentlast,accn,name,delegatename)|SSideff(CherryPickEnv,_)->Sync(no_name,NestedProof)|SQed_->Sync(no_name,Unknown)inletmake_syncwhy=function|Sync(name,_)->Sync(name,why)|MaybeASync(_,_,name,_)->Sync(name,why)|ASync(_,_,name,_)->Sync(name,why)inletcheck_policyrc=ifasync_policy()thenrcelsemake_syncPolicyrcinletis_vernac_exact=function|VernacSynPure(VernacExactProof_)->true|_->falseinmatchcur,(VCS.visitid).step,brkindwith|(parent,x),SFork_,_whenis_vernac_exactx.expr.CAst.v.expr&&(not(Vernacprop.has_query_controlx.expr))->Sync(no_name,Immediate)|_,_,{VCS.kind=Edit_}->check_policy(collect(Somecur)[]id)|_->ifis_definedcurthenSync(no_name,Transparent)elseifkeep==VtDropthenSync(no_name,Aborted)elseletrc=collect(Somecur)[]idinifis_emptyrcthenmake_syncAlreadyEvaluatedrcelseif(is_vtkeepkeep)&&(not(State.is_cached_and_validid))thencheck_policyrcelsemake_syncAlreadyEvaluatedrcletstring_of_reason=function|Transparent->"non opaque"|AlreadyEvaluated->"proof already evaluated"|Policy->"policy"|NestedProof->"contains nested proof"|Immediate->"proof term given explicitly"|Aborted->"aborted proof"|Doesn'tGuaranteeOpacity->"not a simple opaque lemma"|MutualProofs->"block of mutually recursive proofs"|Alias->"contains Undo-like command"|Print->"contains Print-like command"|NoPU_NoHint_NoES->"no 'Proof using..', no .aux file, inside a section"|Unknown->"unsupported case"letlog_strings=stm_prerr_debug(fun()->"STM: "^s)letlog_processing_asyncidname=log_stringPrintf.(sprintf"%s: proof %s: asynch"(Stateid.to_stringid)name)letlog_processing_syncidnamereason=log_stringPrintf.(sprintf"%s: proof %s: synch (cause: %s)"(Stateid.to_stringid)name(string_of_reasonreason))letwall_clock_last_fork=ref0.0letknown_state~doc?(redefine_qed=false)~cacheid=leterror_absorbing_tacticidblocknameexn=(* We keep the static/dynamic part of block detection separate, since
the static part could be performed earlier. As of today there is
no advantage in doing so since no UI can exploit such piece of info *)detect_proof_blockidblockname;letboxes=VCS.box_ofidinletvalid_boxes=CList.map_filter(function|ProofBlock({block_stop}asdecl,name)whenStateid.equalblock_stopid->Some(decl,name)|_->None)boxesinassert(List.lengthvalid_boxes<2);ifvalid_boxes=[]thenExninfo.iraiseexnelseletdecl,name=List.hdvalid_boxesintrylet_,dynamic_check=List.assocname!proof_block_delimitersinmatchdynamic_checkdummy_docdeclwith|Leaks->Exninfo.iraiseexn|ValidBlock{base_state;goals_to_admit;recovery_command}->beginlettac=Proofview.Goal.enterbeginfungl->ifCList.mem_fEvar.equal(Proofview.Goal.goalgl)goals_to_admitthenProofview.give_upelseProofview.tclUNIT()endinmatch(VCS.get_infobase_state).statewith|FullState{Vernacstate.interp={lemmas}}->Option.iterPG_compat.unfreezelemmas;PG_compat.with_current_proof(fun_p->feedback~id:idFeedback.AddedAxiom;fst(Proof.solveGoal_select.SelectAllNonetacp),());(* STATE SPEC:
* - start: Modifies the input state adding a proof.
* - end : maybe after recovery command.
*)(* STATE: We use an updated state with proof *)letst=Vernacstate.freeze_full_state()inOption.iter(funexpr->ignore(stm_vernac_interpidst{verbose=true;expr;indentation=0;strlen=0}))recovery_command|_->assertfalseendwithNot_found->CErrors.user_err(str"Unknown proof block delimiter "++strname++str".")in(* Absorb tactic errors from f () *)letresilient_tacticidblocknamef=if(cur_opt()).async_proofs_tac_error_resilience=FNone||(async_proofs_is_master(cur_opt())&&(cur_opt()).async_proofs_mode=APoff)thenf()elsetryf()withewhenCErrors.noncriticale->letie=Exninfo.captureeinerror_absorbing_tacticidblocknameiein(* Absorb errors from f x *)letresilient_commandfx=ifnot(cur_opt()).async_proofs_cmd_error_resilience||(async_proofs_is_master(cur_opt())&&(cur_opt()).async_proofs_mode=APoff)thenfxelsetryfxwithewhenCErrors.noncriticale->()in(* extract proof_ending in qed case, note that `Abort` and `Proof
term.` could also fail in this case, however that'd be a bug I do
believe as proof injection shouldn't happen here. *)letextract_pe(x:aast)=matchx.expr.CAst.v.exprwith|VernacSynPure(VernacEndProofpe)->x.expr.CAst.v.control,pe|_->CErrors.anomalyPp.(str"Non-qed command classified incorrectly")in(* ugly functions to process nested lemmas, i.e. hard to reproduce
* side effects *)letinject_non_pstate(s_synterp,l_synterp,s_interp,l_interp)=Summary.Synterp.unfreeze_summaries~partial:trues_synterp;Lib.Synterp.unfreezel_synterp;Summary.Interp.unfreeze_summaries~partial:trues_interp;Lib.Interp.unfreezel_interp;ifPG_compat.there_are_pending_proofs()thenPG_compat.update_sigma_univs(Global.universes())inletrecpure_cherry_pick_non_pstatesafe_idid=State.purify(funid->stm_prerr_endline(fun()->"cherry-pick non pstate "^Stateid.to_stringid);reach~safe_idid;letst=Vernacstate.freeze_full_state()inVernacstate.Stm.non_pstatest)id(* traverses the dag backward from nodes being already calculated *)andreach?safe_id?(redefine_qed=false)?(cache=cache)id=stm_prerr_endline(fun()->"reaching: "^Stateid.to_stringid);ifnotredefine_qed&&State.is_cached~cacheidthenbegin!Hooks.state_computed~docid~in_cache:true;stm_prerr_endline(fun()->"reached (cache)");State.install_cachedidendelseletstep,cache_step,feedback_processed=letview=VCS.visitidinmatchview.stepwith|SAlias(id,_)->(fun()->reachview.next;reachid),cache,true|SCmd{cast=x;cqueue=SkipQueue}->(fun()->reachview.next),cache,true|SCmd{cast=x;ceff=eff;ctac=true;cblock}->(fun()->resilient_tacticidcblock(fun()->reachview.next;letst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx))),eff||cache,true|SCmd{cast=x;ceff=eff}->(fun()->(match(cur_opt()).async_proofs_modewith|APon|APonLazy->resilient_commandreachview.next|APoff->reachview.next);letst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx)),eff||cache,true|SFork((x,_,_,_),None)->(fun()->resilient_commandreachview.next;letst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx);wall_clock_last_fork:=Unix.gettimeofday()),true,true|SFork((x,_,_,_),Someprev)->(fun()->(* nested proof *)reach~cache:trueprev;reachview.next;(tryletst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx);withewhenCErrors.noncriticale->let(e,info)=Exninfo.captureeinletinfo=Stateid.addinfo~valid:previdinExninfo.iraise(e,info));wall_clock_last_fork:=Unix.gettimeofday()),true,true|SQed({qast=x;keep;brinfo;brname}asqed,eop)->letrecaux=function|ASync(block_start,nodes,name,delegate)->(fun()->letkeep'=get_vtkeepkeepinletdrop_pt=keep'==VtKeepAxiominletblock_stop,exn_info,loc=eop,Stateid.{id;valid=eop},x.expr.CAst.locinlog_processing_asyncidname;VCS.create_proof_task_boxnodes~qed:id~block_start;beginmatchbrinfo,qed.fproofwith|{VCS.kind=Edit_},None->assertfalse|{VCS.kind=Edit(_,_,okeep,_)},Some(ofp,cancel)->assert(redefine_qed=true);ifokeep<>keepthenmsg_warning(strbrk("The command closing the proof changed. "^"The kernel cannot take this into account and will "^(ifnotdrop_ptthen"not check "else"reject ")^"the "^(ifnotdrop_ptthen"new"else"incomplete")^" proof. Reprocess the command declaring "^"the proof's statement to avoid that."));letfp,cancel=Slaves.build_proof~doc?loc~drop_pt~exn_info~block_start~block_stop~name()inFuture.replace(Option.getofp)fp;qed.fproof<-Some(Somefp,cancel);(* We don't generate a new state, but we still need
* to install the right one *)State.install_cachedid|{VCS.kind=Proof_},Some_->assertfalse|{VCS.kind=Proof_},None->reach~cache:trueblock_start;letfp,cancel=ifdelegatethenSlaves.build_proof~doc?loc~drop_pt~exn_info~block_start~block_stop~name()elseProofTask.build_proof_here~doc?loc~drop_ptexn_infoblock_stop,reffalseinqed.fproof<-Some(Somefp,cancel);let()=matchkeep'with|VtKeepAxiom|VtKeepOpaque->()|VtKeepDefined->CErrors.anomaly(Pp.str"Cannot delegate transparent proofs, this is a bug in the STM.")inletfp'=Future.chainfp(function|Somep->p|None->CErrors.anomalyPp.(str"Attempting to force admitted proof contents."))inletproof=PG_compat.close_future_proof~feedback_id:idfp'inifnotdelegatethenignore(Future.computefp);reachview.next;letst=Vernacstate.freeze_full_state()inletcontrol,pe=extract_pexinignore(stm_qed_delay_proof~id~st~proof~loc~controlpe);feedback~id:idIncomplete|{VCS.kind=Master},_->assertfalseend;PG_compat.discard_all()),notredefine_qed,true|Sync(name,Immediate)->(fun()->reacheop;letst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx);PG_compat.discard_all()),true,true|Sync(name,reason)->(fun()->log_processing_syncidnamereason;reacheop;letwall_clock=Unix.gettimeofday()inletloc=x.expr.CAst.locinrecord_pb_timename?loc(wall_clock-.!wall_clock_last_fork);letproof=matchkeepwith|VtDrop->None|VtKeepVtKeepAxiom->qed.fproof<-Some(None,reffalse);None|VtKeepopaque->letopaque=matchopaquewith|VtKeepOpaque->Opaque|VtKeepDefined->Transparent|VtKeepAxiom->assertfalseintrySome(PG_compat.close_proof~opaque~keep_body_ucst_separate:false)withexn->letiexn=Exninfo.captureexninExninfo.iraise(State.exn_onid~valid:eopiexn)inifkeep<>VtKeepVtKeepAxiomthenreachview.next;letwall_clock2=Unix.gettimeofday()inletst=Vernacstate.freeze_full_state()inlet_st=matchproofwith|None->stm_vernac_interpidstx|Someproof->letcontrol,pe=extract_pexin{stwithinterp=stm_qed_delay_proof~id~st~proof~loc~controlpe}inletwall_clock3=Unix.gettimeofday()inAux_file.record_in_aux_at?loc:x.expr.CAst.loc"proof_check_time"(Printf.sprintf"%.3f"(wall_clock3-.wall_clock2));PG_compat.discard_all()),true,true|MaybeASync(start,nodes,name,delegate)->(fun()->reach~cache:truestart;ifCList.is_empty(Environ.named_context(Global.env()))(* no sections *)||PG_compat.get_pstate()|>(* #[using] attribute *)Option.cata(funx->Option.has_some(Declare.Proof.get_used_variablesx))falsethenUtil.pi1(aux(ASync(start,nodes,name,delegate)))()elseUtil.pi1(aux(Sync(name,NoPU_NoHint_NoES)))()),notredefine_qed,trueinaux(collect_proofkeep(view.next,x)brnamebrinfoeop)|SSideff(ReplayCommandx,_)->(fun()->reachview.next;letst=Vernacstate.freeze_full_state()inignore(stm_vernac_interpidstx)),cache,true|SSideff(CherryPickEnv,origin)->(fun()->reachview.next;inject_non_pstate(pure_cherry_pick_non_pstateview.nextorigin);),cache,trueinletcache_step=(cur_opt()).async_proofs_cache=SomeForce||cache_stepinState.define~doc?safe_id~cache:cache_step~redefine:redefine_qed~feedback_processedstepid;stm_prerr_endline(fun()->"reached: "^Stateid.to_stringid)inreach~redefine_qedidend(* }}} *)[@@@ocaml.warning"+60"](********************************* STM API ************************************)(******************************************************************************)(** STM initialization options: *)typestm_init_options={doc_type:stm_doc_type(** The STM does set some internal flags differently depending on
the specified [doc_type]. This distinction should disappear at
some some point. *);injections:Coqargs.injection_commandlist(** Injects Require and Set/Unset commands before the initial
state is ready *)}letinit_processstm_flags=Spawned.init_channels();set_cur_optstm_flags;CoqworkmgrApi.(initstm_flags.AsyncOpts.async_proofs_worker_priority);if(cur_opt()).async_proofs_mode=APonthenControl.enable_thread_delay:=true;if!Flags.async_proofs_worker_id="master"&&(cur_opt()).async_proofs_n_tacworkers>0thenPartac.enable_par~nworkers:(cur_opt()).async_proofs_n_tacworkersletinit_core()=State.register_root_state()letnew_doc{doc_type;injections}=(* We must reset the whole state before creating a document! *)State.restore_root_state();letdoc=letps=Pcoq.freeze()inVCS.initdoc_typeStateid.initialpsinSafe_typing.allow_delayed_constants:=(cur_opt()).async_proofs_mode<>APoff;lettop=matchdoc_typewith|Interactivetop->Coqargs.dirpath_of_toptop|VoDocf->letldir=Coqargs.(dirpath_of_top(TopPhysicalf))inVCS.set_ldirldir;set_compilation_hintsf;ldir|VosDocf->letldir=Coqargs.(dirpath_of_top(TopPhysicalf))inVCS.set_ldirldir;set_compilation_hintsf;ldirin(* Start this library and import initial libraries. *)letintern=Vernacinterp.fs_interninCoqinit.start_library~intern~topinjections;(* We record the state at this point! *)State.define~doc~cache:true~redefine:true(fun()->())Stateid.initial;Backtrack.record();Slaves.init(cur_opt()).async_proofs_worker_priority;ifasync_proofs_is_master(cur_opt())thenbeginstm_prerr_endline(fun()->"Initializing workers");Query.init(cur_opt()).async_proofs_worker_priority;letopts=match(cur_opt()).async_proofs_private_flagswith|None->[]|Somes->Str.split_delim(Str.regexp",")sinbegintryletenv_opt=Str.regexp"^extra-env="inletenv=List.find(funs->Str.string_matchenv_opts0)optsinasync_proofs_workers_extra_env:=Array.of_list(Str.split_delim(Str.regexp";")(Str.replace_firstenv_opt""env))withNot_found->()end;end;doc,VCS.cur_tip()letobserve~docid=!Hooks.sentence_execid;letvcs=VCS.backup()intryReach.known_state~doc~cache:(VCS.is_interactive())id;VCS.print()withe->lete=Exninfo.captureeinVCS.print();VCS.restorevcs;Exninfo.iraiseeletfinish~doc=lethead=VCS.current_branch()inlet()=observe~doc(VCS.get_branch_poshead)inlettip=VCS.cur_tip()inifnot(State.is_cached~cache:truetip)thenCErrors.anomalyPp.(str"Stm.finish: tip not cached");VCS.print();State.get_cachedtipletwait~doc=let()=observe~doc(VCS.get_branch_posVCS.Branch.master)inSlaves.wait_all_done();VCS.print()letrecjoin_admitted_proofsid=ifStateid.equalidStateid.initialthen()elseletview=VCS.visitidinmatchview.stepwith|SQed({keep=VtKeepVtKeepAxiom;fproof=Some(fp,_)},_)->Option.iter(funfp->ignore(Future.forcefp))fp;join_admitted_proofsview.next|_->join_admitted_proofsview.next(* Error resiliency may have tolerated some broken commands *)letreccheck_no_err_states~docvisitedid=letopenStateidinifSet.memidvisitedthenvisitedelsematchstate_of_id~docidwith|Errore->raisee|_->letview=VCS.visitidinmatchview.stepwith|SQed(_,id)->letvisited=check_no_err_states~doc(Set.addidvisited)idincheck_no_err_states~docvisitedview.next|_->check_no_err_states~doc(Set.addidvisited)view.nextletjoin~doc=let()=wait~docinstm_prerr_endline(fun()->"Joining the environment");let()=Opaques.Summary.join()instm_prerr_endline(fun()->"Joining Admitted proofs");join_admitted_proofs(VCS.get_branch_posVCS.Branch.master);stm_prerr_endline(fun()->"Checking no error states");ignore(check_no_err_states~doc(Stateid.Set.singletonStateid.initial)(VCS.get_branch_posVCS.Branch.master));lettip=VCS.cur_tip()inifnot(State.is_cached~cache:truetip)thenCErrors.anomalyPp.(str"Stm.join: tip not cached");VCS.print()typebranch_result=Ok|UnfocusofStateid.tletmerge_proof_branch~valid?idqastkeepbrname=letbrinfo=VCS.get_branchbrnameinletqedfproof={qast;keep;brname;brinfo;fproof}inmatchbrinfowith|{VCS.kind=Proof_}->VCS.checkoutVCS.Branch.master;letid=VCS.new_node?idNone()inletparsing=Option.get@@VCS.get_parsing_state(VCS.cur_tip())inVCS.mergeid~ours:(Qed(qedNone))brname;VCS.set_parsing_stateidparsing;VCS.delete_branchbrname;VCS.propagate_qed();Ok|{VCS.kind=Edit(qed_id,master_id,_,_)}->letofp=matchVCS.visitqed_idwith|{step=SQed({fproof},_)}->fproof|_->assertfalseinVCS.rewrite_mergeqed_id~ours:(Qed(qedofp))~at:master_idbrname;VCS.delete_branchbrname;VCS.gc();let_st:unit=Reach.known_state~doc:dummy_doc(* XXX should be taken in input *)~redefine_qed:true~cache:falseqed_idinVCS.checkoutVCS.Branch.master;Unfocusqed_id|{VCS.kind=Master}->Exninfo.iraise(State.exn_on~validStateid.dummy(PG_compat.NoCurrentProof,Exninfo.null))(* When tty is true, this code also does some of the job of the user interface:
jump back to a state that is valid *)lethandle_failure(e,info)vcs=VCS.restorevcs;VCS.print();Exninfo.iraise(e,info)letsnapshot_vos~doc~output_native_objectsldirlong_f_dot_vo=let_:Vernacstate.t=finish~docinifList.length(VCS.branches())>1thenCErrors.user_err(str"Cannot dump a vos with open proofs.");(* LATER: when create_vos is true, it could be more efficient to not allocate the futures; but for now it seems useful for synchronization of the workers,
below, [snapshot] gets computed even if [create_vos] is true. *)lettasks=Slaves.dump_snapshot()inletexcept=List.fold_left(fune(r,_)->Future.UUIDSet.addr.Stateid.uuide)Future.UUIDSet.emptytasksinlettodo_proofs=Library.ProofsTodoSomeEmptyexceptinLibrary.save_library_totodo_proofs~output_native_objectsldirlong_f_dot_voletreset_task_queue=Slaves.reset_task_queue(* Document building *)(* We process a meta command found in the document *)letprocess_back_meta_command~newtip~headoidaast=letvalid=VCS.get_branch_posheadinletold_parsing=Option.get@@VCS.get_parsing_stateoidin(* Merge in and discard all the branches currently open that were not open in `oid` *)let{mine;others}=Backtrack.branches_ofoidinList.iter(funbranch->ifnot(List.mem_assocbranch(mine::others))thenignore(merge_proof_branch~validaastVtDropbranch))(VCS.branches());(* We add a node on top of every branch, to represent state aliasing *)VCS.checkout_shallowest_proof_branch();lethead=VCS.current_branch()inList.iter(funb->VCS.checkoutb;letid=if(VCS.Branch.equalbhead)thenSomenewtipelseNoneinletproof_mode=VCS.get_proof_mode@@VCS.cur_tip()inletid=VCS.new_node?idproof_mode()inVCS.commitid(Alias(oid,aast));VCS.set_parsing_stateidold_parsing)(VCS.branches());VCS.checkout_shallowest_proof_branch();Backtrack.record();Oklet{Goptions.get=get_allow_nested_proofs}=Goptions.declare_bool_option_and_ref~key:Vernac_classifier.stm_allow_nested_proofs_option_name~value:false()(** [process_transaction] adds a node in the document *)letprocess_transaction~doc?(newtip=Stateid.fresh())xc=let{verbose;expr}=xinstm_pperr_endline(fun()->str"{{{ processing: "++pr_astx);letvcs=VCS.backup()intrylethead=VCS.current_branch()inVCS.checkouthead;lethead_parsing=Option.get@@VCS.(get_parsing_state(get_branch_poshead))inletproof_mode=VCS.(get_proof_mode(get_branch_poshead))inletrc=beginstm_prerr_endline(fun()->" classified as: "^Vernac_classifier.string_of_vernac_classificationc);matchcwith(* Meta *)|VtMeta->letid=Backtrack.undo_vernac_classifierexpr~docinprocess_back_meta_command~newtip~headidx(* Query *)|VtQuery->letid=VCS.new_node~id:newtipproof_mode()inletqueue=ifVCS.is_vos_doc()&&VCS.((get_branchhead).kind=Master)&&may_pierce_opaquex.expr.CAst.v.exprthenSkipQueueelseMainQueueinVCS.commitid(mkTransCmdx[]falsequeue);VCS.set_parsing_stateidhead_parsing;Backtrack.record();Ok(* Proof *)|VtStartProof(guarantee,names)->ifnot(get_allow_nested_proofs())&&VCS.proof_nesting()>0then"Nested proofs are discouraged and not allowed by default. \
This error probably means that you forgot to close the last \"Proof.\" with \"Qed.\" or \"Defined.\". \
If you really intended to use nested proofs, you can do so by turning the \"Nested Proofs Allowed\" flag on."|>Pp.strbrk|>(funs->(UserErrors,Exninfo.null))|>State.exn_on~valid:Stateid.dummynewtip|>Exninfo.iraiseelseletproof_mode=Some(Synterp.get_default_proof_mode())inletid=VCS.new_node~id:newtipproof_mode()inletbname=VCS.mk_branch_namexinVCS.checkoutVCS.Branch.master;ifVCS.Branch.equalheadVCS.Branch.masterthenbeginVCS.commitid(Fork(x,bname,guarantee,names));VCS.branchbname(Proof(VCS.proof_nesting()+1))endelsebeginVCS.branchbname(Proof(VCS.proof_nesting()+1));VCS.mergeid~ours:(Fork(x,bname,guarantee,names))headend;VCS.set_parsing_stateidhead_parsing;Backtrack.record();Ok|VtProofStep{proof_block_detection=cblock}->letid=VCS.new_node~id:newtipproof_mode()inletqueue=MainQueueinVCS.commitid(mkTransTacxcblockqueue);(* Static proof block detection delayed until an error really occurs.
If/when and UI will make something useful with this piece of info,
detection should occur here.
detect_proof_block id cblock; *)VCS.set_parsing_stateidhead_parsing;Backtrack.record();Ok|VtQedkeep->letvalid=VCS.get_branch_posheadinletrc=merge_proof_branch~valid~id:newtipxkeepheadinVCS.checkout_shallowest_proof_branch();Backtrack.record();rc(* Side effect in a (still open) proof is replayed on all branches*)|VtSideff(l,w)->letid=VCS.new_node~id:newtipproof_mode()inletnew_ids=match(VCS.get_branchhead).VCS.kindwith|Edit_->VCS.commitid(mkTransCmdxltrueMainQueue);[]|Master->VCS.commitid(mkTransCmdxlfalseMainQueue);[]|Proof_->VCS.checkoutVCS.Branch.master;VCS.commitid(mkTransCmdxltrueMainQueue);(* We can't replay a Definition since universes may be differently
* inferred. This holds in Coq >= 8.5 *)letaction=matchx.expr.CAst.v.exprwith|VernacSynPure(VernacDefinition(_,_,DefineBody_))->CherryPickEnv|_->ReplayCommandxinVCS.propagate_sideff~actioninVCS.checkout_shallowest_proof_branch();Backtrack.record();letparsing_state=beginmatchwwith|VtNow->(* We need to execute to get the new parsing state *)let()=observe~doc:dummy_doc(VCS.get_branch_pos(VCS.current_branch()))inletparsing=Pcoq.freeze()in(* If execution has not been put in cache, we need to save the parsing state *)if(VCS.get_infoid).state==EmptyStatethenVCS.set_parsing_stateidparsing;parsing|VtLater->VCS.set_parsing_stateidhead_parsing;head_parsingendin(* We save the parsing state on non-master branches *)List.iter(funid->if(VCS.get_infoid).state==EmptyStatethenVCS.set_parsing_stateidparsing_state)new_ids;Ok|VtProofModeproof_mode->letid=VCS.new_node~id:newtip(Someproof_mode)()inVCS.commitid(mkTransCmdx[]falseMainQueue);VCS.set_parsing_stateidhead_parsing;Backtrack.record();Okendinletpr_rcrc=matchrcwith|Ok->Pp.(seq[str"newtip (";str(Stateid.to_string(VCS.cur_tip()));str")"])|_->Pp.(str"unfocus")instm_pperr_endline(fun()->str"processed with "++pr_rcrc++str" }}}");VCS.print();rcwithe->lete=Exninfo.captureeinhandle_failureevcsletget_ast~docid=match(VCS.visitid).stepwith|SCmd{cast={expr}}|SFork(({expr},_,_,_),_)|SSideff((ReplayCommand{expr}),_)|SQed({qast={expr}},_)->Someexpr|SAlias_|SSideff(CherryPickEnv,_)->Noneletstop_workern=Slaves.cancel_workernletparse_sentence~docsid~entrypa=letps=Option.get@@VCS.get_parsing_statesidinletproof_mode=VCS.get_proof_modesidinPcoq.unfreezeps;Pcoq.Entry.parse(entryproof_mode)pa(* You may need to know the len + indentation of previous command to compute
* the indentation of the current one.
* Eg. foo. bar.
* Here bar is indented of the indentation of foo + its strlen (4) *)letind_len_loc_of_idsid=ifStateid.equalsidStateid.initialthenNoneelsematch(VCS.visitsid).stepwith|SCmd{ctac=true;cast={indentation;strlen;expr}}->Some(indentation,strlen,expr.CAst.loc)|_->None(* the indentation logic works like this: if the beginning of the
command is different than the start we are in a new line, thus
indentation follows from the starting position. Otherwise, we use
the previous one plus the offset.
Note, this could maybe improved by handling more cases in
compute_indentation. *)letcompute_indentation?locsid=Option.cata(funloc->letopenLocin(* The effective length is the length on the last line *)letlen=loc.ep-loc.bpinletprev_indent=matchind_len_loc_of_idsidwith|None->0|Some(i,l,p)->i+lin(* Now if the line has not changed, the need to add the previous indent *)leteff_indent=loc.bp-loc.bol_posinifloc.bol_pos=0theneff_indent+prev_indent,lenelseeff_indent,len)(0,0)loctypeadd_focus=NewAddTip|UnfocusofStateid.tletadd~doc~ontop?newtipverbast=!Hooks.document_addastontop;letloc=ast.CAst.locinletcur_tip=VCS.cur_tip()inifnot(Stateid.equalontopcur_tip)thenuser_err?loc(str"Stm.add called for a different state ("++str(Stateid.to_stringontop)++str") than the tip: "++str(Stateid.to_stringcur_tip)++str"."++fnl()++str"This is not supported yet, sorry.");letindentation,strlen=compute_indentation?locontopin(* XXX: Classifiy vernac should be moved inside process transaction *)letclas=Vernac_classifier.classify_vernacastinletaast={verbose=verb;indentation;strlen;expr=ast}inmatchprocess_transaction~doc?newtipaastclaswith|Ok->doc,VCS.cur_tip(),NewAddTip|Unfocusqed_id->doc,qed_id,Unfocus(VCS.cur_tip())typefocus={start:Stateid.t;stop:Stateid.t;tip:Stateid.t}typeedit_focus=NewTip|Focusoffocusletquery~doc~at~routes=State.purify(funs->ifStateid.equalatStateid.dummythenignore(finish~doc:dummy_doc)elseReach.known_state~doc~cache:trueat;letrecloop()=matchparse_sentence~docat~entry:Pvernac.main_entryswith|None->()|Someast->letloc=ast.CAst.locinletindentation,strlen=compute_indentation?locatinletst=State.get_cachedatinletaast={verbose=true;indentation;strlen;expr=ast}inignore(stm_vernac_interp~routeatstaast);loop()inloop())sletedit_at~docid=assert(VCS.is_interactive());!Hooks.document_editid;ifStateid.equalidStateid.dummythenanomaly(str"edit_at dummy.")elseletvcs=VCS.backup()inleton_cur_branchid=letrecauxcur=matchVCS.visitcurwith|{step=SFork_}->false|{next}->ifid=curthentrueelseauxnextinaux(VCS.get_branch_pos(VCS.current_branch()))inletrecis_pure_auxid=letview=VCS.visitidinmatchview.stepwith|SCmd_->is_pure_auxview.next|SFork_->true|_->falseinletis_pureid=match(VCS.visitid).stepwith|SQed(_,last_step)->is_pure_auxlast_step|_->assertfalseinletis_ancestor_of_cur_branchid=Stateid.Set.memid(VCS.reachable(VCS.get_branch_pos(VCS.current_branch())))inlethas_failedqed_id=matchVCS.visitqed_idwith|{step=SQed({fproof=Some(Somefp,_)},_)}->Future.is_exnfp|_->falseinletrecmaster_for_brroottip=ifStateid.equaltipStateid.initialthentipelsematchVCS.visittipwith|{step=(SFork_|SQed_)}->tip|{step=SSideff(ReplayCommand_,id)}->id|{step=SSideff_}->tip|{next}->master_for_brrootnextinletreopen_branchstartat_idqed_idtipold_branch=letmaster_id,cancel_switch,keep=(* Hum, this should be the real start_id in the cluster and not next *)matchVCS.visitqed_idwith|{step=SQed({fproof=Some(_,cs);keep},_)}->start,cs,keep|_->anomaly(str"ProofTask not ending with Qed.")inVCS.branch~root:master_id~pos:idVCS.edit_branch(Edit(qed_id,master_id,keep,old_branch));VCS.delete_boxes_ofid;cancel_switch:=true;Reach.known_state~doc~cache:(VCS.is_interactive())id;VCS.checkout_shallowest_proof_branch();Focus{stop=qed_id;start=master_id;tip}inletno_edit=function|Edit(_,_,_,_)->Proof1|x->xinletbacktoidbn=List.iterVCS.delete_branch(VCS.branches());letancestors=VCS.reachableidinlet{mine=brname,brinfo;others}=Backtrack.branches_ofidinList.iter(fun(name,{VCS.kind=k;root;pos})->ifnot(VCS.Branch.equalnameVCS.Branch.master)&&Stateid.Set.memrootancestorsthenVCS.branch~root~posnamek)others;VCS.reset_branchVCS.Branch.master(master_for_brbrinfo.VCS.rootid);VCS.branch~root:brinfo.VCS.root~pos:brinfo.VCS.pos(Option.defaultbrnamebn)(no_editbrinfo.VCS.kind);VCS.delete_boxes_ofid;VCS.gc();VCS.print();Reach.known_state~doc~cache:(VCS.is_interactive())id;VCS.checkout_shallowest_proof_branch();NewTipintryletrc=letfocused=List.exists((=)VCS.edit_branch)(VCS.branches())inletbranch_info=matchsnd(VCS.get_infoid).vcs_backupwith|Some{mine=bn,{VCS.kind=Proof_}}->Somebn|Some{mine=_,{VCS.kind=Edit(_,_,_,bn)}}->Somebn|_->Noneinmatchfocused,VCS.proof_task_box_ofid,branch_infowith|_,Some_,None->assertfalse|false,Some{qed=qed_id;lemma=start},Somebn->lettip=VCS.cur_tip()inifhas_failedqed_id&&is_pureqed_id&¬(cur_opt()).async_proofs_never_reopen_branchthenreopen_branchstartidqed_idtipbnelsebacktoid(Somebn)|true,Some{qed=qed_id},Somebn->ifon_cur_branchidthenbeginassertfalseendelseifis_ancestor_of_cur_branchidthenbeginbacktoid(Somebn)endelsebeginanomaly(str"Cannot leave an Edit branch open.")end|true,None,_->ifon_cur_branchidthenbeginVCS.reset_branch(VCS.current_branch())id;Reach.known_state~doc~cache:(VCS.is_interactive())id;VCS.checkout_shallowest_proof_branch();NewTipendelseifis_ancestor_of_cur_branchidthenbeginbacktoidNoneendelsebeginanomaly(str"Cannot leave an Edit branch open.")end|false,None,Somebn->backtoid(Somebn)|false,None,None->backtoidNoneinVCS.print();doc,rcwith|Vcs_aux.Expired->user_errPp.(str"Unknown state "++Stateid.printid++str".")|e->let(e,info)=Exninfo.captureeinmatchStateid.getinfowith|None->VCS.print();anomaly(str("edit_at "^Stateid.to_stringid^": ")++CErrors.print_no_reporte++str".")|Some(_,id)->stm_prerr_endline(fun()->"Failed at state "^Stateid.to_stringid);VCS.restorevcs;VCS.print();Exninfo.iraise(e,info)letget_current_state~doc=VCS.cur_tip()letget_ldir~doc=VCS.get_ldir()letget_docdid=dummy_doc(*********************** TTY API (PG, coqtop, coqc) ***************************)(******************************************************************************)letcurrent_proof_depth~doc=lethead=VCS.current_branch()inmatchVCS.get_branchheadwith|{VCS.kind=Master}->0|{VCS.pos=cur;VCS.kind=(Proof_|Edit_);VCS.root=root}->letrecdistanceroot=ifStateid.equalcurrootthen0else1+distance(VCS.visitcur).nextindistancecurletunmanglen=letn=VCS.Branch.to_stringninletidx=String.indexn'_'+1inNames.Id.of_string(String.subnidx(String.lengthn-idx))letproofnameb=matchVCS.get_branchbwith|{VCS.kind=(Proof_|Edit_)}->Someb|_->Noneletget_all_proof_names~doc=List.mapunmangle(CList.map_filterproofname(VCS.branches()))(* Export hooks *)letstate_computed_hookv=Hooks.state_computed:=vletstate_ready_hookv=Hooks.state_ready:=vletforward_feedback_hookv=Hooks.forward_feedback:=vletunreachable_state_hookv=Hooks.unreachable_state:=vletdocument_add_hookv=Hooks.document_add:=vletdocument_edit_hookv=Hooks.document_edit:=vletsentence_exec_hookv=Hooks.sentence_exec:=vtypedocument=VCS.vcsletbackup()=VCS.backup()letrestored=VCS.restored(* vim:set foldmethod=marker: *)