aboutsummaryrefslogtreecommitdiff
path: root/proofs
diff options
context:
space:
mode:
Diffstat (limited to 'proofs')
-rw-r--r--proofs/clenv.ml720
-rw-r--r--proofs/clenv.mli173
-rw-r--r--proofs/clenvtac.ml135
-rw-r--r--proofs/clenvtac.mli23
-rw-r--r--proofs/doc.tex14
-rw-r--r--proofs/dune6
-rw-r--r--proofs/evar_refiner.ml65
-rw-r--r--proofs/evar_refiner.mli20
-rw-r--r--proofs/goal.ml140
-rw-r--r--proofs/goal.mli71
-rw-r--r--proofs/goal_select.ml68
-rw-r--r--proofs/goal_select.mli26
-rw-r--r--proofs/logic.ml598
-rw-r--r--proofs/logic.mli73
-rw-r--r--proofs/miscprint.ml69
-rw-r--r--proofs/miscprint.mli36
-rw-r--r--proofs/pfedit.ml220
-rw-r--r--proofs/pfedit.mli95
-rw-r--r--proofs/proof.ml554
-rw-r--r--proofs/proof.mli260
-rw-r--r--proofs/proof_bullet.ml199
-rw-r--r--proofs/proof_bullet.mli46
-rw-r--r--proofs/proof_global.ml498
-rw-r--r--proofs/proof_global.mli185
-rw-r--r--proofs/proofs.mllib14
-rw-r--r--proofs/refine.ml169
-rw-r--r--proofs/refine.mli51
-rw-r--r--proofs/refiner.ml332
-rw-r--r--proofs/refiner.mli133
-rw-r--r--proofs/tacmach.ml209
-rw-r--r--proofs/tacmach.mli123
-rw-r--r--proofs/tactypes.ml54
32 files changed, 5379 insertions, 0 deletions
diff --git a/proofs/clenv.ml b/proofs/clenv.ml
new file mode 100644
index 0000000000..1f1bdf4da7
--- /dev/null
+++ b/proofs/clenv.ml
@@ -0,0 +1,720 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Nameops
+open Termops
+open Constr
+open Namegen
+open Environ
+open Evd
+open EConstr
+open Vars
+open Reduction
+open Reductionops
+open Tacred
+open Pretype_errors
+open Evarutil
+open Unification
+open Tactypes
+
+(******************************************************************)
+(* Clausal environments *)
+
+type clausenv = {
+ env : env;
+ evd : evar_map;
+ templval : constr freelisted;
+ templtyp : constr freelisted }
+
+let cl_env ce = ce.env
+let cl_sigma ce = ce.evd
+
+let clenv_nf_meta clenv c = nf_meta clenv.evd c
+let clenv_term clenv c = meta_instance clenv.evd c
+let clenv_meta_type clenv mv = Typing.meta_type clenv.evd mv
+let clenv_value clenv = meta_instance clenv.evd clenv.templval
+let clenv_type clenv = meta_instance clenv.evd clenv.templtyp
+
+let refresh_undefined_univs clenv =
+ match EConstr.kind clenv.evd clenv.templval.rebus with
+ | Var _ -> clenv, Univ.empty_level_subst
+ | App (f, args) when isVar clenv.evd f -> clenv, Univ.empty_level_subst
+ | _ ->
+ let evd', subst = Evd.refresh_undefined_universes clenv.evd in
+ let map_freelisted f = { f with rebus = subst_univs_level_constr subst f.rebus } in
+ { clenv with evd = evd'; templval = map_freelisted clenv.templval;
+ templtyp = map_freelisted clenv.templtyp }, subst
+
+let clenv_hnf_constr ce t = hnf_constr (cl_env ce) (cl_sigma ce) t
+
+let clenv_get_type_of ce c = Retyping.get_type_of (cl_env ce) (cl_sigma ce) c
+
+exception NotExtensibleClause
+
+let clenv_push_prod cl =
+ let typ = whd_all (cl_env cl) (cl_sigma cl) (clenv_type cl) in
+ let rec clrec typ = match EConstr.kind cl.evd typ with
+ | Cast (t,_,_) -> clrec t
+ | Prod (na,t,u) ->
+ let mv = new_meta () in
+ let dep = not (noccurn (cl_sigma cl) 1 u) in
+ let na' = if dep then na else Anonymous in
+ let e' = meta_declare mv t ~name:na' cl.evd in
+ let concl = if dep then subst1 (mkMeta mv) u else u in
+ let def = applist (cl.templval.rebus,[mkMeta mv]) in
+ { templval = mk_freelisted def;
+ templtyp = mk_freelisted concl;
+ evd = e';
+ env = cl.env }
+ | _ -> raise NotExtensibleClause
+ in clrec typ
+
+(* Instantiate the first [bound] products of [t] with metas (all products if
+ [bound] is [None]; unfold local defs *)
+
+(** [clenv_environments sigma n t] returns [sigma',lmeta,ccl] where
+ [lmetas] is a list of metas to be applied to a proof of [t] so that
+ it produces the unification pattern [ccl]; [sigma'] is [sigma]
+ extended with [lmetas]; if [n] is defined, it limits the size of
+ the list even if [ccl] is still a product; otherwise, it stops when
+ [ccl] is not a product; example: if [t] is [forall x y, x=y -> y=x]
+ and [n] is [None], then [lmetas] is [Meta n1;Meta n2;Meta n3] and
+ [ccl] is [Meta n1=Meta n2]; if [n] is [Some 1], [lmetas] is [Meta n1]
+ and [ccl] is [forall y, Meta n1=y -> y=Meta n1] *)
+
+let clenv_environments evd bound t =
+ let open EConstr in
+ let open Vars in
+ let rec clrec (e,metas) n t =
+ match n, EConstr.kind evd t with
+ | (Some 0, _) -> (e, List.rev metas, t)
+ | (n, Cast (t,_,_)) -> clrec (e,metas) n t
+ | (n, Prod (na,t1,t2)) ->
+ let mv = new_meta () in
+ let dep = not (noccurn evd 1 t2) in
+ let na' = if dep then na else Anonymous in
+ let e' = meta_declare mv t1 ~name:na' e in
+ clrec (e', (mkMeta mv)::metas) (Option.map ((+) (-1)) n)
+ (if dep then (subst1 (mkMeta mv) t2) else t2)
+ | (n, LetIn (na,b,_,t)) -> clrec (e,metas) n (subst1 b t)
+ | (n, _) -> (e, List.rev metas, t)
+ in
+ clrec (evd,[]) bound t
+
+let mk_clenv_from_env env sigma n (c,cty) =
+ let evd = clear_metas sigma in
+ let (evd,args,concl) = clenv_environments evd n cty in
+ { templval = mk_freelisted (applist (c,args));
+ templtyp = mk_freelisted concl;
+ evd = evd;
+ env = env }
+
+let mk_clenv_from_n gls n (c,cty) =
+ let env = Proofview.Goal.env gls in
+ let sigma = Tacmach.New.project gls in
+ mk_clenv_from_env env sigma n (c, cty)
+
+let mk_clenv_from gls = mk_clenv_from_n gls None
+
+let mk_clenv_type_of gls t = mk_clenv_from gls (t,Tacmach.New.pf_unsafe_type_of gls t)
+
+(******************************************************************)
+
+(* [mentions clenv mv0 mv1] is true if mv1 is defined and mentions
+ * mv0, or if one of the free vars on mv1's freelist mentions
+ * mv0 *)
+
+let mentions clenv mv0 =
+ let rec menrec mv1 =
+ Int.equal mv0 mv1 ||
+ let mlist =
+ try match meta_opt_fvalue clenv.evd mv1 with
+ | Some (b,_) -> b.freemetas
+ | None -> Metaset.empty
+ with Not_found -> Metaset.empty in
+ Metaset.exists menrec mlist
+ in menrec
+
+let error_incompatible_inst clenv mv =
+ let na = meta_name clenv.evd mv in
+ match na with
+ Name id ->
+ user_err ~hdr:"clenv_assign"
+ (str "An incompatible instantiation has already been found for " ++
+ Id.print id)
+ | _ ->
+ anomaly ~label:"clenv_assign" (Pp.str "non dependent metavar already assigned.")
+
+(* TODO: replace by clenv_unify (mkMeta mv) rhs ? *)
+let clenv_assign mv rhs clenv =
+ let rhs_fls = mk_freelisted rhs in
+ if Metaset.exists (mentions clenv mv) rhs_fls.freemetas then
+ user_err Pp.(str "clenv_assign: circularity in unification");
+ try
+ if meta_defined clenv.evd mv then
+ if not (EConstr.eq_constr clenv.evd (fst (meta_fvalue clenv.evd mv)).rebus rhs) then
+ error_incompatible_inst clenv mv
+ else
+ clenv
+ else
+ let st = (Conv,TypeNotProcessed) in
+ {clenv with evd = meta_assign mv (rhs_fls.rebus,st) clenv.evd}
+ with Not_found ->
+ user_err Pp.(str "clenv_assign: undefined meta")
+
+
+
+(* [clenv_dependent hyps_only clenv]
+ * returns a list of the metavars which appear in the template of clenv,
+ * and which are dependent, This is computed by taking the metavars of the
+ * template in right-to-left order, and collecting the metavars which appear
+ * in their types, and adding in all the metavars appearing in the
+ * type of clenv.
+ * If [hyps_only] then metavariables occurring in the concl are _excluded_
+ * If [iter] is also set then all metavariables *recursively* occurring
+ * in the concl are _excluded_
+
+ Details of the strategies used for computing the set of unresolved
+ dependent metavariables
+
+ We typically have a clause of the form
+
+ lem(?T:Type,?T,?U:Type,?V:Type,?x:?T,?y:?U,?z:?V,?H:hyp(?x,?z)) :concl(?y,?z)
+
+ Then, we compute:
+ A = the set of all unresolved metas
+ C = the set of metas occurring in concl (here ?y, ?z)
+ C* = the recursive closure of C wrt types (here ?y, ?z, ?U, ?V)
+ D = the set of metas occurring in a type of meta (here ?x, ?T, ?z, ?U, ?V)
+ NL = the set of duplicated metas even if non dependent (here ?T)
+ (we make the assumption that duplicated metas have internal dependencies)
+
+ Then, for the "apply"-style tactic (hyps_only), missing metas are
+ A inter ((D minus C) union NL)
+
+ for the optimized "apply"-style tactic (taking in care, f_equal style
+ lemma, from 2/8/10, Coq > 8.3), missing metas are
+ A inter (( D minus C* ) union NL)
+
+ for the "elim"-style tactic, missing metas are
+ A inter (D union C union NL)
+
+ In any case, we respect the order given in A.
+*)
+
+let clenv_metas_in_type_of_meta evd mv =
+ (mk_freelisted (meta_instance evd (meta_ftype evd mv))).freemetas
+
+let dependent_in_type_of_metas clenv mvs =
+ List.fold_right
+ (fun mv -> Metaset.union (clenv_metas_in_type_of_meta clenv.evd mv))
+ mvs Metaset.empty
+
+let dependent_closure clenv mvs =
+ let rec aux mvs acc =
+ Metaset.fold
+ (fun mv deps ->
+ let metas_of_meta_type = clenv_metas_in_type_of_meta clenv.evd mv in
+ aux metas_of_meta_type (Metaset.union deps metas_of_meta_type))
+ mvs acc in
+ aux mvs mvs
+
+let clenv_dependent_gen hyps_only ?(iter=true) clenv =
+ let all_undefined = undefined_metas clenv.evd in
+ let deps_in_concl = (mk_freelisted (clenv_type clenv)).freemetas in
+ let deps_in_hyps = dependent_in_type_of_metas clenv all_undefined in
+ let deps_in_concl =
+ if hyps_only && iter then dependent_closure clenv deps_in_concl
+ else deps_in_concl in
+ List.filter
+ (fun mv ->
+ if hyps_only then
+ Metaset.mem mv deps_in_hyps && not (Metaset.mem mv deps_in_concl)
+ else
+ Metaset.mem mv deps_in_hyps || Metaset.mem mv deps_in_concl)
+ all_undefined
+
+let clenv_missing ce = clenv_dependent_gen true ce
+let clenv_dependent ce = clenv_dependent_gen false ce
+
+(******************************************************************)
+
+let clenv_unify ?(flags=default_unify_flags ()) cv_pb t1 t2 clenv =
+ { clenv with
+ evd = w_unify ~flags clenv.env clenv.evd cv_pb t1 t2 }
+
+let clenv_unify_meta_types ?(flags=default_unify_flags ()) clenv =
+ { clenv with evd = w_unify_meta_types ~flags:flags clenv.env clenv.evd }
+
+let clenv_unique_resolver_gen ?(flags=default_unify_flags ()) clenv concl =
+ if isMeta clenv.evd (fst (decompose_app_vect clenv.evd (whd_nored clenv.evd clenv.templtyp.rebus))) then
+ clenv_unify CUMUL ~flags (clenv_type clenv) concl
+ (clenv_unify_meta_types ~flags clenv)
+ else
+ clenv_unify CUMUL ~flags
+ (meta_reducible_instance clenv.evd clenv.templtyp) concl clenv
+
+let old_clenv_unique_resolver ?flags clenv gl =
+ let concl = Goal.V82.concl clenv.evd (sig_it gl) in
+ clenv_unique_resolver_gen ?flags clenv concl
+
+let clenv_unique_resolver ?flags clenv gl =
+ let concl = Proofview.Goal.concl gl in
+ clenv_unique_resolver_gen ?flags clenv concl
+
+let adjust_meta_source evd mv = function
+ | loc,Evar_kinds.VarInstance id ->
+ let rec match_name c l =
+ match EConstr.kind evd c, l with
+ | Lambda (Name id,_,c), a::l when EConstr.eq_constr evd a (mkMeta mv) -> Some id
+ | Lambda (_,_,c), a::l -> match_name c l
+ | _ -> None in
+ (* This is very ad hoc code so that an evar inherits the name of the binder
+ in situations like "ex_intro (fun x => P) ?ev p" *)
+ let f = function (mv',(Cltyp (_,t) | Clval (_,_,t))) ->
+ if Metaset.mem mv t.freemetas then
+ let f,l = decompose_app evd t.rebus in
+ match EConstr.kind evd f with
+ | Meta mv'' ->
+ (match meta_opt_fvalue evd mv'' with
+ | Some (c,_) -> match_name c.rebus l
+ | None -> None)
+ | _ -> None
+ else None in
+ let id = try List.find_map f (Evd.meta_list evd) with Not_found -> id in
+ loc,Evar_kinds.VarInstance id
+ | src -> src
+
+(* [clenv_pose_metas_as_evars clenv dep_mvs]
+ * For each dependent evar in the clause-env which does not have a value,
+ * pose a value for it by constructing a fresh evar. We do this in
+ * left-to-right order, so that every evar's type is always closed w.r.t.
+ * metas.
+
+ * Node added 14/4/08 [HH]: before this date, evars were collected in
+ clenv_dependent by collect_metas in the fold_constr order which is
+ (almost) the left-to-right order of dependencies in term. However,
+ due to K-redexes, collect_metas was sometimes missing some metas.
+ The call to collect_metas has been replaced by a call to
+ undefined_metas, but then the order was the one of definition of
+ the metas (numbers in increasing order) which is _not_ the
+ dependency order when a clenv_fchain occurs (because clenv_fchain
+ plugs a term with a list of consecutive metas in place of a - a priori -
+ arbitrary metavariable belonging to another sequence of consecutive metas:
+ e.g., clenv_fchain may plug (H ?1 ?2) at the position ?6 of
+ (nat_ind ?3 ?4 ?5 ?6), leading to a dependency order 3<4<5<1<2).
+ To ensure the dependency order, we check that the type of each meta
+ to pose is already meta-free, otherwise we postpone the transformation,
+ hoping that no cycle may happen.
+
+ Another approach could have been to use decimal numbers for metas so that
+ in the example above, (H ?1 ?2) would have been renumbered (H ?6.1 ?6.2)
+ then making the numeric order match the dependency order.
+*)
+
+let clenv_pose_metas_as_evars clenv dep_mvs =
+ let rec fold clenv evs = function
+ | [] -> clenv, evs
+ | mv::mvs ->
+ let ty = clenv_meta_type clenv mv in
+ (* Postpone the evar-ization if dependent on another meta *)
+ (* This assumes no cycle in the dependencies - is it correct ? *)
+ if occur_meta clenv.evd ty then fold clenv evs (mvs@[mv])
+ else
+ let src = evar_source_of_meta mv clenv.evd in
+ let src = adjust_meta_source clenv.evd mv src in
+ let evd = clenv.evd in
+ let (evd, evar) = new_evar (cl_env clenv) evd ~src ty in
+ let clenv = clenv_assign mv evar {clenv with evd=evd} in
+ fold clenv (fst (destEvar evd evar) :: evs) mvs in
+ fold clenv [] dep_mvs
+
+(******************************************************************)
+
+(* [clenv_fchain mv clenv clenv']
+ *
+ * Resolves the value of "mv" (which must be undefined) in clenv to be
+ * the template of clenv' be the value "c", applied to "n" fresh
+ * metavars, whose types are chosen by destructing "clf", which should
+ * be a clausale forme generated from the type of "c". The process of
+ * resolution can cause unification of already-existing metavars, and
+ * of the fresh ones which get created. This operation is a composite
+ * of operations which pose new metavars, perform unification on
+ * terms, and make bindings.
+
+ Otherwise said, from
+
+ [clenv] = [env;sigma;metas |- c:T]
+ [clenv'] = [env';sigma';metas' |- d:U]
+ [mv] = [mi] of type [Ti] in [metas]
+
+ then, if the unification of [Ti] and [U] produces map [rho], the
+ chaining is [env';sigma';rho'(metas),rho(metas') |- c:rho'(T)] for
+ [rho'] being [rho;mi:=d].
+
+ In particular, it assumes that [env'] and [sigma'] extend [env] and [sigma].
+*)
+
+let fchain_flags () =
+ { (default_unify_flags ()) with
+ allow_K_in_toplevel_higher_order_unification = true }
+
+let clenv_fchain ?with_univs ?(flags=fchain_flags ()) mv clenv nextclenv =
+ (* Add the metavars of [nextclenv] to [clenv], with their name-environment *)
+ let clenv' =
+ { templval = clenv.templval;
+ templtyp = clenv.templtyp;
+ evd = meta_merge ?with_univs nextclenv.evd clenv.evd;
+ env = nextclenv.env } in
+ (* unify the type of the template of [nextclenv] with the type of [mv] *)
+ let clenv'' =
+ clenv_unify ~flags CUMUL
+ (clenv_term clenv' nextclenv.templtyp)
+ (clenv_meta_type clenv' mv)
+ clenv' in
+ (* assign the metavar *)
+ let clenv''' =
+ clenv_assign mv (clenv_term clenv' nextclenv.templval) clenv''
+ in
+ clenv'''
+
+(***************************************************************)
+(* Bindings *)
+
+type arg_bindings = constr explicit_bindings
+
+(* [clenv_independent clenv]
+ * returns a list of metavariables which appear in the term cval,
+ * and which are not dependent. That is, they do not appear in
+ * the types of other metavars which are in cval, nor in the type
+ * of cval, ctyp. *)
+
+let clenv_independent clenv =
+ let mvs = collect_metas clenv.evd (clenv_value clenv) in
+ let ctyp_mvs = (mk_freelisted (clenv_type clenv)).freemetas in
+ let deps = Metaset.union (dependent_in_type_of_metas clenv mvs) ctyp_mvs in
+ List.filter (fun mv -> not (Metaset.mem mv deps)) mvs
+
+let qhyp_eq h1 h2 = match h1, h2 with
+| NamedHyp n1, NamedHyp n2 -> Id.equal n1 n2
+| AnonHyp i1, AnonHyp i2 -> Int.equal i1 i2
+| _ -> false
+
+let check_bindings bl =
+ match List.duplicates qhyp_eq (List.map (fun {CAst.v=x} -> fst x) bl) with
+ | NamedHyp s :: _ ->
+ user_err
+ (str "The variable " ++ Id.print s ++
+ str " occurs more than once in binding list.");
+ | AnonHyp n :: _ ->
+ user_err
+ (str "The position " ++ int n ++
+ str " occurs more than once in binding list.")
+ | [] -> ()
+
+let explain_no_such_bound_variable evd id =
+ let fold l (n, clb) =
+ let na = match clb with
+ | Cltyp (na, _) -> na
+ | Clval (na, _, _) -> na
+ in
+ if na != Anonymous then Name.get_id na :: l else l
+ in
+ let mvl = List.fold_left fold [] (Evd.meta_list evd) in
+ user_err ~hdr:"Evd.meta_with_name"
+ (str"No such bound variable " ++ Id.print id ++
+ (if mvl == [] then str " (no bound variables at all in the expression)."
+ else
+ (str" (possible name" ++
+ str (if List.length mvl == 1 then " is: " else "s are: ") ++
+ pr_enum Id.print mvl ++ str").")))
+
+let meta_with_name evd id =
+ let na = Name id in
+ let fold (l1, l2 as l) (n, clb) =
+ let (na',def) = match clb with
+ | Cltyp (na, _) -> (na, false)
+ | Clval (na, _, _) -> (na, true)
+ in
+ if Name.equal na na' then if def then (n::l1,l2) else (n::l1,n::l2)
+ else l
+ in
+ let (mvl, mvnodef) = List.fold_left fold ([], []) (Evd.meta_list evd) in
+ match mvnodef, mvl with
+ | _,[] ->
+ explain_no_such_bound_variable evd id
+ | ([n],_|_,[n]) ->
+ n
+ | _ ->
+ user_err ~hdr:"Evd.meta_with_name"
+ (str "Binder name \"" ++ Id.print id ++
+ strbrk "\" occurs more than once in clause.")
+
+let meta_of_binder clause loc mvs = function
+ | NamedHyp s -> meta_with_name clause.evd s
+ | AnonHyp n ->
+ try List.nth mvs (n-1)
+ with (Failure _|Invalid_argument _) ->
+ user_err (str "No such binder.")
+
+let error_already_defined b =
+ match b with
+ | NamedHyp id ->
+ user_err
+ (str "Binder name \"" ++ Id.print id ++
+ str"\" already defined with incompatible value.")
+ | AnonHyp n ->
+ anomaly
+ (str "Position " ++ int n ++ str" already defined.")
+
+let clenv_unify_binding_type clenv c t u =
+ if isMeta clenv.evd (fst (decompose_app_vect clenv.evd (whd_nored clenv.evd u))) then
+ (* Not enough information to know if some subtyping is needed *)
+ CoerceToType, clenv, c
+ else
+ (* Enough information so as to try a coercion now *)
+ try
+ let evd,c = w_coerce_to_type (cl_env clenv) clenv.evd c t u in
+ TypeProcessed, { clenv with evd = evd }, c
+ with
+ | PretypeError (_,_,ActualTypeNotCoercible (_,_,
+ (NotClean _ | ConversionFailed _))) as e ->
+ raise e
+ | e when precatchable_exception e ->
+ TypeNotProcessed, clenv, c
+
+let clenv_assign_binding clenv k c =
+ let k_typ = clenv_hnf_constr clenv (clenv_meta_type clenv k) in
+ let c_typ = nf_betaiota clenv.env clenv.evd (clenv_get_type_of clenv c) in
+ let status,clenv',c = clenv_unify_binding_type clenv c c_typ k_typ in
+ { clenv' with evd = meta_assign k (c,(Conv,status)) clenv'.evd }
+
+let clenv_match_args bl clenv =
+ if List.is_empty bl then
+ clenv
+ else
+ let mvs = clenv_independent clenv in
+ check_bindings bl;
+ List.fold_left
+ (fun clenv {CAst.loc;v=(b,c)} ->
+ let k = meta_of_binder clenv loc mvs b in
+ if meta_defined clenv.evd k then
+ if EConstr.eq_constr clenv.evd (fst (meta_fvalue clenv.evd k)).rebus c then clenv
+ else error_already_defined b
+ else
+ clenv_assign_binding clenv k c)
+ clenv bl
+
+exception NoSuchBinding
+
+let clenv_constrain_last_binding c clenv =
+ let all_mvs = collect_metas clenv.evd clenv.templval.rebus in
+ let k = try List.last all_mvs with Failure _ -> raise NoSuchBinding in
+ clenv_assign_binding clenv k c
+
+let error_not_right_number_missing_arguments n =
+ user_err
+ (strbrk "Not the right number of missing arguments (expected " ++
+ int n ++ str ").")
+
+let clenv_constrain_dep_args hyps_only bl clenv =
+ if List.is_empty bl then
+ clenv
+ else
+ let occlist = clenv_dependent_gen hyps_only clenv in
+ if Int.equal (List.length occlist) (List.length bl) then
+ List.fold_left2 clenv_assign_binding clenv occlist bl
+ else
+ if hyps_only then
+ (* Tolerance for compatibility <= 8.3 *)
+ let occlist' = clenv_dependent_gen hyps_only ~iter:false clenv in
+ if Int.equal (List.length occlist') (List.length bl) then
+ List.fold_left2 clenv_assign_binding clenv occlist' bl
+ else
+ error_not_right_number_missing_arguments (List.length occlist)
+ else
+ error_not_right_number_missing_arguments (List.length occlist)
+
+(****************************************************************)
+(* Clausal environment for an application *)
+
+let make_clenv_binding_gen hyps_only n env sigma (c,t) = function
+ | ImplicitBindings largs ->
+ let clause = mk_clenv_from_env env sigma n (c,t) in
+ clenv_constrain_dep_args hyps_only largs clause
+ | ExplicitBindings lbind ->
+ let t = rename_bound_vars_as_displayed sigma Id.Set.empty [] t in
+ let clause = mk_clenv_from_env env sigma n
+ (c, t)
+ in clenv_match_args lbind clause
+ | NoBindings ->
+ mk_clenv_from_env env sigma n (c,t)
+
+let make_clenv_binding_env_apply env sigma n =
+ make_clenv_binding_gen true n env sigma
+
+let make_clenv_binding_env env sigma =
+ make_clenv_binding_gen false None env sigma
+
+let make_clenv_binding_apply env sigma n = make_clenv_binding_gen true n env sigma
+let make_clenv_binding env sigma = make_clenv_binding_gen false None env sigma
+
+(****************************************************************)
+(* Pretty-print *)
+
+let pr_clenv clenv =
+ h 0
+ (str"TEMPL: " ++ Termops.Internal.print_constr_env clenv.env clenv.evd clenv.templval.rebus ++
+ str" : " ++ Termops.Internal.print_constr_env clenv.env clenv.evd clenv.templtyp.rebus ++ fnl () ++
+ pr_evar_map (Some 2) clenv.env clenv.evd)
+
+(****************************************************************)
+(** Evar version of mk_clenv *)
+
+type hole = {
+ hole_evar : EConstr.constr;
+ hole_type : EConstr.types;
+ hole_deps : bool;
+ hole_name : Name.t;
+}
+
+type clause = {
+ cl_holes : hole list;
+ cl_concl : EConstr.types;
+}
+
+let make_evar_clause env sigma ?len t =
+ let open EConstr in
+ let open Vars in
+ let bound = match len with
+ | None -> -1
+ | Some n -> assert (0 <= n); n
+ in
+ (* FIXME: do the renaming online *)
+ let t = rename_bound_vars_as_displayed sigma Id.Set.empty [] t in
+ let rec clrec (sigma, holes) inst n t =
+ if n = 0 then (sigma, holes, t)
+ else match EConstr.kind sigma t with
+ | Cast (t, _, _) -> clrec (sigma, holes) inst n t
+ | Prod (na, t1, t2) ->
+ (* Share the evar instances as we are living in the same context *)
+ let inst, ctx, args, subst = match inst with
+ | None ->
+ (* Dummy type *)
+ let ctx, _, args, subst = push_rel_context_to_named_context env sigma mkProp in
+ Some (ctx, args, subst), ctx, args, subst
+ | Some (ctx, args, subst) -> inst, ctx, args, subst
+ in
+ let (sigma, ev) = new_evar_instance ~typeclass_candidate:false ctx sigma (csubst_subst subst t1) args in
+ let dep = not (noccurn sigma 1 t2) in
+ let hole = {
+ hole_evar = ev;
+ hole_type = t1;
+ hole_deps = dep;
+ (* We fix it later *)
+ hole_name = na;
+ } in
+ let t2 = if dep then subst1 ev t2 else t2 in
+ clrec (sigma, hole :: holes) inst (pred n) t2
+ | LetIn (na, b, _, t) -> clrec (sigma, holes) inst n (subst1 b t)
+ | _ -> (sigma, holes, t)
+ in
+ let (sigma, holes, t) = clrec (sigma, []) None bound t in
+ let holes = List.rev holes in
+ let clause = { cl_concl = t; cl_holes = holes } in
+ (sigma, clause)
+
+let explain_no_such_bound_variable holes id =
+ let fold h accu = match h.hole_name with
+ | Anonymous -> accu
+ | Name id -> id :: accu
+ in
+ let mvl = List.fold_right fold holes [] in
+ let expl = match mvl with
+ | [] -> str " (no bound variables at all in the expression)."
+ | [id] -> str "(possible name is: " ++ Id.print id ++ str ")."
+ | _ -> str "(possible names are: " ++ pr_enum Id.print mvl ++ str ")."
+ in
+ user_err (str "No such bound variable " ++ Id.print id ++ expl)
+
+let evar_with_name holes id =
+ let map h = match h.hole_name with
+ | Anonymous -> None
+ | Name id' -> if Id.equal id id' then Some h else None
+ in
+ let hole = List.map_filter map holes in
+ match hole with
+ | [] -> explain_no_such_bound_variable holes id
+ | [h] -> h.hole_evar
+ | _ ->
+ user_err
+ (str "Binder name \"" ++ Id.print id ++
+ str "\" occurs more than once in clause.")
+
+let evar_of_binder holes = function
+| NamedHyp s -> evar_with_name holes s
+| AnonHyp n ->
+ try
+ let nondeps = List.filter (fun hole -> not hole.hole_deps) holes in
+ let h = List.nth nondeps (pred n) in
+ h.hole_evar
+ with e when CErrors.noncritical e ->
+ user_err (str "No such binder.")
+
+let define_with_type sigma env ev c =
+ let open EConstr in
+ let t = Retyping.get_type_of env sigma ev in
+ let ty = Retyping.get_type_of env sigma c in
+ let j = Environ.make_judge c ty in
+ let (sigma, j) = Coercion.inh_conv_coerce_to true env sigma j t in
+ let (ev, _) = destEvar sigma ev in
+ let sigma = Evd.define ev j.Environ.uj_val sigma in
+ sigma
+
+let solve_evar_clause env sigma hyp_only clause = function
+| NoBindings -> sigma
+| ImplicitBindings largs ->
+ let open EConstr in
+ let fold holes h =
+ if h.hole_deps then
+ (* Some subsequent term uses the hole *)
+ let (ev, _) = destEvar sigma h.hole_evar in
+ let is_dep hole = occur_evar sigma ev hole.hole_type in
+ let in_hyp = List.exists is_dep holes in
+ let in_ccl = occur_evar sigma ev clause.cl_concl in
+ let dep = if hyp_only then in_hyp && not in_ccl else in_hyp || in_ccl in
+ let h = { h with hole_deps = dep } in
+ h :: holes
+ else
+ (* The hole does not occur anywhere *)
+ h :: holes
+ in
+ let holes = List.fold_left fold [] (List.rev clause.cl_holes) in
+ let map h = if h.hole_deps then Some h.hole_evar else None in
+ let evs = List.map_filter map holes in
+ let len = List.length evs in
+ if Int.equal len (List.length largs) then
+ let fold sigma ev arg = define_with_type sigma env ev arg in
+ let sigma = List.fold_left2 fold sigma evs largs in
+ sigma
+ else
+ error_not_right_number_missing_arguments len
+| ExplicitBindings lbind ->
+ let () = check_bindings lbind in
+ let fold sigma {CAst.v=(binder, c)} =
+ let ev = evar_of_binder clause.cl_holes binder in
+ define_with_type sigma env ev c
+ in
+ let sigma = List.fold_left fold sigma lbind in
+ sigma
diff --git a/proofs/clenv.mli b/proofs/clenv.mli
new file mode 100644
index 0000000000..03acb9e46e
--- /dev/null
+++ b/proofs/clenv.mli
@@ -0,0 +1,173 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(* * (see LICENSE file for the text of the license) *)
+(************************************************************************)
+
+(** This file defines clausenv, which is a deprecated way to handle open terms
+ in the proof engine. Most of the API here is legacy except for the
+ evar-based clauses. *)
+
+open Names
+open Constr
+open Environ
+open Evd
+open EConstr
+open Unification
+open Tactypes
+
+(** {6 The Type of Constructions clausale environments.} *)
+
+type clausenv = {
+ env : env; (** the typing context *)
+ evd : evar_map; (** the mapping from metavar and evar numbers to their
+ types and values *)
+ templval : constr freelisted; (** the template which we are trying to fill
+ out *)
+ templtyp : constr freelisted (** its type *)}
+
+
+(** subject of clenv (instantiated) *)
+val clenv_value : clausenv -> constr
+
+(** type of clenv (instantiated) *)
+val clenv_type : clausenv -> types
+
+(** substitute resolved metas *)
+val clenv_nf_meta : clausenv -> EConstr.constr -> EConstr.constr
+
+(** type of a meta in clenv context *)
+val clenv_meta_type : clausenv -> metavariable -> types
+
+val mk_clenv_from : Proofview.Goal.t -> EConstr.constr * EConstr.types -> clausenv
+val mk_clenv_from_n :
+ Proofview.Goal.t -> int option -> EConstr.constr * EConstr.types -> clausenv
+val mk_clenv_type_of : Proofview.Goal.t -> EConstr.constr -> clausenv
+val mk_clenv_from_env : env -> evar_map -> int option -> EConstr.constr * EConstr.types -> clausenv
+
+(** Refresh the universes in a clenv *)
+val refresh_undefined_univs : clausenv -> clausenv * Univ.universe_level_subst
+
+(** {6 linking of clenvs } *)
+
+val clenv_fchain :
+ ?with_univs:bool -> ?flags:unify_flags -> metavariable -> clausenv -> clausenv -> clausenv
+
+(** {6 Unification with clenvs } *)
+
+(** Unifies two terms in a clenv. The boolean is [allow_K] (see [Unification]) *)
+val clenv_unify :
+ ?flags:unify_flags -> conv_pb -> constr -> constr -> clausenv -> clausenv
+
+(** unifies the concl of the goal with the type of the clenv *)
+val old_clenv_unique_resolver :
+ ?flags:unify_flags -> clausenv -> Goal.goal sigma -> clausenv
+
+val clenv_unique_resolver :
+ ?flags:unify_flags -> clausenv -> Proofview.Goal.t -> clausenv
+
+val clenv_dependent : clausenv -> metavariable list
+
+val clenv_pose_metas_as_evars : clausenv -> metavariable list -> clausenv * Evar.t list
+
+(** {6 Bindings } *)
+
+type arg_bindings = constr explicit_bindings
+
+(** bindings where the key is the position in the template of the
+ clenv (dependent or not). Positions can be negative meaning to
+ start from the rightmost argument of the template. *)
+val clenv_independent : clausenv -> metavariable list
+val clenv_missing : clausenv -> metavariable list
+
+(** for the purpose of inversion tactics *)
+exception NoSuchBinding
+val clenv_constrain_last_binding : constr -> clausenv -> clausenv
+
+val clenv_unify_meta_types : ?flags:unify_flags -> clausenv -> clausenv
+
+(** start with a clenv to refine with a given term with bindings *)
+
+(** the arity of the lemma is fixed
+ the optional int tells how many prods of the lemma have to be used
+ use all of them if None *)
+val make_clenv_binding_env_apply :
+ env -> evar_map -> int option -> EConstr.constr * EConstr.constr -> constr bindings ->
+ clausenv
+
+val make_clenv_binding_apply :
+ env -> evar_map -> int option -> EConstr.constr * EConstr.constr -> constr bindings ->
+ clausenv
+
+val make_clenv_binding_env :
+ env -> evar_map -> EConstr.constr * EConstr.constr -> constr bindings -> clausenv
+
+val make_clenv_binding :
+ env -> evar_map -> EConstr.constr * EConstr.constr -> constr bindings -> clausenv
+
+(** if the clause is a product, add an extra meta for this product *)
+exception NotExtensibleClause
+val clenv_push_prod : clausenv -> clausenv
+
+(** {6 Pretty-print (debug only) } *)
+val pr_clenv : clausenv -> Pp.t
+
+(** {6 Evar-based clauses} *)
+
+(** The following code is an adaptation of the [make_clenv_*] functions above,
+ except that it uses evars instead of metas, and naturally fits in the new
+ refinement monad. It should eventually replace all uses of the
+ aforementioned functions.
+
+ A clause is constructed as follows: assume a type [t := forall (x1 : A1) ...
+ (xn : An), T], we instantiate all the [xi] with a fresh evar [ei] and
+ return [T(xi := ei)] together with the [ei] enriched with a bit of
+ additional data. This is the simple part done by [make_evar_clause].
+
+ The problem lies in the fact we want to solve such holes with some
+ [constr bindings]. This entails some subtleties, because the provided terms
+ may only be well-typed up to a coercion, which we can only infer if we have
+ enough typing information. The meta machinery could insert coercions through
+ tricky instantiation delays. The only solution we have now is to delay the
+ tentative resolution of clauses by providing the [solve_evar_clause]
+ function, to be called at a smart enough time.
+*)
+
+type hole = {
+ hole_evar : EConstr.constr;
+ (** The hole itself. Guaranteed to be an evar. *)
+ hole_type : EConstr.types;
+ (** Type of the hole in the current environment. *)
+ hole_deps : bool;
+ (** Whether the remainder of the clause was dependent in the hole. Note that
+ because let binders are substituted, it does not mean that it actually
+ appears somewhere in the returned clause. *)
+ hole_name : Name.t;
+ (** Name of the hole coming from its binder. *)
+}
+
+type clause = {
+ cl_holes : hole list;
+ cl_concl : EConstr.types;
+}
+
+val make_evar_clause : env -> evar_map -> ?len:int -> EConstr.types ->
+ (evar_map * clause)
+(** An evar version of {!make_clenv_binding}. Given a type [t],
+ [evar_environments env sigma ~len t bl] tries to eliminate at most [len]
+ products of the type [t] by filling it with evars. It returns the resulting
+ type together with the list of holes generated. Assumes that [t] is
+ well-typed in the environment. *)
+
+val solve_evar_clause : env -> evar_map -> bool -> clause -> EConstr.constr bindings ->
+ evar_map
+(** [solve_evar_clause env sigma hyps cl bl] tries to solve the holes contained
+ in [cl] according to the [bl] argument. Assumes that [bl] are well-typed in
+ the environment. The boolean [hyps] is a compatibility flag that allows to
+ consider arguments to be dependent only when they appear in hypotheses and
+ not in the conclusion. This boolean is only used when [bl] is of the form
+ [ImplicitBindings _]. *)
diff --git a/proofs/clenvtac.ml b/proofs/clenvtac.ml
new file mode 100644
index 0000000000..c36b0fa337
--- /dev/null
+++ b/proofs/clenvtac.ml
@@ -0,0 +1,135 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Util
+open Constr
+open Termops
+open Evd
+open EConstr
+open Refiner
+open Logic
+open Reduction
+open Clenv
+
+(* This function put casts around metavariables whose type could not be
+ * infered by the refiner, that is head of applications, predicates and
+ * subject of Cases.
+ * Does check that the casted type is closed. Anyway, the refiner would
+ * fail in this case... *)
+
+let clenv_cast_meta clenv =
+ let rec crec u =
+ match EConstr.kind clenv.evd u with
+ | App _ | Case _ -> crec_hd u
+ | Cast (c,_,_) when isMeta clenv.evd c -> u
+ | Proj (p, c) -> mkProj (p, crec_hd c)
+ | _ -> EConstr.map clenv.evd crec u
+
+ and crec_hd u =
+ match EConstr.kind clenv.evd (strip_outer_cast clenv.evd u) with
+ | Meta mv ->
+ (try
+ let b = Typing.meta_type clenv.evd mv in
+ assert (not (occur_meta clenv.evd b));
+ if occur_meta clenv.evd b then u
+ else mkCast (mkMeta mv, DEFAULTcast, b)
+ with Not_found -> u)
+ | App(f,args) -> mkApp (crec_hd f, Array.map crec args)
+ | Case(ci,p,c,br) ->
+ mkCase (ci, crec_hd p, crec_hd c, Array.map crec br)
+ | Proj (p, c) -> mkProj (p, crec_hd c)
+ | _ -> u
+ in
+ crec
+
+let clenv_value_cast_meta clenv =
+ clenv_cast_meta clenv (clenv_value clenv)
+
+let clenv_pose_dependent_evars ?(with_evars=false) clenv =
+ let dep_mvs = clenv_dependent clenv in
+ let env, sigma = clenv.env, clenv.evd in
+ if not (List.is_empty dep_mvs) && not with_evars then
+ raise
+ (RefinerError (env, sigma, UnresolvedBindings (List.map (meta_name clenv.evd) dep_mvs)));
+ clenv_pose_metas_as_evars clenv dep_mvs
+
+let clenv_refine ?(with_evars=false) ?(with_classes=true) clenv =
+ (* ppedrot: a Goal.enter here breaks things, because the tactic below may
+ solve goals by side effects, while the compatibility layer keeps those
+ useless goals. That deserves a FIXME. *)
+ Proofview.V82.tactic begin fun gl ->
+ let clenv, evars = clenv_pose_dependent_evars ~with_evars clenv in
+ let evd' =
+ if with_classes then
+ let evd' =
+ Typeclasses.resolve_typeclasses ~filter:Typeclasses.all_evars
+ ~fail:(not with_evars) clenv.env clenv.evd
+ in
+ Typeclasses.make_unresolvables (fun x -> List.mem_f Evar.equal x evars) evd'
+ else clenv.evd
+ in
+ let clenv = { clenv with evd = evd' } in
+ tclTHEN
+ (tclEVARS (Evd.clear_metas evd'))
+ (refiner ~check:false EConstr.Unsafe.(to_constr (clenv_cast_meta clenv (clenv_value clenv)))) gl
+ end
+
+let clenv_pose_dependent_evars ?(with_evars=false) clenv =
+ fst (clenv_pose_dependent_evars ~with_evars clenv)
+
+open Unification
+
+let dft = default_unify_flags
+
+let res_pf ?with_evars ?(with_classes=true) ?(flags=dft ()) clenv =
+ Proofview.Goal.enter begin fun gl ->
+ let clenv = clenv_unique_resolver ~flags clenv gl in
+ clenv_refine ?with_evars ~with_classes clenv
+ end
+
+(* [unifyTerms] et [unify] ne semble pas gérer les Meta, en
+ particulier ne semblent pas vérifier que des instances différentes
+ d'une même Meta sont compatibles. D'ailleurs le "fst" jette les metas
+ provenant de w_Unify. (Utilisé seulement dans prolog.ml) *)
+
+let fail_quick_core_unif_flags = {
+ modulo_conv_on_closed_terms = Some TransparentState.full;
+ use_metas_eagerly_in_conv_on_closed_terms = false;
+ use_evars_eagerly_in_conv_on_closed_terms = false;
+ modulo_delta = TransparentState.empty;
+ modulo_delta_types = TransparentState.full;
+ check_applied_meta_types = false;
+ use_pattern_unification = false;
+ use_meta_bound_pattern_unification = true; (* ? *)
+ frozen_evars = Evar.Set.empty;
+ restrict_conv_on_strict_subterms = false; (* ? *)
+ modulo_betaiota = false;
+ modulo_eta = true;
+}
+
+let fail_quick_unif_flags = {
+ core_unify_flags = fail_quick_core_unif_flags;
+ merge_unify_flags = fail_quick_core_unif_flags;
+ subterm_unify_flags = fail_quick_core_unif_flags;
+ allow_K_in_toplevel_higher_order_unification = false;
+ resolve_evars = false
+}
+
+(* let unifyTerms m n = walking (fun wc -> fst (w_Unify CONV m n [] wc)) *)
+let unify ?(flags=fail_quick_unif_flags) m =
+ Proofview.Goal.enter begin fun gl ->
+ let env = Tacmach.New.pf_env gl in
+ let n = Tacmach.New.pf_concl gl in
+ let evd = clear_metas (Tacmach.New.project gl) in
+ try
+ let evd' = w_unify env evd CONV ~flags m n in
+ Proofview.Unsafe.tclEVARSADVANCE evd'
+ with e when CErrors.noncritical e -> Proofview.tclZERO e
+ end
diff --git a/proofs/clenvtac.mli b/proofs/clenvtac.mli
new file mode 100644
index 0000000000..d178478425
--- /dev/null
+++ b/proofs/clenvtac.mli
@@ -0,0 +1,23 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** Legacy components of the previous proof engine. *)
+
+open Clenv
+open EConstr
+open Unification
+
+(** Tactics *)
+val unify : ?flags:unify_flags -> constr -> unit Proofview.tactic
+val clenv_refine : ?with_evars:bool -> ?with_classes:bool -> clausenv -> unit Proofview.tactic
+val res_pf : ?with_evars:bool -> ?with_classes:bool -> ?flags:unify_flags -> clausenv -> unit Proofview.tactic
+
+val clenv_pose_dependent_evars : ?with_evars:bool -> clausenv -> clausenv
+val clenv_value_cast_meta : clausenv -> constr
diff --git a/proofs/doc.tex b/proofs/doc.tex
new file mode 100644
index 0000000000..431027ef02
--- /dev/null
+++ b/proofs/doc.tex
@@ -0,0 +1,14 @@
+
+\newpage
+\section*{The Proof Engine}
+
+\ocwsection \label{proofs}
+This chapter describes the \Coq\ proof engine, which is also called
+the ``refiner'', since it provides a way to build terms by successive
+refining steps. Those steps are either primitive rules or higher-level
+tactics.
+The modules of the proof engine are organized as follows.
+
+\bigskip
+\begin{center}\epsfig{file=proofs.dep.ps,width=\linewidth}\end{center}
+
diff --git a/proofs/dune b/proofs/dune
new file mode 100644
index 0000000000..679c45f6bf
--- /dev/null
+++ b/proofs/dune
@@ -0,0 +1,6 @@
+(library
+ (name proofs)
+ (synopsis "Coq's Higher-level Refinement Proof Engine and Top-level Proof Structure")
+ (public_name coq.proofs)
+ (wrapped false)
+ (libraries interp))
diff --git a/proofs/evar_refiner.ml b/proofs/evar_refiner.ml
new file mode 100644
index 0000000000..6c4193c66b
--- /dev/null
+++ b/proofs/evar_refiner.ml
@@ -0,0 +1,65 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 CErrors
+open Util
+open Evd
+open Evarutil
+open Evarsolve
+open Pp
+open Glob_term
+open Ltac_pretype
+
+(******************************************)
+(* Instantiation of existential variables *)
+(******************************************)
+
+type glob_constr_ltac_closure = ltac_var_map * glob_constr
+
+let depends_on_evar sigma evk _ (pbty,_,t1,t2) =
+ try Evar.equal (head_evar sigma t1) evk
+ with NoHeadEvar ->
+ try Evar.equal (head_evar sigma t2) evk
+ with NoHeadEvar -> false
+
+let define_and_solve_constraints evk c env evd =
+ if Termops.occur_evar evd evk c then
+ Pretype_errors.error_occur_check env evd evk c;
+ let evd = define evk c evd in
+ let (evd,pbs) = extract_changed_conv_pbs evd (depends_on_evar evd evk) in
+ match
+ List.fold_left
+ (fun p (pbty,env,t1,t2) -> match p with
+ | Success evd -> Evarconv.evar_conv_x TransparentState.full env evd pbty t1 t2
+ | UnifFailure _ as x -> x) (Success evd)
+ pbs
+ with
+ | Success evd -> evd
+ | UnifFailure _ -> user_err Pp.(str "Instance does not satisfy the constraints.")
+
+let w_refine (evk,evi) (ltac_var,rawc) sigma =
+ if Evd.is_defined sigma evk then
+ user_err Pp.(str "Instantiate called on already-defined evar");
+ let env = Evd.evar_filtered_env evi in
+ let sigma',typed_c =
+ let flags = {
+ Pretyping.use_typeclasses = true;
+ Pretyping.solve_unification_constraints = true;
+ Pretyping.fail_evar = false;
+ Pretyping.expand_evars = true } in
+ try Pretyping.understand_ltac flags
+ env sigma ltac_var (Pretyping.OfType evi.evar_concl) rawc
+ with e when CErrors.noncritical e ->
+ let loc = Glob_ops.loc_of_glob_constr rawc in
+ user_err ?loc
+ (str "Instance is not well-typed in the environment of " ++
+ Termops.pr_existential_key sigma evk ++ str ".")
+ in
+ define_and_solve_constraints evk typed_c env (evars_reset_evd sigma' sigma)
diff --git a/proofs/evar_refiner.mli b/proofs/evar_refiner.mli
new file mode 100644
index 0000000000..e8f3c4d173
--- /dev/null
+++ b/proofs/evar_refiner.mli
@@ -0,0 +1,20 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Evd
+open Glob_term
+open Ltac_pretype
+
+(** Refinement of existential variables. *)
+
+type glob_constr_ltac_closure = ltac_var_map * glob_constr
+
+val w_refine : Evar.t * evar_info ->
+ glob_constr_ltac_closure -> evar_map -> evar_map
diff --git a/proofs/goal.ml b/proofs/goal.ml
new file mode 100644
index 0000000000..7245d4a004
--- /dev/null
+++ b/proofs/goal.ml
@@ -0,0 +1,140 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Util
+open Pp
+
+module NamedDecl = Context.Named.Declaration
+
+(* This module implements the abstract interface to goals *)
+(* A general invariant of the module, is that a goal whose associated
+ evar is defined in the current evar_map, should not be accessed. *)
+
+(* type of the goals *)
+type goal = Evar.t
+
+let pr_goal e = str "GOAL:" ++ Pp.int (Evar.repr e)
+
+let uid e = string_of_int (Evar.repr e)
+
+(* Layer to implement v8.2 tactic engine ontop of the new architecture.
+ Types are different from what they used to be due to a change of the
+ internal types. *)
+module V82 = struct
+
+ (* Old style env primitive *)
+ let env evars gl =
+ let evi = Evd.find evars gl in
+ Evd.evar_filtered_env evi
+
+ (* Old style hyps primitive *)
+ let hyps evars gl =
+ let evi = Evd.find evars gl in
+ Evd.evar_filtered_hyps evi
+
+ (* same as [hyps], but ensures that existential variables are
+ normalised. *)
+ let nf_hyps evars gl =
+ let hyps = Environ.named_context_of_val (hyps evars gl) in
+ Environ.val_of_named_context (Evarutil.nf_named_context_evar evars hyps)
+
+ (* Access to ".evar_concl" *)
+ let concl evars gl =
+ let evi = Evd.find evars gl in
+ evi.Evd.evar_concl
+
+ (* Old style mk_goal primitive *)
+ let mk_goal evars hyps concl =
+ (* A goal created that way will not be used by refine and will not
+ be shelved. It must not appear as a future_goal, so the future
+ goals are restored to their initial value after the evar is
+ created. *)
+ let prev_future_goals = Evd.save_future_goals evars in
+ let evi = { Evd.evar_hyps = hyps;
+ Evd.evar_concl = concl;
+ Evd.evar_filter = Evd.Filter.identity;
+ Evd.evar_body = Evd.Evar_empty;
+ Evd.evar_source = (Loc.tag Evar_kinds.GoalEvar);
+ Evd.evar_candidates = None }
+ in
+ let (evars, evk) = Evarutil.new_pure_evar_full evars ~typeclass_candidate:false evi in
+ let evars = Evd.restore_future_goals evars prev_future_goals in
+ let ctxt = Environ.named_context_of_val hyps in
+ let inst = Array.map_of_list (NamedDecl.get_id %> EConstr.mkVar) ctxt in
+ let ev = EConstr.mkEvar (evk,inst) in
+ (evk, ev, evars)
+
+ (* Instantiates a goal with an open term *)
+ let partial_solution env sigma evk c =
+ (* Check that the goal itself does not appear in the refined term *)
+ let _ =
+ if not (Evarutil.occur_evar_upto sigma evk c) then ()
+ else Pretype_errors.error_occur_check env sigma evk c
+ in
+ Evd.define evk c sigma
+
+ (* Instantiates a goal with an open term, using name of goal for evk' *)
+ let partial_solution_to env sigma evk evk' c =
+ let id = Evd.evar_ident evk sigma in
+ let sigma = partial_solution env sigma evk c in
+ match id with
+ | None -> sigma
+ | Some id -> Evd.rename evk' id sigma
+
+ (* Parts of the progress tactical *)
+ let same_goal evars1 gl1 evars2 gl2 =
+ let evi1 = Evd.find evars1 gl1 in
+ let evi2 = Evd.find evars2 gl2 in
+ let c1 = EConstr.Unsafe.to_constr evi1.Evd.evar_concl in
+ let c2 = EConstr.Unsafe.to_constr evi2.Evd.evar_concl in
+ Constr.equal c1 c2 &&
+ Environ.eq_named_context_val evi1.Evd.evar_hyps evi2.Evd.evar_hyps
+
+ let weak_progress glss gls =
+ match glss.Evd.it with
+ | [ g ] -> not (same_goal glss.Evd.sigma g gls.Evd.sigma gls.Evd.it)
+ | _ -> true
+
+ let progress glss gls =
+ weak_progress glss gls
+ (* spiwack: progress normally goes like this:
+ (Evd.progress_evar_map gls.Evd.sigma glss.Evd.sigma) || (weak_progress glss gls)
+ This is immensly slow in the current implementation. Maybe we could
+ reimplement progress_evar_map with restricted folds like "fold_undefined",
+ with a good implementation of them.
+ *)
+
+ (* Used by the compatibility layer and typeclasses *)
+ let nf_evar sigma gl =
+ let evi = Evd.find sigma gl in
+ let evi = Evarutil.nf_evar_info sigma evi in
+ let sigma = Evd.add sigma gl evi in
+ (gl, sigma)
+
+ (* Goal represented as a type, doesn't take into account section variables *)
+ let abstract_type sigma gl =
+ let open EConstr in
+ let (gl,sigma) = nf_evar sigma gl in
+ let env = env sigma gl in
+ let genv = Global.env () in
+ let is_proof_var decl =
+ try ignore (Environ.lookup_named (NamedDecl.get_id decl) genv); false
+ with Not_found -> true in
+ Environ.fold_named_context_reverse (fun t decl ->
+ if is_proof_var decl then
+ let decl = Termops.map_named_decl EConstr.of_constr decl in
+ mkNamedProd_or_LetIn decl t
+ else
+ t
+ ) ~init:(concl sigma gl) env
+
+end
+
+module Set = Set.Make(struct type t = goal let compare = Evar.compare end)
diff --git a/proofs/goal.mli b/proofs/goal.mli
new file mode 100644
index 0000000000..af9fb662bf
--- /dev/null
+++ b/proofs/goal.mli
@@ -0,0 +1,71 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(* * (see LICENSE file for the text of the license) *)
+(************************************************************************)
+
+(** This module implements the abstract interface to goals. Most of the code
+ here is useless and should be eventually removed. Consider using
+ {!Proofview.Goal} instead. *)
+
+type goal = Evar.t
+
+(* Gives a unique identifier to each goal. The identifier is
+ guaranteed to contain no space. *)
+val uid : goal -> string
+
+(* Debugging help *)
+val pr_goal : goal -> Pp.t
+
+(* Layer to implement v8.2 tactic engine ontop of the new architecture.
+ Types are different from what they used to be due to a change of the
+ internal types. *)
+module V82 : sig
+
+ (* Old style env primitive *)
+ val env : Evd.evar_map -> goal -> Environ.env
+
+ (* Old style hyps primitive *)
+ val hyps : Evd.evar_map -> goal -> Environ.named_context_val
+
+ (* same as [hyps], but ensures that existential variables are
+ normalised. *)
+ val nf_hyps : Evd.evar_map -> goal -> Environ.named_context_val
+
+ (* Access to ".evar_concl" *)
+ val concl : Evd.evar_map -> goal -> EConstr.constr
+
+ (* Old style mk_goal primitive, returns a new goal with corresponding
+ hypotheses and conclusion, together with a term which is precisely
+ the evar corresponding to the goal, and an updated evar_map. *)
+ val mk_goal : Evd.evar_map ->
+ Environ.named_context_val ->
+ EConstr.constr ->
+ goal * EConstr.constr * Evd.evar_map
+
+ (* Instantiates a goal with an open term *)
+ val partial_solution : Environ.env -> Evd.evar_map -> goal -> EConstr.constr -> Evd.evar_map
+
+ (* Instantiates a goal with an open term, reusing name of goal for
+ second goal *)
+ val partial_solution_to : Environ.env -> Evd.evar_map -> goal -> goal -> EConstr.constr -> Evd.evar_map
+
+ (* Principal part of the progress tactical *)
+ val progress : goal list Evd.sigma -> goal Evd.sigma -> bool
+
+ (* Principal part of tclNOTSAMEGOAL *)
+ val same_goal : Evd.evar_map -> goal -> Evd.evar_map -> goal -> bool
+
+ (* Used by the compatibility layer and typeclasses *)
+ val nf_evar : Evd.evar_map -> goal -> goal * Evd.evar_map
+
+ (* Goal represented as a type, doesn't take into account section variables *)
+ val abstract_type : Evd.evar_map -> goal -> EConstr.types
+
+end
+
+module Set : sig include Set.S with type elt = goal end
diff --git a/proofs/goal_select.ml b/proofs/goal_select.ml
new file mode 100644
index 0000000000..cef3fd3f5e
--- /dev/null
+++ b/proofs/goal_select.ml
@@ -0,0 +1,68 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Names
+
+(* spiwack: I'm choosing, for now, to have [goal_selector] be a
+ different type than [goal_reference] mostly because if it makes sense
+ to print a goal that is out of focus (or already solved) it doesn't
+ make sense to apply a tactic to it. Hence it the types may look very
+ similar, they do not seem to mean the same thing. *)
+type t =
+ | SelectAlreadyFocused
+ | SelectNth of int
+ | SelectList of (int * int) list
+ | SelectId of Id.t
+ | SelectAll
+
+(* Default goal selector: selector chosen when a tactic is applied
+ without an explicit selector. *)
+let default_goal_selector = ref (SelectNth 1)
+let get_default_goal_selector () = !default_goal_selector
+
+let pr_range_selector (i, j) =
+ if i = j then Pp.int i
+ else Pp.(int i ++ str "-" ++ int j)
+
+let pr_goal_selector = function
+ | SelectAlreadyFocused -> Pp.str "!"
+ | SelectAll -> Pp.str "all"
+ | SelectNth i -> Pp.int i
+ | SelectList l ->
+ Pp.(str "["
+ ++ prlist_with_sep pr_comma pr_range_selector l
+ ++ str "]")
+ | SelectId id -> Names.Id.print id
+
+let parse_goal_selector = function
+ | "!" -> SelectAlreadyFocused
+ | "all" -> SelectAll
+ | i ->
+ let err_msg = "The default selector must be \"all\" or a natural number." in
+ begin try
+ let i = int_of_string i in
+ if i < 0 then CErrors.user_err Pp.(str err_msg);
+ SelectNth i
+ with Failure _ -> CErrors.user_err Pp.(str err_msg)
+ end
+
+let () = let open Goptions in
+ declare_string_option
+ { optdepr = false;
+ optname = "default goal selector" ;
+ optkey = ["Default";"Goal";"Selector"] ;
+ optread = begin fun () ->
+ Pp.string_of_ppcmds
+ (pr_goal_selector !default_goal_selector)
+ end;
+ optwrite = begin fun n ->
+ default_goal_selector := parse_goal_selector n
+ end
+ }
diff --git a/proofs/goal_select.mli b/proofs/goal_select.mli
new file mode 100644
index 0000000000..b1c5723885
--- /dev/null
+++ b/proofs/goal_select.mli
@@ -0,0 +1,26 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Names
+
+(* spiwack: I'm choosing, for now, to have [goal_selector] be a
+ different type than [goal_reference] mostly because if it makes sense
+ to print a goal that is out of focus (or already solved) it doesn't
+ make sense to apply a tactic to it. Hence it the types may look very
+ similar, they do not seem to mean the same thing. *)
+type t =
+ | SelectAlreadyFocused
+ | SelectNth of int
+ | SelectList of (int * int) list
+ | SelectId of Id.t
+ | SelectAll
+
+val pr_goal_selector : t -> Pp.t
+val get_default_goal_selector : unit -> t
diff --git a/proofs/logic.ml b/proofs/logic.ml
new file mode 100644
index 0000000000..3581e90b79
--- /dev/null
+++ b/proofs/logic.ml
@@ -0,0 +1,598 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Nameops
+open Constr
+open Vars
+open Termops
+open Environ
+open Reductionops
+open Inductiveops
+open Typing
+open Type_errors
+open Retyping
+
+module NamedDecl = Context.Named.Declaration
+
+type refiner_error =
+
+ (* Errors raised by the refiner *)
+ | BadType of constr * constr * constr
+ | UnresolvedBindings of Name.t list
+ | CannotApply of constr * constr
+ | NotWellTyped of constr
+ | NonLinearProof of constr
+ | MetaInType of EConstr.constr
+
+ (* Errors raised by the tactics *)
+ | IntroNeedsProduct
+ | DoesNotOccurIn of constr * Id.t
+ | NoSuchHyp of Id.t
+
+exception RefinerError of Environ.env * Evd.evar_map * refiner_error
+
+open Pretype_errors
+
+(** FIXME: this is quite brittle. Why not accept any PretypeError? *)
+let is_typing_error = function
+| UnexpectedType (_, _) | NotProduct _
+| VarNotFound _ | TypingError _ -> true
+| _ -> false
+
+let is_unification_error = function
+| CannotUnify _ | CannotUnifyLocal _| CannotGeneralize _
+| NoOccurrenceFound _ | CannotUnifyBindingType _
+| ActualTypeNotCoercible _ | UnifOccurCheck _
+| CannotFindWellTypedAbstraction _ | WrongAbstractionType _
+| UnsolvableImplicit _| AbstractionOverMeta _
+| UnsatisfiableConstraints _ -> true
+| _ -> false
+
+let catchable_exception = function
+ | CErrors.UserError _ | TypeError _
+ | Proof.OpenProof _
+ (* abstract will call close_proof inside a tactic *)
+ | Notation.PrimTokenNotationError _
+ | RefinerError _ | Indrec.RecursionSchemeError _
+ | Nametab.GlobalizationError _
+ (* reduction errors *)
+ | Tacred.ReductionTacticError _ -> true
+ (* unification and typing errors *)
+ | PretypeError(_,_, e) -> is_unification_error e || is_typing_error e
+ | _ -> false
+
+let error_no_such_hypothesis env sigma id = raise (RefinerError (env, sigma, NoSuchHyp id))
+
+(* Tells if the refiner should check that the submitted rules do not
+ produce invalid subgoals *)
+let check = ref false
+let with_check = Flags.with_option check
+
+(* [apply_to_hyp sign id f] splits [sign] into [tail::[id,_,_]::head] and
+ returns [tail::(f head (id,_,_) (rev tail))] *)
+let apply_to_hyp env sigma check sign id f =
+ try apply_to_hyp sign id f
+ with Hyp_not_found ->
+ if check then error_no_such_hypothesis env sigma id
+ else sign
+
+let check_typability env sigma c =
+ if !check then let _ = unsafe_type_of env sigma (EConstr.of_constr c) in ()
+
+(************************************************************************)
+(************************************************************************)
+(* Implementation of the structural rules (moving and deleting
+ hypotheses around) *)
+
+(* The ClearBody tactic *)
+
+(* Reordering of the context *)
+
+(* faire le minimum d'echanges pour que l'ordre donne soit un *)
+(* sous-ordre du resultat. Par exemple, 2 hyps non mentionnee ne sont *)
+(* pas echangees. Choix: les hyps mentionnees ne peuvent qu'etre *)
+(* reculees par rapport aux autres (faire le contraire!) *)
+
+let mt_q = (Id.Map.empty,[])
+let push_val y = function
+ (_,[] as q) -> q
+ | (m, (x,l)::q) -> (m, (x,Id.Set.add y l)::q)
+let push_item x v (m,l) =
+ (Id.Map.add x v m, (x,Id.Set.empty)::l)
+let mem_q x (m,_) = Id.Map.mem x m
+let find_q x (m,q) =
+ let v = Id.Map.find x m in
+ let m' = Id.Map.remove x m in
+ let rec find accs acc = function
+ [] -> raise Not_found
+ | [(x',l)] ->
+ if Id.equal x x' then ((v,Id.Set.union accs l),(m',List.rev acc))
+ else raise Not_found
+ | (x',l as i)::((x'',l'')::q as itl) ->
+ if Id.equal x x' then
+ ((v,Id.Set.union accs l),
+ (m',List.rev acc@(x'',Id.Set.add x (Id.Set.union l l''))::q))
+ else find (Id.Set.union l accs) (i::acc) itl in
+ find Id.Set.empty [] q
+
+let occur_vars_in_decl env sigma hyps d =
+ if Id.Set.is_empty hyps then false else
+ let ohyps = global_vars_set_of_decl env sigma d in
+ Id.Set.exists (fun h -> Id.Set.mem h ohyps) hyps
+
+let reorder_context env sigma sign ord =
+ let ords = List.fold_right Id.Set.add ord Id.Set.empty in
+ if not (Int.equal (List.length ord) (Id.Set.cardinal ords)) then
+ user_err Pp.(str "Order list has duplicates");
+ let rec step ord expected ctxt_head moved_hyps ctxt_tail =
+ match ord with
+ | [] -> List.rev ctxt_tail @ ctxt_head
+ | top::ord' when mem_q top moved_hyps ->
+ let ((d,h),mh) = find_q top moved_hyps in
+ if occur_vars_in_decl env sigma h d then
+ user_err ~hdr:"reorder_context"
+ (str "Cannot move declaration " ++ Id.print top ++ spc() ++
+ str "before " ++
+ pr_sequence Id.print
+ (Id.Set.elements (Id.Set.inter h
+ (global_vars_set_of_decl env sigma d))));
+ step ord' expected ctxt_head mh (d::ctxt_tail)
+ | _ ->
+ (match ctxt_head with
+ | [] -> error_no_such_hypothesis env sigma (List.hd ord)
+ | d :: ctxt ->
+ let x = NamedDecl.get_id d in
+ if Id.Set.mem x expected then
+ step ord (Id.Set.remove x expected)
+ ctxt (push_item x d moved_hyps) ctxt_tail
+ else
+ step ord expected
+ ctxt (push_val x moved_hyps) (d::ctxt_tail)) in
+ step ord ords sign mt_q []
+
+let reorder_val_context env sigma sign ord =
+ let open EConstr in
+ val_of_named_context (reorder_context env sigma (named_context_of_val sign) ord)
+
+
+
+
+let check_decl_position env sigma sign d =
+ let open EConstr in
+ let x = NamedDecl.get_id d in
+ let needed = global_vars_set_of_decl env sigma d in
+ let deps = dependency_closure env sigma (named_context_of_val sign) needed in
+ if Id.List.mem x deps then
+ user_err ~hdr:"Logic.check_decl_position"
+ (str "Cannot create self-referring hypothesis " ++ Id.print x);
+ x::deps
+
+(* Auxiliary functions for primitive MOVE tactic
+ *
+ * [move_hyp with_dep toleft (left,(hfrom,typfrom),right) hto] moves
+ * hyp [hfrom] at location [hto] which belongs to the hyps on the
+ * left side [left] of the full signature if [toleft=true] or to the hyps
+ * on the right side [right] if [toleft=false].
+ * If [with_dep] then dependent hypotheses are moved accordingly. *)
+
+(** Move destination for hypothesis *)
+
+type 'id move_location =
+ | MoveAfter of 'id
+ | MoveBefore of 'id
+ | MoveFirst
+ | MoveLast (** can be seen as "no move" when doing intro *)
+
+(** Printing of [move_location] *)
+
+let pr_move_location pr_id = function
+ | MoveAfter id -> brk(1,1) ++ str "after " ++ pr_id id
+ | MoveBefore id -> brk(1,1) ++ str "before " ++ pr_id id
+ | MoveFirst -> str " at top"
+ | MoveLast -> str " at bottom"
+
+let move_location_eq m1 m2 = match m1, m2 with
+| MoveAfter id1, MoveAfter id2 -> Id.equal id1 id2
+| MoveBefore id1, MoveBefore id2 -> Id.equal id1 id2
+| MoveLast, MoveLast -> true
+| MoveFirst, MoveFirst -> true
+| _ -> false
+
+let split_sign env sigma hfrom hto l =
+ let rec splitrec left toleft = function
+ | [] -> error_no_such_hypothesis env sigma hfrom
+ | d :: right ->
+ let hyp = NamedDecl.get_id d in
+ if Id.equal hyp hfrom then
+ (left,right,d, toleft || move_location_eq hto MoveLast)
+ else
+ let is_toleft = match hto with
+ | MoveAfter h' | MoveBefore h' -> Id.equal hyp h'
+ | _ -> false
+ in
+ splitrec (d::left) (toleft || is_toleft)
+ right
+ in
+ splitrec [] false l
+
+let hyp_of_move_location = function
+ | MoveAfter id -> id
+ | MoveBefore id -> id
+ | _ -> assert false
+
+let move_hyp env sigma toleft (left,declfrom,right) hto =
+ let test_dep d d2 =
+ if toleft
+ then occur_var_in_decl env sigma (NamedDecl.get_id d2) d
+ else occur_var_in_decl env sigma (NamedDecl.get_id d) d2
+ in
+ let rec moverec first middle = function
+ | [] ->
+ if match hto with MoveFirst | MoveLast -> false | _ -> true then
+ error_no_such_hypothesis env sigma (hyp_of_move_location hto);
+ List.rev first @ List.rev middle
+ | d :: _ as right when move_location_eq hto (MoveBefore (NamedDecl.get_id d)) ->
+ List.rev first @ List.rev middle @ right
+ | d :: right ->
+ let hyp = NamedDecl.get_id d in
+ let (first',middle') =
+ if List.exists (test_dep d) middle then
+ if not (move_location_eq hto (MoveAfter hyp)) then
+ (first, d::middle)
+ else
+ user_err ~hdr:"move_hyp" (str "Cannot move " ++ Id.print (NamedDecl.get_id declfrom) ++
+ pr_move_location Id.print hto ++
+ str (if toleft then ": it occurs in the type of " else ": it depends on ")
+ ++ Id.print hyp ++ str ".")
+ else
+ (d::first, middle)
+ in
+ if move_location_eq hto (MoveAfter hyp) then
+ List.rev first' @ List.rev middle' @ right
+ else
+ moverec first' middle' right
+ in
+ let open EConstr in
+ if toleft then
+ let right =
+ List.fold_right push_named_context_val right empty_named_context_val in
+ List.fold_left (fun sign d -> push_named_context_val d sign)
+ right (moverec [] [declfrom] left)
+ else
+ let right =
+ List.fold_right push_named_context_val
+ (moverec [] [declfrom] right) empty_named_context_val in
+ List.fold_left (fun sign d -> push_named_context_val d sign)
+ right left
+
+let move_hyp_in_named_context env sigma hfrom hto sign =
+ let open EConstr in
+ let (left,right,declfrom,toleft) =
+ split_sign env sigma hfrom hto (named_context_of_val sign) in
+ move_hyp env sigma toleft (left,declfrom,right) hto
+
+let insert_decl_in_named_context env sigma decl hto sign =
+ let open EConstr in
+ move_hyp env sigma false ([],decl,named_context_of_val sign) hto
+
+(**********************************************************************)
+
+
+(************************************************************************)
+(************************************************************************)
+(* Implementation of the logical rules *)
+
+(* Will only be used on terms given to the Refine rule which have meta
+variables only in Application and Case *)
+
+let error_unsupported_deep_meta c =
+ user_err (strbrk "Application of lemmas whose beta-iota normal " ++
+ strbrk "form contains metavariables deep inside the term is not " ++
+ strbrk "supported; try \"refine\" instead.")
+
+let collect_meta_variables c =
+ let rec collrec deep acc c = match kind c with
+ | Meta mv -> if deep then error_unsupported_deep_meta () else mv::acc
+ | Cast(c,_,_) -> collrec deep acc c
+ | Case(ci,p,c,br) ->
+ (* Hack assuming only two situations: the legacy one that branches,
+ if with Metas, are Meta, and the new one with eta-let-expanded
+ branches *)
+ let br = Array.map2 (fun n b -> try snd (Term.decompose_lam_n_decls n b) with UserError _ -> b) ci.ci_cstr_ndecls br in
+ Array.fold_left (collrec deep)
+ (Constr.fold (collrec deep) (Constr.fold (collrec deep) acc p) c)
+ br
+ | App _ -> Constr.fold (collrec deep) acc c
+ | Proj (_, c) -> collrec deep acc c
+ | _ -> Constr.fold (collrec true) acc c
+ in
+ List.rev (collrec false [] c)
+
+let check_meta_variables env sigma c =
+ if not (List.distinct_f Int.compare (collect_meta_variables c)) then
+ raise (RefinerError (env, sigma, NonLinearProof c))
+
+let check_conv_leq_goal env sigma arg ty conclty =
+ if !check then
+ let ans = Reductionops.infer_conv env sigma (EConstr.of_constr ty) (EConstr.of_constr conclty) in
+ match ans with
+ | Some evm -> evm
+ | None -> raise (RefinerError (env, sigma, BadType (arg,ty,conclty)))
+ else sigma
+
+exception Stop of EConstr.t list
+let meta_free_prefix sigma a =
+ try
+ let a = Array.map EConstr.of_constr a in
+ let _ = Array.fold_left (fun acc a ->
+ if occur_meta sigma a then raise (Stop acc)
+ else a :: acc) [] a
+ in a
+ with Stop acc -> Array.rev_of_list acc
+
+let goal_type_of env sigma c =
+ if !check then
+ let (sigma,t) = type_of env sigma (EConstr.of_constr c) in
+ (sigma, EConstr.Unsafe.to_constr t)
+ else (sigma, EConstr.Unsafe.to_constr (Retyping.get_type_of env sigma (EConstr.of_constr c)))
+
+let rec mk_refgoals sigma goal goalacc conclty trm =
+ let env = Goal.V82.env sigma goal in
+ let hyps = Goal.V82.hyps sigma goal in
+ let mk_goal hyps concl =
+ Goal.V82.mk_goal sigma hyps concl
+ in
+ if (not !check) && not (occur_meta sigma (EConstr.of_constr trm)) then
+ let t'ty = Retyping.get_type_of env sigma (EConstr.of_constr trm) in
+ let t'ty = EConstr.Unsafe.to_constr t'ty in
+ let sigma = check_conv_leq_goal env sigma trm t'ty conclty in
+ (goalacc,t'ty,sigma,trm)
+ else
+ match kind trm with
+ | Meta _ ->
+ let conclty = nf_betaiota env sigma (EConstr.of_constr conclty) in
+ if !check && occur_meta sigma conclty then
+ raise (RefinerError (env, sigma, MetaInType conclty));
+ let (gl,ev,sigma) = mk_goal hyps conclty in
+ let ev = EConstr.Unsafe.to_constr ev in
+ let conclty = EConstr.Unsafe.to_constr conclty in
+ gl::goalacc, conclty, sigma, ev
+
+ | Cast (t,k, ty) ->
+ check_typability env sigma ty;
+ let sigma = check_conv_leq_goal env sigma trm ty conclty in
+ let res = mk_refgoals sigma goal goalacc ty t in
+ (* we keep the casts (in particular VMcast and NATIVEcast) except
+ when they are annotating metas *)
+ if isMeta t then begin
+ assert (k != VMcast && k != NATIVEcast);
+ res
+ end else
+ let (gls,cty,sigma,ans) = res in
+ let ans = if ans == t then trm else mkCast(ans,k,ty) in
+ (gls,cty,sigma,ans)
+
+ | App (f,l) ->
+ let (acc',hdty,sigma,applicand) =
+ if Termops.is_template_polymorphic_ind env sigma (EConstr.of_constr f) then
+ let ty =
+ (* Template polymorphism of definitions and inductive types *)
+ let firstmeta = Array.findi (fun i x -> occur_meta sigma (EConstr.of_constr x)) l in
+ let args, _ = Option.cata (fun i -> CArray.chop i l) (l, [||]) firstmeta in
+ type_of_global_reference_knowing_parameters env sigma (EConstr.of_constr f) (Array.map EConstr.of_constr args)
+ in
+ let ty = EConstr.Unsafe.to_constr ty in
+ goalacc, ty, sigma, f
+ else
+ mk_hdgoals sigma goal goalacc f
+ in
+ let ((acc'',conclty',sigma), args) = mk_arggoals sigma goal acc' hdty l in
+ let sigma = check_conv_leq_goal env sigma trm conclty' conclty in
+ let ans = if applicand == f && args == l then trm else mkApp (applicand, args) in
+ (acc'',conclty',sigma, ans)
+
+ | Proj (p,c) ->
+ let (acc',cty,sigma,c') = mk_hdgoals sigma goal goalacc c in
+ let c = mkProj (p, c') in
+ let ty = get_type_of env sigma (EConstr.of_constr c) in
+ let ty = EConstr.Unsafe.to_constr ty in
+ (acc',ty,sigma,c)
+
+ | Case (ci,p,c,lf) ->
+ let (acc',lbrty,conclty',sigma,p',c') = mk_casegoals sigma goal goalacc p c in
+ let sigma = check_conv_leq_goal env sigma trm conclty' conclty in
+ let (acc'',sigma,rbranches) = treat_case sigma goal ci lbrty lf acc' in
+ let lf' = Array.rev_of_list rbranches in
+ let ans =
+ if p' == p && c' == c && Array.equal (==) lf' lf then trm
+ else mkCase (ci,p',c',lf')
+ in
+ (acc'',conclty',sigma, ans)
+
+ | _ ->
+ if occur_meta sigma (EConstr.of_constr trm) then
+ anomaly (Pp.str "refiner called with a meta in non app/case subterm.");
+ let (sigma, t'ty) = goal_type_of env sigma trm in
+ let sigma = check_conv_leq_goal env sigma trm t'ty conclty in
+ (goalacc,t'ty,sigma, trm)
+
+(* Same as mkREFGOALS but without knowing the type of the term. Therefore,
+ * Metas should be casted. *)
+
+and mk_hdgoals sigma goal goalacc trm =
+ let env = Goal.V82.env sigma goal in
+ let hyps = Goal.V82.hyps sigma goal in
+ let mk_goal hyps concl =
+ Goal.V82.mk_goal sigma hyps concl in
+ match kind trm with
+ | Cast (c,_, ty) when isMeta c ->
+ check_typability env sigma ty;
+ let (gl,ev,sigma) = mk_goal hyps (nf_betaiota env sigma (EConstr.of_constr ty)) in
+ let ev = EConstr.Unsafe.to_constr ev in
+ gl::goalacc,ty,sigma,ev
+
+ | Cast (t,_, ty) ->
+ check_typability env sigma ty;
+ mk_refgoals sigma goal goalacc ty t
+
+ | App (f,l) ->
+ let (acc',hdty,sigma,applicand) =
+ if Termops.is_template_polymorphic_ind env sigma (EConstr.of_constr f)
+ then
+ let l' = meta_free_prefix sigma l in
+ (goalacc,EConstr.Unsafe.to_constr (type_of_global_reference_knowing_parameters env sigma (EConstr.of_constr f) l'),sigma,f)
+ else mk_hdgoals sigma goal goalacc f
+ in
+ let ((acc'',conclty',sigma), args) = mk_arggoals sigma goal acc' hdty l in
+ let ans = if applicand == f && args == l then trm else mkApp (applicand, args) in
+ (acc'',conclty',sigma, ans)
+
+ | Case (ci,p,c,lf) ->
+ let (acc',lbrty,conclty',sigma,p',c') = mk_casegoals sigma goal goalacc p c in
+ let (acc'',sigma,rbranches) = treat_case sigma goal ci lbrty lf acc' in
+ let lf' = Array.rev_of_list rbranches in
+ let ans =
+ if p' == p && c' == c && Array.equal (==) lf' lf then trm
+ else mkCase (ci,p',c',lf')
+ in
+ (acc'',conclty',sigma, ans)
+
+ | Proj (p,c) ->
+ let (acc',cty,sigma,c') = mk_hdgoals sigma goal goalacc c in
+ let c = mkProj (p, c') in
+ let ty = get_type_of env sigma (EConstr.of_constr c) in
+ let ty = EConstr.Unsafe.to_constr ty in
+ (acc',ty,sigma,c)
+
+ | _ ->
+ if !check && occur_meta sigma (EConstr.of_constr trm) then
+ anomaly (Pp.str "refine called with a dependent meta.");
+ let (sigma, ty) = goal_type_of env sigma trm in
+ goalacc, ty, sigma, trm
+
+and mk_arggoals sigma goal goalacc funty allargs =
+ let foldmap (goalacc, funty, sigma) harg =
+ let t = whd_all (Goal.V82.env sigma goal) sigma (EConstr.of_constr funty) in
+ let t = EConstr.Unsafe.to_constr t in
+ let rec collapse t = match kind t with
+ | LetIn (_, c1, _, b) -> collapse (subst1 c1 b)
+ | _ -> t
+ in
+ let t = collapse t in
+ match kind t with
+ | Prod (_, c1, b) ->
+ let (acc, hargty, sigma, arg) = mk_refgoals sigma goal goalacc c1 harg in
+ (acc, subst1 harg b, sigma), arg
+ | _ ->
+ let env = Goal.V82.env sigma goal in
+ raise (RefinerError (env,sigma,CannotApply (t, harg)))
+ in
+ Array.Smart.fold_left_map foldmap (goalacc, funty, sigma) allargs
+
+and mk_casegoals sigma goal goalacc p c =
+ let env = Goal.V82.env sigma goal in
+ let (acc',ct,sigma,c') = mk_hdgoals sigma goal goalacc c in
+ let ct = EConstr.of_constr ct in
+ let (acc'',pt,sigma,p') = mk_hdgoals sigma goal acc' p in
+ let ((ind, u), spec) =
+ try Tacred.find_hnf_rectype env sigma ct
+ with Not_found -> anomaly (Pp.str "mk_casegoals.") in
+ let indspec = ((ind, EConstr.EInstance.kind sigma u), spec) in
+ let (lbrty,conclty) = type_case_branches_with_names env sigma indspec p c in
+ (acc'',lbrty,conclty,sigma,p',c')
+
+and treat_case sigma goal ci lbrty lf acc' =
+ let rec strip_outer_cast c = match kind c with
+ | Cast (c,_,_) -> strip_outer_cast c
+ | _ -> c in
+ let decompose_app_vect c = match kind c with
+ | App (f,cl) -> (f, cl)
+ | _ -> (c,[||]) in
+ let env = Goal.V82.env sigma goal in
+ Array.fold_left3
+ (fun (lacc,sigma,bacc) ty fi l ->
+ if isMeta (strip_outer_cast fi) then
+ (* Support for non-eta-let-expanded Meta as found in *)
+ (* destruct/case with an non eta-let expanded elimination scheme *)
+ let (r,_,s,fi') = mk_refgoals sigma goal lacc ty fi in
+ r,s,(fi'::bacc)
+ else
+ (* Deal with a branch in expanded form of the form
+ Case(ci,p,c,[|eta-let-exp(Meta);...;eta-let-exp(Meta)|]) as
+ if it were not so, so as to preserve compatibility with when
+ destruct/case generated schemes of the form
+ Case(ci,p,c,[|Meta;...;Meta|];
+ CAUTION: it does not deal with the general case of eta-zeta
+ reduced branches having a form different from Meta, as it
+ would be theoretically the case with third-party code *)
+ let n = List.length l in
+ let ctx, body = Term.decompose_lam_n_decls n fi in
+ let head, args = decompose_app_vect body in
+ (* Strip cast because clenv_cast_meta adds a cast when the branch is
+ eta-expanded but when not when the branch has the single-meta
+ form [Meta] *)
+ let head = strip_outer_cast head in
+ if isMeta head then begin
+ assert (args = Context.Rel.to_extended_vect mkRel 0 ctx);
+ let head' = lift (-n) head in
+ let (r,_,s,head'') = mk_refgoals sigma goal lacc ty head' in
+ let fi' = it_mkLambda_or_LetIn (mkApp (head'',args)) ctx in
+ (r,s,fi'::bacc)
+ end
+ else
+ (* Supposed to be meta-free *)
+ let sigma, t'ty = goal_type_of env sigma fi in
+ let sigma = check_conv_leq_goal env sigma fi t'ty ty in
+ (lacc,sigma,fi::bacc))
+ (acc',sigma,[]) lbrty lf ci.ci_pp_info.cstr_tags
+
+let convert_hyp check sign sigma d =
+ let id = NamedDecl.get_id d in
+ let b = NamedDecl.get_value d in
+ let env = Global.env () in
+ let reorder = ref [] in
+ let sign' =
+ apply_to_hyp env sigma check sign id
+ (fun _ d' _ ->
+ let c = Option.map EConstr.of_constr (NamedDecl.get_value d') in
+ let env = Global.env_of_context sign in
+ if check && not (is_conv env sigma (NamedDecl.get_type d) (EConstr.of_constr (NamedDecl.get_type d'))) then
+ user_err ~hdr:"Logic.convert_hyp"
+ (str "Incorrect change of the type of " ++ Id.print id ++ str ".");
+ if check && not (Option.equal (is_conv env sigma) b c) then
+ user_err ~hdr:"Logic.convert_hyp"
+ (str "Incorrect change of the body of "++ Id.print id ++ str ".");
+ if check then reorder := check_decl_position env sigma sign d;
+ map_named_decl EConstr.Unsafe.to_constr d) in
+ reorder_val_context env sigma sign' !reorder
+
+(************************************************************************)
+(************************************************************************)
+(* Primitive tactics are handled here *)
+
+let prim_refiner r sigma goal =
+ let env = Goal.V82.env sigma goal in
+ let cl = Goal.V82.concl sigma goal in
+ let cl = EConstr.Unsafe.to_constr cl in
+ check_meta_variables env sigma r;
+ let (sgl,cl',sigma,oterm) = mk_refgoals sigma goal [] cl r in
+ let sgl = List.rev sgl in
+ let sigma = Goal.V82.partial_solution env sigma goal (EConstr.of_constr oterm) in
+ (sgl, sigma)
+
+let prim_refiner ~check r sigma goal =
+ if check then
+ with_check (prim_refiner r sigma) goal
+ else
+ prim_refiner r sigma goal
diff --git a/proofs/logic.mli b/proofs/logic.mli
new file mode 100644
index 0000000000..f99076db23
--- /dev/null
+++ b/proofs/logic.mli
@@ -0,0 +1,73 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** Legacy proof engine. Do not use in newly written code. *)
+
+open Names
+open Constr
+open Evd
+
+(** [check] respectively means:\\
+ [Intro]: check that the name does not exist\\
+ [Intro_after]: check that the name does not exist and that variables in
+ its type does not escape their scope\\
+ [Intro_replacing]: check that the name does not exist and that
+ variables in its type does not escape their scope\\
+ [Convert_hyp]:
+ check that the name exist and that its type is convertible\\
+*)
+
+(** The primitive refiner. *)
+
+val prim_refiner : check:bool -> constr -> evar_map -> Goal.goal -> Goal.goal list * evar_map
+
+(** {6 Refiner errors. } *)
+
+type refiner_error =
+
+ (*i Errors raised by the refiner i*)
+ | BadType of constr * constr * constr
+ | UnresolvedBindings of Name.t list
+ | CannotApply of constr * constr
+ | NotWellTyped of constr
+ | NonLinearProof of constr
+ | MetaInType of EConstr.constr
+
+ (*i Errors raised by the tactics i*)
+ | IntroNeedsProduct
+ | DoesNotOccurIn of constr * Id.t
+ | NoSuchHyp of Id.t
+
+exception RefinerError of Environ.env * evar_map * refiner_error
+
+val error_no_such_hypothesis : Environ.env -> evar_map -> Id.t -> 'a
+
+val catchable_exception : exn -> bool
+
+(** Move destination for hypothesis *)
+
+type 'id move_location =
+ | MoveAfter of 'id
+ | MoveBefore of 'id
+ | MoveFirst
+ | MoveLast (** can be seen as "no move" when doing intro *)
+
+val pr_move_location :
+ ('a -> Pp.t) -> 'a move_location -> Pp.t
+
+val convert_hyp : bool -> Environ.named_context_val -> evar_map ->
+ EConstr.named_declaration -> Environ.named_context_val
+
+val move_hyp_in_named_context : Environ.env -> Evd.evar_map -> Id.t -> Id.t move_location ->
+ Environ.named_context_val -> Environ.named_context_val
+
+val insert_decl_in_named_context : Environ.env -> Evd.evar_map ->
+ EConstr.named_declaration -> Id.t move_location ->
+ Environ.named_context_val -> Environ.named_context_val
diff --git a/proofs/miscprint.ml b/proofs/miscprint.ml
new file mode 100644
index 0000000000..ec17b8076f
--- /dev/null
+++ b/proofs/miscprint.ml
@@ -0,0 +1,69 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Names
+open Tactypes
+
+(** Printing of [intro_pattern] *)
+
+let rec pr_intro_pattern prc {CAst.v=pat} = match pat with
+ | IntroForthcoming true -> str "*"
+ | IntroForthcoming false -> str "**"
+ | IntroNaming p -> pr_intro_pattern_naming p
+ | IntroAction p -> pr_intro_pattern_action prc p
+
+and pr_intro_pattern_naming = let open Namegen in function
+ | IntroIdentifier id -> Id.print id
+ | IntroFresh id -> str "?" ++ Id.print id
+ | IntroAnonymous -> str "?"
+
+and pr_intro_pattern_action prc = function
+ | IntroWildcard -> str "_"
+ | IntroOrAndPattern pll -> pr_or_and_intro_pattern prc pll
+ | IntroInjection pl ->
+ str "[=" ++ hv 0 (prlist_with_sep spc (pr_intro_pattern prc) pl) ++
+ str "]"
+ | IntroApplyOn ({CAst.v=c},pat) -> pr_intro_pattern prc pat ++ str "%" ++ prc c
+ | IntroRewrite true -> str "->"
+ | IntroRewrite false -> str "<-"
+
+and pr_or_and_intro_pattern prc = function
+ | IntroAndPattern pl ->
+ str "(" ++ hv 0 (prlist_with_sep pr_comma (pr_intro_pattern prc) pl) ++ str ")"
+ | IntroOrPattern pll ->
+ str "[" ++
+ hv 0 (prlist_with_sep pr_bar (prlist_with_sep spc (pr_intro_pattern prc)) pll)
+ ++ str "]"
+
+(** Printing of bindings *)
+let pr_binding prc = let open CAst in function
+ | {loc;v=(NamedHyp id, c)} -> hov 1 (Names.Id.print id ++ str " := " ++ cut () ++ prc c)
+ | {loc;v=(AnonHyp n, c)} -> hov 1 (int n ++ str " := " ++ cut () ++ prc c)
+
+let pr_bindings prc prlc = function
+ | ImplicitBindings l ->
+ brk (1,1) ++ str "with" ++ brk (1,1) ++
+ pr_sequence prc l
+ | ExplicitBindings l ->
+ brk (1,1) ++ str "with" ++ brk (1,1) ++
+ pr_sequence (fun b -> str"(" ++ pr_binding prlc b ++ str")") l
+ | NoBindings -> mt ()
+
+let pr_bindings_no_with prc prlc = function
+ | ImplicitBindings l ->
+ brk (0,1) ++ prlist_with_sep spc prc l
+ | ExplicitBindings l ->
+ brk (0,1) ++ prlist_with_sep spc (fun b -> str"(" ++ pr_binding prlc b ++ str")") l
+ | NoBindings -> mt ()
+
+let pr_with_bindings prc prlc (c,bl) =
+ hov 1 (prc c ++ pr_bindings prc prlc bl)
+
diff --git a/proofs/miscprint.mli b/proofs/miscprint.mli
new file mode 100644
index 0000000000..f4e2e683d1
--- /dev/null
+++ b/proofs/miscprint.mli
@@ -0,0 +1,36 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Tactypes
+
+(** Printing of [intro_pattern] *)
+
+val pr_intro_pattern :
+ ('a -> Pp.t) -> 'a intro_pattern_expr CAst.t -> Pp.t
+
+val pr_or_and_intro_pattern :
+ ('a -> Pp.t) -> 'a or_and_intro_pattern_expr -> Pp.t
+
+val pr_intro_pattern_naming : Namegen.intro_pattern_naming_expr -> Pp.t
+
+(** Printing of [move_location] *)
+
+val pr_bindings :
+ ('a -> Pp.t) ->
+ ('a -> Pp.t) -> 'a bindings -> Pp.t
+
+val pr_bindings_no_with :
+ ('a -> Pp.t) ->
+ ('a -> Pp.t) -> 'a bindings -> Pp.t
+
+val pr_with_bindings :
+ ('a -> Pp.t) ->
+ ('a -> Pp.t) -> 'a * 'a bindings -> Pp.t
+
diff --git a/proofs/pfedit.ml b/proofs/pfedit.ml
new file mode 100644
index 0000000000..7f1ae6d12b
--- /dev/null
+++ b/proofs/pfedit.ml
@@ -0,0 +1,220 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Util
+open Names
+open Entries
+open Environ
+open Evd
+
+let use_unification_heuristics_ref = ref true
+let () = Goptions.(declare_bool_option {
+ optdepr = false;
+ optname = "Solve unification constraints at every \".\"";
+ optkey = ["Solve";"Unification";"Constraints"];
+ optread = (fun () -> !use_unification_heuristics_ref);
+ optwrite = (fun a -> use_unification_heuristics_ref:=a);
+})
+
+let use_unification_heuristics () = !use_unification_heuristics_ref
+
+exception NoSuchGoal
+let () = CErrors.register_handler begin function
+ | NoSuchGoal -> CErrors.user_err Pp.(str "No such goal.")
+ | _ -> raise CErrors.Unhandled
+end
+
+let get_nth_V82_goal p i =
+ let Proof.{ sigma; goals } = Proof.data p in
+ try { it = List.nth goals (i-1) ; sigma }
+ with Failure _ -> raise NoSuchGoal
+
+let get_goal_context_gen p i =
+ let { it=goal ; sigma=sigma; } = get_nth_V82_goal p i in
+ (sigma, Refiner.pf_env { it=goal ; sigma=sigma; })
+
+let get_goal_context i =
+ try get_goal_context_gen (Proof_global.give_me_the_proof ()) i
+ with Proof_global.NoCurrentProof -> CErrors.user_err Pp.(str "No focused proof.")
+ | NoSuchGoal -> CErrors.user_err Pp.(str "No such goal.")
+
+let get_current_goal_context () =
+ try get_goal_context_gen (Proof_global.give_me_the_proof ()) 1
+ with Proof_global.NoCurrentProof -> CErrors.user_err Pp.(str "No focused proof.")
+ | NoSuchGoal ->
+ (* spiwack: returning empty evar_map, since if there is no goal, under focus,
+ there is no accessible evar either *)
+ let env = Global.env () in
+ (Evd.from_env env, env)
+
+let get_current_context ?p () =
+ let current_proof_by_default = function
+ | Some p -> p
+ | None -> Proof_global.give_me_the_proof ()
+ in
+ try get_goal_context_gen (current_proof_by_default p) 1
+ with Proof_global.NoCurrentProof ->
+ let env = Global.env () in
+ (Evd.from_env env, env)
+ | NoSuchGoal ->
+ (* No more focused goals ? *)
+ let p = (current_proof_by_default p) in
+ let evd = Proof.in_proof p (fun x -> x) in
+ (evd, Global.env ())
+
+let current_proof_statement () =
+ match Proof_global.V82.get_current_initial_conclusions () with
+ | (id,([concl],strength)) -> id,strength,concl
+ | _ -> CErrors.anomaly ~label:"Pfedit.current_proof_statement" (Pp.str "more than one statement.")
+
+let solve ?with_end_tac gi info_lvl tac pr =
+ try
+ 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 tac = let open Goal_select in match gi with
+ | SelectAlreadyFocused ->
+ let open Proofview.Notations in
+ Proofview.numgoals >>= fun n ->
+ if n == 1 then tac
+ else
+ let e = CErrors.UserError
+ (None,
+ Pp.(str "Expected a single focused goal but " ++
+ int n ++ str " goals are focused."))
+ in
+ Proofview.tclZERO e
+
+ | SelectNth i -> Proofview.tclFOCUS i i tac
+ | SelectList l -> Proofview.tclFOCUSLIST l tac
+ | SelectId id -> Proofview.tclFOCUSID id tac
+ | SelectAll -> tac
+ in
+ let tac =
+ if use_unification_heuristics () then
+ Proofview.tclTHEN tac Refine.solve_constraints
+ else tac
+ in
+ let env = Global.env () in
+ let (p,(status,info)) = Proof.run_tactic env tac pr in
+ let env = Global.env () in
+ let sigma = Evd.from_env env in
+ let () =
+ match info_lvl with
+ | None -> ()
+ | Some i -> Feedback.msg_info (hov 0 (Proofview.Trace.pr_info env sigma ~lvl:i info))
+ in
+ (p,status)
+ with
+ Proof_global.NoCurrentProof -> CErrors.user_err Pp.(str "No focused proof")
+
+let by tac = Proof_global.with_current_proof (fun _ -> solve (Goal_select.SelectNth 1) None tac)
+
+let instantiate_nth_evar_com n com =
+ Proof_global.simple_with_current_proof (fun _ p ->
+ Proof.V82.instantiate_evar Global.(env ())n com p)
+
+
+(**********************************************************************)
+(* Shortcut to build a term using tactics *)
+
+open Decl_kinds
+
+let next = let n = ref 0 in fun () -> incr n; !n
+
+let build_constant_by_tactic id ctx sign ?(goal_kind = Global, false, Proof Theorem) typ tac =
+ let evd = Evd.from_ctx ctx in
+ let terminator = Proof_global.make_terminator (fun _ -> ()) in
+ let goals = [ (Global.env_of_context sign , typ) ] in
+ Proof_global.start_proof evd id goal_kind goals terminator;
+ try
+ let status = by tac in
+ let open Proof_global in
+ let { entries; universes } = fst @@ close_proof ~opaque:Transparent ~keep_body_ucst_separate:false (fun x -> x) in
+ match entries with
+ | [entry] ->
+ discard_current ();
+ let univs = UState.demote_seff_univs entry universes in
+ entry, status, univs
+ | _ ->
+ CErrors.anomaly Pp.(str "[build_constant_by_tactic] close_proof returned more than one proof term")
+ with reraise ->
+ let reraise = CErrors.push reraise in
+ Proof_global.discard_current ();
+ iraise reraise
+
+let build_by_tactic ?(side_eff=true) env sigma ?(poly=false) typ tac =
+ let id = Id.of_string ("temporary_proof"^string_of_int (next())) in
+ let sign = val_of_named_context (named_context env) in
+ let gk = Global, poly, Proof Theorem in
+ let ce, status, univs =
+ build_constant_by_tactic id sigma sign ~goal_kind:gk typ tac in
+ let ce =
+ if side_eff then Safe_typing.inline_private_constants_in_definition_entry env ce
+ else { ce with
+ const_entry_body = Future.chain ce.const_entry_body
+ (fun (pt, _) -> pt, ()) } in
+ let (cb, ctx), () = Future.force ce.const_entry_body in
+ let univs = UState.merge ~sideff:side_eff ~extend:true Evd.univ_rigid univs ctx in
+ cb, status, univs
+
+let refine_by_tactic ~name ~poly env sigma ty tac =
+ (* Save the initial side-effects to restore them afterwards. We set the
+ current set of side-effects to be empty so that we can retrieve the
+ 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 prev_future_goals = save_future_goals sigma in
+ (* Start a proof *)
+ let prf = Proof.start ~name ~poly sigma [env, ty] in
+ let (prf, _) =
+ try Proof.run_tactic env tac prf
+ with Logic_monad.TacticFailure e as src ->
+ (* Catch the inner error of the monad tactic *)
+ let (_, info) = CErrors.push src in
+ iraise (e, info)
+ in
+ (* Plug back the retrieved sigma *)
+ let Proof.{ goals; stack; shelf; given_up; sigma; entry } = Proof.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 sigma = restore_future_goals sigma prev_future_goals in
+ (* Push remaining goals as future_goals which is the only way we
+ have to inform the caller that there are goals to collect while
+ not being encapsulated in the monad *)
+ (* Goals produced by tactic "shelve" *)
+ let sigma = List.fold_right (Evd.declare_future_goal ~tag:Evd.ToShelve) shelf sigma in
+ (* Goals produced by tactic "give_up" *)
+ let sigma = List.fold_right (Evd.declare_future_goal ~tag:Evd.ToGiveUp) given_up sigma in
+ (* Other goals *)
+ 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. Note that this is unsound, because the tactic may have solved
+ other goals that were already present during its invocation, so that
+ those goals rely on effects that are not present anymore. Hopefully,
+ this hack will work in most cases. *)
+ let ans = Safe_typing.inline_private_constants_in_constr env ans neff in
+ ans, sigma
diff --git a/proofs/pfedit.mli b/proofs/pfedit.mli
new file mode 100644
index 0000000000..5699320af5
--- /dev/null
+++ b/proofs/pfedit.mli
@@ -0,0 +1,95 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** Global proof state. A quite redundant wrapper on {!Proof_global}. *)
+
+open Names
+open Constr
+open Environ
+open Decl_kinds
+
+(** {6 ... } *)
+(** [get_goal_context n] returns the context of the [n]th subgoal of
+ the current focused proof or raises a [UserError] if there is no
+ focused proof or if there is no more subgoals *)
+
+val get_goal_context : int -> Evd.evar_map * env
+
+(** [get_current_goal_context ()] works as [get_goal_context 1] *)
+
+val get_current_goal_context : unit -> Evd.evar_map * env
+
+(** [get_current_context ()] returns the context of the
+ current focused goal. If there is no focused goal but there
+ is a proof in progress, it returns the corresponding evar_map.
+ If there is no pending proof then it returns the current global
+ environment and empty evar_map. *)
+
+val get_current_context : ?p:Proof.t -> unit -> Evd.evar_map * env
+
+(** [current_proof_statement] *)
+
+val current_proof_statement :
+ unit -> Id.t * goal_kind * EConstr.types
+
+(** {6 ... } *)
+
+(** [solve (SelectNth n) tac] applies tactic [tac] to the [n]th
+ subgoal of the current focused proof or raises a [UserError] if no
+ proof is focused or if there is no [n]th subgoal. [solve SelectAll
+ tac] applies [tac] to all subgoals. *)
+
+val solve : ?with_end_tac:unit Proofview.tactic ->
+ Goal_select.t -> int option -> unit Proofview.tactic ->
+ Proof.t -> Proof.t * bool
+
+(** [by tac] applies tactic [tac] to the 1st subgoal of the current
+ focused proof or raises a UserError if there is no focused proof or
+ if there is no more subgoals.
+ Returns [false] if an unsafe tactic has been used. *)
+
+val by : unit Proofview.tactic -> bool
+
+(** Option telling if unification heuristics should be used. *)
+val use_unification_heuristics : unit -> bool
+
+(** [instantiate_nth_evar_com n c] instantiate the [n]th undefined
+ existential variable of the current focused proof by [c] or raises a
+ UserError if no proof is focused or if there is no such [n]th
+ existential variable *)
+
+val instantiate_nth_evar_com : int -> Constrexpr.constr_expr -> unit
+
+(** [build_by_tactic typ tac] returns a term of type [typ] by calling
+ [tac]. The return boolean, if [false] indicates the use of an unsafe
+ tactic. *)
+
+val build_constant_by_tactic :
+ Id.t -> UState.t -> named_context_val -> ?goal_kind:goal_kind ->
+ EConstr.types -> unit Proofview.tactic ->
+ Safe_typing.private_constants Entries.definition_entry * bool *
+ UState.t
+
+val build_by_tactic : ?side_eff:bool -> env -> UState.t -> ?poly:polymorphic ->
+ EConstr.types -> unit Proofview.tactic ->
+ constr * bool * UState.t
+
+val refine_by_tactic
+ : name:Id.t
+ -> poly:bool
+ -> env -> Evd.evar_map
+ -> EConstr.types
+ -> unit Proofview.tactic
+ -> constr * Evd.evar_map
+(** A variant of the above function that handles open terms as well.
+ Caveat: all effects are purged in the returned term at the end, but other
+ evars solved by side-effects are NOT purged, so that unexpected failures may
+ occur. Ideally all code using this function should be rewritten in the
+ monad. *)
diff --git a/proofs/proof.ml b/proofs/proof.ml
new file mode 100644
index 0000000000..1aeb24606b
--- /dev/null
+++ b/proofs/proof.ml
@@ -0,0 +1,554 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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.
+ A proof deals with the focusing commands (including the braces and bullets),
+ the shelf (see the [shelve] tactic) and given up goal (see the [give_up]
+ tactic). A proof is made of the following:
+ - Proofview: a proof is primarily the data of the current view.
+ That which is shown to the user (as a remainder, a proofview
+ is mainly the logical state of the proof, together with the
+ currently focused goals).
+ - Focus: a proof has a focus stack: the top of the stack contains
+ the context in which to unfocus the current view to a view focused
+ with the rest of the stack.
+ In addition, this contains, for each of the focus context, a
+ "focus kind" and a "focus condition" (in practice, and for modularity,
+ the focus kind is actually stored inside the condition). To unfocus, one
+ needs to know the focus kind, and the condition (for instance "no condition" or
+ the proof under focused must be complete) must be met.
+ - Shelf: A list of goals which have been put aside during the proof. They can be
+ retrieved with the [Unshelve] command, or solved by side effects
+ - Given up goals: as long as there is a given up goal, the proof is not completed.
+ Given up goals cannot be retrieved, the user must go back where the tactic
+ [give_up] was run and solve the goal there.
+*)
+
+open Util
+
+type _focus_kind = int
+type 'a focus_kind = _focus_kind
+type focus_info = Obj.t
+type reason = NotThisWay | AlreadyNoFocus
+type unfocusable =
+ | Cannot of reason
+ | Loose
+ | Strict
+type _focus_condition =
+ | CondNo of bool * _focus_kind
+ | CondDone of bool * _focus_kind
+ | CondEndStack of _focus_kind (* loose_end is false here *)
+type 'a focus_condition = _focus_condition
+
+let next_kind = ref 0
+let new_focus_kind () =
+ let r = !next_kind in
+ incr next_kind;
+ r
+
+(* 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
+
+exception FullyUnfocused
+
+let _ = CErrors.register_handler begin function
+ | CannotUnfocusThisWay ->
+ CErrors.user_err Pp.(str "This proof is focused, but cannot be unfocused this way")
+ | NoSuchGoals (i,j) when Int.equal i j ->
+ CErrors.user_err ~hdr:"Focus" Pp.(str"No such goal (" ++ int i ++ str").")
+ | NoSuchGoals (i,j) ->
+ CErrors.user_err ~hdr:"Focus" Pp.(
+ str"Not every goal in range ["++ int i ++ str","++int j++str"] exist."
+ )
+ | NoSuchGoal id ->
+ CErrors.user_err
+ ~hdr:"Focus"
+ Pp.(str "No such goal: " ++ str (Names.Id.to_string id) ++ str ".")
+ | FullyUnfocused -> CErrors.user_err Pp.(str "The proof is not focused")
+ | _ -> raise CErrors.Unhandled
+end
+
+let check_cond_kind c k =
+ let kind_of_cond = function
+ | CondNo (_,k) | CondDone(_,k) | CondEndStack k -> k in
+ Int.equal (kind_of_cond c) k
+
+let test_cond c k1 pw =
+ match c with
+ | CondNo(_, k) when Int.equal k k1 -> Strict
+ | CondNo(true, _) -> Loose
+ | CondNo(false, _) -> Cannot NotThisWay
+ | CondDone(_, k) when Int.equal k k1 && Proofview.finished pw -> Strict
+ | CondDone(true, _) -> Loose
+ | CondDone(false, _) -> Cannot NotThisWay
+ | CondEndStack k when Int.equal 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)
+
+(* 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_condition*focus_info*Proofview.focus_context) list
+ (** History of the focusings, provides information on how to unfocus
+ the proof and the extra information stored while focusing. The
+ list is empty when the proof is fully unfocused. *)
+ ; shelf : Goal.goal list
+ (** List of goals that have been shelved. *)
+ ; given_up : Goal.goal list
+ (** List of goals that have been given up *)
+ ; initial_euctx : UState.t
+ (** The initial universe context (for the statement) *)
+ ; name : Names.Id.t
+ (** the name of the theorem whose proof is being constructed *)
+ ; poly : bool
+ (** Locality, polymorphism, and "kind" [Coercion, Definition, etc...] *)
+ }
+
+let initial_goals pf = Proofview.initial_goals pf.entry
+let initial_euctx pf = pf.initial_euctx
+
+(*** 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
+ let stack =
+ map_minus_one (fun (_,_,c) -> Proofview.focus_context c) p.focus_stack
+ in
+ let shelf = p.shelf in
+ let given_up = p.given_up in
+ (goals,stack,shelf,given_up,sigma)
+
+type 'a pre_goals = {
+ fg_goals : 'a list;
+ (** List of the focussed goals *)
+ bg_goals : ('a list * 'a list) list;
+ (** Zipper representing the unfocussed background goals *)
+ shelved_goals : 'a list;
+ (** List of the goals on the shelf. *)
+ given_up_goals : 'a list;
+ (** List of the goals that have been given up *)
+}
+
+let map_structured_proof pfts process_goal: 'a pre_goals =
+ let (goals, zipper, shelf, given_up, sigma) = proof pfts in
+ let fg = List.map (process_goal sigma) goals in
+ let map_zip (lg, rg) =
+ let lg = List.map (process_goal sigma) lg in
+ let rg = List.map (process_goal sigma) rg in
+ (lg, rg)
+ in
+ let bg = List.map map_zip zipper in
+ let shelf = List.map (process_goal sigma) shelf in
+ let given_up = List.map (process_goal sigma) given_up in
+ { fg_goals = fg;
+ bg_goals = bg;
+ shelved_goals = shelf;
+ given_up_goals = given_up; }
+
+let rec unroll_focus pv = function
+ | (_,_,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't hide any goal.
+ Unfocusing is handled in {!return}. *)
+let is_done p =
+ Proofview.finished p.proofview &&
+ Proofview.finished (unroll_focus p.proofview p.focus_stack)
+
+(* spiwack: for compatibility with <= 8.2 proof engine *)
+let has_unresolved_evar p =
+ Proofview.V82.has_unresolved_evar p.proofview
+let has_shelved_goals p = not (CList.is_empty (p.shelf))
+let has_given_up_goals p = not (CList.is_empty (p.given_up))
+
+let is_complete p =
+ is_done p && not (has_unresolved_evar p) &&
+ not (has_shelved_goals p) && not (has_given_up_goals p)
+
+(* 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 = (cond,inf,context)::pr.focus_stack }
+
+(* An auxiliary function to read the kind of the next focusing step *)
+let cond_of_focus pr =
+ match pr.focus_stack with
+ | (cond,_,_)::_ -> 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],
+ if the proof is already fully unfocused.
+ This function does not care about the condition of the current focus. *)
+let _unfocus pr =
+ let pr, (_,_,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 (Obj.repr 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
+ begin match try Some (Evd.evar_key id evar_map) with Not_found -> None with
+ | Some ev ->
+ begin match CList.safe_index Evar.equal ev focused_goals with
+ | Some i ->
+ (* goal is already under focus *)
+ _focus cond (Obj.repr inf) i i pr
+ | None ->
+ if CList.mem_f Evar.equal ev pr.shelf then
+ (* goal is on the shelf, put it in focus *)
+ let proofview = Proofview.unshelve [ev] pr.proofview in
+ let shelf =
+ CList.filter (fun ev' -> Evar.equal ev ev' |> not) pr.shelf
+ in
+ let pr = { pr with proofview; shelf } 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 (Obj.repr inf) i i pr
+ else
+ raise CannotUnfocusThisWay
+ end
+ | None ->
+ raise (NoSuchGoal id)
+ end
+
+let rec unfocus kind pr () =
+ let 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 ->
+ begin try
+ 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 kind stack =
+ match stack with
+ | (cond,inf,_)::stack ->
+ if check_cond_kind cond kind then inf
+ else get_in_focus_stack kind stack
+ | [] -> raise NoSuchFocus
+let get_at_focus kind pr =
+ Obj.magic (get_in_focus_stack kind pr.focus_stack)
+
+let is_last_focus kind pr =
+ let (cond,_,_) = List.hd pr.focus_stack in
+ 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 ()
+let end_of_stack = CondEndStack end_of_stack_kind
+
+let unfocused = is_last_focus end_of_stack_kind
+
+let start ~name ~poly sigma goals =
+ let entry, proofview = Proofview.init sigma goals in
+ let pr = {
+ proofview;
+ entry;
+ focus_stack = [] ;
+ shelf = [] ;
+ given_up = [];
+ initial_euctx =
+ Evd.evar_universe_context (snd (Proofview.proofview proofview))
+ ; name
+ ; poly
+ } in
+ _focus end_of_stack (Obj.repr ()) 1 (List.length goals) pr
+
+let dependent_start ~name ~poly goals =
+ let entry, proofview = Proofview.dependent_init goals in
+ let pr = {
+ proofview;
+ entry;
+ focus_stack = [] ;
+ shelf = [] ;
+ given_up = [];
+ initial_euctx =
+ Evd.evar_universe_context (snd (Proofview.proofview proofview))
+ ; name
+ ; poly
+ } in
+ let number_of_goals = List.length (Proofview.initial_goals pr.entry) in
+ _focus end_of_stack (Obj.repr ()) 1 number_of_goals pr
+
+type open_error_reason =
+ | UnfinishedProof
+ | HasShelvedGoals
+ | HasGivenUpGoals
+ | HasUnresolvedEvar
+
+let print_open_error_reason er = let open Pp in match er with
+ | UnfinishedProof ->
+ str "Attempt to save an incomplete proof"
+ | HasShelvedGoals ->
+ str "Attempt to save a proof with shelved goals"
+ | HasGivenUpGoals ->
+ strbrk "Attempt to save a proof with given up goals. If this is really what you want to do, use Admitted in place of Qed."
+ | HasUnresolvedEvar ->
+ strbrk "Attempt to save a proof with existential variables still non-instantiated"
+
+exception OpenProof of Names.Id.t option * open_error_reason
+
+let _ = CErrors.register_handler begin function
+ | OpenProof (pid, reason) ->
+ let open Pp in
+ Option.cata (fun pid ->
+ str " (in proof " ++ Names.Id.print pid ++ str "): ") (mt()) pid ++ print_open_error_reason reason
+ | _ -> raise CErrors.Unhandled
+ end
+
+let return ?pid (p : t) =
+ if not (is_done p) then
+ raise (OpenProof(pid, UnfinishedProof))
+ else if has_shelved_goals p then
+ raise (OpenProof(pid, HasShelvedGoals))
+ else if has_given_up_goals p then
+ raise (OpenProof(pid, HasGivenUpGoals))
+ else if has_unresolved_evar p then
+ (* spiwack: for compatibility with <= 8.3 proof engine *)
+ raise (OpenProof(pid, HasUnresolvedEvar))
+ else
+ let p = unfocus end_of_stack_kind p () in
+ Proofview.return p.proofview
+
+let compact p =
+ let entry, proofview = Proofview.compact p.entry p.proofview in
+ { p with proofview; entry }
+
+(*** Function manipulation proof extra informations ***)
+
+(*** Tactics ***)
+
+let run_tactic env tac pr =
+ let open Proofview.Notations in
+ let sp = pr.proofview in
+ let undef sigma l = List.filter (fun g -> Evd.is_undefined sigma g) l in
+ let tac =
+ tac >>= fun () ->
+ Proofview.tclEVARMAP >>= fun sigma ->
+ (* Already solved goals are not to be counted as shelved. Nor are
+ they to be marked as unresolvable. *)
+ let retrieved = Evd.filter_future_goals (Evd.is_undefined sigma) (Evd.save_future_goals sigma) in
+ let retrieved,retrieved_given_up = Evd.extract_given_up_future_goals retrieved in
+ (* Check that retrieved given up is empty *)
+ if not (List.is_empty retrieved_given_up) then
+ CErrors.anomaly Pp.(str "Evars generated outside of proof engine (e.g. V82, clear, ...) are not supposed to be explicitly given up.");
+ let sigma = Proofview.Unsafe.mark_as_goals sigma retrieved in
+ Proofview.Unsafe.tclEVARS sigma >>= fun () ->
+ Proofview.tclUNIT retrieved
+ in
+ let (retrieved,proofview,(status,to_shelve,give_up),info_trace) =
+ Proofview.apply env tac sp
+ in
+ let sigma = Proofview.return proofview in
+ let to_shelve = undef sigma to_shelve in
+ let shelf = (undef sigma pr.shelf)@retrieved@to_shelve in
+ let proofview =
+ List.fold_left
+ Proofview.Unsafe.mark_as_unresolvable
+ proofview
+ to_shelve
+ in
+ let given_up = pr.given_up@give_up in
+ let proofview = Proofview.Unsafe.reset_future_goals proofview in
+ { pr with proofview ; shelf ; given_up },(status,info_trace)
+
+(*** Commands ***)
+
+let in_proof p k = k (Proofview.return p.proofview)
+
+(* Remove all the goals from the shelf and adds them at the end of the
+ focused goals. *)
+let unshelve p =
+ { p with proofview = Proofview.unshelve (p.shelf) (p.proofview) ; shelf = [] }
+
+let pr_proof p =
+ let p = map_structured_proof p (fun _sigma g -> g) in
+ Pp.(
+ let pr_goal_list = prlist_with_sep spc Goal.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 p.fg_goals) p.bg_goals ++ str ";" ++ spc () ++
+ str "shelved: " ++ pr_goal_list p.shelved_goals ++ str ";" ++ spc () ++
+ str "given up: " ++ pr_goal_list p.given_up_goals ++
+ str "]"
+ )
+
+(*** Compatibility layer with <=v8.2 ***)
+module V82 = struct
+
+ let background_subgoals p =
+ let it, sigma = Proofview.proofview (unroll_focus p.proofview p.focus_stack) in
+ Evd.{ it; sigma }
+
+ let top_goal p =
+ let { Evd.it=gls ; sigma=sigma; } =
+ Proofview.V82.top_goals p.entry p.proofview
+ in
+ { Evd.it=List.hd gls ; sigma=sigma; }
+
+ let top_evars p =
+ Proofview.V82.top_evars p.entry
+
+ let grab_evars p =
+ if not (is_done p) then
+ raise (OpenProof(None, UnfinishedProof))
+ else
+ { p with proofview = Proofview.V82.grab p.proofview }
+
+ (* Main component of vernac command Existential *)
+ let instantiate_evar env n com pr =
+ let tac =
+ Proofview.tclBIND Proofview.tclEVARMAP begin fun sigma ->
+ let (evk, evi) =
+ let evl = Evarutil.non_instantiated sigma in
+ let evl = Evar.Map.bindings evl in
+ if (n <= 0) then
+ CErrors.user_err Pp.(str "incorrect existential variable index")
+ else if CList.length evl < n then
+ CErrors.user_err Pp.(str "not so many uninstantiated existential variables")
+ else
+ CList.nth evl (n-1)
+ in
+ let env = Evd.evar_filtered_env evi in
+ let rawc = Constrintern.intern_constr env sigma com in
+ let ltac_vars = Glob_ops.empty_lvar in
+ let sigma = Evar_refiner.w_refine (evk, evi) (ltac_vars, rawc) sigma in
+ Proofview.Unsafe.tclEVARS sigma
+ end in
+ let ((), proofview, _, _) = Proofview.apply env tac pr.proofview in
+ let shelf =
+ List.filter begin fun g ->
+ Evd.is_undefined (Proofview.return proofview) g
+ end pr.shelf
+ in
+ { pr with proofview ; shelf }
+
+end
+
+let all_goals p =
+ let add gs set =
+ List.fold_left (fun s g -> Goal.Set.add g s) set gs in
+ let (goals,stack,shelf,given_up,_) = proof p in
+ let set = add goals Goal.Set.empty in
+ let set = List.fold_left (fun s gs -> let (g1, g2) = gs in add g1 (add g2 set)) set stack in
+ let set = add shelf set in
+ let set = add given_up set in
+ let { Evd.it = bgoals ; sigma = bsigma } = V82.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 *)
+ ; shelf : Evar.t list
+ (** A representation of the shelf *)
+ ; given_up : Evar.t list
+ (** A representation of the given up goals *)
+ ; initial_euctx : UState.t
+ (** The initial universe context (for the statement) *)
+ ; 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; shelf; given_up; initial_euctx; 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
+ let stack =
+ map_minus_one (fun (_,_,c) -> Proofview.focus_context c) focus_stack in
+ { sigma; goals; entry; stack; shelf; given_up; initial_euctx; name; poly }
diff --git a/proofs/proof.mli b/proofs/proof.mli
new file mode 100644
index 0000000000..fd5e905a3b
--- /dev/null
+++ b/proofs/proof.mli
@@ -0,0 +1,260 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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.
+ A proof deals with the focusing commands (including the braces and bullets),
+ the shelf (see the [shelve] tactic) and given up goal (see the [give_up]
+ tactic). A proof is made of the following:
+ - Proofview: a proof is primarily the data of the current view.
+ That which is shown to the user (as a remainder, a proofview
+ is mainly the logical state of the proof, together with the
+ currently focused goals).
+ - Focus: a proof has a focus stack: the top of the stack contains
+ the context in which to unfocus the current view to a view focused
+ with the rest of the stack.
+ In addition, this contains, for each of the focus context, a
+ "focus kind" and a "focus condition" (in practice, and for modularity,
+ the focus kind is actually stored inside the condition). To unfocus, one
+ needs to know the focus kind, and the condition (for instance "no condition" or
+ the proof under focused must be complete) must be met.
+ - Shelf: A list of goals which have been put aside during the proof. They can be
+ retrieved with the [Unshelve] command, or solved by side effects
+ - Given up goals: as long as there is a given up goal, the proof is not completed.
+ Given up goals cannot be retrieved, the user must go back where the tactic
+ [give_up] was run and solve the goal there.
+*)
+
+(* Type of a proof. *)
+type t
+
+(* Returns a stylised view of a proof for use by, for instance,
+ ide-s. *)
+(* spiwack: the type of [proof] 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 focused goals, a representation of the
+ focus stack (the goals at each level), a representation of the
+ shelf (the list of goals on the shelf), a representation of the
+ given up goals (the list of the given up goals) and the underlying
+ evar_map *)
+val proof : t ->
+ Goal.goal list
+ * (Goal.goal list * Goal.goal list) list
+ * Goal.goal list
+ * Goal.goal list
+ * Evd.evar_map
+[@@ocaml.deprecated "use [Proof.data]"]
+
+val initial_goals : t -> (EConstr.constr * EConstr.types) list
+[@@ocaml.deprecated "use [Proof.data]"]
+
+val initial_euctx : t -> UState.t
+[@@ocaml.deprecated "use [Proof.data]"]
+
+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 *)
+ ; shelf : Evar.t list
+ (** A representation of the shelf *)
+ ; given_up : Evar.t list
+ (** A representation of the given up goals *)
+ ; initial_euctx : UState.t
+ (** The initial universe context (for the statement) *)
+ ; name : Names.Id.t
+ (** The name of the theorem whose proof is being constructed *)
+ ; poly : bool;
+ (** polymorphism *)
+ }
+
+val data : t -> data
+
+(* Generic records structured like the return type of proof *)
+type 'a pre_goals = {
+ fg_goals : 'a list;
+ [@ocaml.deprecated "use [Proof.data]"]
+ (** List of the focussed goals *)
+ bg_goals : ('a list * 'a list) list;
+ [@ocaml.deprecated "use [Proof.data]"]
+ (** Zipper representing the unfocussed background goals *)
+ shelved_goals : 'a list;
+ [@ocaml.deprecated "use [Proof.data]"]
+ (** List of the goals on the shelf. *)
+ given_up_goals : 'a list;
+ [@ocaml.deprecated "use [Proof.data]"]
+ (** List of the goals that have been given up *)
+}
+[@@ocaml.deprecated "use [Proof.data]"]
+
+(* needed in OCaml 4.05.0, not needed in newer ones *)
+[@@@ocaml.warning "-3"]
+val map_structured_proof : t -> (Evd.evar_map -> Goal.goal -> 'a) -> ('a pre_goals) [@ocaml.warning "-3"]
+[@@ocaml.deprecated "use [Proof.data]"]
+[@@@ocaml.warning "+3"]
+
+(*** General proof functions ***)
+val start
+ : name:Names.Id.t
+ -> poly:bool
+ -> Evd.evar_map -> (Environ.env * EConstr.types) list -> t
+
+val dependent_start
+ : name:Names.Id.t
+ -> poly:bool
+ -> Proofview.telescope -> t
+
+(* Returns [true] if the considered proof is completed, that is if no goal remain
+ to be considered (this does not require that all evars have been solved). *)
+val is_done : t -> bool
+
+(* Like is_done, but this time it really means done (i.e. nothing left to do) *)
+val is_complete : t -> bool
+
+(* Returns the list of partial proofs to initial goals. *)
+val partial_proof : t -> EConstr.constr list
+
+val compact : t -> t
+
+(* Returns the proofs (with their type) of the initial goals.
+ Raises [UnfinishedProof] is some goals remain to be considered.
+ Raises [HasShelvedGoals] if some goals are left on the shelf.
+ Raises [HasGivenUpGoals] if some goals have been given up.
+ Raises [HasUnresolvedEvar] if some evars have been left undefined. *)
+type open_error_reason =
+ | UnfinishedProof
+ | HasShelvedGoals
+ | HasGivenUpGoals
+ | HasUnresolvedEvar
+
+exception OpenProof of Names.Id.t option * open_error_reason
+
+val return : ?pid:Names.Id.t -> t -> Evd.evar_map
+
+(*** Focusing actions ***)
+
+(* ['a focus_kind] is the type used by focusing and unfocusing
+ commands to synchronise. Focusing and unfocusing commands use
+ a particular ['a focus_kind], and if they don't match, the unfocusing command
+ will fail.
+ When focusing with an ['a focus_kind], an information of type ['a] is
+ stored at the focusing point. An example use is the "induction" tactic
+ of the declarative mode where sub-tactics must be aware of the current
+ induction argument. *)
+type 'a focus_kind
+val new_focus_kind : unit -> 'a focus_kind
+
+(* To be authorized to unfocus one must meet the condition prescribed by
+ the action which focused.
+ Conditions always carry a focus kind, and inherit their type parameter
+ from it.*)
+type 'a focus_condition
+(* [no_cond] only checks that the unfocusing command uses the right
+ [focus_kind].
+ If [loose_end] (default [false]) is [true], then if the [focus_kind]
+ doesn't match, then unfocusing can occur, provided it unfocuses
+ an earlier focus.
+ For instance bullets can be unfocused in the following situation
+ [{- solve_goal. }] because they use a loose-end condition. *)
+val no_cond : ?loose_end:bool -> 'a focus_kind -> 'a focus_condition
+(* [done_cond] checks that the unfocusing command uses the right [focus_kind]
+ and that the focused proofview is complete.
+ If [loose_end] (default [false]) is [true], then if the [focus_kind]
+ doesn't match, then unfocusing can occur, provided it unfocuses
+ an earlier focus.
+ For instance bullets can be unfocused in the following situation
+ [{ - solve_goal. }] because they use a loose-end condition. *)
+val done_cond : ?loose_end:bool -> 'a focus_kind -> 'a focus_condition
+
+(* 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? *)
+val focus : 'a focus_condition -> 'a -> int -> t -> t
+
+(* focus on goal named id *)
+val focus_id : 'aa focus_condition -> 'a -> Names.Id.t -> t -> t
+
+exception FullyUnfocused
+exception CannotUnfocusThisWay
+
+(* This is raised when trying to focus on non-existing subgoals. It is
+ handled by an error message but one may need to catch it and
+ settle a better error message in some case (suggesting a better
+ bullet for example), see proof_global.ml function Bullet.pop and
+ Bullet.push. *)
+exception NoSuchGoals of int * int
+
+(* Unfocusing command.
+ Raises [FullyUnfocused] if the proof is not focused.
+ Raises [CannotUnfocusThisWay] if the proof the unfocusing condition
+ is not met. *)
+val unfocus : 'a focus_kind -> t -> unit -> t
+
+(* [unfocused p] returns [true] when [p] is fully unfocused. *)
+val unfocused : t -> bool
+
+(* [get_at_focus k] gets the information stored at the closest focus point
+ of kind [k].
+ Raises [NoSuchFocus] if there is no focus point of kind [k]. *)
+exception NoSuchFocus
+val get_at_focus : 'a focus_kind -> t -> 'a
+
+(* [is_last_focus k] check if the most recent focus is of kind [k] *)
+val is_last_focus : 'a focus_kind -> t -> bool
+
+(* returns [true] if there is no goal under focus. *)
+val no_focused_goal : t -> bool
+
+(*** Tactics ***)
+
+(* the returned boolean signal whether an unsafe tactic has been
+ used. In which case it is [false]. *)
+val run_tactic
+ : Environ.env
+ -> unit Proofview.tactic -> t -> t * (bool*Proofview_monad.Info.tree)
+
+val maximal_unfocus : 'a focus_kind -> t -> t
+
+(*** Commands ***)
+
+val in_proof : t -> (Evd.evar_map -> 'a) -> 'a
+
+(* Remove all the goals from the shelf and adds them at the end of the
+ focused goals. *)
+val unshelve : t -> t
+
+val pr_proof : t -> Pp.t
+
+(*** Compatibility layer with <=v8.2 ***)
+module V82 : sig
+
+ (* All the subgoals of the proof, including those which are not focused. *)
+ val background_subgoals : t -> Goal.goal list Evd.sigma
+
+ val top_goal : t -> Goal.goal Evd.sigma
+
+ (* returns the existential variable used to start the proof *)
+ val top_evars : t -> Evar.t list
+
+ (* Turns the unresolved evars into goals.
+ Raises [UnfinishedProof] if there are still unsolved goals. *)
+ val grab_evars : t -> t
+
+ (* Implements the Existential command *)
+ val instantiate_evar :
+ Environ.env -> int -> Constrexpr.constr_expr -> t -> t
+end
+
+(* returns the set of all goals in the proof *)
+val all_goals : t -> Goal.Set.t
diff --git a/proofs/proof_bullet.ml b/proofs/proof_bullet.ml
new file mode 100644
index 0000000000..2ca4f0afb4
--- /dev/null
+++ b/proofs/proof_bullet.ml
@@ -0,0 +1,199 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Proof
+
+type t =
+ | Dash of int
+ | Star of int
+ | Plus of int
+
+let bullet_eq b1 b2 = match b1, b2 with
+| Dash n1, Dash n2 -> n1 = n2
+| Star n1, Star n2 -> n1 = n2
+| Plus n1, Plus n2 -> n1 = n2
+| _ -> false
+
+let pr_bullet b =
+ match b with
+ | Dash n -> Pp.(str (String.make n '-'))
+ | Star n -> Pp.(str (String.make n '*'))
+ | Plus n -> Pp.(str (String.make n '+'))
+
+
+type behavior = {
+ name : string;
+ put : Proof.t -> t -> Proof.t;
+ suggest: Proof.t -> Pp.t
+}
+
+let behaviors = Hashtbl.create 4
+let register_behavior b = Hashtbl.add behaviors b.name b
+
+(*** initial modes ***)
+let none = {
+ name = "None";
+ put = (fun x _ -> x);
+ suggest = (fun _ -> Pp.mt ())
+}
+let _ = register_behavior none
+
+module Strict = struct
+ type suggestion =
+ | Suggest of t (* this bullet is mandatory here *)
+ | Unfinished of t (* no mandatory bullet here, but this bullet is unfinished *)
+ | NoBulletInUse (* No mandatory bullet (or brace) here, no bullet pending,
+ some focused goals exists. *)
+ | NeedClosingBrace (* Some unfocussed goal exists "{" needed to focus them *)
+ | ProofFinished (* No more goal anywhere *)
+
+ (* give a message only if more informative than the standard coq message *)
+ let suggest_on_solved_goal sugg =
+ match sugg with
+ | NeedClosingBrace -> Pp.(str"Try unfocusing with \"}\".")
+ | NoBulletInUse -> Pp.mt ()
+ | ProofFinished -> Pp.mt ()
+ | Suggest b -> Pp.(str"Focus next goal with bullet " ++ pr_bullet b ++ str".")
+ | Unfinished b -> Pp.(str"The current bullet " ++ pr_bullet b ++ str" is unfinished.")
+
+ (* give always a message. *)
+ let suggest_on_error sugg =
+ match sugg with
+ | NeedClosingBrace -> Pp.(str"Try unfocusing with \"}\".")
+ | NoBulletInUse -> assert false (* This should never raise an error. *)
+ | ProofFinished -> Pp.(str"No more subgoals.")
+ | Suggest b -> Pp.(str"Expecting " ++ pr_bullet b ++ str".")
+ | Unfinished b -> Pp.(str"Current bullet " ++ pr_bullet b ++ str" is not finished.")
+
+ exception FailedBullet of t * suggestion
+
+ let _ =
+ CErrors.register_handler
+ (function
+ | FailedBullet (b,sugg) ->
+ let prefix = Pp.(str"Wrong bullet " ++ pr_bullet b ++ str": ") in
+ CErrors.user_err ~hdr:"Focus" Pp.(prefix ++ suggest_on_error sugg)
+ | _ -> raise CErrors.Unhandled)
+
+
+ (* spiwack: we need only one focus kind as we keep a stack of (distinct!) bullets *)
+ let bullet_kind = (new_focus_kind () : t list focus_kind)
+ let bullet_cond = done_cond ~loose_end:true bullet_kind
+
+ (* spiwack: as it is bullets are reset (locally) by *any* non-bullet focusing command
+ experience will tell if this is the right discipline of if we want to be finer and
+ reset them only for a choice of bullets. *)
+ let get_bullets pr =
+ if is_last_focus bullet_kind pr then
+ get_at_focus bullet_kind pr
+ else
+ []
+
+ let has_bullet bul pr =
+ let rec has_bullet = function
+ | b'::_ when bullet_eq bul b' -> true
+ | _::l -> has_bullet l
+ | [] -> false
+ in
+ has_bullet (get_bullets pr)
+
+ (* pop a bullet from proof [pr]. There should be at least one
+ bullet in use. If pop impossible (pending proofs on this level
+ of bullet or higher) then raise [Proof.CannotUnfocusThisWay]. *)
+ let pop pr =
+ match get_bullets pr with
+ | b::_ -> unfocus bullet_kind pr () , b
+ | _ -> assert false
+
+ let push (b:t) pr =
+ focus bullet_cond (b::get_bullets pr) 1 pr
+
+ let suggest_bullet (prf : Proof.t): suggestion =
+ if is_done prf then ProofFinished
+ else if not (no_focused_goal prf)
+ then (* No suggestion if a bullet is not mandatory, look for an unfinished bullet *)
+ match get_bullets prf with
+ | b::_ -> Unfinished b
+ | _ -> NoBulletInUse
+ else (* There is no goal under focus but some are unfocussed,
+ let us look at the bullet needed. *)
+ let rec loop prf =
+ match pop prf with
+ | prf, b ->
+ (* pop went well, this means that there are no more goals
+ *under this* bullet b, see if a new b can be pushed. *)
+ begin
+ try ignore (push b prf); Suggest b
+ with _ ->
+ (* b could not be pushed, so we must look for a outer bullet *)
+ loop prf
+ end
+ | exception _ ->
+ (* No pop was possible, but there are still
+ subgoals somewhere: there must be a "}" to use. *)
+ NeedClosingBrace
+ in
+ loop prf
+
+ let rec pop_until (prf : Proof.t) bul : Proof.t =
+ let prf', b = pop prf in
+ if bullet_eq bul b then prf'
+ else pop_until prf' bul
+
+ let put p bul =
+ try
+ if not (has_bullet bul p) then
+ (* bullet is not in use, so pushing it is always ok unless
+ no goal under focus. *)
+ push bul p
+ else
+ match suggest_bullet p with
+ | Suggest suggested_bullet when bullet_eq bul suggested_bullet
+ -> (* suggested_bullet is mandatory and you gave the right one *)
+ let p' = pop_until p bul in
+ push bul p'
+ (* the bullet you gave is in use but not the right one *)
+ | sugg -> raise (FailedBullet (bul,sugg))
+ with NoSuchGoals _ -> (* push went bad *)
+ raise (FailedBullet (bul,suggest_bullet p))
+
+ let strict = {
+ name = "Strict Subproofs";
+ put = put;
+ suggest = (fun prf -> suggest_on_solved_goal (suggest_bullet prf))
+
+ }
+ let _ = register_behavior strict
+end
+
+(* Current bullet behavior, controlled by the option *)
+let current_behavior = ref Strict.strict
+
+let () =
+ Goptions.(declare_string_option {
+ optdepr = false;
+ optname = "bullet behavior";
+ optkey = ["Bullet";"Behavior"];
+ optread = begin fun () ->
+ (!current_behavior).name
+ end;
+ optwrite = begin fun n ->
+ current_behavior :=
+ try Hashtbl.find behaviors n
+ with Not_found ->
+ CErrors.user_err Pp.(str ("Unknown bullet behavior: \"" ^ n ^ "\"."))
+ end
+ })
+
+let put p b =
+ (!current_behavior).put p b
+
+let suggest p =
+ (!current_behavior).suggest p
diff --git a/proofs/proof_bullet.mli b/proofs/proof_bullet.mli
new file mode 100644
index 0000000000..0fcc647a6f
--- /dev/null
+++ b/proofs/proof_bullet.mli
@@ -0,0 +1,46 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(**********************************************************)
+(* *)
+(* Bullets *)
+(* *)
+(**********************************************************)
+
+type t =
+ | Dash of int
+ | Star of int
+ | Plus of int
+
+(** A [behavior] is the data of a put function which
+ is called when a bullet prefixes a tactic, a suggest function
+ suggesting the right bullet to use on a given proof, together
+ with a name to identify the behavior. *)
+type behavior = {
+ name : string;
+ put : Proof.t -> t -> Proof.t;
+ suggest: Proof.t -> Pp.t
+}
+
+(** A registered behavior can then be accessed in Coq
+ through the command [Set Bullet Behavior "name"].
+
+ Two modes are registered originally:
+ * "Strict Subproofs":
+ - If this bullet follows another one of its kind, defocuses then focuses
+ (which fails if the focused subproof is not complete).
+ - If it is the first bullet of its kind, then focuses a new subproof.
+ * "None": bullets don't do anything *)
+val register_behavior : behavior -> unit
+
+(** Handles focusing/defocusing with bullets:
+ *)
+val put : Proof.t -> t -> Proof.t
+val suggest : Proof.t -> Pp.t
diff --git a/proofs/proof_global.ml b/proofs/proof_global.ml
new file mode 100644
index 0000000000..f8adc58921
--- /dev/null
+++ b/proofs/proof_global.ml
@@ -0,0 +1,498 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(* * (see LICENSE file for the text of the license) *)
+(************************************************************************)
+
+(***********************************************************************)
+(* *)
+(* This module defines proof facilities relevant to the *)
+(* toplevel. In particular it defines the global proof *)
+(* environment. *)
+(* *)
+(***********************************************************************)
+
+open Util
+open Pp
+open Names
+
+module NamedDecl = Context.Named.Declaration
+
+(*** Proof Modes ***)
+
+(* Type of proof modes :
+ - A function [set] to set it *from standard mode*
+ - A function [reset] to reset the *standard mode* from it *)
+type proof_mode_name = string
+type proof_mode = {
+ name : proof_mode_name ;
+ set : unit -> unit ;
+ reset : unit -> unit
+}
+
+let proof_modes = Hashtbl.create 6
+let find_proof_mode n =
+ try Hashtbl.find proof_modes n
+ with Not_found ->
+ CErrors.user_err Pp.(str (Format.sprintf "No proof mode named \"%s\"." n))
+
+let register_proof_mode ({name = n} as m) =
+ Hashtbl.add proof_modes n (CEphemeron.create m)
+
+(* initial mode: standard mode *)
+let standard = { name = "No" ; set = (fun ()->()) ; reset = (fun () -> ()) }
+let _ = register_proof_mode standard
+
+(* Default proof mode, to be set at the beginning of proofs. *)
+let default_proof_mode = ref (find_proof_mode "No")
+
+let get_default_proof_mode_name () =
+ (CEphemeron.default !default_proof_mode standard).name
+
+let proof_mode_opt_name = ["Default";"Proof";"Mode"]
+let () =
+ Goptions.(declare_string_option {
+ optdepr = false;
+ optname = "default proof mode" ;
+ optkey = proof_mode_opt_name ;
+ optread = begin fun () ->
+ (CEphemeron.default !default_proof_mode standard).name
+ end;
+ optwrite = begin fun n ->
+ default_proof_mode := find_proof_mode n
+ end
+ })
+
+(*** Proof Global Environment ***)
+
+(* Extra info on proofs. *)
+type lemma_possible_guards = int list list
+
+type proof_object = {
+ id : Names.Id.t;
+ entries : Safe_typing.private_constants Entries.definition_entry list;
+ persistence : Decl_kinds.goal_kind;
+ universes: UState.t;
+}
+
+type opacity_flag = Opaque | Transparent
+
+type proof_ending =
+ | Admitted of Names.Id.t * Decl_kinds.goal_kind * Entries.parameter_entry * UState.t
+ | Proved of opacity_flag *
+ lident option *
+ proof_object
+
+type proof_terminator = proof_ending -> unit
+type closed_proof = proof_object * proof_terminator
+
+type pstate = {
+ terminator : proof_terminator CEphemeron.key;
+ endline_tactic : Genarg.glob_generic_argument option;
+ section_vars : Constr.named_context option;
+ proof : Proof.t;
+ mode : proof_mode CEphemeron.key;
+ universe_decl: UState.universe_decl;
+ strength : Decl_kinds.goal_kind;
+}
+
+type t = pstate list
+
+let make_terminator f = f
+let apply_terminator f = f
+
+(* The head of [!pstates] is the actual current proof, the other ones are
+ to be resumed when the current proof is closed or aborted. *)
+let pstates = ref ([] : pstate list)
+
+(* Current proof_mode, for bookkeeping *)
+let current_proof_mode = ref !default_proof_mode
+
+(* combinators for proof modes *)
+let update_proof_mode () =
+ match !pstates with
+ | { mode = m } :: _ ->
+ CEphemeron.iter_opt !current_proof_mode (fun x -> x.reset ());
+ current_proof_mode := m;
+ CEphemeron.iter_opt !current_proof_mode (fun x -> x.set ())
+ | _ ->
+ CEphemeron.iter_opt !current_proof_mode (fun x -> x.reset ());
+ current_proof_mode := find_proof_mode "No"
+
+(* combinators for the current_proof lists *)
+let push a l = l := a::!l;
+ update_proof_mode ()
+
+exception NoSuchProof
+let () = CErrors.register_handler begin function
+ | NoSuchProof -> CErrors.user_err Pp.(str "No such proof.")
+ | _ -> raise CErrors.Unhandled
+end
+
+exception NoCurrentProof
+let () = CErrors.register_handler begin function
+ | NoCurrentProof -> CErrors.user_err Pp.(str "No focused proof (No proof-editing in progress).")
+ | _ -> raise CErrors.Unhandled
+end
+
+(*** Proof Global manipulation ***)
+
+let get_all_proof_names () =
+ List.map Proof.(function pf -> (data pf.proof).name) !pstates
+
+let cur_pstate () =
+ match !pstates with
+ | np::_ -> np
+ | [] -> raise NoCurrentProof
+
+let give_me_the_proof () = (cur_pstate ()).proof
+let give_me_the_proof_opt () = try Some (give_me_the_proof ()) with | NoCurrentProof -> None
+let get_current_proof_name () = (Proof.data (cur_pstate ()).proof).Proof.name
+
+let with_current_proof f =
+ match !pstates with
+ | [] -> raise NoCurrentProof
+ | p :: rest ->
+ let et =
+ match p.endline_tactic with
+ | None -> Proofview.tclUNIT ()
+ | Some tac ->
+ let open Geninterp in
+ let ist = { lfun = Id.Map.empty; extra = TacStore.empty } in
+ let Genarg.GenArg (Genarg.Glbwit tag, tac) = tac in
+ let tac = Geninterp.interp tag ist tac in
+ Ftactic.run tac (fun _ -> Proofview.tclUNIT ())
+ in
+ let (newpr,ret) = f et p.proof in
+ let p = { p with proof = newpr } in
+ pstates := p :: rest;
+ ret
+
+let simple_with_current_proof f = with_current_proof (fun t p -> f t p , ())
+
+let compact_the_proof () = simple_with_current_proof (fun _ -> Proof.compact)
+
+(* Sets the tactic to be used when a tactic line is closed with [...] *)
+let set_endline_tactic tac =
+ match !pstates with
+ | [] -> raise NoCurrentProof
+ | p :: rest -> pstates := { p with endline_tactic = Some tac } :: rest
+
+(* spiwack: it might be considered to move error messages away.
+ Or else to remove special exceptions from Proof_global.
+ Arguments for the former: there is no reason Proof_global is only
+ accessed directly through vernacular commands. Error message should be
+ pushed to external layers, and so we should be able to have a finer
+ control on error message on complex actions. *)
+let msg_proofs () =
+ match get_all_proof_names () with
+ | [] -> (spc () ++ str"(No proof-editing in progress).")
+ | l -> (str"." ++ fnl () ++ str"Proofs currently edited:" ++ spc () ++
+ (pr_sequence Id.print l) ++ str".")
+
+let there_is_a_proof () = not (List.is_empty !pstates)
+let there_are_pending_proofs () = there_is_a_proof ()
+let check_no_pending_proof () =
+ if not (there_are_pending_proofs ()) then
+ ()
+ else begin
+ CErrors.user_err
+ (str"Proof editing in progress" ++ msg_proofs () ++ fnl() ++
+ str"Use \"Abort All\" first or complete proof(s).")
+ end
+
+let pf_name_eq id ps =
+ let Proof.{ name } = Proof.data ps.proof in
+ Id.equal name id
+
+let discard_gen id =
+ pstates := List.filter (fun pf -> not (pf_name_eq id pf)) !pstates
+
+let discard {CAst.loc;v=id} =
+ let n = List.length !pstates in
+ discard_gen id;
+ if Int.equal (List.length !pstates) n then
+ CErrors.user_err ?loc
+ ~hdr:"Pfedit.delete_proof" (str"No such proof" ++ msg_proofs ())
+
+let discard_current () =
+ if List.is_empty !pstates then raise NoCurrentProof else pstates := List.tl !pstates
+
+let discard_all () = pstates := []
+
+(* [set_proof_mode] sets the proof mode to be used after it's called. It is
+ typically called by the Proof Mode command. *)
+let set_proof_mode m id =
+ pstates := List.map
+ (fun ps -> if pf_name_eq id ps then { ps with mode = m } else ps)
+ !pstates;
+ update_proof_mode ()
+
+let set_proof_mode mn =
+ set_proof_mode (find_proof_mode mn) (get_current_proof_name ())
+
+let activate_proof_mode mode =
+ CEphemeron.iter_opt (find_proof_mode mode) (fun x -> x.set ())
+let disactivate_current_proof_mode () =
+ CEphemeron.iter_opt !current_proof_mode (fun x -> x.reset ())
+
+(** [start_proof sigma id pl str goals terminator] starts a proof of name
+ [id] with goals [goals] (a list of pairs of environment and
+ conclusion); [str] describes what kind of theorem/definition this
+ is (spiwack: for potential printing, I believe is used only by
+ closing commands and the xml plugin); [terminator] is used at the
+ end of the proof to close the proof. The proof is started in the
+ evar map [sigma] (which can typically contain universe
+ constraints), and with universe bindings pl. *)
+let start_proof sigma name ?(pl=UState.default_univ_decl) kind goals terminator =
+ let initial_state = {
+ terminator = CEphemeron.create terminator;
+ proof = Proof.start ~name ~poly:(pi2 kind) sigma goals;
+ endline_tactic = None;
+ section_vars = None;
+ mode = find_proof_mode "No";
+ universe_decl = pl;
+ strength = kind } in
+ push initial_state pstates
+
+let start_dependent_proof name ?(pl=UState.default_univ_decl) kind goals terminator =
+ let initial_state = {
+ terminator = CEphemeron.create terminator;
+ proof = Proof.dependent_start ~name ~poly:(pi2 kind) goals;
+ endline_tactic = None;
+ section_vars = None;
+ mode = find_proof_mode "No";
+ universe_decl = pl;
+ strength = kind } in
+ push initial_state pstates
+
+let get_used_variables () = (cur_pstate ()).section_vars
+let get_universe_decl () = (cur_pstate ()).universe_decl
+
+let set_used_variables l =
+ let open Context.Named.Declaration in
+ let env = Global.env () in
+ let ids = List.fold_right Id.Set.add l Id.Set.empty in
+ let ctx = Environ.keep_hyps env ids in
+ let ctx_set =
+ List.fold_right Id.Set.add (List.map NamedDecl.get_id ctx) Id.Set.empty in
+ let vars_of = Environ.global_vars_set in
+ let aux env entry (ctx, all_safe as orig) =
+ match entry with
+ | LocalAssum (x,_) ->
+ if Id.Set.mem x all_safe then orig
+ else (ctx, all_safe)
+ | LocalDef (x,bo, ty) as decl ->
+ if Id.Set.mem x all_safe then orig else
+ let vars = Id.Set.union (vars_of env bo) (vars_of env ty) in
+ if Id.Set.subset vars all_safe
+ then (decl :: ctx, Id.Set.add x all_safe)
+ else (ctx, all_safe) in
+ let ctx, _ =
+ Environ.fold_named_context aux env ~init:(ctx,ctx_set) in
+ match !pstates with
+ | [] -> raise NoCurrentProof
+ | p :: rest ->
+ if not (Option.is_empty p.section_vars) then
+ CErrors.user_err Pp.(str "Used section variables can be declared only once");
+ pstates := { p with section_vars = Some ctx} :: rest;
+ ctx, []
+
+let get_open_goals () =
+ let Proof.{ goals; stack; shelf } = Proof.data (cur_pstate ()).proof in
+ List.length goals +
+ List.fold_left (+) 0
+ (List.map (fun (l1,l2) -> List.length l1 + List.length l2) stack) +
+ List.length shelf
+
+type closed_proof_output = (Constr.t * Safe_typing.private_constants) list * UState.t
+
+let private_poly_univs =
+ let b = ref true in
+ let _ = Goptions.(declare_bool_option {
+ optdepr = false;
+ optname = "use private polymorphic universes for Qed constants";
+ optkey = ["Private";"Polymorphic";"Universes"];
+ optread = (fun () -> !b);
+ optwrite = ((:=) b);
+ })
+ in
+ fun () -> !b
+
+let close_proof ~opaque ~keep_body_ucst_separate ?feedback_id ~now
+ (fpl : closed_proof_output Future.computation) =
+ let { section_vars; proof; terminator; universe_decl; strength } = cur_pstate () in
+ let Proof.{ name; poly; entry; initial_euctx } = Proof.data proof in
+ let opaque = match opaque with Opaque -> true | Transparent -> false in
+ let constrain_variables ctx =
+ UState.constrain_variables (fst (UState.context_set initial_euctx)) ctx
+ in
+ let fpl, univs = Future.split2 fpl in
+ let universes = if poly || now then Future.force univs else initial_euctx in
+ (* Because of dependent subgoals at the beginning of proofs, we could
+ have existential variables in the initial types of goals, we need to
+ normalise them for the kernel. *)
+ let subst_evar k =
+ Proof.in_proof proof (fun m -> Evd.existential_opt_value0 m k) in
+ let nf = UnivSubst.nf_evars_and_universes_opt_subst subst_evar
+ (UState.subst universes) in
+
+ let make_body =
+ if poly || now then
+ let make_body t (c, eff) =
+ let body = c in
+ let allow_deferred =
+ not poly && (keep_body_ucst_separate ||
+ not (Safe_typing.empty_private_constants = eff))
+ in
+ let typ = if allow_deferred then t else nf t in
+ let used_univs_body = Vars.universes_of_constr body in
+ let used_univs_typ = Vars.universes_of_constr typ in
+ if allow_deferred then
+ let initunivs = UState.const_univ_entry ~poly initial_euctx in
+ let ctx = constrain_variables universes in
+ (* For vi2vo compilation proofs are computed now but we need to
+ complement the univ constraints of the typ with the ones of
+ the body. So we keep the two sets distinct. *)
+ let used_univs = Univ.LSet.union used_univs_body used_univs_typ in
+ let ctx_body = UState.restrict ctx used_univs in
+ let univs = UState.check_mono_univ_decl ctx_body universe_decl in
+ (initunivs, typ), ((body, univs), eff)
+ else if poly && opaque && private_poly_univs () then
+ let used_univs = Univ.LSet.union used_univs_body used_univs_typ in
+ let universes = UState.restrict universes used_univs in
+ let typus = UState.restrict universes used_univs_typ in
+ let udecl = UState.check_univ_decl ~poly typus universe_decl in
+ let ubody = Univ.ContextSet.diff
+ (UState.context_set universes)
+ (UState.context_set typus)
+ in
+ (udecl, typ), ((body, ubody), eff)
+ else
+ (* Since the proof is computed now, we can simply have 1 set of
+ constraints in which we merge the ones for the body and the ones
+ for the typ. We recheck the declaration after restricting with
+ the actually used universes.
+ TODO: check if restrict is really necessary now. *)
+ let used_univs = Univ.LSet.union used_univs_body used_univs_typ in
+ let ctx = UState.restrict universes used_univs in
+ let univs = UState.check_univ_decl ~poly ctx universe_decl in
+ (univs, typ), ((body, Univ.ContextSet.empty), eff)
+ in
+ fun t p -> Future.split2 (Future.chain p (make_body t))
+ else
+ fun t p ->
+ (* Already checked the univ_decl for the type universes when starting the proof. *)
+ let univctx = Entries.Monomorphic_const_entry (UState.context_set universes) in
+ let t = nf t in
+ Future.from_val (univctx, t),
+ Future.chain p (fun (pt,eff) ->
+ (* Deferred proof, we already checked the universe declaration with
+ the initial universes, ensure that the final universes respect
+ the declaration as well. If the declaration is non-extensible,
+ this will prevent the body from adding universes and constraints. *)
+ let univs = Future.force univs in
+ let univs = constrain_variables univs in
+ let used_univs = Univ.LSet.union
+ (Vars.universes_of_constr t)
+ (Vars.universes_of_constr pt)
+ in
+ let univs = UState.restrict univs used_univs in
+ let univs = UState.check_mono_univ_decl univs universe_decl in
+ (pt,univs),eff)
+ in
+ let entry_fn p (_, t) =
+ let t = EConstr.Unsafe.to_constr t in
+ let univstyp, body = make_body t p in
+ let univs, typ = Future.force univstyp in
+ {Entries.
+ const_entry_body = body;
+ const_entry_secctx = section_vars;
+ const_entry_feedback = feedback_id;
+ const_entry_type = Some typ;
+ const_entry_inline_code = false;
+ const_entry_opaque = opaque;
+ const_entry_universes = univs; }
+ in
+ let entries = Future.map2 entry_fn fpl Proofview.(initial_goals entry) in
+ { id = name; entries = entries; persistence = strength;
+ universes },
+ fun pr_ending -> CEphemeron.get terminator pr_ending
+
+let return_proof ?(allow_partial=false) () =
+ let { proof } = cur_pstate () in
+ if allow_partial then begin
+ let proofs = Proof.partial_proof proof in
+ let Proof.{sigma=evd} = Proof.data proof in
+ let eff = Evd.eval_side_effects evd in
+ (* ppedrot: FIXME, this is surely wrong. There is no reason to duplicate
+ side-effects... This may explain why one need to uniquize side-effects
+ thereafter... *)
+ let proofs = List.map (fun c -> EConstr.Unsafe.to_constr c, eff) proofs in
+ proofs, Evd.evar_universe_context evd
+ end else
+ let Proof.{name=pid;entry} = Proof.data proof in
+ let initial_goals = Proofview.initial_goals entry in
+ let evd = Proof.return ~pid proof in
+ let eff = Evd.eval_side_effects evd in
+ let evd = Evd.minimize_universes evd in
+ (* ppedrot: FIXME, this is surely wrong. There is no reason to duplicate
+ side-effects... This may explain why one need to uniquize side-effects
+ thereafter... *)
+ let proofs =
+ List.map (fun (c, _) -> (EConstr.to_constr evd c, eff)) initial_goals in
+ proofs, Evd.evar_universe_context evd
+
+let close_future_proof ~opaque ~feedback_id proof =
+ close_proof ~opaque ~keep_body_ucst_separate:true ~feedback_id ~now:false proof
+let close_proof ~opaque ~keep_body_ucst_separate fix_exn =
+ close_proof ~opaque ~keep_body_ucst_separate ~now:true
+ (Future.from_val ~fix_exn (return_proof ()))
+
+(** Gets the current terminator without checking that the proof has
+ been completed. Useful for the likes of [Admitted]. *)
+let get_terminator () = CEphemeron.get ( cur_pstate() ).terminator
+let set_terminator hook =
+ match !pstates with
+ | [] -> raise NoCurrentProof
+ | p :: ps -> pstates := { p with terminator = CEphemeron.create hook } :: ps
+
+module V82 = struct
+ let get_current_initial_conclusions () =
+ let { proof; strength } = cur_pstate () in
+ let Proof.{ name; entry } = Proof.data proof in
+ let initial = Proofview.initial_goals entry in
+ let goals = List.map (fun (o, c) -> c) initial in
+ name, (goals, strength)
+end
+
+let freeze ~marshallable =
+ if marshallable then CErrors.anomaly (Pp.str"full marshalling of proof state not supported.")
+ else !pstates
+let unfreeze s = pstates := s; update_proof_mode ()
+let proof_of_state = function { proof }::_ -> proof | _ -> raise NoCurrentProof
+let copy_terminators ~src ~tgt =
+ assert(List.length src = List.length tgt);
+ List.map2 (fun op p -> { p with terminator = op.terminator }) src tgt
+
+let update_global_env pf_info =
+ with_current_proof (fun _ p ->
+ Proof.in_proof p (fun sigma ->
+ let tac = Proofview.Unsafe.tclEVARS (Evd.update_sigma_env sigma (Global.env ())) in
+ let (p,(status,info)) = Proof.run_tactic (Global.env ()) tac p in
+ (p, ())))
+
+(* XXX: Bullet hook, should be really moved elsewhere *)
+let () =
+ let hook n =
+ try
+ let prf = give_me_the_proof () in
+ (Proof_bullet.suggest prf)
+ with NoCurrentProof -> mt ()
+ in
+ Proofview.set_nosuchgoals_hook hook
+
diff --git a/proofs/proof_global.mli b/proofs/proof_global.mli
new file mode 100644
index 0000000000..e762f3b7dc
--- /dev/null
+++ b/proofs/proof_global.mli
@@ -0,0 +1,185 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(* * (see LICENSE file for the text of the license) *)
+(************************************************************************)
+
+(** This module defines proof facilities relevant to the
+ toplevel. In particular it defines the global proof
+ environment. *)
+
+type t
+
+val there_are_pending_proofs : unit -> bool
+val check_no_pending_proof : unit -> unit
+
+val get_current_proof_name : unit -> Names.Id.t
+val get_all_proof_names : unit -> Names.Id.t list
+
+val discard : Names.lident -> unit
+val discard_current : unit -> unit
+val discard_all : unit -> unit
+
+val give_me_the_proof_opt : unit -> Proof.t option
+exception NoCurrentProof
+val give_me_the_proof : unit -> Proof.t
+(** @raise NoCurrentProof when outside proof mode. *)
+
+val compact_the_proof : unit -> unit
+
+(** When a proof is closed, it is reified into a [proof_object], where
+ [id] is the name of the proof, [entries] the list of the proof terms
+ (in a form suitable for definitions). Together with the [terminator]
+ function which takes a [proof_object] together with a [proof_end]
+ (i.e. an proof ending command) and registers the appropriate
+ values. *)
+type lemma_possible_guards = int list list
+
+type proof_object = {
+ id : Names.Id.t;
+ entries : Safe_typing.private_constants Entries.definition_entry list;
+ persistence : Decl_kinds.goal_kind;
+ universes: UState.t;
+}
+
+type opacity_flag = Opaque | Transparent
+
+type proof_ending =
+ | Admitted of Names.Id.t * Decl_kinds.goal_kind * Entries.parameter_entry *
+ UState.t
+ | Proved of opacity_flag *
+ Names.lident option *
+ proof_object
+type proof_terminator
+type closed_proof = proof_object * proof_terminator
+
+val make_terminator : (proof_ending -> unit) -> proof_terminator
+val apply_terminator : proof_terminator -> proof_ending -> unit
+
+(** [start_proof id str pl goals terminator] starts a proof of name
+ [id] with goals [goals] (a list of pairs of environment and
+ conclusion); [str] describes what kind of theorem/definition this
+ is; [terminator] is used at the end of the proof to close the proof
+ (e.g. to declare the built constructions as a coercion or a setoid
+ morphism). The proof is started in the evar map [sigma] (which can
+ typically contain universe constraints), and with universe bindings
+ pl. *)
+val start_proof :
+ Evd.evar_map -> Names.Id.t -> ?pl:UState.universe_decl ->
+ Decl_kinds.goal_kind -> (Environ.env * EConstr.types) list ->
+ proof_terminator -> unit
+
+(** Like [start_proof] except that there may be dependencies between
+ initial goals. *)
+val start_dependent_proof :
+ Names.Id.t -> ?pl:UState.universe_decl -> Decl_kinds.goal_kind ->
+ Proofview.telescope -> proof_terminator -> unit
+
+(** Update the proofs global environment after a side-effecting command
+ (e.g. a sublemma definition) has been run inside it. Assumes
+ there_are_pending_proofs. *)
+val update_global_env : unit -> unit
+
+(* Takes a function to add to the exceptions data relative to the
+ state in which the proof was built *)
+val close_proof : opaque:opacity_flag -> keep_body_ucst_separate:bool -> Future.fix_exn -> closed_proof
+
+(* Intermediate step necessary to delegate the future.
+ * Both access the current proof state. The former is supposed to be
+ * chained with a computation that completed the proof *)
+
+type closed_proof_output = (Constr.t * Safe_typing.private_constants) list * UState.t
+
+(* If allow_partial is set (default no) then an incomplete proof
+ * is allowed (no error), and a warn is given if the proof is complete. *)
+val return_proof : ?allow_partial:bool -> unit -> closed_proof_output
+val close_future_proof : opaque:opacity_flag -> feedback_id:Stateid.t ->
+ closed_proof_output Future.computation -> closed_proof
+
+(** Gets the current terminator without checking that the proof has
+ been completed. Useful for the likes of [Admitted]. *)
+val get_terminator : unit -> proof_terminator
+val set_terminator : proof_terminator -> unit
+
+exception NoSuchProof
+
+val get_open_goals : unit -> int
+
+(** Runs a tactic on the current proof. Raises [NoCurrentProof] is there is
+ no current proof.
+ The return boolean is set to [false] if an unsafe tactic has been used. *)
+val with_current_proof :
+ (unit Proofview.tactic -> Proof.t -> Proof.t * 'a) -> 'a
+val simple_with_current_proof :
+ (unit Proofview.tactic -> Proof.t -> Proof.t) -> unit
+
+(** Sets the tactic to be used when a tactic line is closed with [...] *)
+val set_endline_tactic : Genarg.glob_generic_argument -> unit
+
+(** Sets the section variables assumed by the proof, returns its closure
+ * (w.r.t. type dependencies and let-ins covered by it) + a list of
+ * ids to be cleared *)
+val set_used_variables :
+ Names.Id.t list -> Constr.named_context * Names.lident list
+val get_used_variables : unit -> Constr.named_context option
+
+(** Get the universe declaration associated to the current proof. *)
+val get_universe_decl : unit -> UState.universe_decl
+
+module V82 : sig
+ val get_current_initial_conclusions : unit -> Names.Id.t *(EConstr.types list *
+ Decl_kinds.goal_kind)
+end
+
+val freeze : marshallable:bool -> t
+val unfreeze : t -> unit
+val proof_of_state : t -> Proof.t
+val copy_terminators : src:t -> tgt:t -> t
+
+
+(**********************************************************)
+(* Proof Mode API *)
+(* The current Proof Mode API is deprecated and a new one *)
+(* will be (hopefully) defined in 8.8 *)
+(**********************************************************)
+
+(** Type of proof modes :
+ - A name
+ - A function [set] to set it *from standard mode*
+ - A function [reset] to reset the *standard mode* from it
+
+*)
+type proof_mode_name = string
+type proof_mode = {
+ name : proof_mode_name ;
+ set : unit -> unit ;
+ reset : unit -> unit
+}
+
+(** Registers a new proof mode which can then be adressed by name
+ in [set_default_proof_mode].
+ One mode is already registered - the standard mode - named "No",
+ It corresponds to Coq default setting are they are set when coqtop starts. *)
+val register_proof_mode : proof_mode -> unit
+(* Can't make this deprecated due to limitations of camlp5 *)
+(* [@@ocaml.deprecated "the current proof mode API is deprecated, use with care, see PR #459 and #566 "] *)
+
+val proof_mode_opt_name : string list
+
+val get_default_proof_mode_name : unit -> proof_mode_name
+[@@ocaml.deprecated "the current proof mode API is deprecated, use with care, see PR #459 and #566 "]
+
+(** [set_proof_mode] sets the proof mode to be used after it's called. It is
+ typically called by the Proof Mode command. *)
+val set_proof_mode : proof_mode_name -> unit
+[@@ocaml.deprecated "the current proof mode API is deprecated, use with care, see PR #459 and #566 "]
+
+val activate_proof_mode : proof_mode_name -> unit
+[@@ocaml.deprecated "the current proof mode API is deprecated, use with care, see PR #459 and #566 "]
+
+val disactivate_current_proof_mode : unit -> unit
+[@@ocaml.deprecated "the current proof mode API is deprecated, use with care, see PR #459 and #566 "]
diff --git a/proofs/proofs.mllib b/proofs/proofs.mllib
new file mode 100644
index 0000000000..0ce726db25
--- /dev/null
+++ b/proofs/proofs.mllib
@@ -0,0 +1,14 @@
+Miscprint
+Goal
+Evar_refiner
+Refine
+Proof
+Logic
+Goal_select
+Proof_bullet
+Proof_global
+Refiner
+Tacmach
+Pfedit
+Clenv
+Clenvtac
diff --git a/proofs/refine.ml b/proofs/refine.ml
new file mode 100644
index 0000000000..1d796fece5
--- /dev/null
+++ b/proofs/refine.ml
@@ -0,0 +1,169 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Util
+open Proofview.Notations
+open Context.Named.Declaration
+
+module NamedDecl = Context.Named.Declaration
+
+let extract_prefix env info =
+ let ctx1 = List.rev (EConstr.named_context env) in
+ let ctx2 = List.rev (Evd.evar_context info) in
+ let rec share l1 l2 accu = match l1, l2 with
+ | d1 :: l1, d2 :: l2 ->
+ if d1 == d2 then share l1 l2 (d1 :: accu)
+ else (accu, d2 :: l2)
+ | _ -> (accu, l2)
+ in
+ share ctx1 ctx2 []
+
+let typecheck_evar ev env sigma =
+ let info = Evd.find sigma ev in
+ (* Typecheck the hypotheses. *)
+ let type_hyp (sigma, env) decl =
+ let t = NamedDecl.get_type decl in
+ let sigma, _ = Typing.sort_of env sigma t in
+ let sigma = match decl with
+ | LocalAssum _ -> sigma
+ | LocalDef (_,body,_) -> Typing.check env sigma body t
+ in
+ (sigma, EConstr.push_named decl env)
+ in
+ let (common, changed) = extract_prefix env info in
+ let env = Environ.reset_with_named_context (EConstr.val_of_named_context common) env in
+ let (sigma, env) = List.fold_left type_hyp (sigma, env) changed in
+ (* Typecheck the conclusion *)
+ let sigma, _ = Typing.sort_of env sigma (Evd.evar_concl info) in
+ sigma
+
+(* Get the side-effect's constant declarations to update the monad's
+ * environmnent *)
+let add_if_undefined env eff =
+ let open Entries in
+ try ignore(Environ.lookup_constant eff.seff_constant env); env
+ with Not_found -> Environ.add_constant eff.seff_constant eff.seff_body env
+
+(* Add the side effects to the monad's environment, if not already done. *)
+let add_side_effects env eff =
+ List.fold_left add_if_undefined env eff
+
+let generic_refine ~typecheck f gl =
+ let sigma = Proofview.Goal.sigma gl in
+ let env = Proofview.Goal.env gl in
+ let concl = Proofview.Goal.concl gl in
+ let state = Proofview.Goal.state gl in
+ (* Save the [future_goals] state to restore them after the
+ refinement. *)
+ let prev_future_goals = Evd.save_future_goals sigma in
+ (* Create the refinement term *)
+ Proofview.Unsafe.tclEVARS (Evd.reset_future_goals sigma) >>= fun () ->
+ f >>= fun (v, c) ->
+ Proofview.tclEVARMAP >>= fun sigma ->
+ Proofview.V82.wrap_exceptions begin fun () ->
+ let evs = Evd.save_future_goals sigma in
+ (* Redo the effects in sigma in the monad's env *)
+ let privates_csts = Evd.eval_side_effects sigma in
+ let sideff = Safe_typing.side_effects_of_private_constants privates_csts in
+ let env = add_side_effects env sideff in
+ (* Check that the introduced evars are well-typed *)
+ let fold accu ev = typecheck_evar ev env accu in
+ let sigma = if typecheck then Evd.fold_future_goals fold sigma evs else sigma in
+ (* Check that the refined term is typesafe *)
+ let sigma = if typecheck then Typing.check env sigma c concl else sigma in
+ (* Check that the goal itself does not appear in the refined term *)
+ let self = Proofview.Goal.goal gl in
+ let _ =
+ if not (Evarutil.occur_evar_upto sigma self c) then ()
+ else Pretype_errors.error_occur_check env sigma self c
+ in
+ (* Restore the [future goals] state. *)
+ let sigma = Evd.restore_future_goals sigma prev_future_goals in
+ (* Select the goals *)
+ let evs = Evd.map_filter_future_goals (Proofview.Unsafe.advance sigma) evs in
+ let comb,shelf,given_up,evkmain = Evd.dispatch_future_goals evs in
+ (* Proceed to the refinement *)
+ let sigma = match Proofview.Unsafe.advance sigma self with
+ | None ->
+ (* Nothing to do, the goal has been solved by side-effect *)
+ sigma
+ | Some self ->
+ match evkmain with
+ | None -> Evd.define self c sigma
+ | Some evk ->
+ let id = Evd.evar_ident self sigma in
+ let sigma = Evd.define self c sigma in
+ match id with
+ | None -> sigma
+ | Some id -> Evd.rename evk id sigma
+ in
+ (* Mark goals *)
+ let sigma = Proofview.Unsafe.mark_as_goals sigma comb in
+ let comb = CList.map (fun x -> Proofview.goal_with_state x state) comb in
+ let trace env sigma = Pp.(hov 2 (str"simple refine"++spc()++
+ Termops.Internal.print_constr_env env sigma c)) in
+ Proofview.Trace.name_tactic trace (Proofview.tclUNIT v) >>= fun v ->
+ Proofview.Unsafe.tclSETENV (Environ.reset_context env) <*>
+ Proofview.Unsafe.tclEVARS sigma <*>
+ Proofview.Unsafe.tclSETGOALS comb <*>
+ Proofview.Unsafe.tclPUTSHELF shelf <*>
+ Proofview.Unsafe.tclPUTGIVENUP given_up <*>
+ Proofview.tclUNIT v
+ end
+
+let lift c =
+ Proofview.tclEVARMAP >>= fun sigma ->
+ Proofview.V82.wrap_exceptions begin fun () ->
+ let (sigma, c) = c sigma in
+ Proofview.Unsafe.tclEVARS sigma >>= fun () ->
+ Proofview.tclUNIT c
+ end
+
+let make_refine_enter ~typecheck f gl = generic_refine ~typecheck (lift f) gl
+
+let refine_one ~typecheck f =
+ Proofview.Goal.enter_one (make_refine_enter ~typecheck f)
+
+let refine ~typecheck f =
+ let f evd =
+ let (evd,c) = f evd in (evd,((), c))
+ in
+ Proofview.Goal.enter (make_refine_enter ~typecheck f)
+
+(** Useful definitions *)
+
+let with_type env evd c t =
+ let my_type = Retyping.get_type_of env evd c in
+ let j = Environ.make_judge c my_type in
+ let (evd,j') =
+ Coercion.inh_conv_coerce_to true env evd j t
+ in
+ evd , j'.Environ.uj_val
+
+let refine_casted ~typecheck f = Proofview.Goal.enter begin fun gl ->
+ let concl = Proofview.Goal.concl gl in
+ let env = Proofview.Goal.env gl in
+ let f h =
+ let (h, c) = f h in
+ with_type env h c concl
+ in
+ refine ~typecheck f
+end
+
+(** {7 solve_constraints}
+
+ Ensure no remaining unification problems are left. Run at every "." by default. *)
+
+let solve_constraints =
+ let open Proofview in
+ tclENV >>= fun env -> tclEVARMAP >>= fun sigma ->
+ try let sigma = Evarconv.solve_unif_constraints_with_heuristics env sigma in
+ Unsafe.tclEVARSADVANCE sigma
+ with e -> tclZERO e
diff --git a/proofs/refine.mli b/proofs/refine.mli
new file mode 100644
index 0000000000..1af6463a02
--- /dev/null
+++ b/proofs/refine.mli
@@ -0,0 +1,51 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** The primitive refine tactic used to fill the holes in partial proofs. This
+ is the recommanded way to write tactics when the proof term is easy to
+ write down. Note that this is not the user-level refine tactic defined
+ in Ltac which is actually based on the one below. *)
+
+open Proofview
+
+(** {6 The refine tactic} *)
+
+(** {7 Refinement primitives} *)
+
+val refine : typecheck:bool -> (Evd.evar_map -> Evd.evar_map * EConstr.t) -> unit tactic
+(** In [refine ~typecheck 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. Exceptions
+ raised during the interpretation of [t] are caught and result in
+ tactic failures. If [typecheck] is [true] [t] is type-checked beforehand. *)
+
+val refine_one : typecheck:bool -> (Evd.evar_map -> Evd.evar_map * ('a * EConstr.t)) -> 'a tactic
+(** A variant of [refine] which assumes exactly one goal under focus *)
+
+val generic_refine : typecheck:bool -> ('a * EConstr.t) tactic ->
+ Proofview.Goal.t -> 'a tactic
+(** The general version of refine. *)
+
+(** {7 Helper functions} *)
+
+val with_type : Environ.env -> Evd.evar_map ->
+ EConstr.constr -> EConstr.types -> Evd.evar_map * EConstr.constr
+(** [with_type env sigma c t] ensures that [c] is of type [t]
+ inserting a coercion if needed. *)
+
+val refine_casted : typecheck:bool -> (Evd.evar_map -> Evd.evar_map * EConstr.t) -> unit tactic
+(** Like {!refine} except the refined term is coerced to the conclusion of the
+ current goal. *)
+
+(** {7 Unification constraint handling} *)
+
+val solve_constraints : unit tactic
+(** Solve any remaining unification problems, applying heuristics. *)
diff --git a/proofs/refiner.ml b/proofs/refiner.ml
new file mode 100644
index 0000000000..bce227dabb
--- /dev/null
+++ b/proofs/refiner.ml
@@ -0,0 +1,332 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Evd
+open Logic
+
+type tactic = Proofview.V82.tac
+
+module NamedDecl = Context.Named.Declaration
+
+let sig_it x = x.it
+let project x = x.sigma
+
+(* Getting env *)
+
+let pf_env gls = Global.env_of_context (Goal.V82.hyps (project gls) (sig_it gls))
+let pf_hyps gls = EConstr.named_context_of_val (Goal.V82.hyps (project gls) (sig_it gls))
+
+let refiner ~check pr goal_sigma =
+ let (sgl,sigma') = prim_refiner ~check pr goal_sigma.sigma goal_sigma.it in
+ { it = sgl; sigma = sigma'; }
+
+(* Profiling refiner *)
+let refiner ~check =
+ if Flags.profile then
+ let refiner_key = CProfile.declare_profile "refiner" in
+ CProfile.profile2 refiner_key (refiner ~check)
+ else refiner ~check
+
+(*********************)
+(* Tacticals *)
+(*********************)
+
+
+let unpackage glsig = (ref (glsig.sigma)), glsig.it
+
+let repackage r v = {it = v; sigma = !r; }
+
+let apply_sig_tac r tac g =
+ Control.check_for_interrupt (); (* Breakpoint *)
+ let glsigma = tac (repackage r g) in
+ r := glsigma.sigma;
+ glsigma.it
+
+(* [goal_goal_list : goal sigma -> goal list sigma] *)
+let goal_goal_list gls = {it=[gls.it]; sigma=gls.sigma; }
+
+(* identity tactic without any message *)
+let tclIDTAC gls = goal_goal_list gls
+
+(* the message printing identity tactic *)
+let tclIDTAC_MESSAGE s gls =
+ Feedback.msg_info (hov 0 s); tclIDTAC gls
+
+(* General failure tactic *)
+let tclFAIL_s s gls = user_err ~hdr:"Refiner.tclFAIL_s" (str s)
+
+(* A special exception for levels for the Fail tactic *)
+exception FailError of int * Pp.t Lazy.t
+
+(* The Fail tactic *)
+let tclFAIL lvl s g = raise (FailError (lvl,lazy s))
+
+let tclFAIL_lazy lvl s g = raise (FailError (lvl,s))
+
+let start_tac gls =
+ let sigr, g = unpackage gls in
+ (sigr, [g])
+
+let finish_tac (sigr,gl) = repackage sigr gl
+
+(* Apply [tacfi.(i)] on the first n subgoals, [tacli.(i)] on the last
+ m subgoals, and [tac] on the others *)
+let thens3parts_tac tacfi tac tacli (sigr,gs) =
+ let nf = Array.length tacfi in
+ let nl = Array.length tacli in
+ let ng = List.length gs in
+ if ng<nf+nl then user_err ~hdr:"Refiner.thensn_tac" (str "Not enough subgoals.");
+ let gll =
+ (List.map_i (fun i ->
+ apply_sig_tac sigr (if i<nf then tacfi.(i) else if i>=ng-nl then tacli.(nl-ng+i) else tac))
+ 0 gs) in
+ (sigr,List.flatten gll)
+
+(* Apply [taci.(i)] on the first n subgoals and [tac] on the others *)
+let thensf_tac taci tac = thens3parts_tac taci tac [||]
+
+(* Apply [tac i] on the ith subgoal (no subgoals number check) *)
+let thensi_tac tac (sigr,gs) =
+ let gll =
+ List.map_i (fun i -> apply_sig_tac sigr (tac i)) 1 gs in
+ (sigr, List.flatten gll)
+
+let then_tac tac = thensf_tac [||] tac
+
+(* [tclTHENS3PARTS tac1 [|t1 ; ... ; tn|] tac2 [|t'1 ; ... ; t'm|] gls]
+ applies the tactic [tac1] to [gls] then, applies [t1], ..., [tn] to
+ the first [n] resulting subgoals, [t'1], ..., [t'm] to the last [m]
+ subgoals and [tac2] to the rest of the subgoals in the middle. Raises an
+ error if the number of resulting subgoals is strictly less than [n+m] *)
+let tclTHENS3PARTS tac1 tacfi tac tacli gls =
+ finish_tac (thens3parts_tac tacfi tac tacli (then_tac tac1 (start_tac gls)))
+
+(* [tclTHENSFIRSTn tac1 [|t1 ; ... ; tn|] tac2 gls] applies the tactic [tac1]
+ to [gls] and applies [t1], ..., [tn] to the first [n] resulting
+ subgoals, and [tac2] to the others subgoals. Raises an error if
+ the number of resulting subgoals is strictly less than [n] *)
+let tclTHENSFIRSTn tac1 taci tac = tclTHENS3PARTS tac1 taci tac [||]
+
+(* [tclTHENSLASTn tac1 tac2 [|t1 ;...; tn|] gls] applies the tactic [tac1]
+ to [gls] and applies [t1], ..., [tn] to the last [n] resulting
+ subgoals, and [tac2] to the other subgoals. Raises an error if the
+ number of resulting subgoals is strictly less than [n] *)
+let tclTHENSLASTn tac1 tac taci = tclTHENS3PARTS tac1 [||] tac taci
+
+(* [tclTHEN_i tac taci gls] applies the tactic [tac] to [gls] and applies
+ [(taci i)] to the i_th resulting subgoal (starting from 1), whatever the
+ number of subgoals is *)
+let tclTHEN_i tac taci gls =
+ finish_tac (thensi_tac taci (then_tac tac (start_tac gls)))
+
+let tclTHENLASTn tac1 taci = tclTHENSLASTn tac1 tclIDTAC taci
+let tclTHENFIRSTn tac1 taci = tclTHENSFIRSTn tac1 taci tclIDTAC
+
+(* [tclTHEN tac1 tac2 gls] applies the tactic [tac1] to [gls] and applies
+ [tac2] to every resulting subgoals *)
+let tclTHEN tac1 tac2 = tclTHENS3PARTS tac1 [||] tac2 [||]
+
+(* [tclTHENSV tac1 [t1 ; ... ; tn] gls] applies the tactic [tac1] to
+ [gls] and applies [t1],..., [tn] to the [n] resulting subgoals. Raises
+ an error if the number of resulting subgoals is not [n] *)
+let tclTHENSV tac1 tac2v =
+ tclTHENS3PARTS tac1 tac2v (tclFAIL_s "Wrong number of tactics.") [||]
+
+let tclTHENS tac1 tac2l = tclTHENSV tac1 (Array.of_list tac2l)
+
+(* [tclTHENLAST tac1 tac2 gls] applies the tactic [tac1] to [gls] and [tac2]
+ to the last resulting subgoal *)
+let tclTHENLAST tac1 tac2 = tclTHENSLASTn tac1 tclIDTAC [|tac2|]
+
+(* [tclTHENFIRST tac1 tac2 gls] applies the tactic [tac1] to [gls] and [tac2]
+ to the first resulting subgoal *)
+let tclTHENFIRST tac1 tac2 = tclTHENSFIRSTn tac1 [|tac2|] tclIDTAC
+
+(* [tclTHENLIST [t1;..;tn]] applies [t1] then [t2] ... then [tn]. More
+ convenient than [tclTHEN] when [n] is large. *)
+let rec tclTHENLIST = function
+ [] -> tclIDTAC
+ | t1::tacl -> tclTHEN t1 (tclTHENLIST tacl)
+
+(* [tclMAP f [x1..xn]] builds [(f x1);(f x2);...(f xn)] *)
+let tclMAP tacfun l =
+ List.fold_right (fun x -> (tclTHEN (tacfun x))) l tclIDTAC
+
+(* PROGRESS tac ptree applies tac to the goal ptree and fails if tac leaves
+the goal unchanged *)
+let tclPROGRESS tac ptree =
+ let rslt = tac ptree in
+ if Goal.V82.progress rslt ptree then rslt
+ else user_err ~hdr:"Refiner.PROGRESS" (str"Failed to progress.")
+
+(* Execute tac, show the names of new hypothesis names created by tac
+ in the "as" format and then forget everything. From the logical
+ point of view [tclSHOWHYPS tac] is therefore equivalent to idtac,
+ except that it takes the time and memory of tac and prints "as"
+ information). The resulting (unchanged) goals are printed *after*
+ the as-expression, which forces pg to some gymnastic.
+ TODO: Have something similar (better?) in the xml protocol.
+ NOTE: some tactics delete hypothesis and reuse names (induction,
+ destruct), this is not detected by this tactical. *)
+let tclSHOWHYPS (tac : tactic) (goal: Goal.goal Evd.sigma)
+ : Goal.goal list Evd.sigma =
+ let oldhyps = pf_hyps goal in
+ let rslt:Goal.goal list Evd.sigma = tac goal in
+ let { it = gls; sigma = sigma; } = rslt in
+ let hyps =
+ List.map (fun gl -> pf_hyps { it = gl; sigma=sigma; }) gls in
+ let cmp d1 d2 = Names.Id.equal (NamedDecl.get_id d1) (NamedDecl.get_id d2) in
+ let newhyps =
+ List.map
+ (fun hypl -> List.subtract cmp hypl oldhyps)
+ hyps
+ in
+ let s =
+ let frst = ref true in
+ List.fold_left
+ (fun acc lh -> acc ^ (if !frst then (frst:=false;"") else " | ")
+ ^ (List.fold_left
+ (fun acc d -> (Names.Id.to_string (NamedDecl.get_id d)) ^ " " ^ acc)
+ "" lh))
+ "" newhyps in
+ Feedback.msg_notice
+ (str "<infoH>"
+ ++ (hov 0 (str s))
+ ++ (str "</infoH>"));
+ tclIDTAC goal;;
+
+
+let catch_failerror (e, info) =
+ if catchable_exception e then Control.check_for_interrupt ()
+ else match e with
+ | FailError (0,_) ->
+ Control.check_for_interrupt ()
+ | FailError (lvl,s) ->
+ iraise (FailError (lvl - 1, s), info)
+ | e -> iraise (e, info)
+ (** FIXME: do we need to add a [Errors.push] here? *)
+
+(* ORELSE0 t1 t2 tries to apply t1 and if it fails, applies t2 *)
+let tclORELSE0 t1 t2 g =
+ try
+ t1 g
+ with (* Breakpoint *)
+ | e when CErrors.noncritical e ->
+ let e = CErrors.push e in catch_failerror e; t2 g
+
+(* ORELSE t1 t2 tries to apply t1 and if it fails or does not progress,
+ then applies t2 *)
+let tclORELSE t1 t2 = tclORELSE0 (tclPROGRESS t1) t2
+
+(* applies t1;t2then if t1 succeeds or t2else if t1 fails
+ t2* are called in terminal position (unless t1 produces more than
+ 1 subgoal!) *)
+let tclORELSE_THEN t1 t2then t2else gls =
+ match
+ try Some(tclPROGRESS t1 gls)
+ with e when CErrors.noncritical e ->
+ let e = CErrors.push e in catch_failerror e; None
+ with
+ | None -> t2else gls
+ | Some sgl ->
+ let sigr, gl = unpackage sgl in
+ finish_tac (then_tac t2then (sigr,gl))
+
+(* TRY f tries to apply f, and if it fails, leave the goal unchanged *)
+let tclTRY f = (tclORELSE0 f tclIDTAC)
+
+let tclTHENTRY f g = (tclTHEN f (tclTRY g))
+
+(* Try the first tactic that does not fail in a list of tactics *)
+
+let rec tclFIRST = function
+ | [] -> tclFAIL_s "No applicable tactic."
+ | t::rest -> tclORELSE0 t (tclFIRST rest)
+
+let ite_gen tcal tac_if continue tac_else gl=
+ let success=ref false in
+ let tac_if0 gl=
+ let result=tac_if gl in
+ success:=true;result in
+ let tac_else0 e gl=
+ if !success then
+ iraise e
+ else
+ try
+ tac_else gl
+ with
+ e' when CErrors.noncritical e' -> iraise e in
+ try
+ tcal tac_if0 continue gl
+ with (* Breakpoint *)
+ | e when CErrors.noncritical e ->
+ let e = CErrors.push e in catch_failerror e; tac_else0 e gl
+
+(* Try the first tactic and, if it succeeds, continue with
+ the second one, and if it fails, use the third one *)
+
+let tclIFTHENELSE=ite_gen tclTHEN
+
+(* Idem with tclTHENS and tclTHENSV *)
+
+let tclIFTHENSELSE=ite_gen tclTHENS
+
+let tclIFTHENSVELSE=ite_gen tclTHENSV
+
+let tclIFTHENTRYELSEMUST tac1 tac2 gl =
+ tclIFTHENELSE tac1 (tclTRY tac2) tac2 gl
+
+(* Fails if a tactic did not solve the goal *)
+let tclCOMPLETE tac = tclTHEN tac (tclFAIL_s "Proof is not complete.")
+
+(* Try the first thats solves the current goal *)
+let tclSOLVE tacl = tclFIRST (List.map tclCOMPLETE tacl)
+
+
+(* Iteration tacticals *)
+
+let tclDO n t =
+ let rec dorec k =
+ if k < 0 then user_err ~hdr:"Refiner.tclDO"
+ (str"Wrong argument : Do needs a positive integer.");
+ if Int.equal k 0 then tclIDTAC
+ else if Int.equal k 1 then t else (tclTHEN t (dorec (k-1)))
+ in
+ dorec n
+
+
+(* Beware: call by need of CAML, g is needed *)
+let rec tclREPEAT t g =
+ tclORELSE_THEN t (tclREPEAT t) tclIDTAC g
+
+let tclAT_LEAST_ONCE t = (tclTHEN t (tclREPEAT t))
+
+(* Repeat on the first subgoal (no failure if no more subgoal) *)
+let rec tclREPEAT_MAIN t g =
+ (tclORELSE (tclTHEN_i t (fun i -> if Int.equal i 1 then (tclREPEAT_MAIN t) else
+ tclIDTAC)) tclIDTAC) g
+
+(* Change evars *)
+let tclEVARS sigma gls = tclIDTAC {gls with sigma=sigma}
+
+let tclEVARUNIVCONTEXT ctx gls = tclIDTAC {gls with sigma= Evd.set_universe_context gls.sigma ctx}
+
+(* Push universe context *)
+let tclPUSHCONTEXT rigid ctx tac gl =
+ tclTHEN (tclEVARS (Evd.merge_context_set rigid (project gl) ctx)) tac gl
+
+let tclPUSHEVARUNIVCONTEXT ctx gl =
+ tclEVARS (Evd.merge_universe_context (project gl) ctx) gl
+
+let tclPUSHCONSTRAINTS cst gl =
+ tclEVARS (Evd.add_constraints (project gl) cst) gl
diff --git a/proofs/refiner.mli b/proofs/refiner.mli
new file mode 100644
index 0000000000..52cbf7658b
--- /dev/null
+++ b/proofs/refiner.mli
@@ -0,0 +1,133 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** Legacy proof engine. Do not use in newly written code. *)
+
+open Evd
+open EConstr
+
+(** The refiner (handles primitive rules and high-level tactics). *)
+type tactic = Proofview.V82.tac
+
+val sig_it : 'a sigma -> 'a
+val project : 'a sigma -> evar_map
+
+val pf_env : Goal.goal sigma -> Environ.env
+val pf_hyps : Goal.goal sigma -> named_context
+
+val refiner : check:bool -> Constr.t -> tactic
+
+(** {6 Tacticals. } *)
+
+(** [tclIDTAC] is the identity tactic without message printing*)
+val tclIDTAC : tactic
+val tclIDTAC_MESSAGE : Pp.t -> tactic
+
+(** [tclEVARS sigma] changes the current evar map *)
+val tclEVARS : evar_map -> tactic
+val tclEVARUNIVCONTEXT : UState.t -> tactic
+
+val tclPUSHCONTEXT : Evd.rigid -> Univ.ContextSet.t -> tactic -> tactic
+val tclPUSHEVARUNIVCONTEXT : UState.t -> tactic
+
+val tclPUSHCONSTRAINTS : Univ.Constraint.t -> tactic
+
+(** [tclTHEN tac1 tac2 gls] applies the tactic [tac1] to [gls] and applies
+ [tac2] to every resulting subgoals *)
+val tclTHEN : tactic -> tactic -> tactic
+
+(** [tclTHENLIST [t1;..;tn]] applies [t1] THEN [t2] ... THEN [tn]. More
+ convenient than [tclTHEN] when [n] is large *)
+val tclTHENLIST : tactic list -> tactic
+
+(** [tclMAP f [x1..xn]] builds [(f x1);(f x2);...(f xn)] *)
+val tclMAP : ('a -> tactic) -> 'a list -> tactic
+
+(** [tclTHEN_i tac1 tac2 gls] applies the tactic [tac1] to [gls] and applies
+ [(tac2 i)] to the [i]{^ th} resulting subgoal (starting from 1) *)
+val tclTHEN_i : tactic -> (int -> tactic) -> tactic
+
+(** [tclTHENLAST tac1 tac2 gls] applies the tactic [tac1] to [gls] and [tac2]
+ to the last resulting subgoal (previously called [tclTHENL]) *)
+val tclTHENLAST : tactic -> tactic -> tactic
+
+(** [tclTHENFIRST tac1 tac2 gls] applies the tactic [tac1] to [gls] and [tac2]
+ to the first resulting subgoal *)
+val tclTHENFIRST : tactic -> tactic -> tactic
+
+(** [tclTHENS tac1 [|t1 ; ... ; tn|] gls] applies the tactic [tac1] to
+ [gls] and applies [t1],..., [tn] to the [n] resulting subgoals. Raises
+ an error if the number of resulting subgoals is not [n] *)
+val tclTHENSV : tactic -> tactic array -> tactic
+
+(** Same with a list of tactics *)
+val tclTHENS : tactic -> tactic list -> tactic
+
+(** [tclTHENS3PARTS tac1 [|t1 ; ... ; tn|] tac2 [|t'1 ; ... ; t'm|] gls]
+ applies the tactic [tac1] to [gls] then, applies [t1], ..., [tn] to
+ the first [n] resulting subgoals, [t'1], ..., [t'm] to the last [m]
+ subgoals and [tac2] to the rest of the subgoals in the middle. Raises an
+ error if the number of resulting subgoals is strictly less than [n+m] *)
+val tclTHENS3PARTS : tactic -> tactic array -> tactic -> tactic array -> tactic
+
+(** [tclTHENSLASTn tac1 [t1 ; ... ; tn] tac2 gls] applies [t1],...,[tn] on the
+ last [n] resulting subgoals and [tac2] on the remaining first subgoals *)
+val tclTHENSLASTn : tactic -> tactic -> tactic array -> tactic
+
+(** [tclTHENSFIRSTn tac1 [t1 ; ... ; tn] tac2 gls] first applies [tac1], then
+ applies [t1],...,[tn] on the first [n] resulting subgoals and
+ [tac2] for the remaining last subgoals (previously called tclTHENST) *)
+val tclTHENSFIRSTn : tactic -> tactic array -> tactic -> tactic
+
+(** [tclTHENLASTn tac1 [t1 ; ... ; tn] gls] first applies [tac1] then,
+ applies [t1],...,[tn] on the last [n] resulting subgoals and leaves
+ unchanged the other subgoals *)
+val tclTHENLASTn : tactic -> tactic array -> tactic
+
+(** [tclTHENFIRSTn tac1 [t1 ; ... ; tn] gls] first applies [tac1] then,
+ applies [t1],...,[tn] on the first [n] resulting subgoals and leaves
+ unchanged the other subgoals (previously called [tclTHENSI]) *)
+val tclTHENFIRSTn : tactic -> tactic array -> tactic
+
+(** A special exception for levels for the Fail tactic *)
+exception FailError of int * Pp.t Lazy.t
+
+(** Takes an exception and either raise it at the next
+ level or do nothing. *)
+val catch_failerror : Exninfo.iexn -> unit
+
+val tclORELSE0 : tactic -> tactic -> tactic
+val tclORELSE : tactic -> tactic -> tactic
+val tclREPEAT : tactic -> tactic
+val tclREPEAT_MAIN : tactic -> tactic
+val tclFIRST : tactic list -> tactic
+val tclSOLVE : tactic list -> tactic
+val tclTRY : tactic -> tactic
+val tclTHENTRY : tactic -> tactic -> tactic
+val tclCOMPLETE : tactic -> tactic
+val tclAT_LEAST_ONCE : tactic -> tactic
+val tclFAIL : int -> Pp.t -> tactic
+val tclFAIL_lazy : int -> Pp.t Lazy.t -> tactic
+val tclDO : int -> tactic -> tactic
+val tclPROGRESS : tactic -> tactic
+val tclSHOWHYPS : tactic -> tactic
+
+(** [tclIFTHENELSE tac1 tac2 tac3 gls] first applies [tac1] to [gls] then,
+ if it succeeds, applies [tac2] to the resulting subgoals,
+ and if not applies [tac3] to the initial goal [gls] *)
+val tclIFTHENELSE : tactic -> tactic -> tactic -> tactic
+val tclIFTHENSELSE : tactic -> tactic list -> tactic ->tactic
+val tclIFTHENSVELSE : tactic -> tactic array -> tactic ->tactic
+
+(** [tclIFTHENTRYELSEMUST tac1 tac2 gls] applies [tac1] then [tac2]. If [tac1]
+ has been successful, then [tac2] may fail. Otherwise, [tac2] must succeed.
+ Equivalent to [(tac1;try tac2)||tac2] *)
+
+val tclIFTHENTRYELSEMUST : tactic -> tactic -> tactic
diff --git a/proofs/tacmach.ml b/proofs/tacmach.ml
new file mode 100644
index 0000000000..df90354717
--- /dev/null
+++ b/proofs/tacmach.ml
@@ -0,0 +1,209 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Util
+open Namegen
+open Termops
+open Environ
+open Reductionops
+open Evd
+open Typing
+open Tacred
+open Logic
+open Context.Named.Declaration
+
+module NamedDecl = Context.Named.Declaration
+
+let re_sig it gc = { it = it; sigma = gc; }
+
+(**************************************************************)
+(* Operations for handling terms under a local typing context *)
+(**************************************************************)
+
+type tactic = Proofview.V82.tac
+
+let sig_it = Refiner.sig_it
+let project = Refiner.project
+let pf_env = Refiner.pf_env
+let pf_hyps = Refiner.pf_hyps
+
+let test_conversion env sigma pb c1 c2 =
+ Reductionops.check_conv ~pb env sigma c1 c2
+
+let pf_concl gls = Goal.V82.concl (project gls) (sig_it gls)
+let pf_hyps_types gls =
+ let sign = Environ.named_context (pf_env gls) in
+ List.map (function LocalAssum (id,x)
+ | LocalDef (id,_,x) -> id, EConstr.of_constr x)
+ sign
+
+let pf_nth_hyp_id gls n = List.nth (pf_hyps gls) (n-1) |> NamedDecl.get_id
+
+let pf_last_hyp gl = List.hd (pf_hyps gl)
+
+let pf_get_hyp gls id =
+ let env, sigma = pf_env gls, project gls in
+ try
+ Context.Named.lookup id (pf_hyps gls)
+ with Not_found ->
+ raise (RefinerError (env, sigma, NoSuchHyp id))
+
+let pf_get_hyp_typ gls id =
+ id |> pf_get_hyp gls |> NamedDecl.get_type
+
+let pf_ids_of_hyps gls = ids_of_named_context (pf_hyps gls)
+let pf_ids_set_of_hyps gls =
+ Environ.ids_of_named_context_val (Environ.named_context_val (pf_env gls))
+
+let pf_get_new_id id gls =
+ next_ident_away id (pf_ids_set_of_hyps gls)
+
+let pf_global gls id =
+ let env = pf_env gls in
+ let sigma = project gls in
+ Evd.fresh_global env sigma (Constrintern.construct_reference (pf_hyps gls) id)
+
+let pf_apply f gls = f (pf_env gls) (project gls)
+let pf_eapply f gls x =
+ on_sig gls (fun evm -> f (pf_env gls) evm x)
+let pf_reduce = pf_apply
+let pf_e_reduce = pf_apply
+
+let pf_whd_all = pf_reduce whd_all
+let pf_hnf_constr = pf_reduce hnf_constr
+let pf_nf = pf_reduce simpl
+let pf_nf_betaiota = pf_reduce nf_betaiota
+let pf_compute = pf_reduce compute
+let pf_unfoldn ubinds = pf_reduce (unfoldn ubinds)
+let pf_unsafe_type_of = pf_reduce unsafe_type_of
+let pf_type_of = pf_reduce type_of
+let pf_get_type_of = pf_reduce Retyping.get_type_of
+
+let pf_conv_x gl = pf_reduce test_conversion gl Reduction.CONV
+let pf_conv_x_leq gl = pf_reduce test_conversion gl Reduction.CUMUL
+let pf_const_value = pf_reduce (fun env _ c -> EConstr.of_constr (constant_value_in env c))
+
+let pf_reduce_to_quantified_ind = pf_reduce reduce_to_quantified_ind
+let pf_reduce_to_atomic_ind = pf_reduce reduce_to_atomic_ind
+
+let pf_hnf_type_of gls = pf_get_type_of gls %> pf_whd_all gls
+
+(* Pretty-printers *)
+
+open Pp
+
+let db_pr_goal sigma g =
+ let env = Goal.V82.env sigma g in
+ let penv = Termops.Internal.print_named_context env in
+ let pc = Termops.Internal.print_constr_env env sigma (Goal.V82.concl sigma g) in
+ str" " ++ hv 0 (penv ++ fnl () ++
+ str "============================" ++ fnl () ++
+ str" " ++ pc) ++ fnl ()
+
+let pr_gls gls =
+ hov 0 (pr_evar_map (Some 2) (pf_env gls) (sig_sig gls) ++ fnl () ++ db_pr_goal (project gls) (sig_it gls))
+
+let pr_glls glls =
+ hov 0 (pr_evar_map (Some 2) (Global.env()) (sig_sig glls) ++ fnl () ++
+ prlist_with_sep fnl (db_pr_goal (project glls)) (sig_it glls))
+
+(* Variants of [Tacmach] functions built with the new proof engine *)
+module New = struct
+
+ let project gl =
+ Proofview.Goal.sigma gl
+
+ let pf_apply f gl =
+ f (Proofview.Goal.env gl) (project gl)
+
+ let of_old f gl =
+ f { Evd.it = Proofview.Goal.goal gl ; sigma = project gl; }
+
+ let pf_global id gl =
+ (* We only check for the existence of an [id] in [hyps] *)
+ let hyps = Proofview.Goal.hyps gl in
+ Constrintern.construct_reference hyps id
+
+ let pf_env = Proofview.Goal.env
+ let pf_concl = Proofview.Goal.concl
+
+ let pf_unsafe_type_of gl t =
+ pf_apply unsafe_type_of gl t
+
+ let pf_type_of gl t =
+ pf_apply type_of gl t
+
+ let pf_conv_x gl t1 t2 = pf_apply is_conv gl t1 t2
+
+ let pf_ids_of_hyps gl =
+ (* We only get the identifiers in [hyps] *)
+ let hyps = Proofview.Goal.hyps gl in
+ ids_of_named_context hyps
+
+ let pf_ids_set_of_hyps gl =
+ (* We only get the identifiers in [hyps] *)
+ let env = Proofview.Goal.env gl in
+ Environ.ids_of_named_context_val (Environ.named_context_val env)
+
+ let pf_get_new_id id gl =
+ let ids = pf_ids_set_of_hyps gl in
+ next_ident_away id ids
+
+ let pf_get_hyp id gl =
+ let hyps = Proofview.Goal.env gl in
+ let sigma = project gl in
+ let sign =
+ try EConstr.lookup_named id hyps
+ with Not_found -> raise (RefinerError (hyps, sigma, NoSuchHyp id))
+ in
+ sign
+
+ let pf_get_hyp_typ id gl =
+ pf_get_hyp id gl |> NamedDecl.get_type
+
+ let pf_hyps_types gl =
+ let env = Proofview.Goal.env gl in
+ let sign = Environ.named_context env in
+ List.map (function LocalAssum (id,x)
+ | LocalDef (id,_,x) -> id, EConstr.of_constr x)
+ sign
+
+ let pf_last_hyp gl =
+ let hyps = Proofview.Goal.hyps gl in
+ List.hd hyps
+
+ let pf_nf_concl (gl : Proofview.Goal.t) =
+ (* We normalize the conclusion just after *)
+ let concl = Proofview.Goal.concl gl in
+ let sigma = project gl in
+ nf_evar sigma concl
+
+ let pf_whd_all gl t = pf_apply whd_all gl t
+
+ let pf_get_type_of gl t = pf_apply Retyping.get_type_of gl t
+
+ let pf_reduce_to_quantified_ind gl t =
+ pf_apply reduce_to_quantified_ind gl t
+
+ let pf_hnf_constr gl t = pf_apply hnf_constr gl t
+ let pf_hnf_type_of gl t =
+ pf_whd_all gl (pf_get_type_of gl t)
+
+ let pf_whd_all gl t = pf_apply whd_all gl t
+ let pf_compute gl t = pf_apply compute gl t
+
+ let pf_nf_evar gl t = nf_evar (project gl) t
+
+ let pf_undefined_evars gl =
+ let sigma = Proofview.Goal.sigma gl in
+ let ev = Proofview.Goal.goal gl in
+ let evi = Evd.find sigma ev in
+ Evarutil.filtered_undefined_evars_of_evar_info sigma evi
+end
diff --git a/proofs/tacmach.mli b/proofs/tacmach.mli
new file mode 100644
index 0000000000..213ed7bfda
--- /dev/null
+++ b/proofs/tacmach.mli
@@ -0,0 +1,123 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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 Names
+open Constr
+open Environ
+open EConstr
+open Locus
+
+(** Operations for handling terms under a local typing context. *)
+
+open Evd
+
+type tactic = Proofview.V82.tac
+
+val sig_it : 'a sigma -> 'a
+val project : Goal.goal sigma -> evar_map
+
+val re_sig : 'a -> evar_map -> 'a sigma
+
+val pf_concl : Goal.goal sigma -> types
+val pf_env : Goal.goal sigma -> env
+val pf_hyps : Goal.goal sigma -> named_context
+(*i val pf_untyped_hyps : Goal.goal sigma -> (Id.t * constr) list i*)
+val pf_hyps_types : Goal.goal sigma -> (Id.t * types) list
+val pf_nth_hyp_id : Goal.goal sigma -> int -> Id.t
+val pf_last_hyp : Goal.goal sigma -> named_declaration
+val pf_ids_of_hyps : Goal.goal sigma -> Id.t list
+val pf_global : Goal.goal sigma -> Id.t -> evar_map * constr
+val pf_unsafe_type_of : Goal.goal sigma -> constr -> types
+val pf_type_of : Goal.goal sigma -> constr -> evar_map * types
+val pf_hnf_type_of : Goal.goal sigma -> constr -> types
+
+val pf_get_hyp : Goal.goal sigma -> Id.t -> named_declaration
+val pf_get_hyp_typ : Goal.goal sigma -> Id.t -> types
+
+val pf_get_new_id : Id.t -> Goal.goal sigma -> Id.t
+
+val pf_apply : (env -> evar_map -> 'a) -> Goal.goal sigma -> 'a
+val pf_eapply : (env -> evar_map -> 'a -> evar_map * 'b) ->
+ Goal.goal sigma -> 'a -> Goal.goal sigma * 'b
+val pf_reduce :
+ (env -> evar_map -> constr -> constr) ->
+ Goal.goal sigma -> constr -> constr
+val pf_e_reduce :
+ (env -> evar_map -> constr -> evar_map * constr) ->
+ Goal.goal sigma -> constr -> evar_map * constr
+
+val pf_whd_all : Goal.goal sigma -> constr -> constr
+val pf_hnf_constr : Goal.goal sigma -> constr -> constr
+val pf_nf : Goal.goal sigma -> constr -> constr
+val pf_nf_betaiota : Goal.goal sigma -> constr -> constr
+val pf_reduce_to_quantified_ind : Goal.goal sigma -> types -> (inductive * EInstance.t) * types
+val pf_reduce_to_atomic_ind : Goal.goal sigma -> types -> (inductive * EInstance.t) * types
+val pf_compute : Goal.goal sigma -> constr -> constr
+val pf_unfoldn : (occurrences * evaluable_global_reference) list
+ -> Goal.goal sigma -> constr -> constr
+
+val pf_const_value : Goal.goal sigma -> pconstant -> constr
+val pf_conv_x : Goal.goal sigma -> constr -> constr -> bool
+val pf_conv_x_leq : Goal.goal sigma -> constr -> constr -> bool
+
+(** {6 Pretty-printing functions (debug only). } *)
+val pr_gls : Goal.goal sigma -> Pp.t
+val pr_glls : Goal.goal list sigma -> Pp.t
+[@@ocaml.deprecated "Please move to \"new\" proof engine"]
+
+(** Variants of [Tacmach] functions built with the new proof engine *)
+module New : sig
+
+ val pf_apply : (env -> evar_map -> 'a) -> Proofview.Goal.t -> 'a
+ val pf_global : Id.t -> Proofview.Goal.t -> GlobRef.t
+
+ (** FIXME: encapsulate the level in an existential type. *)
+ val of_old : (Goal.goal Evd.sigma -> 'a) -> Proofview.Goal.t -> 'a
+
+ val project : Proofview.Goal.t -> Evd.evar_map
+ val pf_env : Proofview.Goal.t -> Environ.env
+ val pf_concl : Proofview.Goal.t -> types
+
+ (** WRONG: To be avoided at all costs, it typechecks the term entirely but
+ forgets the universe constraints necessary to retypecheck it *)
+ val pf_unsafe_type_of : Proofview.Goal.t -> constr -> types
+
+ (** This function does no type inference and expects an already well-typed term.
+ It recomputes its type in the fastest way possible (no conversion is ever involved) *)
+ val pf_get_type_of : Proofview.Goal.t -> constr -> types
+
+ (** This function entirely type-checks the term and computes its type
+ and the implied universe constraints. *)
+ val pf_type_of : Proofview.Goal.t -> constr -> evar_map * types
+ val pf_conv_x : Proofview.Goal.t -> t -> t -> bool
+
+ val pf_get_new_id : Id.t -> Proofview.Goal.t -> Id.t
+ val pf_ids_of_hyps : Proofview.Goal.t -> Id.t list
+ val pf_ids_set_of_hyps : Proofview.Goal.t -> Id.Set.t
+ val pf_hyps_types : Proofview.Goal.t -> (Id.t * types) list
+
+ val pf_get_hyp : Id.t -> Proofview.Goal.t -> named_declaration
+ val pf_get_hyp_typ : Id.t -> Proofview.Goal.t -> types
+ val pf_last_hyp : Proofview.Goal.t -> named_declaration
+
+ val pf_nf_concl : Proofview.Goal.t -> types
+ val pf_reduce_to_quantified_ind : Proofview.Goal.t -> types -> (inductive * EInstance.t) * types
+
+ val pf_hnf_constr : Proofview.Goal.t -> constr -> types
+ val pf_hnf_type_of : Proofview.Goal.t -> constr -> types
+
+ val pf_whd_all : Proofview.Goal.t -> constr -> constr
+ val pf_compute : Proofview.Goal.t -> constr -> constr
+
+ val pf_nf_evar : Proofview.Goal.t -> constr -> constr
+
+ (** Gathers the undefined evars of the given goal. *)
+ val pf_undefined_evars : Proofview.Goal.t -> Evar.Set.t
+end
diff --git a/proofs/tactypes.ml b/proofs/tactypes.ml
new file mode 100644
index 0000000000..86a7e9c527
--- /dev/null
+++ b/proofs/tactypes.ml
@@ -0,0 +1,54 @@
+(************************************************************************)
+(* * The Coq Proof Assistant / The Coq Development Team *)
+(* v * INRIA, CNRS and contributors - Copyright 1999-2018 *)
+(* <O___,, * (see CREDITS file for the list of authors) *)
+(* \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) *)
+(************************************************************************)
+
+(** Tactic-related types that are not totally Ltac specific and still used in
+ lower API. It's not clear whether this is a temporary API or if this is
+ meant to stay. *)
+
+open Names
+
+(** Introduction patterns *)
+
+type 'constr intro_pattern_expr =
+ | IntroForthcoming of bool
+ | IntroNaming of Namegen.intro_pattern_naming_expr
+ | IntroAction of 'constr intro_pattern_action_expr
+and 'constr intro_pattern_action_expr =
+ | IntroWildcard
+ | IntroOrAndPattern of 'constr or_and_intro_pattern_expr
+ | IntroInjection of ('constr intro_pattern_expr) CAst.t list
+ | IntroApplyOn of 'constr CAst.t * 'constr intro_pattern_expr CAst.t
+ | IntroRewrite of bool
+and 'constr or_and_intro_pattern_expr =
+ | IntroOrPattern of ('constr intro_pattern_expr) CAst.t list list
+ | IntroAndPattern of ('constr intro_pattern_expr) CAst.t list
+
+(** Bindings *)
+
+type quantified_hypothesis = AnonHyp of int | NamedHyp of Id.t
+
+type 'a explicit_bindings = (quantified_hypothesis * 'a) CAst.t list
+
+type 'a bindings =
+ | ImplicitBindings of 'a list
+ | ExplicitBindings of 'a explicit_bindings
+ | NoBindings
+
+type 'a with_bindings = 'a * 'a bindings
+
+type 'a delayed_open = Environ.env -> Evd.evar_map -> Evd.evar_map * 'a
+
+type delayed_open_constr = EConstr.constr delayed_open
+type delayed_open_constr_with_bindings = EConstr.constr with_bindings delayed_open
+
+type intro_pattern = delayed_open_constr intro_pattern_expr CAst.t
+type intro_patterns = delayed_open_constr intro_pattern_expr CAst.t list
+type or_and_intro_pattern = delayed_open_constr or_and_intro_pattern_expr CAst.t
+type intro_pattern_naming = Namegen.intro_pattern_naming_expr CAst.t