123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382(************************************************************************)(* * 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) *)(************************************************************************)(** This file extends Matching with the main logic for Ltac's
(lazy)match and (lazy)match goal. *)openNamesopenContextopenTacexpropenContext.Named.DeclarationmoduleNamedDecl=Context.Named.Declaration(** [t] is the type of matching successes. It ultimately contains a
{!Tacexpr.glob_tactic_expr} representing the left-hand side of the
corresponding matching rule, a matching substitution to be
applied, a context substitution mapping identifier to context like
those of {!Matching.matching_result}), and a {!Term.constr}
substitution mapping corresponding to matched hypotheses. *)type'at={subst:Constr_matching.bound_ident_map*Ltac_pretype.extended_patvar_map;context:Constr_matching.contextId.Map.t;terms:EConstr.constrId.Map.t;lhs:'a;}(** {6 Utilities} *)(** Some of the functions of {!Matching} return the substitution with a
[patvar_map] instead of an [extended_patvar_map]. [adjust] coerces
substitution of the former type to the latter. *)letadjust:Constr_matching.bound_ident_map*Ltac_pretype.patvar_map->Constr_matching.bound_ident_map*Ltac_pretype.extended_patvar_map=fun(l,lc)->(l,Id.Map.map(func->[],c)lc)(** Adds a binding to a {!Id.Map.t} if the identifier is [Some id] *)letid_map_try_addidxm=matchidwith|Someid->Id.Map.addidxm|None->m(** Adds a binding to a {!Id.Map.t} if the name is [Name id] *)letid_map_try_add_nameidxm=matchidwith|Nameid->Id.Map.addidxm|Anonymous->m(** Takes the union of two {!Id.Map.t}. If there is conflict,
the binding of the right-hand argument shadows that of the left-hand
argument. *)letid_map_right_biased_unionm1m2=ifId.Map.is_emptym1thenm2(* Don't reconstruct the whole map *)elseId.Map.foldId.Map.addm2m1(** Tests whether the substitution [s] is empty. *)letis_empty_subst(ln,lm)=Id.Map.(is_emptyln&&is_emptylm)(** {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_instancesenvsigma(ctx',c')(ctx,c)=(* 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) *)CList.equalId.equalctxctx'&&Reductionops.is_convenvsigmac'c(** 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_coherenceenvsigma(ln1,lcm)(ln,lm)=letmergeidoc1oc2=matchoc1,oc2with|None,None->None|None,Somec|Somec,None->Somec|Somec1,Somec2->ifequal_instancesenvsigmac1c2thenSomec1elseraiseNot_coherent_metasinlet(+++)lfun1lfun2=Id.Map.foldId.Map.addlfun1lfun2in(* ppedrot: Is that even correct? *)letmerged=ln+++ln1in(merged,Id.Map.mergemergelcmlm)letmatching_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->unitt->'rtac)->unitt->'rtac}(** The empty substitution. *)letempty_subst=Id.Map.empty,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(** The empty context substitution. *)letempty_context_subst=Id.Map.empty(** Compose two context substitutions, in case of conflict the
right hand substitution shadows the left hand one. *)letcontext_subst_prod=id_map_right_biased_union(** The empty term substitution. *)letempty_term_subst=Id.Map.empty(** Compose two terms substitutions, in case of conflict the
right hand substitution shadows the left hand one. *)letterm_subst_prod=id_map_right_biased_union(** Merge two writers (and ignore the first value component). *)letmergem1m2=trySome{subst=subst_prodm1.substm2.subst;context=context_subst_prodm1.contextm2.context;terms=term_subst_prodm1.termsm2.terms;lhs=m2.lhs;}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;context=empty_context_subst;terms=empty_term_subst;lhs=();}inletevallhsctx=Proofview.tclUNIT{ctxwithlhs}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_error(** Declares a substitution, a context substitution and a term substitution. *)letputsubstcontextterms:unitm=lets={subst;context;terms;lhs=()}in{stream=funkctx->matchmergesctxwith|None->letinfo=Exninfo.reify()inProofview.tclZERO~infomatching_error|Somes->k()s}(** Declares a substitution. *)letput_substsubst:unitm=putsubstempty_context_substempty_term_subst(** Declares a term substitution. *)letput_termsterms:unitm=putempty_substempty_context_substterms(** {6 Pattern-matching} *)(** [wildcard_match_term lhs] matches a term against a wildcard
pattern ([_ => lhs]). It has a single success with an empty
substitution. *)letwildcard_match_term=return(** [pattern_match_term pat term lhs] returns the possible
matchings of [term] with the pattern [pat => lhs]. *)letpattern_match_termpattermlhs=matchpatwith|Termp->begintryput_subst(Constr_matching.extended_matchesE.envE.sigmapterm)<*>returnlhswithConstr_matching.PatternMatchingFailure->failend|Subterm(id_ctxt,p)->letrecmaps(e,info)={stream=funkctx->matchIStream.peekswith|IStream.Nil->Proofview.tclZERO~infoe|IStream.Cons({Constr_matching.m_sub;m_ctx},s)->letsubst=adjustm_subinletcontext=id_map_try_addid_ctxtm_ctxId.Map.emptyinletterms=empty_term_substinletnctx={subst;context;terms;lhs=()}inmatchmergectxnctxwith|None->(maps(e,info)).streamkctx|Somenctx->Proofview.tclOR(klhsnctx)(fune->(mapse).streamkctx)}inmap(Constr_matching.match_subtermE.envE.sigmapterm)imatching_error(** [rule_match_term term rule] matches the term [term] with the
matching rule [rule]. *)letrule_match_termterm=function|Alllhs->wildcard_match_termlhs|Pat([],pat,lhs)->pattern_match_termpattermlhs|Pat_->(* Rules with hypotheses, only work in match goal. *)fail(** [match_term term rules] matches the term [term] with the set of
matching rules [rules].*)letrecmatch_term(e,info)termrules=matchruleswith|[]->{stream=fun__->Proofview.tclZERO~infoe}|r::rules->{stream=funkctx->lethead=rule_match_termtermrinlettaile=match_termetermrulesinProofview.tclOR(head.streamkctx)(fune->(taile).streamkctx)}(** [hyp_match_type hypname pat hyps] matches a single
hypothesis pattern [hypname:pat] against the hypotheses in
[hyps]. Tries the hypotheses in order. For each success returns
the name of the matched hypothesis. *)lethyp_match_typeexcepthypnamepathyps=pickhyps>>=fundecl->letid=NamedDecl.get_iddeclinifId.Set.memidexceptthenfailelsepattern_match_termpat(NamedDecl.get_typedecl)()<*>put_terms(id_map_try_add_namehypname(EConstr.mkVarid)empty_term_subst)<*>returnid(** [hyp_match_type hypname bodypat typepat hyps] matches a single
hypothesis pattern [hypname := bodypat : typepat] against the
hypotheses in [hyps].Tries the hypotheses in order. For each
success returns the name of the matched hypothesis. *)lethyp_match_body_and_typeexcepthypnamebodypattypepathyps=pickhyps>>=function|LocalDef(id,body,hyp)->ifId.Set.memid.binder_nameexceptthenfailelsepattern_match_termbodypatbody()<*>pattern_match_termtypepathyp()<*>put_terms(id_map_try_add_namehypname(EConstr.mkVarid.binder_name)empty_term_subst)<*>returnid.binder_name|LocalAssum(id,hyp)->fail(** [hyp_match pat hyps] dispatches to
{!hyp_match_type} or {!hyp_match_body_and_type} depending on whether
[pat] is [Hyp _] or [Def _]. *)lethyp_matchexceptpathyps=matchpatwith|Hyp({CAst.v=hypname},typepat)->hyp_match_typeexcepthypnametypepathyps|Def({CAst.v=hypname},bodypat,typepat)->hyp_match_body_and_typeexcepthypnamebodypattypepathyps(** [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_matchexceptpatshypslhs=matchpatswith|pat::pats->hyp_matchexceptpathyps>>=funmatched_hyp->letexcept=Id.Set.addmatched_hypexceptinhyp_pattern_list_matchexceptpatshypslhs|[]->returnlhs(** [rule_match_goal hyps concl rule] matches the rule [rule]
against the goal [hyps|-concl]. *)letrule_match_goalhypsconcl=function|Alllhs->wildcard_match_termlhs|Pat(hyppats,conclpat,lhs)->(* the rules are applied from the topmost one (in the concrete
syntax) to the bottommost. *)lethyppats=List.revhyppatsinpattern_match_termconclpatconcl()<*>hyp_pattern_list_matchId.Set.emptyhyppatshypslhs(** [match_goal hyps concl rules] matches the goal [hyps|-concl]
with the set of matching rules [rules]. *)letrecmatch_goal(e,info)hypsconclrules=matchruleswith|[]->{stream=fun__->Proofview.tclZERO~infoe}|r::rules->{stream=funkctx->lethead=rule_match_goalhypsconclrinlettaile=match_goalehypsconclrulesinProofview.tclOR(head.streamkctx)(fune->(taile).streamkctx)}end(** [match_term env sigma term rules] matches the term [term] with the
set of matching rules [rules]. The environment [env] and the
evar_map [sigma] are not currently used, but avoid code
duplication. *)letmatch_termenvsigmatermrules=letmoduleE=structletenv=envletsigma=sigmaendinletmoduleM=PatternMatching(E)inM.run(M.match_termimatching_errortermrules)(** [match_goal env sigma hyps concl rules] matches the goal
[hyps|-concl] with the set of matching rules [rules]. The
environment [env] and the evar_map [sigma] are used to check
convertibility for pattern variables shared between hypothesis
patterns or the conclusion pattern. *)letmatch_goalenvsigmahypsconclrules=letmoduleE=structletenv=envletsigma=sigmaendinletmoduleM=PatternMatching(E)inM.run(M.match_goalimatching_errorhypsconclrules)