123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556(************************************************************************)(* * 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) *)(************************************************************************)openPpopenCErrorsopenUtilopenNamesopenConstropenTermopsopenEConstropenInductiveopsopenConstr_matchingopenCoqlibopenDeclarationsopenContext.Rel.DeclarationmoduleRelDecl=Context.Rel.Declaration(* I implemented the following functions which test whether a term t
is an inductive but non-recursive type, a general conjunction, a
general disjunction, or a type with no constructors.
They are more general than matching with or_term, and_term, etc,
since they do not depend on the name of the type. Hence, they
also work on ad-hoc disjunctions introduced by the user.
-- Eduardo (6/8/97). *)type'amatching_function=Environ.env->Evd.evar_map->EConstr.constr->'aoptiontypetesting_function=Environ.env->Evd.evar_map->EConstr.constr->boolletmkmetan=Nameops.make_ident"X"(Somen)letmeta1=mkmeta1letmeta2=mkmeta2letmatch_with_non_recursive_typeenvsigmat=matchEConstr.kindsigmatwith|App_->let(hdapp,args)=decompose_appsigmatin(matchEConstr.kindsigmahdappwith|Ind(ind,u)->if(Environ.lookup_mind(fstind)env).mind_finite==CoFinitethenSome(hdapp,args)elseNone|_->None)|_->Noneletis_non_recursive_typeenvsigmat=Option.has_some(match_with_non_recursive_typeenvsigmat)(* Test dependencies *)(* NB: we consider also the let-in case in the following function,
since they may appear in types of inductive constructors (see #2629) *)letrechas_nodep_prod_afternenvsigmac=matchEConstr.kindsigmacwith|Prod(_,_,b)|LetIn(_,_,_,b)->(n>0||Vars.noccurnsigma1b)&&(has_nodep_prod_after(n-1)envsigmab)|_->truelethas_nodep_prodenvsigmac=has_nodep_prod_after0envsigmac(* A general conjunctive type is a non-recursive with-no-indices inductive
type with only one constructor and no dependencies between argument;
it is strict if it has the form
"Inductive I A1 ... An := C (_:A1) ... (_:An)" *)(* style: None = record; Some false = conjunction; Some true = strict conj *)letis_strict_conjunction=function|Sometrue->true|_->falseletis_lax_conjunction=function|Somefalse->true|_->falseletprod_assumsigmat=fst(decompose_prod_assumsigmat)(* whd_beta normalize the types of arguments in a product *)letrecwhd_beta_prodenvsigmac=matchEConstr.kindsigmacwith|Prod(n,t,c)->mkProd(n,Reductionops.whd_betaenvsigmat,whd_beta_prodenvsigmac)|LetIn(n,d,t,c)->mkLetIn(n,d,t,whd_beta_prodenvsigmac)|_->cletmatch_with_one_constructorenvsigmastyleonlybinaryallow_rect=let(hdapp,args)=decompose_appsigmatinletres=matchEConstr.kindsigmahdappwith|Indind->let(mib,mip)=Inductive.lookup_mind_specifenv(fstind)inifInt.equal(Array.lengthmip.mind_consnames)1&&(allow_rec||not(mis_is_recursive(fstind,mib,mip)))&&(Int.equalmip.mind_nrealargs0)thenifis_strict_conjunctionstyle(* strict conjunction *)thenlet(ctx,_)=mip.mind_nf_lc.(0)inletctx=List.skipn(Context.Rel.lengthmib.mind_params_ctxt)(List.revctx)inif(* Constructor has a type of the form
c : forall (a_0 ... a_n : Type) (x_0 : A_0) ... (x_n : A_n). T **)List.for_all(fundecl->letc=RelDecl.get_typedeclinis_local_assumdecl&&Constr.isRelc&&Int.equal(Constr.destRelc)mib.mind_nparams)ctxthenSome(hdapp,args)elseNoneelseletctx,cty=mip.mind_nf_lc.(0)inletcty=EConstr.of_constr(Term.it_mkProd_or_LetInctyctx)inletctyp=whd_beta_prodenvsigma(Termops.prod_applist_assumsigma(Context.Rel.lengthmib.mind_params_ctxt)ctyargs)inletcargs=List.mapRelDecl.get_type(prod_assumsigmactyp)inifnot(is_lax_conjunctionstyle)||has_nodep_prodenvsigmactypthen(* Record or non strict conjunction *)Some(hdapp,List.revcargs)elseNoneelseNone|_->Noneinmatchreswith|Some(hdapp,args)whennotonlybinary->res|Some(hdapp,[_;_])->res|_->Noneletmatch_with_conjunction?(strict=false)?(onlybinary=false)envsigmat=match_with_one_constructorenvsigma(Somestrict)onlybinaryfalsetletmatch_with_recordenvsigmat=match_with_one_constructorenvsigmaNonefalsefalsetletis_conjunction?(strict=false)?(onlybinary=false)envsigmat=Option.has_some(match_with_conjunctionenvsigma~strict~onlybinaryt)letis_recordenvsigmat=Option.has_some(match_with_recordenvsigmat)letmatch_with_tupleenvsigmat=lett=match_with_one_constructorenvsigmaNonefalsetruetinOption.map(fun(hd,l)->letind,_=destIndsigmahdinlet(mib,mip)=Inductive.lookup_mind_specifenvindinletisrec=mis_is_recursive(ind,mib,mip)in(hd,l,isrec))tletis_tupleenvsigmat=Option.has_some(match_with_tupleenvsigmat)(* A general disjunction type is a non-recursive with-no-indices inductive
type with of which all constructors have a single argument;
it is strict if it has the form
"Inductive I A1 ... An := C1 (_:A1) | ... | Cn : (_:An)" *)lettest_strict_disjunction(mib,mip)=letn=List.lengthmib.mind_params_ctxtinletchecki(ctx,_)=matchList.skipnn(List.revctx)with|[LocalAssum(_,c)]->Constr.isRelc&&Int.equal(Constr.destRelc)(n-i)|_->falseinArray.for_all_icheck0mip.mind_nf_lcletmatch_with_disjunction?(strict=false)?(onlybinary=false)envsigmat=let(hdapp,args)=decompose_appsigmatinletres=matchEConstr.kindsigmahdappwith|Ind(ind,u)->letcar=constructors_nrealargsenvindinlet(mib,mip)=Inductive.lookup_mind_specifenvindinifArray.for_all(funar->Int.equalar1)car&¬(mis_is_recursive(ind,mib,mip))&&(Int.equalmip.mind_nrealargs0)thenifstricttheniftest_strict_disjunction(mib,mip)thenSome(hdapp,args)elseNoneelseletmap(ctx,cty)=letar=EConstr.of_constr(Term.it_mkProd_or_LetInctyctx)inpi2(destProdsigma(prod_applistsigmaarargs))inletcargs=Array.mapmapmip.mind_nf_lcinSome(hdapp,Array.to_listcargs)elseNone|_->Noneinmatchreswith|Some(hdapp,args)whennotonlybinary->res|Some(hdapp,[_;_])->res|_->Noneletis_disjunction?(strict=false)?(onlybinary=false)envsigmat=Option.has_some(match_with_disjunction~strict~onlybinaryenvsigmat)(* An empty type is an inductive type, possible with indices, that has no
constructors *)letmatch_with_empty_typeenvsigmat=let(hdapp,args)=decompose_appsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinletnconstr=Array.lengthmip.mind_consnamesinifInt.equalnconstr0thenSomehdappelseNone|_->Noneletis_empty_typeenvsigmat=Option.has_some(match_with_empty_typeenvsigmat)(* This filters inductive types with one constructor with no arguments;
Parameters and indices are allowed *)letmatch_with_unit_or_eq_typeenvsigmat=let(hdapp,args)=decompose_appsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinletnconstr=Array.lengthmip.mind_consnamesinifInt.equalnconstr1&&Int.equalmip.mind_consnrealargs.(0)0thenSomehdappelseNone|_->Noneletis_unit_or_eq_typeenvsigmat=Option.has_some(match_with_unit_or_eq_typeenvsigmat)(* A unit type is an inductive type with no indices but possibly
(useless) parameters, and that has no arguments in its unique
constructor *)letis_unit_typeenvsigmat=matchmatch_with_conjunctionenvsigmatwith|Some(_,[])->true|_->false(* Checks if a given term is an application of an
inductive binary relation R, so that R has only one constructor
establishing its reflexivity. *)typeequation_kind=|MonomorphicLeibnizEqofconstr*constr|PolymorphicLeibnizEqofconstr*constr*constr|HeterogenousEqofconstr*constr*constr*constrexceptionNoEquationFoundopenGlob_termopenEvar_kindsletmkPatternc=snd(Patternops.pattern_of_glob_constrc)letmkGAppfargs=DAst.make@@GApp(f,args)letmkGHole=DAst.make@@GHole(QuestionMark{Evar_kinds.default_question_markwithEvar_kinds.qm_obligation=Definefalse;},Namegen.IntroAnonymous,None)letmkGProdidc1c2=DAst.make@@GProd(Name(Id.of_stringid),Explicit,c1,c2)letmkGArrowc1c2=DAst.make@@GProd(Anonymous,Explicit,c1,c2)letmkGVarid=DAst.make@@GVar(Id.of_stringid)letmkGPatVarid=DAst.make@@GPatVar(Evar_kinds.FirstOrderPatVar(Id.of_stringid))letmkGRefr=DAst.make@@GRef(Lazy.forcer,None)letmkGAppRefrargs=mkGApp(mkGRefr)args(** forall x : _, _ x x *)letcoq_refl_leibniz1_pattern=mkPattern(mkGProd"x"mkGHole(mkGAppmkGHole[mkGVar"x";mkGVar"x";]))(** forall A:_, forall x:A, _ A x x *)letcoq_refl_leibniz2_pattern=mkPattern(mkGProd"A"mkGHole(mkGProd"x"(mkGVar"A")(mkGAppmkGHole[mkGVar"A";mkGVar"x";mkGVar"x";])))(** forall A:_, forall x:A, _ A x A x *)letcoq_refl_jm_pattern=mkPattern(mkGProd"A"mkGHole(mkGProd"x"(mkGVar"A")(mkGAppmkGHole[mkGVar"A";mkGVar"x";mkGVar"A";mkGVar"x";])))letmatch_with_equationenvsigmat=ifnot(isAppsigmat)thenraiseNoEquationFound;let(hdapp,args)=destAppsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,u)->(tryifCoqlib.check_ind_ref"core.eq.type"indthenSome(build_coq_eq_data()),hdapp,PolymorphicLeibnizEq(args.(0),args.(1),args.(2))elseifCoqlib.check_ind_ref"core.identity.type"indthenSome(build_coq_identity_data()),hdapp,PolymorphicLeibnizEq(args.(0),args.(1),args.(2))elseifCoqlib.check_ind_ref"core.JMeq.type"indthenSome(build_coq_jmeq_data()),hdapp,HeterogenousEq(args.(0),args.(1),args.(2),args.(3))elselet(mib,mip)=Inductive.lookup_mind_specifenvindinletconstr_types=mip.mind_nf_lcinletnconstr=Array.lengthmip.mind_consnamesinifInt.equalnconstr1thenlet(ctx,cty)=constr_types.(0)inletcty=EConstr.of_constr(Term.it_mkProd_or_LetInctyctx)inifis_matchingenvsigmacoq_refl_leibniz1_patternctythenNone,hdapp,MonomorphicLeibnizEq(args.(0),args.(1))elseifis_matchingenvsigmacoq_refl_leibniz2_patternctythenNone,hdapp,PolymorphicLeibnizEq(args.(0),args.(1),args.(2))elseifis_matchingenvsigmacoq_refl_jm_patternctythenNone,hdapp,HeterogenousEq(args.(0),args.(1),args.(2),args.(3))elseraiseNoEquationFoundelseraiseNoEquationFoundwithUserError_->raiseNoEquationFound)|_->raiseNoEquationFound(* Note: An "equality type" is any type with a single argument-free
constructor: it captures eq, eq_dep, JMeq, eq_true, etc. but also
True/unit which is the degenerate equality type (isomorphic to ()=());
in particular, True/unit are provable by "reflexivity" *)letis_inductive_equalityenvind=let(mib,mip)=Inductive.lookup_mind_specifenvindinletnconstr=Array.lengthmip.mind_consnamesinInt.equalnconstr1&&Int.equal(constructor_nrealargsenv(ind,1))0letmatch_with_equality_typeenvsigmat=let(hdapp,args)=decompose_appsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,_)whenis_inductive_equalityenvind->Some(hdapp,args)|_->Noneletis_equality_typeenvsigmat=Option.has_some(match_with_equality_typeenvsigmat)(* Arrows/Implication/Negation *)(** X1 -> X2 **)letcoq_arrow_pattern=mkPattern(mkGArrow(mkGPatVar"X1")(mkGPatVar"X2"))letmatch_arrow_patternenvsigmat=letresult=matchesenvsigmacoq_arrow_patterntinmatchId.Map.bindingsresultwith|[(m1,arg);(m2,mind)]->assert(Id.equalm1meta1&&Id.equalm2meta2);(arg,mind)|_->anomaly(Pp.str"Incorrect pattern matching.")letmatch_with_imp_termenvsigmac=matchEConstr.kindsigmacwith|Prod(_,a,b)whenVars.noccurnsigma1b->Some(a,b)|_->Noneletis_imp_termenvsigmac=Option.has_some(match_with_imp_termenvsigmac)letmatch_with_nottypeenvsigmat=trylet(arg,mind)=match_arrow_patternenvsigmatinifis_empty_typeenvsigmamindthenSome(mind,arg)elseNonewithPatternMatchingFailure->Noneletis_nottypeenvsigmat=Option.has_some(match_with_nottypeenvsigmat)(* Forall *)letmatch_with_forall_termenvsigmac=matchEConstr.kindsigmacwith|Prod(nam,a,b)->Some(nam,a,b)|_->Noneletis_forall_termenvsigmac=Option.has_some(match_with_forall_termenvsigmac)letmatch_with_nodep_indenvsigmat=let(hdapp,args)=decompose_appsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinifArray.length(mib.mind_packets)>1thenNoneelseletnodep_constr(ctx,cty)=letc=EConstr.of_constr(Term.it_mkProd_or_LetInctyctx)inhas_nodep_prod_after(Context.Rel.lengthmib.mind_params_ctxt)envsigmacinifArray.for_allnodep_constrmip.mind_nf_lcthenletparams=ifInt.equalmip.mind_nrealargs0thenargselsefst(List.chopmib.mind_nparamsargs)inSome(hdapp,params,mip.mind_nrealargs)elseNone|_->Noneletis_nodep_indenvsigmat=Option.has_some(match_with_nodep_indenvsigmat)letmatch_with_sigma_typeenvsigmat=let(hdapp,args)=decompose_appsigmatinmatchEConstr.kindsigmahdappwith|Ind(ind,_)->let(mib,mip)=Inductive.lookup_mind_specifenvindinifInt.equal(Array.length(mib.mind_packets))1&&(Int.equalmip.mind_nrealargs0)&&(Int.equal(Array.lengthmip.mind_consnames)1)&&has_nodep_prod_after(Context.Rel.lengthmib.mind_params_ctxt+1)envsigma(let(ctx,cty)=mip.mind_nf_lc.(0)inEConstr.of_constr(Term.it_mkProd_or_LetInctyctx))then(*allowing only 1 existential*)Some(hdapp,args)elseNone|_->Noneletis_sigma_typeenvsigmat=Option.has_some(match_with_sigma_typeenvsigmat)(***** Destructing patterns bound to some theory *)letrecfirst_matchmatcher=function|[]->raisePatternMatchingFailure|(pat,check,build_set)::lwhencheck()->(try(build_set(),matcherpat)withPatternMatchingFailure->first_matchmatcherl)|_::l->first_matchmatcherl(*** Equality *)letmatch_eqsigmaeqn(ref,hetero)=letref=tryLazy.forcerefwithewhenCErrors.noncriticale->raisePatternMatchingFailureinmatchEConstr.kindsigmaeqnwith|App(c,[|t;x;y|])->ifnothetero&&isRefXsigmarefcthenPolymorphicLeibnizEq(t,x,y)elseraisePatternMatchingFailure|App(c,[|t;x;t';x'|])->ifhetero&&isRefXsigmarefcthenHeterogenousEq(t,x,t',x')elseraisePatternMatchingFailure|_->raisePatternMatchingFailureletno_check()=trueletcheck_jmeq_loaded()=has_ref"core.JMeq.type"letequalities=[(lazy(lib_ref"core.eq.type"),false),no_check,build_coq_eq_data;(lazy(lib_ref"core.JMeq.type"),true),check_jmeq_loaded,build_coq_jmeq_data;(lazy(lib_ref"core.identity.type"),false),no_check,build_coq_identity_data]letfind_eq_datasigmaeqn=(* fails with PatternMatchingFailure *)letd,k=first_match(match_eqsigmaeqn)equalitiesinlethd,u=destIndsigma(fst(destAppsigmaeqn))ind,u,kletextract_eq_argsenvsigma=function|MonomorphicLeibnizEq(e1,e2)->lett=Retyping.get_type_ofenvsigmae1in(t,e1,e2)|PolymorphicLeibnizEq(t,e1,e2)->(t,e1,e2)|HeterogenousEq(t1,e1,t2,e2)->ifReductionops.is_convenvsigmat1t2then(t1,e1,e2)elseraisePatternMatchingFailureletfind_eq_data_decomposeenvsigmaeqn=let(lbeq,u,eq_args)=find_eq_datasigmaeqnin(lbeq,u,extract_eq_argsenvsigmaeq_args)letfind_this_eq_data_decomposeenvsigmaeqn=let(lbeq,u,eq_args)=try(*first_match (match_eq eqn) inversible_equalities*)find_eq_datasigmaeqnwithPatternMatchingFailure->user_err(str"No primitive equality found.")inleteq_args=tryextract_eq_argsenvsigmaeq_argswithPatternMatchingFailure->user_errPp.(str"Don't know what to do with JMeq on arguments not of same type.")in(lbeq,u,eq_args)(*** Sigma-types *)letmatch_sigmaenvsigmaex=matchEConstr.kindsigmaexwith|App(f,[|a;p;car;cdr|])whenisRefXsigma(lib_ref"core.sig.intro")f->build_sigma(),(snd(destConstructsigmaf),a,p,car,cdr)|App(f,[|a;p;car;cdr|])whenisRefXsigma(lib_ref"core.sigT.intro")f->build_sigma_type(),(snd(destConstructsigmaf),a,p,car,cdr)|_->raisePatternMatchingFailureletfind_sigma_data_decomposeenvex=(* fails with PatternMatchingFailure *)match_sigmaenvex(* Pattern "(sig ?1 ?2)" *)letcoq_sig_pattern=lazy(mkPattern(mkGAppRef(lazy(lib_ref"core.sig.type"))[mkGPatVar"X1";mkGPatVar"X2"]))letmatch_sigmaenvsigmat=matchId.Map.bindings(matchesenvsigma(Lazy.forcecoq_sig_pattern)t)with|[(_,a);(_,p)]->(a,p)|_->anomaly(Pp.str"Unexpected pattern.")letis_matching_sigmaenvsigmat=is_matchingenvsigma(Lazy.forcecoq_sig_pattern)t(*** Decidable equalities *)(* The expected form of the goal for the tactic Decide Equality *)(* Pattern "{<?1>x=y}+{~(<?1>x=y)}" *)(* i.e. "(sumbool (eq ?1 x y) ~(eq ?1 x y))" *)letcoq_eqdec~sum~rev=lazy(leteqn=mkGAppRef(lazy(lib_ref"core.eq.type"))(List.mapmkGPatVar["X1";"X2";"X3"])inletargs=[eqn;mkGAppRef(lazy(lib_ref"core.not.type"))[eqn]]inletargs=ifrevthenList.revargselseargsinmkPattern(mkGAppRefsumargs))letsumbool_type=lazy(lib_ref"core.sumbool.type")letor_type=lazy(lib_ref"core.or.type")(** [{ ?X2 = ?X3 :> ?X1 } + { ~ ?X2 = ?X3 :> ?X1 }] *)letcoq_eqdec_inf_pattern=coq_eqdec~sum:sumbool_type~rev:false(** [{ ~ ?X2 = ?X3 :> ?X1 } + { ?X2 = ?X3 :> ?X1 }] *)letcoq_eqdec_inf_rev_pattern=coq_eqdec~sum:sumbool_type~rev:true(** %coq_or_ref (?X2 = ?X3 :> ?X1) (~ ?X2 = ?X3 :> ?X1) *)letcoq_eqdec_pattern=coq_eqdec~sum:or_type~rev:false(** %coq_or_ref (~ ?X2 = ?X3 :> ?X1) (?X2 = ?X3 :> ?X1) *)letcoq_eqdec_rev_pattern=coq_eqdec~sum:or_type~rev:trueletmatch_eqdecenvsigmat=leteqonleft,op,subst=trytrue,sumbool_type,matchesenvsigma(Lazy.forcecoq_eqdec_inf_pattern)twithPatternMatchingFailure->tryfalse,sumbool_type,matchesenvsigma(Lazy.forcecoq_eqdec_inf_rev_pattern)twithPatternMatchingFailure->trytrue,or_type,matchesenvsigma(Lazy.forcecoq_eqdec_pattern)twithPatternMatchingFailure->false,or_type,matchesenvsigma(Lazy.forcecoq_eqdec_rev_pattern)tinmatchId.Map.bindingssubstwith|[(_,typ);(_,c1);(_,c2)]->eqonleft,Lazy.forceop,c1,c2,typ|_->anomaly(Pp.str"Unexpected pattern.")(* Patterns "~ ?" and "? -> False" *)letcoq_not_pattern=lazy(mkPattern(mkGAppRef(lazy(lib_ref"core.not.type"))[mkGHole]))letcoq_imp_False_pattern=lazy(mkPattern(mkGArrowmkGHole(mkGRef(lazy(lib_ref"core.False.type")))))letis_matching_notenvsigmat=is_matchingenvsigma(Lazy.forcecoq_not_pattern)tletis_matching_imp_Falseenvsigmat=is_matchingenvsigma(Lazy.forcecoq_imp_False_pattern)t(* Remark: patterns that have references to the standard library must
be evaluated lazily (i.e. at the time they are used, not a the time
coqtop starts) *)