aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--proofs/proofview.ml617
-rw-r--r--proofs/proofview.mli503
2 files changed, 641 insertions, 479 deletions
diff --git a/proofs/proofview.ml b/proofs/proofview.ml
index 4b46417e9..5f2a5b786 100644
--- a/proofs/proofview.ml
+++ b/proofs/proofview.ml
@@ -6,19 +6,12 @@
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
-(* The proofview datastructure is a pure datastructure underlying the notion
- of proof (namely, a proof is a proofview which can evolve and has safety
- mechanisms attached).
- The general idea of the structure is that it is composed of a chemical
- solution: an unstructured bag of stuff which has some relations with
- one another, which represents the various subnodes of the proof. Together
- with a comb: a datastructure that gives some order to some of these nodes,
- namely the (focused) open goals.
- The natural candidate for the solution is an {!Evd.evar_map}, that is
- a calculus of evars. The comb is then a list of goals (evars wrapped
- with some extra information, like possible name anotations).
- There is also need of a list of the evars which initialised the proofview
- to be able to return information about the proofview. *)
+
+(** This files defines the basic mechanism of proofs: the [proofview]
+ type is the state which tactics manipulate (a global state for
+ existential variables, together with the list of goals), and the type
+ ['a tactic] is the (abstract) type of tactics modifying the proof
+ state and returning a value of type ['a]. *)
open Pp
open Util
@@ -29,9 +22,20 @@ type proofview = Proofview_monad.proofview
type entry = (Term.constr * Term.types) list
+(** Returns a stylised view of a proofview for use by, for instance,
+ ide-s. *)
+(* spiwack: the type of [proofview] will change as we push more
+ refined functions to ide-s. This would be better than spawning a
+ new nearly identical function everytime. Hence the generic name. *)
+(* In this version: returns the list of focused goals together with
+ the [evar_map] context. *)
let proofview p =
p.comb , p.solution
+
+
+(** {6 Starting and querying a proof view} *)
+
type telescope =
| TNil of Evd.evar_map
| TCons of Environ.env * Evd.evar_map * Term.types * (Evd.evar_map -> Term.constr -> telescope)
@@ -55,8 +59,6 @@ let dependent_init =
let solution = Evd.reset_future_goals solution in
entry, { v with solution }
-(* Initialises a proofview, the argument is a list of environement,
- conclusion types, and optional names, creating that many initial goals. *)
let init =
let rec aux sigma = function
| [] -> TNil sigma
@@ -66,14 +68,10 @@ let init =
let initial_goals initial = initial
-(* Returns whether this proofview is finished or not. That is,
- if it has empty subgoals in the comb. There could still be unsolved
- subgoaled, but they would then be out of the view, focused out. *)
let finished = function
| {comb = []} -> true
| _ -> false
-(* Returns the current value of the proofview partial proofs. *)
let return { solution=defs } = defs
let return_constr { solution = defs } c = Evarutil.nf_evar defs c
@@ -81,23 +79,34 @@ let return_constr { solution = defs } c = Evarutil.nf_evar defs c
let partial_proof entry pv = CList.map (return_constr pv) (CList.map fst entry)
-(* Type of the object which allow to unfocus a view.*)
-(* First component is a reverse list of what comes before
- and second component is what goes after (in the expected
- order) *)
+(** {6 Focusing commands} *)
+
+(** A [focus_context] represents the part of the proof view which has
+ been removed by a focusing action, it can be used to unfocus later
+ on. *)
+(* First component is a reverse list of the goals which come before
+ and second component is the list of the goals which go after (in
+ the expected order). *)
type focus_context = Evar.t list * Evar.t list
+
+(** Returns a stylised view of a focus_context for use by, for
+ instance, ide-s. *)
+(* spiwack: the type of [focus_context] will change as we push more
+ refined functions to ide-s. This would be better than spawning a
+ new nearly identical function everytime. Hence the generic name. *)
+(* In this version: the goals in the context, as a "zipper" (the first
+ list is in reversed order). *)
let focus_context f = f
-(* This (internal) function extracts a sublist between two indices, and
- returns this sublist together with its context:
- if it returns [(a,(b,c))] then [a] is the sublist and (rev b)@a@c is the
- original list.
- The focused list has lenght [j-i-1] and contains the goals from
- number [i] to number [j] (both included) the first goal of the list
- being numbered [1].
- [focus_sublist i j l] raises [IndexOutOfRange] if
- [i > length l], or [j > length l] or [ j < i ]. *)
+(** This (internal) function extracts a sublist between two indices,
+ and returns this sublist together with its context: if it returns
+ [(a,(b,c))] then [a] is the sublist and (rev b)@a@c is the
+ original list. The focused list has lenght [j-i-1] and contains
+ the goals from number [i] to number [j] (both included) the first
+ goal of the list being numbered [1]. [focus_sublist i j l] raises
+ [IndexOutOfRange] if [i > length l], or [j > length l] or [j <
+ i]. *)
let focus_sublist i j l =
let (left,sub_right) = CList.goto (i-1) l in
let (sub, right) =
@@ -106,23 +115,25 @@ let focus_sublist i j l =
in
(sub, (left,right))
-(* Inverse operation to the previous one. *)
+(** Inverse operation to the previous one. *)
let unfocus_sublist (left,right) s =
CList.rev_append left (s@right)
-(* [focus i j] focuses a proofview on the goals from index [i] to index [j]
- (inclusive). (i.e. goals number [i] to [j] become the only goals of the
- returned proofview). The first goal has index 1.
- It returns the focus proof, and a context for the focus trace. *)
+(** [focus i j] focuses a proofview on the goals from index [i] to
+ index [j] (inclusive, goals are indexed from [1]). I.e. goals
+ number [i] to [j] become the only focused goals of the returned
+ proofview. It returns the focused proofview, and a context for
+ the focus stack. *)
let focus i j sp =
let (new_comb, context) = focus_sublist i j sp.comb in
( { sp with comb = new_comb } , context )
-(* [advance sigma g] returns [Some g'] if [g'] is undefined and
- is the current avatar of [g] (for instance [g] was changed by [clear]
- into [g']). It returns [None] if [g] has been (partially) solved. *)
+(** [advance sigma g] returns [Some g'] if [g'] is undefined and is
+ the current avatar of [g] (for instance [g] was changed by [clear]
+ into [g']). It returns [None] if [g] has been (partially)
+ solved. *)
(* spiwack: [advance] is probably performance critical, and the good
behaviour of its definition may depend sensitively to the actual
definition of [Evd.find]. Currently, [Evd.find] starts looking for
@@ -146,49 +157,48 @@ let rec advance sigma g =
unsolved (after advancing cleared goals). *)
let undefined defs l = CList.map_filter (advance defs) l
-(* Unfocuses a proofview with respect to a context. *)
+(** Unfocuses a proofview with respect to a context. *)
let unfocus c sp =
{ sp with comb = undefined sp.solution (unfocus_sublist c sp.comb) }
-(* The tactic monad:
- - Tactics are objects which apply a transformation to all
- the subgoals of the current view at the same time. By opposition
- to the old vision of applying it to a single goal. It allows
- tactics such as [shelve_unifiable] or tactics to reorder
- the focused goals (not done yet).
- (* spiwack: the ordering of goals, though, is actually rather
- brittle. It would be much more interesting to find a more
- robust way to adress goals, I have no idea at this time
- though*)
- or global automation tactic for dependent subgoals (instantiating
- an evar has influences on the other goals of the proof in progress,
- not being able to take that into account causes the current eauto
- tactic to fail on some instances where it could succeed).
- Another benefit is that it is possible to write tactics that can
- be executed even if there are no focused goals.
- - Tactics form a monad ['a tactic], in a sense a tactic can be
- seens as a function (without argument) which returns a value
- of type 'a and modifies the environement (in our case: the view).
- Tactics of course have arguments, but these are given at the
- meta-level as OCaml functions.
- Most tactics in the sense we are used to return [()], that is
- no really interesting values. But some might pass information
- around.
- (* spiwack: as far as I'm aware this doesn't really relate to
- F. Kirchner and C. Muñoz.
- *)
- The tactics seen in Coq's Ltac are (for now at least) only
- [unit tactic], the return values are kept for the OCaml toolkit.
- The operation or the monad are [Proofview.tclUNIT] (which is the
- "return" of the tactic monad) [Proofview.tclBIND] (which is
- the "bind") and [Proofview.tclTHEN] (which is a specialized
- bind on unit-returning tactics).
+(** {6 The tactic monad} *)
+
+(** - Tactics are objects which apply a transformation to all the
+ subgoals of the current view at the same time. By opposition to
+ the old vision of applying it to a single goal. It allows tactics
+ such as [shelve_unifiable], tactics to reorder the focused goals,
+ or global automation tactic for dependent subgoals (instantiating
+ an evar has influences on the other goals of the proof in
+ progress, not being able to take that into account causes the
+ current eauto tactic to fail on some instances where it could
+ succeed). Another benefit is that it is possible to write tactics
+ that can be executed even if there are no focused goals.
+ - Tactics form a monad ['a tactic], in a sense a tactic can be
+ seens as a function (without argument) which returns a value of
+ type 'a and modifies the environement (in our case: the view).
+ Tactics of course have arguments, but these are given at the
+ meta-level as OCaml functions. Most tactics in the sense we are
+ used to return [()], that is no really interesting values. But
+ some might pass information around. The tactics seen in Coq's
+ Ltac are (for now at least) only [unit tactic], the return values
+ are kept for the OCaml toolkit. The operation or the monad are
+ [Proofview.tclUNIT] (which is the "return" of the tactic monad)
+ [Proofview.tclBIND] (which is the "bind") and [Proofview.tclTHEN]
+ (which is a specialized bind on unit-returning tactics).
+ - Tactics have support for full-backtracking. Tactics can be seen
+ having multiple success: if after returning the first success a
+ failure is encountered, the tactic can backtrack and use a second
+ success if available. The state is backtracked to its previous
+ value, except the non-logical state defined in the {!NonLogical}
+ module below.
*)
+(* spiwack: as far as I'm aware this doesn't really relate to
+ F. Kirchner and C. Muñoz. *)
module Proof = Logical
-(* type of tactics:
+(** type of tactics:
tactics can
- access the environment,
@@ -197,59 +207,51 @@ module Proof = Logical
- backtrack on previous changes of the proofview *)
type +'a tactic = 'a Proof.t
-type 'a case =
-| Fail of exn
-| Next of 'a * (exn -> 'a tactic)
-
-(* Applies a tactic to the current proofview. *)
+(** Applies a tactic to the current proofview. *)
let apply env t sp =
let (next_r,(next_state,_),status) =
Logic_monad.NonLogical.run (Proof.run t () (sp,env))
in
next_r,next_state,status
-(*** tacticals ***)
-
-let catchable_exception = function
- | Logic_monad.Exception _ -> false
- | e -> Errors.noncritical e
+(** {7 Monadic primitives} *)
-(* Unit of the tactic monad *)
+(** Unit of the tactic monad. *)
let tclUNIT = Proof.return
-(* Bind operation of the tactic monad *)
+(** Bind operation of the tactic monad. *)
let tclBIND = Proof.(>>=)
-(* Interpretes the ";" (semicolon) of Ltac.
- As a monadic operation, it's a specialized "bind"
- on unit-returning tactic (meaning "there is no value to bind") *)
+(** Interpretes the ";" (semicolon) of Ltac. As a monadic operation,
+ it's a specialized "bind". *)
let tclTHEN = Proof.(>>)
-(* [tclIGNORE t] has the same operational content as [t],
- but drops the value at the end. *)
+(** [tclIGNORE t] has the same operational content as [t], but drops
+ the returned value. *)
let tclIGNORE = Proof.ignore
-(* [tclOR t1 t2 = t1] as long as [t1] succeeds. Whenever the successes
- of [t1] have been depleted and it failed with [e], then it behaves
- as [t2 e]. No interleaving at this point. *)
-let tclOR = Proof.plus
+module Monad = Proof
+
+
-(* [tclZERO e] always fails with error message [e]*)
+(** {7 Failure and backtracking} *)
+
+
+(** [tclZERO e] fails with exception [e]. It has no success. *)
let tclZERO = Proof.zero
-(* [tclCASE t] observes the head of the tactic and returns it as a value *)
-let tclCASE t =
- let open Logic_monad in
- let map = function
- | Nil e -> Fail e
- | Cons (x, t) -> Next (x, t)
- in
- Proof.map map (Proof.split t)
+(** [tclOR t1 t2] behaves like [t1] as long as [t1] succeeds. Whenever
+ the successes of [t1] have been depleted and it failed with [e],
+ then it behaves as [t2 e]. In other words, [tclOR] inserts a
+ backtracking point. *)
+let tclOR = Proof.plus
-(* [tclORELSE t1 t2] behaves like [t1] if [t1] succeeds at least once
- or [t2] if [t1] fails. *)
+(** [tclORELSE t1 t2] is equal to [t1] if [t1] has at least one
+ success or [t2 e] if [t1] fails with [e]. It is analogous to
+ [try/with] handler of exception in that it is not a backtracking
+ point. *)
let tclORELSE t1 t2 =
let open Logic_monad in
let open Proof in
@@ -257,9 +259,9 @@ let tclORELSE t1 t2 =
| Nil e -> t2 e
| Cons (a,t1') -> plus (return a) t1'
-(* [tclIFCATCH a s f] is a generalisation of [tclORELSE]: if [a]
- succeeds at least once then it behaves as [tclBIND a s] otherwise, if [a]
- fails with [e], then it behaves as [f e]. *)
+(** [tclIFCATCH a s f] is a generalisation of {!tclORELSE}: if [a]
+ succeeds at least once then it behaves as [tclBIND a s] otherwise,
+ if [a] fails with [e], then it behaves as [f e]. *)
let tclIFCATCH a s f =
let open Logic_monad in
let open Proof in
@@ -267,23 +269,25 @@ let tclIFCATCH a s f =
| Nil e -> f e
| Cons (x,a') -> plus (s x) (fun e -> (a' e) >>= fun x' -> (s x'))
-(* [tclONCE t] fails if [t] fails, otherwise it has exactly one
- success. *)
+(** [tclONCE t] behave like [t] except it has at most one success:
+ [tclONCE t] stops after the first success of [t]. If [t] fails
+ with [e], [tclONCE t] also fails with [e]. *)
let tclONCE = Proof.once
-let tclBREAK = Proof.break
-
exception MoreThanOneSuccess
let _ = Errors.register_handler begin function
| MoreThanOneSuccess -> Errors.error "This tactic has more than one success."
| _ -> raise Errors.Unhandled
end
-(* [tclONCE e t] succeeds as [t] if [t] has exactly one
- success. Otherwise it fails. It may behave differently than [t] as
- there may be extra non-logical effects used to discover that [t]
- does not have a second success. Moreover the second success may be
- conditional on the error recieved: [e] is used. *)
+(** [tclEXACTLY_ONCE e t] succeeds as [t] if [t] has exactly one
+ success. Otherwise it fails. The tactic [t] is run until its first
+ success, then a failure with exception [e] is simulated. It [t]
+ yields another success, then [tclEXACTLY_ONCE e t] fails with
+ [MoreThanOneSuccess] (it is a user error). Otherwise,
+ [tclEXACTLY_ONCE e t] succeeds with the first success of
+ [t]. Notice that the choice of [e] is relevant, as the presence of
+ further successes may depend on [e] (see {!tclOR}). *)
let tclEXACTLY_ONCE e t =
let open Logic_monad in
let open Proof in
@@ -295,12 +299,34 @@ let tclEXACTLY_ONCE e t =
| _ -> tclZERO MoreThanOneSuccess
-(* Focuses a tactic at a range of subgoals, found by their indices. *)
+(** [tclCASE t] wraps the {!Proofview_monad.Logical.split} primitive. *)
+type 'a case =
+| Fail of exn
+| Next of 'a * (exn -> 'a tactic)
+let tclCASE t =
+ let open Logic_monad in
+ let map = function
+ | Nil e -> Fail e
+ | Cons (x, t) -> Next (x, t)
+ in
+ Proof.map map (Proof.split t)
+
+let tclBREAK = Proof.break
+
+
+
+(** {7 Focusing tactics} *)
+
exception NoSuchGoals of int
let _ = Errors.register_handler begin function
| NoSuchGoals n -> Errors.error ("No such " ^ String.plural n "goal" ^".")
| _ -> raise Errors.Unhandled
end
+
+(** [tclFOCUS_gen nosuchgoal i j t] applies [t] in a context where
+ only the goals numbered [i] to [j] are focused (the rest of the goals
+ is restored at the end of the tactic). If the range [i]-[j] is not
+ valid, then it [tclFOCUS_gen nosuchgoal i j t] is [nosuchgoal]. *)
let tclFOCUS_gen nosuchgoal i j t =
let open Proof in
Pv.get >>= fun initial ->
@@ -315,6 +341,7 @@ let tclFOCUS_gen nosuchgoal i j t =
let tclFOCUS i j t = tclFOCUS_gen (tclZERO (NoSuchGoals (j+1-i))) i j t
let tclTRYFOCUS i j t = tclFOCUS_gen (tclUNIT ()) i j t
+(** Like {!tclFOCUS} but selects a single goal by name. *)
let tclFOCUSID id t =
let open Proof in
Pv.get >>= fun initial ->
@@ -332,16 +359,9 @@ let tclFOCUSID id t =
aux 1 initial.comb
-(* Dispatch tacticals are used to apply a different tactic to each goal under
- consideration. They come in two flavours:
- [tclDISPATCH] takes a list of [unit tactic]-s and build a [unit tactic].
- [tclDISPATCHL] takes a list of ['a tactic] and returns an ['a list tactic].
- They both work by applying each of the tactic to the corresponding
- goal (starting with the first goal). In the case of [tclDISPATCHL],
- the tactic returns a list of the same size as the argument list (of
- tactics), each element being the result of the tactic executed in
- the corresponding goal. *)
+(** {7 Dispatching on goals} *)
+
exception SizeMismatch of int*int
let _ = Errors.register_handler begin function
| SizeMismatch (i,_) ->
@@ -398,10 +418,26 @@ let fold_left2_goal i s l =
| reraise -> tclZERO reraise
end
-(* spiwack: we use an parametrised function to generate the dispatch
- tacticals. [tclDISPATCHGEN] takes an argument [join] to reify the
- list of produced value into the final value. *)
-let tclDISPATCHGEN f join tacs =
+(** Dispatch tacticals are used to apply a different tactic to each
+ goal under focus. They come in two flavours: [tclDISPATCH] takes a
+ list of [unit tactic]-s and build a [unit tactic]. [tclDISPATCHL]
+ takes a list of ['a tactic] and returns an ['a list tactic].
+
+ They both work by applying each of the tactic in a focus
+ restricted to the corresponding goal (starting with the first
+ goal). In the case of [tclDISPATCHL], the tactic returns a list of
+ the same size as the argument list (of tactics), each element
+ being the result of the tactic executed in the corresponding goal.
+
+ When the length of the tactic list is not the number of goal,
+ raises [SizeMismatch (g,t)] where [g] is the number of available
+ goals, and [t] the number of tactics passed.
+
+ [tclDISPATCHGEN join tacs] generalises both functions as the
+ successive results of [tacs] are stored in reverse order in a
+ list, and [join] is used to convert the result into the expected
+ form. *)
+let tclDISPATCHGEN join tacs =
match tacs with
| [] ->
begin
@@ -417,20 +453,23 @@ let tclDISPATCHGEN f join tacs =
| { comb=[goal] ; solution } ->
begin match advance solution goal with
| None -> tclUNIT (join [])
- | Some _ -> Proof.map (fun res -> join [res]) (f tac)
+ | Some _ -> Proof.map (fun res -> join [res]) tac
end
| {comb} -> tclZERO (SizeMismatch(CList.length comb,1))
end
| _ ->
- let iter _ t cur = Proof.map (fun y -> y :: cur) (f t) in
+ let iter _ t cur = Proof.map (fun y -> y :: cur) t in
let ans = fold_left2_goal iter [] tacs in
Proof.map join ans
-let tclDISPATCH tacs = tclDISPATCHGEN Util.identity Pervasives.ignore tacs
+let tclDISPATCH tacs = tclDISPATCHGEN Pervasives.ignore tacs
-let tclDISPATCHL tacs =
- tclDISPATCHGEN Util.identity CList.rev tacs
+let tclDISPATCHL tacs = tclDISPATCHGEN CList.rev tacs
+
+(** [extend_to_list startxs rx endxs l] builds a list
+ [startxs@[rx,...,rx]@endxs] of the same length as [l]. Raises
+ [SizeMismatch] if [startxs@endxs] is already longer than [l]. *)
let extend_to_list startxs rx endxs l =
(* spiwack: I use [l] essentially as a natural number *)
let rec duplicate acc = function
@@ -451,6 +490,11 @@ let extend_to_list startxs rx endxs l =
in
copy startxs l
+(** [tclEXTEND b r e] is a variant of {!tclDISPATCH}, where the [r]
+ tactic is "repeated" enough time such that every goal has a tactic
+ assigned to it ([b] is the list of tactics applied to the first
+ goals, [e] to the last goals, and [r] is applied to every goal in
+ between). *)
let tclEXTEND tacs1 rtac tacs2 =
let open Proof in
Comb.get >>= fun comb ->
@@ -461,10 +505,11 @@ let tclEXTEND tacs1 rtac tacs2 =
tclZERO (SizeMismatch(
CList.length comb,
(CList.length tacs1)+(CList.length tacs2)))
-(* spiwack: failure occur only when the number of goals is too
+(* spiwack: failure occurs only when the number of goals is too
small. Hence we can assume that [rtac] is replicated 0 times for
any error message. *)
+(** [tclEXTEND [] tac []]. *)
let tclINDEPENDENT tac =
let open Proof in
Pv.get >>= fun initial ->
@@ -473,7 +518,152 @@ let tclINDEPENDENT tac =
| [_] -> tac
| _ -> iter_goal (fun _ -> tac)
-(* Equality function on goals *)
+
+
+(** {7 Goal manipulation} *)
+
+(** Shelves all the goals under focus. *)
+let shelve =
+ let open Proof in
+ Comb.get >>= fun initial ->
+ Comb.set [] >>
+ Shelf.put initial
+
+
+(** [contained_in_info e evi] checks whether the evar [e] appears in
+ the hypotheses, the conclusion or the body of the evar_info
+ [evi]. Note: since we want to use it on goals, the body is actually
+ supposed to be empty. *)
+let contained_in_info sigma e evi =
+ Evar.Set.mem e (Evd.evars_of_filtered_evar_info (Evarutil.nf_evar_info sigma evi))
+
+(** [depends_on sigma src tgt] checks whether the goal [src] appears
+ as an existential variable in the definition of the goal [tgt] in
+ [sigma]. *)
+let depends_on sigma src tgt =
+ let evi = Evd.find sigma tgt in
+ contained_in_info sigma src evi
+
+(** [unifiable sigma g l] checks whether [g] appears in another
+ subgoal of [l]. The list [l] may contain [g], but it does not
+ affect the result. *)
+let unifiable sigma g l =
+ CList.exists (fun tgt -> not (Evar.equal g tgt) && depends_on sigma g tgt) l
+
+(** [partition_unifiable sigma l] partitions [l] into a pair [(u,n)]
+ where [u] is composed of the unifiable goals, i.e. the goals on
+ whose definition other goals of [l] depend, and [n] are the
+ non-unifiable goals. *)
+let partition_unifiable sigma l =
+ CList.partition (fun g -> unifiable sigma g l) l
+
+(** Shelves the unifiable goals under focus, i.e. the goals which
+ appear in other goals under focus (the unfocused goals are not
+ considered). *)
+let shelve_unifiable =
+ let open Proof in
+ Pv.get >>= fun initial ->
+ let (u,n) = partition_unifiable initial.solution initial.comb in
+ Comb.set n >>
+ Shelf.put u
+
+(** [guard_no_unifiable] fails with error [UnresolvedBindings] if some
+ goals are unifiable (see {!shelve_unifiable}) in the current focus. *)
+let guard_no_unifiable =
+ let open Proof in
+ Pv.get >>= fun initial ->
+ let (u,n) = partition_unifiable initial.solution initial.comb in
+ match u with
+ | [] -> tclUNIT ()
+ | gls ->
+ let l = CList.map (fun g -> Evd.dependent_evar_ident g initial.solution) gls in
+ let l = CList.map (fun id -> Names.Name id) l in
+ tclZERO (Logic.RefinerError (Logic.UnresolvedBindings l))
+
+(** [unshelve l p] adds all the goals in [l] at the end of the focused
+ goals of p *)
+let unshelve l p =
+ (* advance the goals in case of clear *)
+ let l = undefined p.solution l in
+ { p with comb = p.comb@l }
+
+
+(** [goodmod p m] computes the representative of [p] modulo [m] in the
+ interval [[0,m-1]].*)
+let goodmod p m =
+ let p' = p mod m in
+ (* if [n] is negative [n mod l] is negative of absolute value less
+ than [l], so [(n mod l)+l] is the representative of [n] in the
+ interval [[0,l-1]].*)
+ if p' < 0 then p'+m else p'
+
+let cycle n =
+ Comb.modify begin fun initial ->
+ let l = CList.length initial in
+ let n' = goodmod n l in
+ let (front,rear) = CList.chop n' initial in
+ rear@front
+ end
+
+let swap i j =
+ Comb.modify begin fun initial ->
+ let l = CList.length initial in
+ let i = if i>0 then i-1 else i and j = if j>0 then j-1 else j in
+ let i = goodmod i l and j = goodmod j l in
+ CList.map_i begin fun k x ->
+ match k with
+ | k when Int.equal k i -> CList.nth initial j
+ | k when Int.equal k j -> CList.nth initial i
+ | _ -> x
+ end 0 initial
+ end
+
+let revgoals =
+ Comb.modify CList.rev
+
+let numgoals =
+ let open Proof in
+ Comb.get >>= fun comb ->
+ return (CList.length comb)
+
+
+
+(** {7 Access primitives} *)
+
+let tclEVARMAP = Solution.get
+
+let tclENV = Env.get
+
+
+
+(** {7 Put-like primitives} *)
+
+
+let emit_side_effects eff x =
+ { x with solution = Evd.emit_side_effects eff x.solution }
+
+let tclEFFECTS eff =
+ let open Proof in
+ return () >>= fun () -> (* The Global.env should be taken at exec time *)
+ Env.set (Global.env ()) >>
+ Pv.modify (fun initial -> emit_side_effects eff initial)
+
+let mark_as_unsafe = Status.put false
+
+(** Gives up on the goal under focus. Reports an unsafe status. Proofs
+ with given up goals cannot be closed. *)
+let give_up =
+ let open Proof in
+ Comb.get >>= fun initial ->
+ Comb.set [] >>
+ mark_as_unsafe >>
+ Giveup.put initial
+
+
+
+(** {7 Control primitives} *)
+
+(** Equality function on goals *)
let goal_equal evars1 gl1 evars2 gl2 =
let evi1 = Evd.find evars1 gl1 in
let evi2 = Evd.find evars2 gl2 in
@@ -493,20 +683,6 @@ let tclPROGRESS t =
else
tclZERO (Errors.UserError ("Proofview.tclPROGRESS" , Pp.str"Failed to progress."))
-let tclEVARMAP = Solution.get
-
-let tclENV = Env.get
-
-
-let emit_side_effects eff x =
- { x with solution = Evd.emit_side_effects eff x.solution }
-
-let tclEFFECTS eff =
- let open Proof in
- return () >>= fun () -> (* The Global.env should be taken at exec time *)
- Env.set (Global.env ()) >>
- Pv.modify (fun initial -> emit_side_effects eff initial)
-
exception Timeout
let _ = Errors.register_handler begin function
| Timeout -> Errors.errorlabstrm "Proofview.tclTIMEOUT" (Pp.str"Tactic timeout!")
@@ -570,120 +746,12 @@ let tclTIME s t =
tclOR (tclUNIT x) (fun e -> aux (n+1) (k e))
in aux 0 t
-let mark_as_unsafe = Status.put false
-
-(* Shelves all the goals under focus. *)
-let shelve =
- let open Proof in
- Comb.get >>= fun initial ->
- Comb.set [] >>
- Shelf.put initial
-
-
-(* [contained_in_info e evi] checks whether the evar [e] appears in
- the hypotheses, the conclusion or the body of the evar_info
- [evi]. Note: since we want to use it on goals, the body is actually
- supposed to be empty. *)
-let contained_in_info sigma e evi =
- Evar.Set.mem e (Evd.evars_of_filtered_evar_info (Evarutil.nf_evar_info sigma evi))
-
-(* [depends_on sigma src tgt] checks whether the goal [src] appears as an
- existential variable in the definition of the goal [tgt] in [sigma]. *)
-let depends_on sigma src tgt =
- let evi = Evd.find sigma tgt in
- contained_in_info sigma src evi
-
-(* [unifiable sigma g l] checks whether [g] appears in another subgoal
- of [l]. The list [l] may contain [g], but it does not affect the
- result. *)
-let unifiable sigma g l =
- CList.exists (fun tgt -> not (Evar.equal g tgt) && depends_on sigma g tgt) l
-
-(* [partition_unifiable sigma l] partitions [l] into a pair [(u,n)]
- where [u] is composed of the unifiable goals, i.e. the goals on
- whose definition other goals of [l] depend, and [n] are the
- non-unifiable goals. *)
-let partition_unifiable sigma l =
- CList.partition (fun g -> unifiable sigma g l) l
-
-(* Shelves the unifiable goals under focus, i.e. the goals which
- appear in other goals under focus (the unfocused goals are not
- considered). *)
-let shelve_unifiable =
- let open Proof in
- Pv.get >>= fun initial ->
- let (u,n) = partition_unifiable initial.solution initial.comb in
- Comb.set n >>
- Shelf.put u
-
-let check_no_dependencies =
- let open Proof in
- Pv.get >>= fun initial ->
- let (u,n) = partition_unifiable initial.solution initial.comb in
- match u with
- | [] -> tclUNIT ()
- | gls ->
- let l = CList.map (fun g -> Evd.dependent_evar_ident g initial.solution) gls in
- let l = CList.map (fun id -> Names.Name id) l in
- tclZERO (Logic.RefinerError (Logic.UnresolvedBindings l))
-
-(* [unshelve l p] adds all the goals in [l] at the end of the focused
- goals of p *)
-let unshelve l p =
- (* advance the goals in case of clear *)
- let l = undefined p.solution l in
- { p with comb = p.comb@l }
-
-(* Gives up on the goal under focus. Reports an unsafe status. Proofs
- with given up goals cannot be closed. *)
-let give_up =
- let open Proof in
- Comb.get >>= fun initial ->
- Comb.set [] >>
- mark_as_unsafe >>
- Giveup.put initial
-(** [goodmod p m] computes the representative of [p] modulo [m] in the
- interval [[0,m-1]].*)
-let goodmod p m =
- let p' = p mod m in
- (* if [n] is negative [n mod l] is negative of absolute value less
- than [l], so [(n mod l)+l] is the representative of [n] in the
- interval [[0,l-1]].*)
- if p' < 0 then p'+m else p'
-let cycle n =
- Comb.modify begin fun initial ->
- let l = CList.length initial in
- let n' = goodmod n l in
- let (front,rear) = CList.chop n' initial in
- rear@front
- end
-
-let swap i j =
- Comb.modify begin fun initial ->
- let l = CList.length initial in
- let i = if i>0 then i-1 else i and j = if j>0 then j-1 else j in
- let i = goodmod i l and j = goodmod j l in
- CList.map_i begin fun k x ->
- match k with
- | k when Int.equal k i -> CList.nth initial j
- | k when Int.equal k j -> CList.nth initial i
- | _ -> x
- end 0 initial
- end
-
-let revgoals =
- Comb.modify CList.rev
-
-let numgoals =
- let open Proof in
- Comb.get >>= fun comb ->
- return (CList.length comb)
+(** {7 Unsafe primitives} *)
module Unsafe = struct
- (* A [Proofview.tactic] version of [Refiner.tclEVARS] *)
let tclEVARS evd =
Pv.modify (fun ps -> { ps with solution = evd })
@@ -702,10 +770,12 @@ module Unsafe = struct
let reset_future_goals p =
{ p with solution = Evd.reset_future_goals p.solution }
-
end
-module Monad = Proof
+
+
+(** {7 Notations} *)
+
module Notations = struct
let (>>=) = tclBIND
let (<*>) = tclTHEN
@@ -714,9 +784,18 @@ end
open Notations
+
+
+(** {6 Goal-dependent tactics} *)
+
(* To avoid shadowing by the local [Goal] module *)
module GoalV82 = Goal.V82
+let catchable_exception = function
+ | Logic_monad.Exception _ -> false
+ | e -> Errors.noncritical e
+
+
module Goal = struct
type 'a t = {
@@ -800,6 +879,10 @@ module Goal = struct
end
+
+
+(** {6 The refine tactic} *)
+
module Refine =
struct
@@ -873,6 +956,10 @@ struct
end
end
+
+
+(** {6 Non-logical state} *)
+
module NonLogical = Logic_monad.NonLogical
let tclLIFT = Proof.lift
diff --git a/proofs/proofview.mli b/proofs/proofview.mli
index 914af982d..3529c3c1b 100644
--- a/proofs/proofview.mli
+++ b/proofs/proofview.mli
@@ -6,26 +6,19 @@
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
-(* The proofview datastructure is a pure datastructure underlying the notion
- of proof (namely, a proof is a proofview which can evolve and has safety
- mechanisms attached).
- The general idea of the structure is that it is composed of a chemical
- solution: an unstructured bag of stuff which has some relations with
- one another, which represents the various subnodes of the proof. Together
- with a comb: a datastructure that gives some order to some of these nodes,
- namely the (focused) open goals.
- The natural candidate for the solution is an {!Evd.evar_map}, that is
- a calculus of evars. The comb is then a list of goals (evars wrapped
- with some extra information, like possible name anotations).
- There is also need of a list of the evars which initialised the proofview
- to be able to return information about the proofview. *)
+(** This files defines the basic mechanism of proofs: the [proofview]
+ type is the state which tactics manipulate (a global state for
+ existential variables, together with the list of goals), and the type
+ ['a tactic] is the (abstract) type of tactics modifying the proof
+ state and returning a value of type ['a]. *)
open Term
+(** Main state of tactics *)
type proofview
-(* Returns a stylised view of a proofview for use by, for instance,
- ide-s. *)
+(** Returns a stylised view of a proofview for use by, for instance,
+ ide-s. *)
(* spiwack: the type of [proofview] will change as we push more
refined functions to ide-s. This would be better than spawning a
new nearly identical function everytime. Hence the generic name. *)
@@ -33,243 +26,272 @@ type proofview
the [evar_map] context. *)
val proofview : proofview -> Goal.goal list * Evd.evar_map
+
+(** {6 Starting and querying a proof view} *)
+
+(** Abstract representation of the initial goals of a proof. *)
type entry
-(* Initialises a proofview, the argument is a list of environement,
- conclusion types, creating that many initial goals. *)
+(** Initialises a proofview, the main argument is a list of
+ environements (including a [named_context] which are used as
+ hypotheses) pair with conclusion types, creating accordingly many
+ initial goals. Because a proof does not necessarily starts in an
+ empty [evar_map] (indeed a proof can be triggered by an incomplete
+ pretyping), [init] takes an additional argument to represent the
+ initial [evar_map]. *)
val init : Evd.evar_map -> (Environ.env * Term.types) list -> entry * proofview
+(** A [telescope] is a list of environment and conclusion like in
+ {!init}, except that each element may depend on the previous
+ goals. The telescope passes the goals in the form of a
+ [Term.constr] which represents the goal as an [evar]. The
+ [evar_map] is threaded in state passing style. *)
type telescope =
| TNil of Evd.evar_map
| TCons of Environ.env * Evd.evar_map * Term.types * (Evd.evar_map -> Term.constr -> telescope)
-(* Like [init], but goals are allowed to be depedenent on one
- another. Dependencies between goals is represented with the type
- [telescope] instead of [list]. *)
+(** Like {!init}, but goals are allowed to be dependent on one
+ another. Dependencies between goals is represented with the type
+ [telescope] instead of [list]. Note that the first [evar_map] of
+ the telescope plays the role of the [evar_map] argument in
+ [init]. *)
val dependent_init : telescope -> entry * proofview
-(* Returns whether this proofview is finished or not. That is,
- if it has empty subgoals in the comb. There could still be unsolved
- subgoaled, but they would then be out of the view, focused out. *)
+(** [finished pv] is [true] if and only if [pv] is complete. That is,
+ if it has an empty list of focused goals. There could still be
+ unsolved subgoaled, but they would then be out of focus. *)
val finished : proofview -> bool
-(* Returns the current value of the proofview partial proofs. *)
+(** Returns the current [evar] state. *)
val return : proofview -> Evd.evar_map
val partial_proof : entry -> proofview -> constr list
val initial_goals : entry -> (constr * types) list
-(*** Focusing operations ***)
-(* Type of the object which allow to unfocus a view.*)
+
+(** {6 Focusing commands} *)
+
+(** A [focus_context] represents the part of the proof view which has
+ been removed by a focusing action, it can be used to unfocus later
+ on. *)
type focus_context
-(* Returns a stylised view of a focus_context for use by, for
- instance, ide-s. *)
+(** Returns a stylised view of a focus_context for use by, for
+ instance, ide-s. *)
(* spiwack: the type of [focus_context] will change as we push more
refined functions to ide-s. This would be better than spawning a
new nearly identical function everytime. Hence the generic name. *)
-(* In this version: returns the number of goals that are held *)
+(* In this version: the goals in the context, as a "zipper" (the first
+ list is in reversed order). *)
val focus_context : focus_context -> Goal.goal list * Goal.goal list
-(* [focus i j] focuses a proofview on the goals from index [i] to index [j]
- (inclusive). (i.e. goals number [i] to [j] become the only goals of the
- returned proofview).
- It returns the focus proof, and a context for the focus trace. *)
+(** [focus i j] focuses a proofview on the goals from index [i] to
+ index [j] (inclusive, goals are indexed from [1]). I.e. goals
+ number [i] to [j] become the only focused goals of the returned
+ proofview. It returns the focused proofview, and a context for
+ the focus stack. *)
val focus : int -> int -> proofview -> proofview * focus_context
-(* Unfocuses a proofview with respect to a context. *)
+(** Unfocuses a proofview with respect to a context. *)
val unfocus : focus_context -> proofview -> proofview
-(* The tactic monad:
- - Tactics are objects which apply a transformation to all
- the subgoals of the current view at the same time. By opposition
- to the old vision of applying it to a single goal. It allows
- tactics such as [shelve_unifiable] or tactics to reorder
- the focused goals (not done yet).
- (* spiwack: the ordering of goals, though, is actually rather
- brittle. It would be much more interesting to find a more
- robust way to adress goals, I have no idea at this time
- though*)
- or global automation tactic for dependent subgoals (instantiating
- an evar has influences on the other goals of the proof in progress,
- not being able to take that into account causes the current eauto
- tactic to fail on some instances where it could succeed).
- Another benefit is that it is possible to write tactics that can
- be executed even if there are no focused goals.
- - Tactics form a monad ['a tactic], in a sense a tactic can be
- seens as a function (without argument) which returns a value
- of type 'a and modifies the environement (in our case: the view).
- Tactics of course have arguments, but these are given at the
- meta-level as OCaml functions.
- Most tactics in the sense we are used to return [()], that is
- no really interesting values. But some might pass information
- around.
- (* spiwack: as far as I'm aware this doesn't really relate to
- F. Kirchner and C. Muñoz.
- *)
- The tactics seen in Coq's Ltac are (for now at least) only
- [unit tactic], the return values are kept for the OCaml toolkit.
- The operation or the monad are [Proofview.tclUNIT] (which is the
- "return" of the tactic monad) [Proofview.tclBIND] (which is
- the "bind") and [Proofview.tclTHEN] (which is a specialized
- bind on unit-returning tactics).
+
+(** {6 The tactic monad} *)
+
+(** - Tactics are objects which apply a transformation to all the
+ subgoals of the current view at the same time. By opposition to
+ the old vision of applying it to a single goal. It allows tactics
+ such as [shelve_unifiable], tactics to reorder the focused goals,
+ or global automation tactic for dependent subgoals (instantiating
+ an evar has influences on the other goals of the proof in
+ progress, not being able to take that into account causes the
+ current eauto tactic to fail on some instances where it could
+ succeed). Another benefit is that it is possible to write tactics
+ that can be executed even if there are no focused goals.
+ - Tactics form a monad ['a tactic], in a sense a tactic can be
+ seens as a function (without argument) which returns a value of
+ type 'a and modifies the environement (in our case: the view).
+ Tactics of course have arguments, but these are given at the
+ meta-level as OCaml functions. Most tactics in the sense we are
+ used to return [()], that is no really interesting values. But
+ some might pass information around. The tactics seen in Coq's
+ Ltac are (for now at least) only [unit tactic], the return values
+ are kept for the OCaml toolkit. The operation or the monad are
+ [Proofview.tclUNIT] (which is the "return" of the tactic monad)
+ [Proofview.tclBIND] (which is the "bind") and [Proofview.tclTHEN]
+ (which is a specialized bind on unit-returning tactics).
+ - Tactics have support for full-backtracking. Tactics can be seen
+ having multiple success: if after returning the first success a
+ failure is encountered, the tactic can backtrack and use a second
+ success if available. The state is backtracked to its previous
+ value, except the non-logical state defined in the {!NonLogical}
+ module below.
*)
+(** The abstract type of tactics *)
type +'a tactic
-type 'a case =
-| Fail of exn
-| Next of 'a * (exn -> 'a tactic)
-
-(* Applies a tactic to the current proofview. *)
-(* the return boolean signals the use of an unsafe tactic, in which
- case it is [false]. *)
+(** Applies a tactic to the current proofview. Returns a tuple
+ [a,pv,(b,sh,gu)] where [a] is the return value of the tactic, [pv]
+ is the updated proofview, [b] a boolean which is [true] if the
+ tactic has not done any action considered unsafe (such as
+ admitting a lemma), [sh] is the list of goals which have been
+ shelved by the tactic, and [gu] the list of goals on which the
+ tactic has given up. In case of multiple success the first one is
+ selected. If there is no success, fails with
+ {!Logic_monad.TacticFailure}*)
val apply : Environ.env -> 'a tactic -> proofview -> 'a
* proofview
* (bool*Goal.goal list*Goal.goal list)
-(*** tacticals ***)
+(** {7 Monadic primitives} *)
-(* Unit of the tactic monad *)
+(** Unit of the tactic monad. *)
val tclUNIT : 'a -> 'a tactic
-(* Bind operation of the tactic monad *)
+(** Bind operation of the tactic monad. *)
val tclBIND : 'a tactic -> ('a -> 'b tactic) -> 'b tactic
-(* Interprets the ";" (semicolon) of Ltac.
- As a monadic operation, it's a specialized "bind"
- on unit-returning tactic (meaning "there is no value to bind") *)
+(** Interprets the ";" (semicolon) of Ltac. As a monadic operation,
+ it's a specialized "bind". *)
val tclTHEN : unit tactic -> 'a tactic -> 'a tactic
-(* [tclIGNORE t] has the same operational content as [t],
- but drops the value at the end. *)
+(** [tclIGNORE t] has the same operational content as [t], but drops
+ the returned value. *)
val tclIGNORE : 'a tactic -> unit tactic
-(* [tclOR t1 t2 = t1] as long as [t1] succeeds. Whenever the successes
- of [t1] have been depleted and it failed with [e], then it behaves
- as [t2 e]. No interleaving at this point. *)
-val tclOR : 'a tactic -> (exn -> 'a tactic) -> 'a tactic
+(** Generic monadic combinators for tactics. *)
+module Monad : Monad.S with type +'a t = 'a tactic
+
+(** {7 Failure and backtracking} *)
-(* [tclZERO] always fails *)
+(** [tclZERO e] fails with exception [e]. It has no success. *)
val tclZERO : exn -> 'a tactic
-(* [tclORELSE t1 t2] behaves like [t1] if [t1] succeeds at least once
- or [t2 e] if [t1] fails with [e]. *)
-val tclORELSE : 'a tactic -> (exn -> 'a tactic) -> 'a tactic
+(** [tclOR t1 t2] behaves like [t1] as long as [t1] succeeds. Whenever
+ the successes of [t1] have been depleted and it failed with [e],
+ then it behaves as [t2 e]. In other words, [tclOR] inserts a
+ backtracking point. *)
+val tclOR : 'a tactic -> (exn -> 'a tactic) -> 'a tactic
-(* [tclCASE t] observes the head of the tactic and returns it as a value *)
-val tclCASE : 'a tactic -> 'a case tactic
+(** [tclORELSE t1 t2] is equal to [t1] if [t1] has at least one
+ success or [t2 e] if [t1] fails with [e]. It is analogous to
+ [try/with] handler of exception in that it is not a backtracking
+ point. *)
+val tclORELSE : 'a tactic -> (exn -> 'a tactic) -> 'a tactic
-(* [tclIFCATCH a s f] is a generalisation of [tclORELSE]: if [a]
- succeeds at least once then it behaves as [tclBIND a s] otherwise, if [a]
- fails with [e], then it behaves as [f e]. *)
+(** [tclIFCATCH a s f] is a generalisation of {!tclORELSE}: if [a]
+ succeeds at least once then it behaves as [tclBIND a s] otherwise,
+ if [a] fails with [e], then it behaves as [f e]. *)
val tclIFCATCH : 'a tactic -> ('a -> 'b tactic) -> (exn -> 'b tactic) -> 'b tactic
-(* [tclONCE t] fails if [t] fails, otherwise it has exactly one
- success. *)
+(** [tclONCE t] behave like [t] except it has at most one success:
+ [tclONCE t] stops after the first success of [t]. If [t] fails
+ with [e], [tclONCE t] also fails with [e]. *)
val tclONCE : 'a tactic -> 'a tactic
-(* [tclBREAK f t] is a generalization of [tclONCE t]. Instead of stopping at the
- first element encountered, it waits for the tactic to return an exception
- satisfying [f], and drop the remaining results. [tclONCE t] is equivalent to
- [tclBREAK (fun _ -> true) t]. *)
+(** [tclEXACTLY_ONCE e t] succeeds as [t] if [t] has exactly one
+ success. Otherwise it fails. The tactic [t] is run until its first
+ success, then a failure with exception [e] is simulated. It [t]
+ yields another success, then [tclEXACTLY_ONCE e t] fails with
+ [MoreThanOneSuccess] (it is a user error). Otherwise,
+ [tclEXACTLY_ONCE e t] succeeds with the first success of
+ [t]. Notice that the choice of [e] is relevant, as the presence of
+ further successes may depend on [e] (see {!tclOR}). *)
+exception MoreThanOneSuccess
+val tclEXACTLY_ONCE : exn -> 'a tactic -> 'a tactic
+
+(** [tclCASE t] splits [t] into its first success and a
+ continuation. It is the most general primitive to control
+ backtracking. *)
+type 'a case =
+ | Fail of exn
+ | Next of 'a * (exn -> 'a tactic)
+val tclCASE : 'a tactic -> 'a case tactic
+
+(** [tclBREAK p t] is a generalization of [tclONCE t]. Instead of
+ stopping after the first success, it succeeds like [t] until a
+ failure with an exception [e] such that [p e=true] is raised. At
+ which point it drop the remaining successes. [tclONCE t] is
+ equivalent to [tclBREAK (fun _ -> true) t]. *)
val tclBREAK : (exn -> bool) -> 'a tactic -> 'a tactic
-(* [tclEXACTLY_ONCE e t] succeeds as [t] if [t] has exactly one
- success. Otherwise it fails. It may behave differently than [t] as
- there may be extra non-logical effects used to discover that [t]
- does not have a second success. Moreover the second success may be
- conditional on the error received: [e] is used. *)
-val tclEXACTLY_ONCE : exn -> 'a tactic -> 'a tactic
-(* Focuses a tactic at a range of subgoals, found by their indices.
- The other goals are restored to the focus when the tactic is done.
+(** {7 Focusing tactics} *)
- If the specified range doesn't correspond to existing goals, fails
- with [NoSuchGoals]. *)
+(** [tclFOCUS i j t] applies [t] after focusing on the goals number
+ [i] to [j] (see {!focus}). The rest of the goals is restored after
+ the tactic action. If the specified range doesn't correspond to
+ existing goals, fails with [NoSuchGoals] (a user error). *)
exception NoSuchGoals of int
val tclFOCUS : int -> int -> 'a tactic -> 'a tactic
+(** [tclFOCUSID x t] applies [t] on a (single) focused goal like
+ {!tclFOCUS}. The goal is found by its name rather than its
+ number.*)
val tclFOCUSID : Names.Id.t -> 'a tactic -> 'a tactic
-(* Focuses a tactic at a range of subgoals, found by their indices.
- The other goals are restored to the focus when the tactic is done.
-
- If the specified range doesn't correspond to existing goals, behaves
- like [tclUNIT ()]. *)
+(** [tclTRYFOCUS i j t] behaves like {!tclFOCUS}, except that if the
+ specified range doesn't correspond to existing goals, behaves like
+ [tclUNIT ()] instead of failing. *)
val tclTRYFOCUS : int -> int -> unit tactic -> unit tactic
-(* Dispatch tacticals are used to apply a different tactic to each goal under
- consideration. They come in two flavours:
- [tclDISPATCH] takes a list of [unit tactic]-s and build a [unit tactic].
- [tclDISPATCHL] takes a list of ['a tactic] and returns an ['a list tactic].
- They both work by applying each of the tactic to the corresponding
- goal (starting with the first goal). In the case of [tclDISPATCHL],
- the tactic returns a list of the same size as the argument list (of
- tactics), each element being the result of the tactic executed in
- the corresponding goal.
+(** {7 Dispatching on goals} *)
+
+(** Dispatch tacticals are used to apply a different tactic to each
+ goal under focus. They come in two flavours: [tclDISPATCH] takes a
+ list of [unit tactic]-s and build a [unit tactic]. [tclDISPATCHL]
+ takes a list of ['a tactic] and returns an ['a list tactic].
- When the length of the tactic list is not the number of goal,
- raises [SizeMismatch (g,t)] where [g] is the number of available
- goals, and [t] the number of tactics passed. *)
+ They both work by applying each of the tactic in a focus
+ restricted to the corresponding goal (starting with the first
+ goal). In the case of [tclDISPATCHL], the tactic returns a list of
+ the same size as the argument list (of tactics), each element
+ being the result of the tactic executed in the corresponding goal.
+
+ When the length of the tactic list is not the number of goal,
+ raises [SizeMismatch (g,t)] where [g] is the number of available
+ goals, and [t] the number of tactics passed. *)
exception SizeMismatch of int*int
val tclDISPATCH : unit tactic list -> unit tactic
val tclDISPATCHL : 'a tactic list -> 'a list tactic
-(* [tclEXTEND b r e] is a variant to [tclDISPATCH], where the [r] tactic
- is "repeated" enough time such that every goal has a tactic assigned to it
- ([b] is the list of tactics applied to the first goals, [e] to the last goals, and [r]
- is applied to every goal in between. *)
+(** [tclEXTEND b r e] is a variant of {!tclDISPATCH}, where the [r]
+ tactic is "repeated" enough time such that every goal has a tactic
+ assigned to it ([b] is the list of tactics applied to the first
+ goals, [e] to the last goals, and [r] is applied to every goal in
+ between). *)
val tclEXTEND : unit tactic list -> unit tactic -> unit tactic list -> unit tactic
-(* [tclINDEPENDENT tac] runs [tac] on each goal successively, from the
- first one to the last one. Backtracking in one goal is independent of
- backtracking in another. *)
+(** [tclINDEPENDENT tac] runs [tac] on each goal successively, from
+ the first one to the last one. Backtracking in one goal is
+ independent of backtracking in another. It is equivalent to
+ [tclEXTEND [] tac []]. *)
val tclINDEPENDENT : unit tactic -> unit tactic
-(* [tclPROGRESS t] behaves has [t] as long as [t] progresses. *)
-val tclPROGRESS : 'a tactic -> 'a tactic
-
-(* [tclEVARMAP] doesn't affect the proof, it returns the current evar_map. *)
-val tclEVARMAP : Evd.evar_map tactic
-
-(* [tclENV] doesn't affect the proof, it returns the current
- environment. It is not the environment of a particular goal,
- rather the "global" environment of the proof. The goal-wise
- environment is returned by {!Proofview.Goal.env}. *)
-val tclENV : Environ.env tactic
-
-(* [tclEFFECTS eff] add the effects [eff] to the current state. *)
-val tclEFFECTS : Declareops.side_effects -> unit tactic
-(* Checks for interrupts *)
-val tclCHECKINTERRUPT : unit tactic
+(** {7 Goal manipulation} *)
-(* Shelves all the goals under focus. The goals are placed on the
- shelf for later use (or being solved by side-effects). *)
+(** Shelves all the goals under focus. The goals are placed on the
+ shelf for later use (or being solved by side-effects). *)
val shelve : unit tactic
-(* Shelves the unifiable goals under focus, i.e. the goals which
- appear in other goals under focus (the unfocused goals are not
- considered). *)
+(** Shelves the unifiable goals under focus, i.e. the goals which
+ appear in other goals under focus (the unfocused goals are not
+ considered). *)
val shelve_unifiable : unit tactic
-(* This fails with error UnresolvedBindings if some goals are
- dependent in the current list of goals under focus *)
-val check_no_dependencies : unit tactic
+(** [guard_no_unifiable] fails with error [UnresolvedBindings] if some
+ goals are unifiable (see {!shelve_unifiable}) in the current focus. *)
+val guard_no_unifiable : unit tactic
-(* [unshelve l p] adds all the goals in [l] at the end of the focused
- goals of p *)
+(** [unshelve l p] adds all the goals in [l] at the end of the focused
+ goals of p *)
val unshelve : Goal.goal list -> proofview -> proofview
-
-(* Gives up on the goal under focus. Reports an unsafe status. Proofs
- with given up goals cannot be closed. *)
-val give_up : unit tactic
-
(** If [n] is positive, [cycle n] puts the [n] first goal last. If [n]
is negative, then it puts the [n] last goals first.*)
val cycle : int -> unit tactic
@@ -280,12 +302,49 @@ val cycle : int -> unit tactic
[1] as well, rather than raising an error. *)
val swap : int -> int -> unit tactic
-(** [revgoals] reverse the list of focused goals. *)
+(** [revgoals] reverses the list of focused goals. *)
val revgoals : unit tactic
(** [numgoals] returns the number of goals under focus. *)
val numgoals : int tactic
+
+(** {7 Access primitives} *)
+
+(** [tclEVARMAP] doesn't affect the proof, it returns the current
+ [evar_map]. *)
+val tclEVARMAP : Evd.evar_map tactic
+
+(** [tclENV] doesn't affect the proof, it returns the current
+ environment. It is not the environment of a particular goal,
+ rather the "global" environment of the proof. The goal-wise
+ environment is obtained via {!Proofview.Goal.env}. *)
+val tclENV : Environ.env tactic
+
+
+(** {7 Put-like primitives} *)
+
+(** [tclEFFECTS eff] add the effects [eff] to the current state. *)
+val tclEFFECTS : Declareops.side_effects -> unit tactic
+
+(** [mark_as_unsafe] declares the current tactic is unsafe. *)
+val mark_as_unsafe : unit tactic
+
+(** Gives up on the goal under focus. Reports an unsafe status. Proofs
+ with given up goals cannot be closed. *)
+val give_up : unit tactic
+
+
+(** {7 Control primitives} *)
+
+(** [tclPROGRESS t] checks the state of the proof after [t]. It it is
+ identical to the state before, then [tclePROGRESS t] fails, otherwise
+ it succeeds like [t]. *)
+val tclPROGRESS : 'a tactic -> 'a tactic
+
+(** Checks for interrupts *)
+val tclCHECKINTERRUPT : unit tactic
+
exception Timeout
(** [tclTIMEOUT n t] can have only one success.
In case of timeout if fails with [tclZERO Timeout]. *)
@@ -295,48 +354,51 @@ val tclTIMEOUT : int -> 'a tactic -> 'a tactic
identifying annotation if present *)
val tclTIME : string option -> 'a tactic -> 'a tactic
-(** [mark_as_unsafe] signals that the current tactic is unsafe. *)
-val mark_as_unsafe : unit tactic
+(** {7 Unsafe primitives} *)
+(** The primitives in the [Unsafe] module should be avoided as much as
+ possible, since they can make the proof state inconsistent. They are
+ nevertheless helpful, in particular when interfacing the pretyping and
+ the proof engine. *)
module Unsafe : sig
- (* Assumes the new evar_map does not change existing goals *)
+ (** [tclEVARS sigma] replaces the current [evar_map] by [sigma]. If
+ [sigma] has new unresolved [evar]-s they will not appear as
+ goal. If goals have been solved in [sigma] they will still
+ appear as unsolved goals. *)
val tclEVARS : Evd.evar_map -> unit tactic
-
- (* [tclNEWGOALS gls] adds the goals [gls] to the ones currently
- being proved, appending them to the list of focussed goals. If a
- goal is already solved, it is not added. Prefer the other
- primitives when possible. *)
- val tclNEWGOALS : Goal.goal list -> unit tactic
-
- (* Assumes the new evar_map might be solving some existing goals *)
+ (** Like {!tclEVARS} but also checks whether goals have been solved. *)
val tclEVARSADVANCE : Evd.evar_map -> unit tactic
- (* Set the evar universe context *)
+ (** [tclNEWGOALS gls] adds the goals [gls] to the ones currently
+ being proved, appending them to the list of focused goals. If a
+ goal is already solved, it is not added. *)
+ val tclNEWGOALS : Goal.goal list -> unit tactic
+
+ (** Sets the evar universe context. *)
val tclEVARUNIVCONTEXT : Evd.evar_universe_context -> unit tactic
- (* Clears the future goals store in the proof. *)
+ (** Clears the future goals store in the proof view. *)
val reset_future_goals : proofview -> proofview
end
-module Monad : Monad.S with type +'a t = 'a tactic
+(** {7 Notations} *)
-(* Notations for building tactics. *)
module Notations : sig
- (* tclBIND *)
+ (** {!tclBIND} *)
val (>>=) : 'a tactic -> ('a -> 'b tactic) -> 'b tactic
- (* tclTHEN *)
+ (** {!tclTHEN} *)
val (<*>) : unit tactic -> 'a tactic -> 'a tactic
- (* tclOR *)
+ (** {!tclOR}: [t1+t2] = [tclOR t1 (fun _ -> t2)]. *)
val (<+>) : 'a tactic -> 'a tactic -> 'a tactic
+
end
-(* The module goal provides an interface for goal-dependent tactics. *)
-(* spiwack: there are still parts of the code which depend on proofs/goal.ml.
- Eventually I'll try to remove it in favour of [Proofview.Goal] *)
+(** {6 Goal-dependent tactics} *)
+
module Goal : sig
(** The type of goals. The parameter type is a phantom argument indicating
@@ -352,65 +414,78 @@ module Goal : sig
(** Normalises the argument goal. *)
val normalize : 'a t -> [ `NF ] t tactic
- (* [concl], [hyps], [env] and [sigma] given a goal [gl] return
- respectively the conclusion of [gl], the hypotheses of [gl], the
- environment of [gl] (i.e. the global environment and the hypotheses)
- and the current evar map. *)
+ (** [concl], [hyps], [env] and [sigma] given a goal [gl] return
+ respectively the conclusion of [gl], the hypotheses of [gl], the
+ environment of [gl] (i.e. the global environment and the
+ hypotheses) and the current evar map. *)
val concl : [ `NF ] t -> Term.constr
val hyps : [ `NF ] t -> Context.named_context
val env : 'a t -> Environ.env
val sigma : 'a t -> Evd.evar_map
+ (** Returns the goal's conclusion even if the goal is not
+ normalised. *)
val raw_concl : 'a t -> Term.constr
- (* [nf_enter t] execute the goal-dependent tactic [t] in each goal
- independently. In particular [t] need not backtrack the same way in
- each goal. *)
+ (** [nf_enter t] applies the goal-dependent tactic [t] in each goal
+ independently, in the manner of {!tclINDEPENDENT} except that
+ the current goal is also given as an argument to [t]. The goal
+ is normalised with respect to evars. *)
val nf_enter : ([ `NF ] t -> unit tactic) -> unit tactic
- (** Same as nf_enter, but does not normalize the goal beforehand. *)
+ (** Like {!nf_enter}, but does not normalize the goal beforehand. *)
val enter : ([ `LZ ] t -> unit tactic) -> unit tactic
(** Recover the list of current goals under focus, without evar-normalization *)
val goals : [ `LZ ] t tactic list tactic
- (* compatibility: avoid if possible *)
+ (** Compatibility: avoid if possible *)
val goal : [ `NF ] t -> Evar.t
end
-(** A light interface for building tactics out of partial term. Be careful,
- the [refine] function does not do any typechecking, so you may construct
- ill-typed terms without noticing. *)
+
+(** {6 The refine tactic} *)
+
module Refine : sig
+ (** {7 Refinement primitives} *)
+
+ val refine : ?unsafe:bool -> (Evd.evar_map -> Evd.evar_map * Constr.t) -> unit tactic
+ (** In [refine ?unsafe t], [t] is a term with holes under some
+ [evar_map] context. The term [t] is used as a partial solution
+ for the current goal (refine is a goal-dependent tactic), the
+ new holes created by [t] become the new subgoals. Exception
+ raised during the interpretation of [t] are caught and result in
+ tactic failures. If [unsafe] is [true] (default) [t] is
+ type-checked beforehand. *)
+
+ (** {7 Helper functions} *)
+
val with_type : Environ.env -> Evd.evar_map ->
Term.constr -> Term.types -> Evd.evar_map * Term.constr
(** [with_type env sigma c t] ensures that [c] is of type [t]
inserting a coercion if needed. *)
- val refine : ?unsafe:bool -> (Evd.evar_map -> Evd.evar_map * Constr.t) -> unit tactic
- (** Given a term with holes that have been generated through
- {!new_evar}, this function fills the current hole with the given
- constr and creates goals for all the holes in their generation
- order. The [evar_map] in the argument function is assumed to
- only increase. Exceptions raised by the function are caught. *)
-
- val refine_casted : ?unsafe:bool -> (Evd.evar_map -> Evd.evar_map * Constr.t) -> unit tactic
-(** Like {!refine} except the refined term is coerced to the conclusion of the
- current goal. *)
+ val refine_casted : ?unsafe:bool -> (Evd.evar_map -> Evd.evar_map*Constr.t) -> unit tactic
+ (** Like {!refine} except the refined term is coerced to the conclusion of the
+ current goal. *)
end
-(* The [NonLogical] module allows the execution of side effects in tactics
- (non-logical side-effects are not discarded at failures). *)
+
+(** {6 Non-logical state} *)
+
+(** The [NonLogical] module allows the execution of effects (including
+ I/O) in tactics (non-logical side-effects are not discarded at
+ failures). *)
module NonLogical : module type of Logic_monad.NonLogical
-(* [tclLIFT c] includes the non-logical command [c] in a tactic. *)
+(** [tclLIFT c] is a tactic which behaves exactly as [c]. *)
val tclLIFT : 'a NonLogical.t -> 'a tactic
-
+(**/**)
(*** Compatibility layer with <= 8.2 tactics ***)
module V82 : sig