(************************************************************************) (* * 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) *) (************************************************************************)
open Pp open CErrors open Util open Names open Constr open EConstr open Declarations open Tactypes open Proofview open Proofview.Notations open Tacmach
let catch_failerror (e, info) = match e with
| FailError (lvl,s) when lvl > 0 ->
tclZERO ~info (FailError (lvl - 1, s))
| e ->
Control.check_for_interrupt ();
tclUNIT ()
(* The following tacticals allow to apply a tactic to the branches generated by the application of an elimination tactic.
Two auxiliary types --branch_args and branch_assumptions-- are used to keep track of some information about the ``branches'' of
the elimination. *)
let fix_empty_or_and_pattern nv l = (* 1- The syntax does not distinguish between "[ ]" for one clause with no
names and "[ ]" for no clause at all *) (* 2- More generally, we admit "[ ]" for any disjunctive pattern of
arbitrary length *) match l with
| IntroOrPattern [[]] -> IntroOrPattern (List.make nv [])
| _ -> l
let check_or_and_pattern_size ?loc check_and names branchsigns = let n = Array.length branchsigns in let msg p1 p2 = strbrk "a conjunctive pattern made of " ++ int p1 ++ (if p1 == p2 then mt () else str " or " ++ int p2) ++ str " patterns"in let err1 p1 p2 =
user_err ?loc (str "Expects " ++ msg p1 p2 ++ str ".") in let errn n =
user_err ?loc (str "Expects a disjunctive pattern with " ++ int n
++ str " branches.") in let err1' p1 p2 =
user_err ?loc (strbrk "Expects a disjunctive pattern with 1 branch or " ++ msg p1 p2 ++ str ".") in let errforthcoming ?loc =
user_err ?loc (strbrk "Unexpected non atomic pattern.") in match names with
| IntroAndPattern l -> ifnot (Int.equal n 1) then errn n; let l' = List.filter CAst.(function {v=IntroForthcoming _} -> true | {v=IntroNaming _} | {v=IntroAction _} -> false) l in if l' != [] then errforthcoming ?loc:(List.hd l').CAst.loc; if check_and then let p1 = List.count (fun x -> x) branchsigns.(0) in let p2 = List.length branchsigns.(0) in let p = List.length l in ifnot (Int.equal p p1 || Int.equal p p2) then err1 p1 p2; if Int.equal p p1 then
IntroAndPattern
(List.extend branchsigns.(0) (CAst.make @@ IntroNaming Namegen.IntroAnonymous) l) else
names else
names
| IntroOrPattern ll -> ifnot (Int.equal n (List.length ll)) then if Int.equal n 1 then let p1 = List.count (fun x -> x) branchsigns.(0) in let p2 = List.length branchsigns.(0) in
err1' p1 p2 else errn n;
names
let get_and_check_or_and_pattern_gen ?loc check_and names branchsigns = let names = check_or_and_pattern_size ?loc check_and names branchsigns in match names with
| IntroAndPattern l -> [|l|]
| IntroOrPattern l -> Array.of_list l
let get_and_check_or_and_pattern ?loc = get_and_check_or_and_pattern_gen ?loc true
let compute_induction_names check_and branchletsigns = function
| None ->
Array.make (Array.length branchletsigns) []
| Some {CAst.loc;v=names} -> let names = fix_empty_or_and_pattern (Array.length branchletsigns) names in
get_and_check_or_and_pattern_gen check_and ?loc names branchletsigns
let is_recursive_argument env self recarg = match Declareops.dest_recarg recarg with
| Norec | Mrec (RecArgPrim _) -> false
| Mrec (RecArgInd ind) -> Environ.QInd.equal env self ind
(* Compute the let-in signature of case analysis or standard induction scheme *) let compute_constructor_signatures env ~rec_flag ((_,k as ity),u) = let rec analrec c recargs = match c, recargs with
| RelDecl.LocalAssum _ :: c, recarg::rest -> let rest = analrec c rest in if rec_flag && is_recursive_argument env ity recarg thentrue :: true :: rest elsetrue :: rest
| RelDecl.LocalDef _ :: c, rest -> false :: analrec c rest
| [], [] -> []
| _ -> anomaly (Pp.str "compute_constructor_signatures.") in let (mib,mip) = Inductive.lookup_mind_specif env ity in letmap (ctx, _) = List.skipn (Context.Rel.length mib.mind_params_ctxt) (List.rev ctx) in let lc = Array.mapmap mip.mind_nf_lc in let lrecargs = Declareops.dest_subterms mip.mind_recargs in
Array.map2 analrec lc lrecargs
let tclIDTAC = tclUNIT ()
let tclTHEN t1 t2 =
t1 <*> t2
let tclFAILn ?info lvl msg = let info = match info with (* If the backtrace points here it means the caller didn't save
the backtrace correctly *)
| None -> Exninfo.reify ()
| Some info -> info in
tclZERO ~info (FailError (lvl,lazy msg))
let tclFAIL ?info msg = tclFAILn ?info 0 msg
let tclZEROMSG ?info ?loc msg = let info = match info with (* If the backtrace points here it means the caller didn't save
the backtrace correctly *)
| None -> Exninfo.reify ()
| Some info -> info in let info = match loc with
| None -> info
| Some loc -> Loc.add_loc info loc in let err = UserError msg in
tclZERO ~info err
(* spiwack: I chose to give the Ltac + the same semantics as [Proofview.tclOR], however, for consistency with the or-else tactical, we may consider wrapping the first argument with [tclPROGRESS]. It strikes me as a bad idea, but consistency can be
considered valuable. *) let tclOR t1 t2 =
tclINDEPENDENT begin
Proofview.tclOR
t1 beginfun e ->
catch_failerror e <*> t2 end end
let tclORD t1 t2 =
tclINDEPENDENT begin
Proofview.tclOR
t1 beginfun e ->
catch_failerror e <*> t2 () end end
let tclONCE = Proofview.tclONCE
let tclEXACTLY_ONCE t = Proofview.tclEXACTLY_ONCE (FailError(0,lazy (assert false))) t
let tclIFCATCH t tt te =
tclINDEPENDENT begin
Proofview.tclIFCATCH t
tt
(fun e -> catch_failerror e <*> te ()) end
let tclORELSE0 t1 t2 =
tclINDEPENDENT begin
tclORELSE
t1 beginfun e ->
catch_failerror e <*> t2 end end
let tclORELSE0L t1 t2 =
tclINDEPENDENTL begin
tclORELSE
t1 beginfun e ->
catch_failerror e <*> t2 end end
let tclORELSE t1 t2 =
tclORELSE0 (tclPROGRESS t1) t2
let tclTHENS3PARTS t1 l1 repeat l2 =
tclINDEPENDENT begin
t1 <*>
Proofview.tclORELSE (* converts the [SizeMismatch] error into an ltac error *) begin tclEXTEND (Array.to_list l1) repeat (Array.to_list l2) end begin function (e, info) -> match e with
| SizeMismatch (i,_)-> let errmsg =
str"Incorrect number of goals" ++ spc() ++
str"(expected "++int i++str(String.plural i " tactic") ++ str")" in
tclFAIL errmsg
| reraise -> tclZERO ~info reraise end end let tclTHENSFIRSTn t1 l repeat =
tclTHENS3PARTS t1 l repeat [||] let tclTHENFIRSTn t1 l =
tclTHENSFIRSTn t1 l (tclUNIT()) let tclTHENFIRST t1 t2 =
tclTHENFIRSTn t1 [|t2|]
let tclBINDFIRST t1 t2 =
t1 >>= fun ans ->
Proofview.Unsafe.tclGETGOALS >>= fun gls -> match gls with
| [] -> tclFAIL (str "Expect at least one goal.")
| hd::tl ->
Proofview.Unsafe.tclSETGOALS [hd] <*> t2 ans >>= fun ans ->
Proofview.Unsafe.tclNEWGOALS tl <*>
Proofview.tclUNIT ans
let tclTHENSLASTn t1 repeat l =
tclTHENS3PARTS t1 [||] repeat l
let tclTHENLASTn t1 l =
tclTHENS3PARTS t1 [||] (tclUNIT()) l let tclTHENLAST t1 t2 = tclTHENLASTn t1 [|t2|]
let option_of_failure f x = try Some (f x) with Failure _ -> None
let tclBINDLAST t1 t2 =
t1 >>= fun ans ->
Proofview.Unsafe.tclGETGOALS >>= fun gls -> match option_of_failure List.sep_last gls with
| None -> tclFAIL (str "Expect at least one goal.")
| Some (last,firstn) ->
Proofview.Unsafe.tclSETGOALS [last] <*> t2 ans >>= fun ans ->
Proofview.Unsafe.tclGETGOALS >>= fun newgls ->
tclEVARMAP >>= fun sigma -> let firstn = Proofview.Unsafe.undefined sigma firstn in
Proofview.Unsafe.tclSETGOALS (firstn@newgls) <*>
Proofview.tclUNIT ans
let tclTHENS t l =
tclINDEPENDENT begin
t <*>Proofview.tclORELSE (* converts the [SizeMismatch] error into an ltac error *) begin tclDISPATCH l end begin function (e, info) -> match e with
| SizeMismatch (i,_)-> let errmsg =
str"Incorrect number of goals" ++ spc() ++
str"(expected "++int i++str(String.plural i " tactic") ++ str")" in
tclFAIL errmsg
| reraise -> tclZERO ~info reraise end end let tclTHENLIST l = List.fold_left tclTHEN (tclUNIT()) l
(* [tclMAP f [x1..xn]] builds [(f x1);(f x2);...(f xn)] *) let tclMAP tacfun l = List.fold_right (fun x -> (tclTHEN (tacfun x))) l (tclUNIT())
let tclTRY t =
tclORELSE0 t (tclUNIT ())
let tclTRYb t =
tclORELSE0L (t <*> tclUNIT true) (tclUNIT false)
let tclIFTHENELSE t1 t2 t3 =
tclINDEPENDENT begin
Proofview.tclIFCATCH t1
(fun () -> t2)
(fun (e, info) -> Proofview.tclORELSE t3 (fun e' -> tclZERO ~info e)) end let tclIFTHENSVELSE t1 a t3 =
Proofview.tclIFCATCH t1
(fun () -> tclDISPATCH (Array.to_list a))
(fun _ -> t3) let tclIFTHENFIRSTELSE t1 t2 t3 =
Proofview.tclIFCATCH t1
(fun () -> tclEXTEND [t2] (tclUNIT ()) [])
(fun _ -> t3) let tclIFTHENTRYELSEMUST t1 t2 =
tclIFTHENELSE t1 (tclTRY t2) t2 let tclIFTHENFIRSTTRYELSEMUST t1 t2 =
tclIFTHENFIRSTELSE t1 (tclTRY t2) t2
(* Try the first tactic that does not fail in a list of tactics *) let rec tclFIRST = function
| [] -> let info = Exninfo.reify () in
tclZEROMSG ~info (str"No applicable tactic.")
| t::rest -> tclORELSE0 t (tclFIRST rest)
let rec tclFIRST_PROGRESS_ON tac = function
| [] -> tclFAIL (str "No applicable tactic")
| [a] -> tac a (* so that returned failure is the one from last item *)
| a::tl -> tclORELSE (tac a) (tclFIRST_PROGRESS_ON tac tl)
let rec tclDO n t = if n < 0 then let info = Exninfo.reify () in
tclZEROMSG ~info (str"Wrong argument : Do needs a positive integer.") elseif n = 0 then tclUNIT () elseif n = 1 then t else (* Thunk to avoid stack overflow with large n *)
tclTHEN t (tclUNIT () >>= fun () -> (tclDO (n-1) t))
let rec tclREPEAT0 t =
tclINDEPENDENT begin
Proofview.tclIFCATCH t
(fun () -> tclCHECKINTERRUPT <*> tclREPEAT0 t)
(fun e -> catch_failerror e <*> tclUNIT ()) end let tclREPEAT t =
tclREPEAT0 (tclPROGRESS t) let rec tclREPEAT_MAIN0 t =
Proofview.tclIFCATCH t
(fun () -> tclTRYFOCUS 1 1 (tclREPEAT_MAIN0 t))
(fun e -> catch_failerror e <*> tclUNIT ()) let tclREPEAT_MAIN t =
tclREPEAT_MAIN0 (tclPROGRESS t)
let tclCOMPLETE t =
t >>= fun res ->
(tclINDEPENDENT
(let info = Exninfo.reify () in
tclZEROMSG ~info (str"Proof is not complete."))
) <*>
tclUNIT res
(* Try the first that solves the current goal *) let tclSOLVE tacl = tclFIRST (List.map tclCOMPLETE tacl)
let tclPROGRESS t =
Proofview.tclINDEPENDENT (Proofview.tclPROGRESS t)
(* Check that holes in arguments have been resolved *)
let check_evar_list env sigma evars origsigma = (* origsigma ⊆ sigma *) (* evars ⊆ sigma *) let reachable = lazy (Evarutil.reachable_from_evars sigma
(Evar.Map.domain (Evd.undefined_map origsigma))) in let rec is_undefined_up_to_restriction sigma evk = if Evd.mem origsigma evk then None else let EvarInfo evi = Evd.find sigma evk in match Evd.evar_body evi with
| Evd.Evar_empty -> Some (evk, Evd.EvarInfo evi)
| Evd.Evar_defined c -> match Constr.kind (EConstr.Unsafe.to_constr c) with
| Evar (evk,l) -> is_undefined_up_to_restriction sigma evk
| _ -> (* We make the assumption that there is no way to refine an evar remaining after typing from the initial term given to
apply/elim and co tactics, is it correct? *)
None in
Evar.Set.fold (fun evk acc -> match is_undefined_up_to_restriction sigma evk with
| Some (evk',evi) -> (* If [evk'] descends from [evk] which descends itself from an originally undefined evar in [origsigma], it is a not
a fresh undefined hole from [sigma]. *) if Evar.Set.mem evk (Lazy.force reachable) then acc else evk'::acc
| _ -> acc)
evars []
let check_evars env sigma extsigma origsigma = (* origsigma ⊆ extsigma ⊆ sigma *) if Evd.undefined_map extsigma != Evd.undefined_map origsigma then match check_evar_list env sigma (Evar.Map.domain (Evd.undefined_map extsigma)) origsigma with
| [] -> ()
| evk :: _ -> let EvarInfo evi = Evd.find sigma evk in let (loc,_) = Evd.evar_source evi in
Pretype_errors.error_unsolvable_implicit ?loc env sigma evk None
let tclMAPDELAYEDWITHHOLES accept_unresolved_holes l tac = let rec aux = function
| [] -> tclUNIT ()
| x :: l ->
Proofview.Goal.enter beginfun gl -> let env = Proofview.Goal.env gl in let sigma_initial = Proofview.Goal.sigma gl in let (sigma, x) = x env sigma_initial in
Proofview.Unsafe.tclEVARS sigma <*> tac x >>= fun () -> aux l >>= fun () -> if accept_unresolved_holes then
tclUNIT () else
tclEVARMAP >>= fun sigma_final -> try let () = check_evars env sigma_final sigma sigma_initial in
tclUNIT () with e when CErrors.noncritical e -> let e, info = Exninfo.capture e in
tclZERO ~info e endin
aux l
(* The following is basically tclMAPDELAYEDWITHHOLES accept_unresolved_holes [fun _ _ -> (sigma,())] (fun () -> tac)
but with value not necessarily in unit *)
let with_holes_check ~sigma_initial ~sigma =
tclEVARMAP >>= fun sigma_final ->
tclENV >>= fun env -> try let () = check_evars env sigma_final sigma sigma_initial in
tclUNIT () with e when CErrors.noncritical e -> let e, info = Exninfo.capture e in
tclZERO ~info e
let tclRUNWITHHOLES accept_unresolved_holes tac0 tac = if accept_unresolved_holes then tac0 >>= tac else
tclEVARMAP >>= fun sigma_initial ->
tac0 >>= fun v ->
tclEVARMAP >>= fun sigma -> if sigma == sigma_initial then tac v else
tac v >>= fun v ->
with_holes_check ~sigma_initial ~sigma <*>
tclUNIT v
let tclWITHHOLES accept_unresolved_holes tac sigma =
tclEVARMAP >>= fun sigma_initial -> if sigma == sigma_initial then tac else
Proofview.Unsafe.tclEVARS sigma <*>
tac >>= fun v ->
(if accept_unresolved_holes then tclUNIT () else with_holes_check ~sigma_initial ~sigma) <*>
tclUNIT v
let tactic_of_delayed d =
Proofview.Goal.enter_one ~__LOC__ @@ fun gl -> let sigma, v = pf_apply d gl in
Proofview.Unsafe.tclEVARS sigma <*>
tclUNIT v
let tclDELAYEDWITHHOLES check x tac =
Proofview.Goal.enter beginfun gl -> let env = Proofview.Goal.env gl in let sigma = Proofview.Goal.sigma gl in let (sigma, x) = x env sigma in
tclWITHHOLES check (tac x) sigma end
let tclTIMEOUT n t =
Proofview.tclOR
(Proofview.tclTIMEOUT n t) begin function (e, info) -> match e with
| Logic_monad.Tac_Timeout as e -> let info = Exninfo.reify () in
Proofview.tclZERO ~info (FailError (0,lazy (CErrors.print e)))
| e -> Proofview.tclZERO ~info e end
let tclTIME s t =
Proofview.tclTIME s t
let nthDecl m gl = let hyps = Proofview.Goal.hyps gl in try List.nth hyps (m-1) with Failure _ -> CErrors.user_err Pp.(str "No such assumption.")
let nLastDecls gl n = tryList.firstn n (Proofview.Goal.hyps gl) with Failure _ -> CErrors.user_err Pp.(str "Not enough hypotheses in the goal.")
let nthHypId m gl = (* We only use [id] *)
nthDecl m gl |> NamedDecl.get_id let nthHyp m gl =
mkVar (nthHypId m gl)
let onNthHypId m tac =
Proofview.Goal.enter beginfun gl -> tac (nthHypId m gl) end let onNthHyp m tac =
Proofview.Goal.enter beginfun gl -> tac (nthHyp m gl) end
let onLastHypId = onNthHypId 1 let onLastHyp = onNthHyp 1
let onNthDecl m tac =
Proofview.Goal.enter beginfun gl ->
Proofview.tclUNIT (nthDecl m gl) >>= tac end let onLastDecl = onNthDecl 1
let nLastHypsId gl n = List.map (NamedDecl.get_id) (nLastDecls gl n) let nLastHyps gl n = List.map mkVar (nLastHypsId gl n)
let ifOnHyp pred tac1 tac2 id =
Proofview.Goal.enter beginfun gl -> let typ = Tacmach.pf_get_hyp_typ id gl in if pf_apply pred gl (id,typ) then
tac1 id else
tac2 id end
let onHyps find tac = Proofview.Goal.enter beginfun gl -> tac (find gl) end
let onNLastDecls n tac = onHyps (fun gl -> nLastDecls gl n) tac let onNLastHypsId n tac = onHyps (fun gl -> nLastHypsId gl n) tac let onNLastHyps n tac = onHyps (fun gl -> nLastHyps gl n) tac
let afterHyp id tac =
Proofview.Goal.enter beginfun gl -> let hyps = Proofview.Goal.hyps gl in let rem, _ = List.split_when (NamedDecl.get_id %> Id.equal id) hyps in
tac rem end
let fullGoal gl = let hyps = Tacmach.pf_ids_of_hyps gl in
None :: List.mapOption.make hyps
let tryAllHyps tac =
Proofview.Goal.enter beginfun gl -> let hyps = Tacmach.pf_ids_of_hyps gl in
tclFIRST_PROGRESS_ON tac hyps end let tryAllHypsAndConcl tac =
Proofview.Goal.enter beginfun gl ->
tclFIRST_PROGRESS_ON tac (fullGoal gl) end
let onClause tac cl =
Proofview.Goal.enter beginfun gl -> let hyps = Tacmach.pf_ids_of_hyps gl in
tclMAP tac (Locusops.simple_clause_of (fun () -> hyps) cl) end
let fullGoal gl = None :: List.mapOption.make (Tacmach.pf_ids_of_hyps gl) let onAllHyps tac =
Proofview.Goal.enter beginfun gl ->
tclMAP tac (Tacmach.pf_ids_of_hyps gl) end let onAllHypsAndConcl tac =
Proofview.Goal.enter beginfun gl ->
tclMAP tac (fullGoal gl) end
let elimination_sort_of_goal gl = (* Retyping will expand evars anyway. *) let c = Proofview.Goal.concl gl in
pf_apply Retyping.get_sort_quality_of gl c
let elimination_sort_of_hyp id gl = (* Retyping will expand evars anyway. *) let c = pf_get_hyp_typ id gl in
pf_apply Retyping.get_sort_quality_of gl c
let elimination_sort_of_clause id gl = match id with
| None -> elimination_sort_of_goal gl
| Some id -> elimination_sort_of_hyp id gl
let pf_constr_of_global ref =
Proofview.tclEVARMAP >>= fun sigma ->
Proofview.tclENV >>= fun env -> let (sigma, c) = Evd.fresh_global env sigma refin
Proofview.Unsafe.tclEVARS sigma <*> Proofview.tclUNIT c
let tclTYPEOFTHEN ?refresh c tac =
Proofview.Goal.enter (fun gl -> let env = Proofview.Goal.env gl in let sigma = Proofview.Goal.sigma gl in let (sigma, t) = Typing.type_of ?refresh env sigma c in
Proofview.Unsafe.tclEVARS sigma <*> tac sigma t)
let tclSELECT = Goal_select.tclSELECT
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.