(************************************************************************) (* * 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) *) (************************************************************************)
(* Module defining the last essential tiles of interactive proofs. Aproofdealswiththefocusingcommands(includingthebracesandbullets), theshelf(seethe[shelve]tactic)andgivenupgoal(seethe[give_up] tactic).Aproofismadeofthefollowing: -Proofview:aproofisprimarilythedataofthecurrentview. Thatwhichisshowntotheuser(asaremainder,aproofview ismainlythelogicalstateoftheproof,togetherwiththe currentlyfocusedgoals). -Focus:aproofhasafocusstack:thetopofthestackcontains thecontextinwhichtounfocusthecurrentviewtoaviewfocused withtherestofthestack. Inaddition,thiscontains,foreachofthefocuscontext,a "focuskind"anda"focuscondition"(inpractice,andformodularity, thefocuskindisactuallystoredinsidethecondition).Tounfocus,one needstoknowthefocuskind,andthecondition(forinstance"nocondition"or theproofunderfocusedmustbecomplete)mustbemet. -Givenupgoals:aslongasthereisagivenupgoal,theproofisnotcompleted. Givenupgoalscannotberetrieved,theusermustgobackwherethetactic [give_up]wasrunandsolvethegoalthere.
*)
open Util
module FocusKind = Dyn.Make()
type'a focus_kind = 'a FocusKind.tag type reason = NotThisWay | AlreadyNoFocus type unfocusable =
| Cannot of reason
| Loose
| Strict type'a focus_condition =
| CondNo ofbool * 'a focus_kind
| CondDone ofbool * 'a focus_kind
| CondEndStack of'a focus_kind (* loose_end is false here *)
let new_focus_kind = FocusKind.create
(* To be authorized to unfocus one must meet the condition prescribed by
the action which focused.*) (* spiwack: we could consider having a list of authorized focus_kind instead
of just one, if anyone needs it *)
exception CannotUnfocusThisWay
(* Cannot focus on non-existing subgoals *)
exception NoSuchGoals of int * int
exception NoSuchGoal of Names.Id.t option
exception FullyUnfocused
let _ = CErrors.register_handler begin function
| CannotUnfocusThisWay ->
Some (Pp.str "This proof is focused, but cannot be unfocused this way")
| NoSuchGoals (i,j) when Int.equal i j ->
Some Pp.(str "[Focus] No such goal (" ++ int i ++ str").")
| NoSuchGoals (i,j) ->
Some Pp.(str "[Focus] Not every goal in range ["++ int i ++ str","++int j++str"] exist.")
| NoSuchGoal (Some id) ->
Some Pp.(str "[Focus] No such goal: " ++ str (Names.Id.to_string id) ++ str ".")
| NoSuchGoal None ->
Some Pp.(str "[Focus] No such goal.")
| FullyUnfocused ->
Some (Pp.str "The proof is not focused")
| _ -> None end
let check_cond_kind c k = let kind_of_cond = function
| CondNo (_,k) | CondDone(_,k) | CondEndStack k -> k in
FocusKind.eq (kind_of_cond c) k
let equal_kind c k = match FocusKind.eq c k with
| None -> false
| Some _ -> true
let test_cond c k1 pw = match c with
| CondNo(_, k) when equal_kind k k1 -> Strict
| CondNo(true, _) -> Loose
| CondNo(false, _) -> Cannot NotThisWay
| CondDone(_, k) when equal_kind k k1 && Proofview.finished pw -> Strict
| CondDone(true, _) -> Loose
| CondDone(false, _) -> Cannot NotThisWay
| CondEndStack k when equal_kind k k1 -> Strict
| CondEndStack _ -> Cannot AlreadyNoFocus
let no_cond ?(loose_end=false) k = CondNo (loose_end, k) let done_cond ?(loose_end=false) k = CondDone (loose_end,k)
type focus_element = FocusElt : 'a focus_condition * 'a * Proofview.focus_context -> focus_element
(* Subpart of the type of proofs. It contains the parts of the proof which
are under control of the undo mechanism *) type t =
{ proofview: Proofview.proofview (** Current focused proofview *)
; entry : Proofview.entry (** Entry for the proofview *)
; focus_stack: focus_element list (** History of the focusings, provides information on how to unfocus theproofandtheextrainformationstoredwhilefocusing.The
list is empty when the proof is fully unfocused. *)
; name : Names.Id.t (** the name of the theorem whose proof is being constructed *)
; poly : bool (** polymorphism *)
; typing_flags : Declarations.typing_flags option
}
(*** General proof functions ***)
let proof p = let (goals,sigma) = Proofview.proofview p.proofview in (* spiwack: beware, the bottom of the stack is used by [Proof]
internally, and should not be exposed. *) let rec map_minus_one f = function
| [] -> assert false
| [_] -> []
| a::l -> f a :: (map_minus_one f l) in letmap (FocusElt (_, _, c)) = Proofview.focus_context sigma c in let stack = map_minus_one map p.focus_stack in
(goals,stack,sigma)
let rec unroll_focus pv = function
| FocusElt (_,_,ctx)::stk -> unroll_focus (Proofview.unfocus ctx pv) stk
| [] -> pv
(* spiwack: a proof is considered completed even if its still focused, if the focus doesn'thideanygoal.
Unfocusing is handled in {!return}. *) let is_done p =
Proofview.finished p.proofview &&
Proofview.finished (unroll_focus p.proofview p.focus_stack)
(* Returns the list of partial proofs to initial goals *) let partial_proof p = Proofview.partial_proof p.entry p.proofview
(*** The following functions implement the basic internal mechanisms
of proofs, they are not meant to be exported in the .mli ***)
(* An auxiliary function to push a {!focus_context} on the focus stack. *) let push_focus cond inf context pr =
{ pr with focus_stack = FocusElt(cond,inf,context)::pr.focus_stack }
type any_focus_condition = AnyFocusCond : 'a focus_condition -> any_focus_condition
(* An auxiliary function to read the kind of the next focusing step *) let cond_of_focus pr = match pr.focus_stack with
| FocusElt (cond,_,_)::_ -> AnyFocusCond cond
| _ -> raise FullyUnfocused
(* An auxiliary function to pop and read the last {!Proofview.focus_context}
on the focus stack. *) let pop_focus pr = match pr.focus_stack with
| focus::other_focuses ->
{ pr with focus_stack = other_focuses }, focus
| _ -> raise FullyUnfocused
(* This function focuses the proof [pr] between indices [i] and [j] *) let _focus cond inf i j pr = let focused, context = Proofview.focus i j pr.proofview in let pr = push_focus cond inf context pr in
{ pr with proofview = focused }
(* This function unfocuses the proof [pr], it raises [FullyUnfocused], iftheproofisalreadyfullyunfocused.
This function does not care about the condition of the current focus. *) let _unfocus pr = let pr, FocusElt (_,_,fc) = pop_focus pr in
{ pr with proofview = Proofview.unfocus fc pr.proofview }
(* Focus command (focuses on the [i]th subgoal) *) (* spiwack: there could also, easily be a focus-on-a-range tactic, is there
a need for it? *) let focus cond inf i pr = try _focus cond inf i i pr with CList.IndexOutOfRange -> raise (NoSuchGoals (i,i))
(* Focus on the goal named id *) let focus_id cond inf id pr = let (focused_goals, evar_map) = Proofview.proofview pr.proofview in beginmatchtry Some (Evd.evar_key id evar_map) with Not_found -> None with
| Some ev -> beginmatch CList.index_opt Evar.equal ev focused_goals with
| Some i -> (* goal is already under focus *)
_focus cond inf i i pr
| None -> if Evd.mem_shelf ev evar_map then (* goal is on the shelf, put it in focus *) let proofview = Proofview.unshelve [ev] pr.proofview in let pr = { pr with proofview } in let (focused_goals, _) = Proofview.proofview pr.proofview in let i = (* Now we know that this will succeed *) try CList.index Evar.equal ev focused_goals with Not_found -> assert false in
_focus cond inf i i pr else raise CannotUnfocusThisWay end
| None -> raise (NoSuchGoal (Some id)) end
let rec unfocus kind pr () = let AnyFocusCond cond = cond_of_focus pr in match test_cond cond kind pr.proofview with
| Cannot NotThisWay -> raise CannotUnfocusThisWay
| Cannot AlreadyNoFocus -> raise FullyUnfocused
| Strict -> let pr = _unfocus pr in
pr
| Loose -> begintry let pr = _unfocus pr in
unfocus kind pr () with FullyUnfocused -> raise CannotUnfocusThisWay end
exception NoSuchFocus (* no handler: should not be allowed to reach toplevel. *) let rec get_in_focus_stack : type a. a focus_kind -> _ -> a = fun kind stack -> match stack with
| FocusElt (cond,inf,_)::stack -> beginmatch check_cond_kind cond kind with
| Some Refl -> inf
| None -> get_in_focus_stack kind stack end
| [] -> raise NoSuchFocus let get_at_focus kind pr =
get_in_focus_stack kind pr.focus_stack
let is_last_focus kind pr = let FocusElt (cond,_,_) = List.hd pr.focus_stack in Option.has_some (check_cond_kind cond kind)
let no_focused_goal p =
Proofview.finished p.proofview
let rec maximal_unfocus k p = if no_focused_goal p then try maximal_unfocus k (unfocus k p ()) with FullyUnfocused | CannotUnfocusThisWay -> p else p
(*** Proof Creation/Termination ***)
(* [end_of_stack] is unfocused by return to close every loose focus. *) let end_of_stack_kind = new_focus_kind "end_of_stack" let end_of_stack = CondEndStack end_of_stack_kind
let unfocused = is_last_focus end_of_stack_kind
let unfocus_all p = unfocus end_of_stack_kind p ()
let start ~name ~poly ?typing_flags sigma goals = let entry, proofview = Proofview.init sigma goals in let pr =
{ proofview
; entry
; focus_stack = []
; name
; poly
; typing_flags
} in
_focus end_of_stack () 1 (List.length goals) pr
let dependent_start ~name ~poly ?typing_flags goals = let entry, proofview = Proofview.dependent_init goals in let pr =
{ proofview
; entry
; focus_stack = []
; name
; poly
; typing_flags
} in let number_of_goals = List.length (Proofview.initial_goals pr.entry) in
_focus end_of_stack () 1 number_of_goals pr
let compact p = let entry, proofview = Proofview.compact p.entry p.proofview in
{ p with proofview; entry }
let update_sigma_univs ugraph p = let proofview = Proofview.Unsafe.update_sigma_univs ugraph p.proofview in
{ p with proofview }
(*** Function manipulation proof extra informations ***)
(*** Tactics ***)
let run_tactic env tac pr = letopen Proofview.Notations in let tac = (* include the future goals in the shelf *)
Proofview.with_shelf tac >>= fun (shelf, v) ->
Proofview.Unsafe.tclNEWSHELVED shelf <*>
Proofview.tclUNIT v in let { name; poly; proofview } = pr in let (result,proofview,env,status,info_trace) =
Proofview.apply ~name ~poly env tac proofview in let sigma = Proofview.return proofview in (* cleanup any shelved goals that got defined (thisisonlyusefulforgoalsthatwerealreadyintheshelf, tclNEWSHELVEDfiltersoutdefinedgoalsinsteadofaddingthem)
XXX should we be doing something advance-aware like tclNEWSHELVED? *) let proofview = Proofview.filter_shelf (Evd.is_undefined sigma) proofview in
{ pr with proofview },(env,status,info_trace),result
(*** Commands ***)
(* Remove all the goals from the shelf and adds them at the end of the
focused goals. *) let unshelve p = let sigma = Proofview.return p.proofview in let shelf = Evd.shelf sigma in let proofview = Proofview.unshelve shelf p.proofview in
{ p with proofview }
let background_subgoals p = let it, _ = Proofview.proofview (unroll_focus p.proofview p.focus_stack) in
it
let all_goals p = let add gs set = List.fold_left (fun s g -> Evar.Set.add g s) set gs in let (goals,stack,sigma) = proof p in letset = add goals Evar.Set.empty in letset = List.fold_left (fun s gs -> let (g1, g2) = gs in add g1 (add g2 set)) set stack in letset = add (Evd.shelf sigma) setin letset = Evar.Set.union (Evd.given_up sigma) setin let bgoals = background_subgoals p in
add bgoals set
type data =
{ sigma : Evd.evar_map (** A representation of the evar_map [EJGA wouldn't it better to just return the proofview?] *)
; goals : Evar.t list (** Focused goals *)
; entry : Proofview.entry (** Entry for the proofview *)
; stack : (Evar.t list * Evar.t list) list (** A representation of the focus stack *)
; name : Names.Id.t (** The name of the theorem whose proof is being constructed *)
; poly : bool (** Locality, polymorphism, and "kind" [Coercion, Definition, etc...] *)
}
let data { proofview; focus_stack; entry; name; poly } = let goals, sigma = Proofview.proofview proofview in (* spiwack: beware, the bottom of the stack is used by [Proof]
internally, and should not be exposed. *) let rec map_minus_one f = function
| [] -> assert false
| [_] -> []
| a::l -> f a :: (map_minus_one f l) in letmap (FocusElt (_, _, c)) = Proofview.focus_context sigma c in let stack = map_minus_one map focus_stack in
{ sigma; goals; entry; stack; name; poly }
let pr_goal e = Pp.(str "GOAL:" ++ int (Evar.repr e))
let goal_uid e = string_of_int (Evar.repr e)
let pr_proof p = let { goals=fg_goals; stack=bg_goals; sigma } = data p in let entry = Proofview.partial_proof p.entry p.proofview in
Pp.( let pr_goal_list = prlist_with_sep spc pr_goal in let rec aux acc = function
| [] -> acc
| (before,after)::stack ->
aux (pr_goal_list before ++ spc () ++ str "{" ++ acc ++ str "}" ++ spc () ++
pr_goal_list after) stack in
str "[" ++ str "focus structure: " ++
aux (pr_goal_list fg_goals) bg_goals ++ str ";" ++ spc () ++
str "shelved: " ++ pr_goal_list (Evd.shelf sigma) ++ str ";" ++ spc () ++
str "given up: " ++ pr_goal_list (Evar.Set.elements @@ Evd.given_up sigma) ++ spc() ++
str "partial proof: " ++ prlist_with_sep spc (Termops.Internal.print_constr_env (Global.env()) sigma) entry ++
str "]"
)
let register_side_effects eff = letopen Names in let cst = Safe_typing.constants_of_private eff.Evd.seff_private in let iter kn = let gr = GlobRef.ConstRef kn in let id = Label.to_id (Constant.label kn) in let sp = Lib.make_path id in
Nametab.push (Nametab.Until 1) sp gr in List.iter iter cst
let solve ?with_end_tac gi info_lvl tac pr = let tac = match with_end_tac with
| None -> tac
| Some etac -> Proofview.tclTHEN tac etac in let tac = match info_lvl with
| None -> tac
| Some _ -> Proofview.Trace.record_info_trace tac in let nosuchgoal = let info = Exninfo.reify () in
Proofview.tclZERO ~info (SuggestNoSuchGoals (1,pr)) in let tac = Goal_select.tclSELECT ~nosuchgoal gi tac in let tac = if use_unification_heuristics () then
Proofview.tclTHEN tac solve_constraints else tac in let env = Global.env () in let env = Environ.update_typing_flags ?typing_flags:pr.typing_flags env in let (p,(_env,status,info),()) = run_tactic env tac pr in let () = register_side_effects (Evd.eval_side_effects (Proofview.return p.proofview)) in let env = Global.env () in let sigma = Evd.from_env env in let () = match info_lvl with
| None -> ()
| Some i -> Feedback.msg_info (Pp.hov 0 (Proofview.Trace.pr_info env sigma ~lvl:i info)) in
(p,status)
(**********************************************************************) (* Shortcut to build a term using tactics *)
let refine_by_tactic ~name ~poly env sigma ty tac = (* Save the initial side-effects to restore them afterwards. We set the currentsetofside-effectstobeemptysothatwecanretrievethe
ones created during the tactic invocation easily. *) let eff = Evd.eval_side_effects sigma in let sigma = Evd.drop_side_effects sigma in (* Save the existing goals *) let sigma = Evd.push_future_goals sigma in (* Start a proof *) let prf = start ~name ~poly sigma [env, ty] in let (prf, _, ()) = try run_tactic env tac prf with Logic_monad.TacticFailure e as src -> (* Catch the inner error of the monad tactic *) let (_, info) = Exninfo.capture src in
Exninfo.iraise (e, info) in (* Plug back the retrieved sigma *) let { goals; stack; sigma; entry } = data prf in
assert (stack = []); let ans = match Proofview.initial_goals entry with
| [_, c, _] -> c
| _ -> assert false in let ans = EConstr.to_constr ~abort_on_undefined_evars:false sigma ans in (* [neff] contains the freshly generated side-effects *) let neff = Evd.eval_side_effects sigma in (* Reset the old side-effects *) let sigma = Evd.drop_side_effects sigma in let sigma = Evd.emit_side_effects eff sigma in (* Restore former goals *) let _goals, sigma = Evd.pop_future_goals sigma in (* Push remaining goals as future_goals which is the only way we havetoinformthecallerthattherearegoalstocollectwhile
not being encapsulated in the monad *) let sigma = List.fold_right Evd.declare_future_goal goals sigma in (* Get rid of the fresh side-effects by internalizing them in the term itself.Notethatthisisunsound,becausethetacticmayhavesolved othergoalsthatwerealreadypresentduringitsinvocation,sothat thosegoalsrelyoneffectsthatarenotpresentanymore.Hopefully,
this hack will work in most cases. *) let neff = neff.Evd.seff_private in let (ans, _) = Safe_typing.inline_private_constants env ((ans, Univ.ContextSet.empty), neff) in
EConstr.of_constr ans, sigma
let get_goal_context_gen pf i = let { sigma; goals } = data pf in let goal = tryList.nth goals (i-1) with Failure _ -> raise (NoSuchGoal None) in let env = Evd.evar_filtered_env (Global.env ()) (Evd.find_undefined sigma goal) in
(sigma, env)
let get_proof_context p = try get_goal_context_gen p 1 with
| NoSuchGoal _ -> (* No more focused goals *) let { sigma } = data p in
sigma, Global.env ()
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet am 2026-09-28)
¤
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 und die Messung sind noch experimentell.