123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238(************************************************************************)(* * 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) *)(************************************************************************)openNamesopenContext.Named.DeclarationmoduleNamedDecl=Context.Named.Declarationtypecontext=Constr_matching.contexttyperesult={subst:Ltac_pretype.patvar_map;}typematch_pattern=|MatchPatternofPattern.constr_pattern|MatchContextofPattern.constr_patterntypematch_context_hyps=match_patternoption*match_patterntypematch_rule=match_context_hypslist*match_pattern(** {6 Utilities} *)(** Tests whether the substitution [s] is empty. *)letis_empty_subst=Id.Map.is_empty(** {6 Non-linear patterns} *)(** The patterns of Ltac are not necessarily linear. Non-linear
pattern are partially handled by the {!Matching} module, however
goal patterns are not primitive to {!Matching}, hence we must deal
with non-linearity between hypotheses and conclusion. Subterms are
considered equal up to the equality implemented in
[equal_instances]. *)(* spiwack: it doesn't seem to be quite the same rule for non-linear
term patterns and non-linearity between hypotheses and/or
conclusion. Indeed, in [Matching], matching is made modulo
syntactic equality, and here we merge modulo conversion. It may be
a good idea to have an entry point of [Matching] with a partial
substitution as argument instead of merging substitution here. That
would ensure consistency. *)letequal_instancesenvsigmac1c2=(* How to compare instances? Do we want the terms to be convertible?
unifiable? Do we want the universe levels to be relevant?
(historically, conv_x is used) *)Reductionops.is_convenvsigmac1c2(** Merges two substitutions. Raises [Not_coherent_metas] when
encountering two instances of the same metavariable which are not
equal according to {!equal_instances}. *)exceptionNot_coherent_metasletverify_metas_coherenceenvsigmas1s2=letmergeidoc1oc2=matchoc1,oc2with|None,None->None|None,Somec|Somec,None->Somec|Somec1,Somec2->ifequal_instancesenvsigmac1c2thenSomec1elseraiseNot_coherent_metasinId.Map.mergemerges1s2letmatching_error=CErrors.UserErrorPp.(str"No matching clauses for match.")letimatching_error=(matching_error,Exninfo.null)(** A functor is introduced to share the environment and the
evar_map. They do not change and it would be a pity to introduce
closures everywhere just for the occasional calls to
{!equal_instances}. *)moduletypeStaticEnvironment=sigvalenv:Environ.envvalsigma:Evd.evar_mapendmodulePatternMatching(E:StaticEnvironment)=struct(** {6 The pattern-matching monad } *)(** To focus on the algorithmic portion of pattern-matching, the
bookkeeping is relegated to a monad: the composition of the
backtracking monad of {!IStream.t} with a "writer" effect. *)(* spiwack: as we don't benefit from the various stream optimisations
of Haskell, it may be costly to give the monad in direct style such as
here. We may want to use some continuation passing style. *)type'atac='aProofview.tactictype'am={stream:'r.('a->result->'rtac)->result->'rtac}(** The empty substitution. *)letempty_subst=Id.Map.empty(** Composes two substitutions using {!verify_metas_coherence}. It
must be a monoid with neutral element {!empty_subst}. Raises
[Not_coherent_metas] when composition cannot be achieved. *)letsubst_prods1s2=ifis_empty_substs1thens2elseifis_empty_substs2thens1elseverify_metas_coherenceE.envE.sigmas1s2(** Merge two writers (and ignore the first value component). *)letmergem1m2=trySome{subst=subst_prodm1.substm2.subst;}withNot_coherent_metas->None(** Monadic [return]: returns a single success with empty substitutions. *)letreturn(typea)(lhs:a):am={stream=funkctx->klhsctx}(** Monadic bind: each success of [x] is replaced by the successes
of [f x]. The substitutions of [x] and [f x] are composed,
dropping the apparent successes when the substitutions are not
coherent. *)let(>>=)(typea)(typeb)(m:am)(f:a->bm):bm={stream=funkctx->m.stream(funxctx->(fx).streamkctx)ctx}(** A variant of [(>>=)] when the first argument returns [unit]. *)let(<*>)(typea)(m:unitm)(y:am):am={stream=funkctx->m.stream(fun()ctx->y.streamkctx)ctx}(** Failure of the pattern-matching monad: no success. *)letfail(typea):am={stream=fun__->letinfo=Exninfo.reify()inProofview.tclZERO~infomatching_error}letrun(m:'am)=letctx={subst=empty_subst;}inletevalxctx=Proofview.tclUNIT(x,ctx)inm.streamevalctx(** Chooses in a list, in the same order as the list *)letrecpick(l:'alist)(e,info):'am=matchlwith|[]->{stream=fun__->Proofview.tclZERO~infoe}|x::l->{stream=funkctx->Proofview.tclOR(kxctx)(fune->(pickle).streamkctx)}letpickl=picklimatching_errorletput_substsubst:unitm=lets={subst}in{stream=funkctx->matchmergesctxwith|None->letinfo=Exninfo.reify()inProofview.tclZERO~infomatching_error|Somes->k()s}(** {6 Pattern-matching} *)letpattern_match_termpatterm=matchpatwith|MatchPatternp->begintryput_subst(Constr_matching.matchesE.envE.sigmapterm)<*>returnNonewithConstr_matching.PatternMatchingFailure->failend|MatchContextp->letrecmaps(e,info)={stream=funkctx->matchIStream.peekswith|IStream.Nil->Proofview.tclZERO~infoe|IStream.Cons({Constr_matching.m_sub=(_,subst);m_ctx},s)->letnctx={subst}inmatchmergectxnctxwith|None->(maps(e,info)).streamkctx|Somenctx->Proofview.tclOR(k(Somem_ctx)nctx)(fune->(mapse).streamkctx)}inletp=Constr_matching.instantiate_patternE.envE.sigmaId.Map.emptypinmap(Constr_matching.match_subtermE.envE.sigma(Id.Set.empty,p)term)imatching_errorlethyp_match_typepathyps=pickhyps>>=fundecl->letid=NamedDecl.get_iddeclinpattern_match_termpat(NamedDecl.get_typedecl)>>=functx->return(id,None,ctx)lethyp_match_body_and_typebodypattypepathyps=pickhyps>>=function|LocalDef(id,body,hyp)->pattern_match_termbodypatbody>>=functx_body->pattern_match_termtypepathyp>>=functx_typ->return(id.binder_name,Somectx_body,ctx_typ)|LocalAssum(id,hyp)->faillethyp_matchpathyps=matchpatwith|None,typepat->hyp_match_typetypepathyps|Somebodypat,typepat->hyp_match_body_and_typebodypattypepathyps(** [hyp_pattern_list_match pats hyps lhs], matches the list of
patterns [pats] against the hypotheses in [hyps], and eventually
returns [lhs]. *)letrechyp_pattern_list_matchpatshypsaccu=matchpatswith|pat::pats->hyp_matchpathyps>>=fun(matched_hyp,_,_asv)->letselect_matched_hypdecl=Id.equal(NamedDecl.get_iddecl)matched_hypinlethyps=CList.remove_firstselect_matched_hyphypsinhyp_pattern_list_matchpatshyps(v::accu)|[]->returnacculetrule_match_goalhypsconcl=function|(hyppats,conclpat)->(* the rules are applied from the topmost one (in the concrete
syntax) to the bottommost. *)lethyppats=List.revhyppatsinpattern_match_termconclpatconcl>>=functx_concl->hyp_pattern_list_matchhyppatshyps[]>>=funhyps->return(hyps,ctx_concl)endletmatch_goalenvsigmaconcl~revrule=letopenProofview.Notationsinlethyps=EConstr.named_contextenvinlethyps=ifrevthenList.revhypselsehypsinletmoduleE=structletenv=envletsigma=sigmaendinletmoduleM=PatternMatching(E)inM.run(M.rule_match_goalhypsconclrule)>>=fun((hyps,ctx_concl),subst)->Proofview.tclUNIT(hyps,ctx_concl,subst.subst)