123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418(************************************************************************)(* * The Rocq Prover / The Rocq 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) *)(************************************************************************)openUtilopenPpopenNamesopenGenargopenLtac2_pluginopenTac2valopenTac2ffiopenTac2envopenTac2expropenProofview.NotationsopenTac2externalsletltac2_ltac1_plugin="rocq-runtime.plugins.ltac2_ltac1"letpname?(plugin=ltac2_ltac1_plugin)s={mltac_plugin=plugin;mltac_tactic=s}letdefine?plugins=define(pname?plugins)letreturnx=Proofview.tclUNITx(** Ltac1 in Ltac2 *)letltac1=Tac2quote_ltac1.ltac1let()=define"ltac1_ref"(listident@->retltac1)@@funids->letopenLtac_plugininletr=matchidswith|[]->raiseNot_found|_::_asids->let(id,path)=List.sep_lastidsinletpath=DirPath.make(List.revpath)inletfp=Libnames.make_pathpathidinifTacenv.exists_tacticfpthenList.hd(Tacenv.locate_extended_all_tactic(Libnames.qualid_of_pathfp))elseraiseNot_foundinTacinterp.Value.of_closure(Tacinterp.default_ist())(Tacenv.interp_ltacr)let()=define"ltac1_run"(ltac1@->tacunit)@@funv->letopenLtac_plugininTacinterp.tactic_of_value(Tacinterp.default_ist())vlet()=define"ltac1_apply"(ltac1@->listltac1@->closure@->tacunit)@@funfargsk->letopenLtac_plugininletopenTacexprinletopenLocusinletkret=Proofview.tclIGNORE(Tac2val.applyk[Tac2ffi.repr_ofltac1ret])inletfoldarg(i,vars,lfun)=letid=Id.of_string("x"^string_of_inti)inletx=Reference(ArgVarCAst.(makeid))in(succi,x::vars,Id.Map.addidarglfun)inlet(_,args,lfun)=List.fold_rightfoldargs(0,[],Id.Map.empty)inletlfun=Id.Map.add(Id.of_string"F")flfuninletist={(Tacinterp.default_ist())withTacinterp.lfun=lfun;}inlettac=CAst.make@@TacArg(TacCall(CAst.make(ArgVarCAst.(make@@Id.of_string"F"),args)))inTacinterp.val_interpisttacklet()=define"ltac1_of_int"(int@->retltac1)Ltac_plugin.Tacinterp.Value.of_intlet()=define"ltac1_to_int"(ltac1@->ret(optionint))Ltac_plugin.Tacinterp.Value.to_intlet()=define"ltac1_of_constr"(constr@->retltac1)Ltac_plugin.Tacinterp.Value.of_constrlet()=define"ltac1_to_constr"(ltac1@->ret(optionconstr))Ltac_plugin.Tacinterp.Value.to_constrlet()=define"ltac1_of_preterm"(preterm@->retltac1)Ltac_plugin.Taccoerce.Value.of_uconstrlet()=define"ltac1_to_preterm"(ltac1@->ret(optionpreterm))Ltac_plugin.Taccoerce.Value.to_uconstrlet()=define"ltac1_of_ident"(ident@->retltac1)Ltac_plugin.Taccoerce.Value.of_identlet()=define"ltac1_to_ident"(ltac1@->ret(optionident))Ltac_plugin.Taccoerce.Value.to_identlet()=define"ltac1_of_list"(listltac1@->retltac1)@@funl->Geninterp.Val.(inject(Basetyp_list)l)let()=define"ltac1_to_list"(ltac1@->ret(option(listltac1)))Ltac_plugin.Tacinterp.Value.to_listlet()=define"ltac1_tag_name"(ltac1@->retstring)@@fun(Dyn(tag,_))->Geninterp.Val.reprtagletgtyprefkn=GTypRef(Otherkn,[])openTac2core.Coreletcore_prefixpathn=KerName.makepath(Label.of_id(Id.of_string_softn))letltac1_coren=core_prefixTac2env.ltac1_prefixnlett_ltac1=ltac1_core"t"letltac1_lambda=ltac1_core"lambda"let()=letinternist(ids,tac)=letmap{CAst.v=id}=idinletids=List.mapmapidsin(* Prevent inner calls to Ltac2 values *)letextra=Tac2intern.drop_ltac2_envist.Genintern.extrainletltacvars=List.fold_rightId.Set.addidsist.Genintern.ltacvarsinletist={istwithGenintern.extra;ltacvars}inlet_,tac=Genintern.internLtac_plugin.Tacarg.wit_tacticisttacinletfoldty_=GTypArrow(gtypreft_ltac1,ty)inletty=List.fold_leftfold(gtypreft_unit)idsinGlbVal(ids,tac),tyinletinterp_(ids,tac)=letclosargs=letaddlfunidv=letv=Tac2ffi.repr_toltac1vinId.Map.addidvlfuninletlfun=List.fold_left2addId.Map.emptyidsargsinletist={env_ist=Id.Map.empty}inletlfun=Tac2interp.set_envistlfuninletist=Ltac_plugin.Tacinterp.default_ist()inletist={istwithGeninterp.lfun=lfun}inlettac=(Ltac_plugin.Tacinterp.eval_tactic_ististtac:unitProofview.tactic)intac>>=fun()->returnv_unitinletlen=List.lengthidsinifInt.equallen0thenclos[]elsereturn(Tac2ffi.of_closure(Tac2val.abstractlenclos))inletsubsts(ids,tac)=(ids,Gensubst.substituteLtac_plugin.Tacarg.wit_tacticstac)inletprintenvsigma(ids,tac)=letids=ifList.is_emptyidsthenmt()elsepr_sequenceId.printids++spc()++str"|-"++spc()instr"ltac1:("++ids++Ltac_plugin.Pptactic.pr_glob_tacticenvtac++str")"inletraw_printenvsigma(ids,tac)=letids=ifList.is_emptyidsthenmt()elsepr_sequence(funid->Id.printid.CAst.v)ids++spc()++str"|-"++spc()instr"ltac1:("++ids++Ltac_plugin.Pptactic.pr_raw_tacticenvsigmatac++str")"inletobj={ml_intern=intern;ml_subst=subst;ml_interp=interp;ml_print=print;ml_raw_print=raw_print;}indefine_ml_objectTac2quote_ltac1.wit_ltac1objlet()=letopenLtac_plugininletinternist(ids,tac)=letmap{CAst.v=id}=idinletids=List.mapmapidsin(* Prevent inner calls to Ltac2 values *)letextra=Tac2intern.drop_ltac2_envist.Genintern.extrainletltacvars=List.fold_rightId.Set.addidsist.Genintern.ltacvarsinletist={istwithGenintern.extra;ltacvars}inlet_,tac=Genintern.internLtac_plugin.Tacarg.wit_tacticisttacinletfoldty_=GTypArrow(gtypreft_ltac1,ty)inletty=List.fold_leftfold(gtypreft_ltac1)idsinGlbVal(ids,tac),tyinletinterp_(ids,tac)=letclosargs=letaddlfunidv=letv=Tac2ffi.repr_toltac1vinId.Map.addidvlfuninletlfun=List.fold_left2addId.Map.emptyidsargsinletist={env_ist=Id.Map.empty}inletlfun=Tac2interp.set_envistlfuninletist=Ltac_plugin.Tacinterp.default_ist()inletist={istwithGeninterp.lfun=lfun}inreturn(Tac2ffi.repr_ofltac1(Tacinterp.Value.of_closureisttac))inletlen=List.lengthidsinifInt.equallen0thenclos[]elsereturn(Tac2ffi.of_closure(Tac2val.abstractlenclos))inletsubsts(ids,tac)=(ids,Gensubst.substituteTacarg.wit_tacticstac)inletprintenvsigma(ids,tac)=letids=ifList.is_emptyidsthenmt()elsepr_sequenceId.printids++str" |- "instr"ltac1val:("++ids++Ltac_plugin.Pptactic.pr_glob_tacticenvtac++str")"inletraw_printenvsigma(ids,tac)=letids=ifList.is_emptyidsthenmt()elsepr_sequence(funid->Id.printid.CAst.v)ids++spc()++str"|-"++spc()instr"ltac1val:("++ids++Ltac_plugin.Pptactic.pr_raw_tacticenvsigmatac++str")"inletobj={ml_intern=intern;ml_subst=subst;ml_interp=interp;ml_print=print;ml_raw_print=raw_print;}indefine_ml_objectTac2quote_ltac1.wit_ltac1valobj(** Ltac2 in Ltac1 *)(** Embedding Ltac2 closures of type [Ltac1.t -> Ltac1.t] inside Ltac1. There is
no relevant data because arguments are passed by conventional names. *)letwit_ltac2_val:(Util.Empty.t,unit,Util.Empty.t)genarg_type=Genarg.make0"ltac2:Ltac1.lambda"(** Ltac2 quotations in Ltac1 code *)letwit_ltac2in1:(Id.tCAst.tlist*raw_tacexpr,Id.tlist*glb_tacexpr,Util.Empty.t)genarg_type=Genarg.make0"ltac2in1"(** Ltac2 quotations in Ltac1 returning Ltac1 values.
When ids are bound interning turns them into Ltac1.lambda. *)letwit_ltac2in1_val:(Id.tCAst.tlist*raw_tacexpr,glb_tacexpr,Util.Empty.t)genarg_type=Genarg.make0"ltac2in1val"letpr_ltac2in1_idsids=ifList.is_emptyidsthenmt()elsehov0(pr_sequenceId.printids++str" |- ")let()=letpr_raw(ids,e)=Genprint.PrinterBasic(fun_env_sigma->letids=List.map(funv->v.CAst.v)idsinpr_ltac2in1_idsids++Tac2print.pr_rawexpr_genE5~avoid:(Id.Set.of_listids)e)inletpr_glb(ids,e)=Genprint.PrinterBasicPp.(fun_env_sigma->pr_ltac2in1_idsids++Tac2print.pr_glbexpr~avoid:(Id.Set.of_listids)e)inGenprint.register_noval_print0wit_ltac2in1pr_rawpr_glblet()=letpr_raw(ids,e)=Genprint.PrinterBasic(fun_env_sigma->letids=List.map(funv->v.CAst.v)idsinpr_ltac2in1_idsids++Tac2print.pr_rawexpr_genE5~avoid:(Id.Set.of_listids)e)inletpr_glbe=Genprint.PrinterBasic(fun_env_sigma->Tac2print.pr_glbexpr~avoid:Id.Set.emptye)inGenprint.register_noval_print0wit_ltac2in1_valpr_rawpr_glblet()=letopenTac2typing_envinletinternist(ids,tac)=lett_ltac1=monomorphic(GTypRef(Othert_ltac1,[]))inletbnd=List.map(funid->Nameid.CAst.v,t_ltac1)idsinlettac=Tac2intern.genintern_warn_not_unitistbndtacin(ist,(List.map(funid->id.CAst.v)ids,tac))inGenintern.register_intern0wit_ltac2in1internlet()=letadd_lambdaidtac=letpat=CAst.make?loc:id.CAst.loc(CPatVar(Nameid.v))inletloc=tac.CAst.locinletmkv=CAst.make?locvinletlam=mk@@CTacFun([pat],tac)inmk@@CTacApp(mk@@CTacRef(AbsKn(TacConstantltac1_lambda)),[lam])inletinternist(bnd,tac)=lettac=List.fold_rightadd_lambdabndtacinlettac=Tac2intern.geninternist[](GTypRef(Othert_ltac1,[]))tacinist,tacinGenintern.register_intern0wit_ltac2in1_valinternlet()=Gensubst.register_subst0wit_ltac2in1(funs(ids,e)->ids,Tac2intern.subst_exprse)let()=Gensubst.register_subst0wit_ltac2in1_valTac2intern.subst_exprlet()=letcreatenamewit=lete=Tac2entries.Pltac.tac2expr_in_envinletinject(loc,v)=Ltac_plugin.Tacexpr.TacGeneric(Somename,in_gen(rawwitwit)v)inLtac_plugin.Tacentries.create_ltac_quotation~plugin:ltac2_ltac1_pluginnameinject(e,None)inlet()=create"ltac2"wit_ltac2in1inlet()=create"ltac2val"wit_ltac2in1_valin()(* Ltac1 runtime representation of Ltac2 closures. *)lettyp_ltac2:valexprGeninterp.Val.typ=Geninterp.Val.create"ltac2:ltac2_eval"let()=Genprint.register_val_print0typ_ltac2(funv->TopPrinterBasic(fun()->Pp.str"<ltac2 closure>"))letcast_typ(typea)(tag:aGeninterp.Val.typ)(v:Geninterp.Val.t):a=letGeninterp.Val.Dyn(tag',v)=vinmatchGeninterp.Val.eqtagtag'with|None->assertfalse|SomeRefl->vlet()=letopenLtac_pluginin(* This is a hack similar to Tacentries.ml_val_tactic_extend *)letintern_fun_e=Empty.aborteinletsubst_funsv=vinlet()=Genintern.register_intern0wit_ltac2_valintern_funinlet()=Gensubst.register_subst0wit_ltac2_valsubst_funin(* These are bound names and not relevant *)lettac_id=Id.of_string"F"inletarg_id=Id.of_string"X"inletinterp_funist()=lettac=cast_typtyp_ltac2@@Id.Map.gettac_idist.Tacinterp.lfuninletarg=Id.Map.getarg_idist.Tacinterp.lfuninlettac=Tac2ffi.to_closuretacinTac2val.applytac[Tac2ffi.repr_ofltac1arg]>>=funans->letans=Tac2ffi.repr_toltac1ansinFtactic.returnansinlet()=Geninterp.register_interp0wit_ltac2_valinterp_funindefine"ltac1_lambda"(valexpr@->retltac1)@@funf->letbody=Tacexpr.TacGeneric(Someltac2_ltac1_plugin,in_gen(glbwitwit_ltac2_val)())inletclos=CAst.make(Tacexpr.TacFun([Namearg_id],CAst.make(Tacexpr.TacArgbody)))inletf=Geninterp.Val.inject(Geninterp.Val.Basetyp_ltac2)finletlfun=Id.Map.singletontac_idfinletist={(Tacinterp.default_ist())withTacinterp.lfun}inTacinterp.Value.of_closureistclosletltac2_eval=letopenLtac_plugininletml_name={Tacexpr.mltac_plugin=ltac2_ltac1_plugin;mltac_tactic="ltac2_eval";}inleteval_funargsist=matchargswith|[]->assertfalse|tac::args->(* By convention the first argument is the tactic being applied, the rest
being the arguments it should be fed with *)lettac=cast_typtyp_ltac2tacinlettac=Tac2ffi.to_closuretacinletargs=List.map(funarg->Tac2ffi.repr_ofltac1arg)argsinProofview.tclIGNORE(Tac2val.applytacargs)inlet()=Tacenv.register_ml_tacticml_name[|eval_fun|]in{Tacexpr.mltac_name=ml_name;mltac_index=0}let()=letopenLtac_plugininletopenTacinterpinletinterpist(ids,tac)=matchidswith|[]->(* Evaluate the Ltac2 quotation eagerly *)letidtac=Value.of_closure{istwithlfun=Id.Map.empty}(CAst.make(Tacexpr.TacId[]))inletist={env_ist=Id.Map.empty}inTac2interp.interpisttac>>=funv->letv=idtacinFtactic.returnv|_::_->(* Return a closure [@f := {blob} |- fun ids => ltac2_eval(f, ids) ] *)(* This name cannot clash with Ltac2 variables which are all lowercase *)letself_id=Id.of_string"F"inletnas=List.map(funid->Nameid)idsinletmk_argid=Tacexpr.Reference(Locus.ArgVar(CAst.makeid))inletargs=List.mapmk_argidsinletclos=CAst.make(Tacexpr.TacFun(nas,CAst.make(Tacexpr.TacML(ltac2_eval,mk_argself_id::args))))inletself=GTacFun(List.map(funid->Nameid)ids,tac)inletself=Tac2interp.interp_value{env_ist=Id.Map.empty}selfinletself=Geninterp.Val.inject(Geninterp.Val.Basetyp_ltac2)selfinletist={istwithlfun=Id.Map.singletonself_idself}inFtactic.return(Value.of_closureistclos)inGeninterp.register_interp0wit_ltac2in1interplet()=letinterpisttac=letist={env_ist=Id.Map.empty}inTac2interp.interpisttac>>=funv->letv=repr_toltac1vinFtactic.returnvinGeninterp.register_interp0wit_ltac2in1_valinterp