123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326(************************************************************************)(* * 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) *)(************************************************************************)openPpopenUtilopenNamesopenConstrintern(* 3c| Fixpoints and co-fixpoints *)(* An (unoptimized) function that maps preorders to partial orders...
Input: a list of associations (x,[y1;...;yn]), all yi distincts
and different of x, meaning x<=y1, ..., x<=yn
Output: a list of associations (x,Inr [y1;...;yn]), collecting all
distincts yi greater than x, _or_, (x, Inl y) meaning that
x is in the same class as y (in which case, x occurs
nowhere else in the association map)
partial_order : ('a * 'a list) list -> ('a * ('a,'a list) union) list
*)letrecpartial_ordercmp=function|[]->[]|(x,xge)::rest->letrecbrowseresxge'=function|[]->letres=List.map(function|(z,Inrzge)whenList.mem_fcmpxzge->(z,Inr(List.unioncmpzgexge'))|r->r)resin(x,Inrxge')::res|y::xge->letreclinky=trymatchList.assoc_fcmpyreswith|Inlz->linkz|Inryge->ifList.mem_fcmpxygethenletres=List.remove_assoc_fcmpyresinletres=List.map(function|(z,Inlt)->ifcmptythen(z,Inlx)else(z,Inlt)|(z,Inrzge)->ifList.mem_fcmpyzgethen(z,Inr(List.add_setcmpx(List.removecmpyzge)))else(z,Inrzge))resinbrowse((y,Inlx)::res)xge'(List.unioncmpxgeyge)elsebrowseres(List.add_setcmpy(List.unioncmpxge'yge))xgewithNot_found->browseres(List.add_setcmpyxge')xgeinlinkyinbrowse(partial_ordercmprest)[]xgeletnon_full_mutual_messagexxgeyygeisfixrest=letreason=ifId.List.memxygethenId.printy++str" depends on "++Id.printx++strbrk" but not conversely"elseifId.List.memyxgethenId.printx++str" depends on "++Id.printy++strbrk" but not conversely"elseId.printy++str" and "++Id.printx++strbrk" are not mutually dependent"inlete=ifList.is_emptyrestthenreasonelsestrbrk"e.g., "++reasoninletk=ifisfixthen"fixpoint"else"cofixpoint"inletw=ifisfixthenstrbrk"Well-foundedness check may fail unexpectedly."++fnl()elsemt()instrbrk"Not a fully mutually defined "++strk++fnl()++str"("++e++str")."++fnl()++wletwarn_non_full_mutual=CWarnings.create~name:"non-full-mutual"~category:CWarnings.CoreCategories.fixpoints(fun(x,xge,y,yge,isfix,rest)->non_full_mutual_messagexxgeyygeisfixrest)letwarn_non_recursive=CWarnings.create~name:"non-recursive"~category:CWarnings.CoreCategories.fixpoints(fun(x,isfix)->letk=ifisfixthen"fixpoint"else"cofixpoint"instrbrk"Not a truly recursive "++strk++str".")letcheck_true_recursivityenvevd~isfixfixl=letnames=List.mapfstfixlinletpreorder=List.map(fun(id,def)->(id,List.filter(funid'->Termops.occur_varenvevdid'def)names))fixlinletpo=partial_orderId.equalpreorderinmatchList.filter(function(_,Inr_)->true|_->false)powith|(x,Inrxge)::(y,Inryge)::rest->warn_non_full_mutual(x,xge,y,yge,isfix,rest)|_->matchpowith|[x,Inr[]]->warn_non_recursive(x,isfix)|_->()(* Interpret the index of a recursion order annotation *)exceptionFoundofintletfind_rec_annotblctxna=letname=Namena.CAst.vintryContext.Rel.fold_outside(fundecln->matchContext.Rel.Declaration.(get_valuedecl,Name.equal(get_namedecl)name)with|None,true->raise(Foundn)|Some_,true->letloc=List.find_map(funid->ifName.equalnameid.CAst.vthenSomeid.CAst.locelseNone)(Constrexpr_ops.names_of_local_bindersbl)inletloc=Option.defaultna.CAst.loclocinCErrors.user_err?loc(Name.printname++str" must be a proper parameter and not a local definition.")|None,false->n+1|Some_,false->n(* let-ins don't count *))~init:0ctx|>ignore;CErrors.user_err?loc:na.loc(str"No parameter named "++Id.printna.v++str".");withFoundk->kletinterp_fix_context~program_mode~cofixenvsigmafix=letsigma,(impl_env,((env',ctx),imps))=interp_context_evars~program_modeenvsigmafix.Vernacexpr.bindersinifnotcofix&&Context.Rel.nhypsctx=0thenCErrors.user_errPp.(str"A fixpoint needs at least one parameter.");letannot=Option.map(find_rec_annotfix.Vernacexpr.bindersctx)fix.Vernacexpr.rec_orderinsigma,((env',ctx),(impl_env,imps),annot)letinterp_fix_ccl~program_modesigmaimpls(env,_)fix=letflags=Pretyping.{all_no_fail_flagswithprogram_mode}inletsigma,(c,impl)=interp_type_evars_impls~flags~implsenvsigmafix.Vernacexpr.rtypeinletr=Retyping.relevance_of_typeenvsigmacinsigma,(c,r,impl)letinterp_fix_body~program_modeenv_recsigmaimpls(_,ctx)fixccl=letopenEConstrinOption.cata(funbody->letenv=push_rel_contextctxenv_recinletsigma,body=interp_casted_constr_evars~program_modeenvsigma~implsbodycclinsigma,Some(it_mkLambda_or_LetInbodyctx))(sigma,None)fix.Vernacexpr.body_defletbuild_fix_type(_,ctx)ccl=EConstr.it_mkProd_or_LetIncclctx(* Jump over let-bindings. *)letcompute_possible_guardness_evidences(ctx,_,recindex)=(* A recursive index is characterized by the number of lambdas to
skip before finding the relevant inductive argument *)matchrecindexwith|Somei->[i]|None->(* If recursive argument was not given by user, we try all args.
An earlier approach was to look only for inductive arguments,
but doing it properly involves delta-reduction, and it finally
doesn't seem to worth the effort (except for huge mutual
fixpoints ?) *)List.interval0(Context.Rel.nhypsctx-1)type('constr,'types,'r)recursive_preentry=Id.tlist*'rlist*'constroptionlist*'typeslist(* Wellfounded definition *)letfix_protosigma=Evd.fresh_global(Global.env())sigma(Coqlib.lib_ref"program.tactic.fix_proto")letfix_proto_relevance=EConstr.ERelevance.relevant(* Would probably be overkill to use a specific fix_proto in SProp when in SProp?? *)letinterp_recursive_evarsenv~program_mode~cofix(fixl:'aVernacexpr.fix_expr_genlist)=letopenContext.Named.DeclarationinletopenEConstrinletfixnames=List.map(funfix->fix.Vernacexpr.fname.CAst.v)fixlin(* Interp arities allowing for unresolved types *)letsigma,decl=interp_mutual_univ_decl_optenv(List.map(funVernacexpr.{univs}->univs)fixl)inletsigma,(fixctxs,fiximppairs,fixannots)=on_sndList.split3@@List.fold_left_map(funsigma->interp_fix_context~program_modeenvsigma~cofix)sigmafixlinletfixctximpenvs,fixctximps=List.splitfiximppairsinletsigma,(fixccls,fixrs,fixcclimps)=on_sndList.split3@@List.fold_left3_map(interp_fix_ccl~program_mode)sigmafixctximpenvsfixctxsfixlinletfixtypes=List.map2build_fix_typefixctxsfixcclsinletfixtypes=List.map(func->Evarutil.nf_evarsigmac)fixtypesinletfiximps=List.map3(functximpscclimps(_,ctx)->ctximps@cclimps)fixctximpsfixcclimpsfixctxsinletsigma,rec_sign=List.fold_left3(fun(sigma,env')idrt->ifprogram_modethenletsigma,sort=Typing.type_of~refresh:trueenvsigmatinletsigma,fixprot=tryletsigma,h_term=fix_protosigmainletapp=mkApp(h_term,[|sort;t|])inTyping.solve_evarsenvsigmaappwithewhenCErrors.noncriticale->sigma,tinsigma,LocalAssum(Context.make_annotidfix_proto_relevance,fixprot)::env'elsesigma,LocalAssum(Context.make_annotidr,t)::env')(sigma,[])fixnamesfixrsfixtypesinletenv_rec=push_named_contextrec_signenvin(* Get interpretation metadatas *)letimpls=compute_internalization_envenvsigmaRecursivefixnamesfixtypesfiximpsin(* Interp bodies with rollback because temp use of notations/implicit *)letsigma,fixdefs=Metasyntax.with_syntax_protection(fun()->letnotations=List.map_append(fun{Vernacexpr.notations}->List.mapMetasyntax.prepare_where_notationnotations)fixlinList.iter(Metasyntax.set_notation_for_interpretationenv_recimpls)notations;List.fold_left4_map(funsigmafixctximpenv->interp_fix_body~program_modeenv_recsigma(Id.Map.foldId.Map.addfixctximpenvimpls))sigmafixctximpenvsfixctxsfixlfixccls)()in(* Instantiate evars and check all are resolved *)letsigma=Evarconv.solve_unif_constraints_with_heuristicsenv_recsigmainletsigma=Evd.minimize_universessigmainletfixctxs=List.map(fun(_,ctx)->ctx)fixctxsin(* Build the fix declaration block *)(env,rec_sign,decl,sigma),(fixnames,fixrs,fixdefs,fixtypes),List.combine3fixctxsfiximpsfixannotsletcheck_recursive~isfixenvevd(fixnames,_,fixdefs,_)=ifList.for_allOption.has_somefixdefsthenbeginletfixdefs=List.mapOption.getfixdefsincheck_true_recursivityenvevd~isfix(List.combinefixnamesfixdefs)endletground_fixpointenvevd(fixnames,fixrs,fixdefs,fixtypes)=Pretyping.check_evars_are_solved~program_mode:falseenvevd;letfixrs=List.map(funr->EConstr.ERelevance.kindevdr)fixrsinletfixdefs=List.map(func->Option.mapEConstr.(to_constrevd)c)fixdefsinletfixtypes=List.mapEConstr.(to_constrevd)fixtypesinEvd.evar_universe_contextevd,(fixnames,fixrs,fixdefs,fixtypes)(* XXX: Unify with interp_recursive *)letinterp_recursive?(check_recursivity=true)?typing_flags~cofixl:((Constr.t,Constr.types,Sorts.relevance)recursive_preentry*UState.universe_decl*UState.t*(EConstr.rel_context*Impargs.manual_implicits*intoption)list)=letenv=Global.env()inletenv=Environ.update_typing_flags?typing_flagsenvinlet(env,_,pl,evd),fix,info=interp_recursive_evarsenv~program_mode:false~cofixlinifcheck_recursivitythencheck_recursive~isfix:(notcofix)envevdfix;letevd=Pretyping.(solve_remaining_evarsall_no_fail_flagsenvevd)inletuctx,fix=ground_fixpointenvevdfixin(fix,pl,uctx,info)letbuild_recthms~indexesfixnamesfixtypesfiximps=letfix_kind,possible_guard=matchindexeswith|Somepossible_fix_indices->Decls.Fixpoint,Pretyping.{possibly_cofix=false;possible_fix_indices}|None->Decls.CoFixpoint,Pretyping.{possibly_cofix=true;possible_fix_indices=List.map(fun_->[])fixtypes}inletthms=List.map3(funnametyp(ctx,impargs,_)->letargs=List.mapContext.Rel.Declaration.get_namectxinDeclare.CInfo.make~name~typ~args~impargs())fixnamesfixtypesfiximpsinfix_kind,possible_guard,thmsletdeclare_recursive?indexes?scope?clearbody~poly?typing_flags?user_warns?using((fixnames,fixrs,fixdefs,fixtypes),udecl,uctx,fiximps)ntns=letfix_kind,possible_guard,cinfo=build_recthms~indexesfixnamesfixtypesfiximpsinletkind=Decls.IsDefinitionfix_kindinletinfo=Declare.Info.make?scope?clearbody~kind~poly~udecl?typing_flags?user_warns~ntns()inmatchOption.List.map(funx->x)fixdefswith|Somefixdefs->(* All bodies are defined *)let_:GlobRef.tlist=Declare.declare_mutual_definitions~cinfo~info~opaque:false~uctx~possible_guard~bodies:(fixdefs,fixrs)?using()inNone|None->(* At least one undefined body *)letevd=Evd.from_ctxuctxinletlemma=Declare.Proof.start_mutual_definitions~info~cinfo~bodies:fixdefs~possible_guard?usingevdinSomelemmaletextract_decreasing_argument~structonly{CAst.v=v;_}=letopenConstrexprinmatchvwith|CStructRecna->na|(CWfRec(na,_)|CMeasureRec(Somena,_,_))whennotstructonly->na|CMeasureRec(None,_,_)whennotstructonly->CErrors.user_errPp.(str"Decreasing argument must be specified in measure clause.")|_->CErrors.user_errPp.(str"Well-founded induction requires Program Fixpoint or Function.")(* This is a special case: if there's only one binder, we pick it as
the recursive argument if none is provided. *)letadjust_rec_order~structonlybindersrec_order=letrec_order=Option.map(funrec_order->letopenConstrexprinmatchbinders,rec_orderwith|[CLocalAssum([{CAst.v=Namex}],_,_,_)],{CAst.v=CMeasureRec(None,mes,rel);CAst.loc}->CAst.make?loc@@CMeasureRec(Some(CAst.makex),mes,rel)|[CLocalDef({CAst.v=Namex},_,_,_)],{CAst.v=CMeasureRec(None,mes,rel);CAst.loc}->CAst.make?loc@@CMeasureRec(Some(CAst.makex),mes,rel)|_,x->x)rec_orderinOption.map(extract_decreasing_argument~structonly)rec_orderletdo_fixpoint?scope?clearbody~poly?typing_flags?user_warns?using(fixl:Vernacexpr.fixpoint_exprlist):Declare.Proof.toption=letfixl=List.map(funfix->Vernacexpr.{fixwithrec_order=adjust_rec_order~structonly:truefix.bindersfix.rec_order})fixlinletntns=List.map_append(fun{Vernacexpr.notations}->List.mapMetasyntax.prepare_where_notationnotations)fixlinlet(_,_,_,infoasfix)=interp_recursive~cofix:false?typing_flagsfixlinletpossible_indexes=List.mapcompute_possible_guardness_evidencesinfoindeclare_recursive~indexes:possible_indexes?scope?clearbody~poly?typing_flags?user_warns?usingfixntnsletdo_cofixpoint?scope?clearbody~poly?typing_flags?user_warns?using(fixl:Vernacexpr.cofixpoint_exprlist)=letfixl=List.map(funfix->{fixwithVernacexpr.rec_order=None})fixlinletntns=List.map_append(fun{Vernacexpr.notations}->List.mapMetasyntax.prepare_where_notationnotations)fixlinletcofix,ntns=interp_recursive~cofix:truefixl,ntnsindeclare_recursive?scope?clearbody~poly?typing_flags?user_warns?usingcofixntns