diff options
Diffstat (limited to 'tactics')
| -rw-r--r-- | tactics/cbn.ml | 797 | ||||
| -rw-r--r-- | tactics/cbn.mli | 13 | ||||
| -rw-r--r-- | tactics/class_tactics.ml | 14 | ||||
| -rw-r--r-- | tactics/eauto.ml | 179 | ||||
| -rw-r--r-- | tactics/hints.ml | 8 | ||||
| -rw-r--r-- | tactics/hints.mli | 32 | ||||
| -rw-r--r-- | tactics/redexpr.ml | 6 | ||||
| -rw-r--r-- | tactics/tactics.mllib | 1 |
8 files changed, 905 insertions, 145 deletions
diff --git a/tactics/cbn.ml b/tactics/cbn.ml new file mode 100644 index 0000000000..21e38df6db --- /dev/null +++ b/tactics/cbn.ml @@ -0,0 +1,797 @@ +(************************************************************************) +(* * The Coq Proof Assistant / The Coq Development Team *) +(* v * INRIA, CNRS and contributors - Copyright 1999-2019 *) +(* <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 Names +open Constr +open Context +open Termops +open Univ +open Evd +open Environ +open EConstr +open Vars +open Context.Rel.Declaration + +(** This module implements a call by name reduction used by the cbn tactic. + + It has an ability to "refold" constants by storing constants and + their parameters in its stack. +*) + +(** Machinery to custom the behavior of the reduction *) +module ReductionBehaviour = Reductionops.ReductionBehaviour + +(** Machinery about stack of unfolded constants *) +module Cst_stack = struct + open EConstr + +(** constant * params * args + +- constant applied to params = term in head applied to args +- there is at most one arguments with an empty list of args, it must be the first. +- in args, the int represents the indice of the first arg to consider *) + type t = (constr * constr list * (int * constr array) list) list + + let empty = [] + let is_empty = CList.is_empty + + let drop_useless = function + | _ :: ((_,_,[])::_ as q) -> q + | l -> l + + let add_param h cst_l = + let append2cst = function + | (c,params,[]) -> (c, h::params, []) + | (c,params,((i,t)::q)) when i = pred (Array.length t) -> + (c, params, q) + | (c,params,(i,t)::q) -> + (c, params, (succ i,t)::q) + in + drop_useless (List.map append2cst cst_l) + + let add_args cl = + List.map (fun (a,b,args) -> (a,b,(0,cl)::args)) + + let add_cst cst = function + | (_,_,[]) :: q as l -> l + | l -> (cst,[],[])::l + + let best_cst = function + | (cst,params,[])::_ -> Some(cst,params) + | _ -> None + + let reference sigma t = match best_cst t with + | Some (c, _) when isConst sigma c -> Some (fst (destConst sigma c)) + | _ -> None + + (** [best_replace d cst_l c] makes the best replacement for [d] + by [cst_l] in [c] *) + let best_replace sigma d cst_l c = + let reconstruct_head = List.fold_left + (fun t (i,args) -> mkApp (t,Array.sub args i (Array.length args - i))) in + List.fold_right + (fun (cst,params,args) t -> Termops.replace_term sigma + (reconstruct_head d args) + (applist (cst, List.rev params)) + t) cst_l c + + let pr env sigma l = + let open Pp in + let p_c c = Termops.Internal.print_constr_env env sigma c in + prlist_with_sep pr_semicolon + (fun (c,params,args) -> + hov 1 (str"(" ++ p_c c ++ str ")" ++ spc () ++ pr_sequence p_c params ++ spc () ++ str "(args:" ++ + pr_sequence (fun (i,el) -> prvect_with_sep spc p_c (Array.sub el i (Array.length el - i))) args ++ + str ")")) l +end + + +(** The type of (machine) stacks (= lambda-bar-calculus' contexts) *) +module Stack : +sig + open EConstr + type 'a app_node + + type cst_member = + | Cst_const of pconstant + | Cst_proj of Projection.t + + type 'a member = + | App of 'a app_node + | Case of case_info * 'a * 'a array * Cst_stack.t + | Proj of Projection.t * Cst_stack.t + | Fix of ('a, 'a) pfixpoint * 'a t * Cst_stack.t + | Primitive of CPrimitives.t * (Constant.t * EInstance.t) * 'a t * CPrimitives.args_red * Cst_stack.t + | Cst of cst_member * int * int list * 'a t * Cst_stack.t + + and 'a t = 'a member list + + exception IncompatibleFold2 + + val pr : ('a -> Pp.t) -> 'a t -> Pp.t + val empty : 'a t + val append_app : 'a array -> 'a t -> 'a t + val decomp : 'a t -> ('a * 'a t) option + val equal : ('a -> 'a -> bool) -> (('a, 'a) pfixpoint -> ('a, 'a) pfixpoint -> bool) + -> 'a t -> 'a t -> bool + val strip_app : 'a t -> 'a t * 'a t + val strip_n_app : int -> 'a t -> ('a t * 'a * 'a t) option + val will_expose_iota : 'a t -> bool + val list_of_app_stack : constr t -> constr list option + val args_size : 'a t -> int + val tail : int -> 'a t -> 'a t + val nth : 'a t -> int -> 'a + val best_state : evar_map -> constr * constr t -> Cst_stack.t -> constr * constr t + val zip : ?refold:bool -> evar_map -> constr * constr t -> constr + val check_native_args : CPrimitives.t -> 'a t -> bool + val get_next_primitive_args : CPrimitives.args_red -> 'a t -> CPrimitives.args_red * ('a t * 'a * 'a t) option +end = +struct + open EConstr + type 'a app_node = int * 'a array * int + (* first releavnt position, arguments, last relevant position *) + + (* + Invariant that this module must ensure : + (behare of direct access to app_node by the rest of Reductionops) + - in app_node (i,_,j) i <= j + - There is no array realocation (outside of debug printing) + *) + + let pr_app_node pr (i,a,j) = + let open Pp in surround ( + prvect_with_sep pr_comma pr (Array.sub a i (j - i + 1)) + ) + + + type cst_member = + | Cst_const of pconstant + | Cst_proj of Projection.t + + type 'a member = + | App of 'a app_node + | Case of case_info * 'a * 'a array * Cst_stack.t + | Proj of Projection.t * Cst_stack.t + | Fix of ('a, 'a) pfixpoint * 'a t * Cst_stack.t + | Primitive of CPrimitives.t * (Constant.t * EInstance.t) * 'a t * CPrimitives.args_red * Cst_stack.t + | Cst of cst_member * int * int list * 'a t * Cst_stack.t + + and 'a t = 'a member list + + (* Debugging printer *) + let rec pr_member pr_c member = + let open Pp in + let pr_c x = hov 1 (pr_c x) in + match member with + | App app -> str "ZApp" ++ pr_app_node pr_c app + | Case (_,_,br,cst) -> + str "ZCase(" ++ + prvect_with_sep (pr_bar) pr_c br + ++ str ")" + | Proj (p,cst) -> + str "ZProj(" ++ Constant.debug_print (Projection.constant p) ++ str ")" + | Fix (f,args,cst) -> + str "ZFix(" ++ Constr.debug_print_fix pr_c f + ++ pr_comma () ++ pr pr_c args ++ str ")" + | Primitive (p,c,args,kargs,cst_l) -> + str "ZPrimitive(" ++ str (CPrimitives.to_string p) + ++ pr_comma () ++ pr pr_c args ++ str ")" + | Cst (mem,curr,remains,params,cst_l) -> + str "ZCst(" ++ pr_cst_member pr_c mem ++ pr_comma () ++ int curr + ++ pr_comma () ++ + prlist_with_sep pr_semicolon int remains ++ + pr_comma () ++ pr pr_c params ++ str ")" + and pr pr_c l = + let open Pp in + prlist_with_sep pr_semicolon (fun x -> hov 1 (pr_member pr_c x)) l + + and pr_cst_member pr_c c = + let open Pp in + match c with + | Cst_const (c, u) -> + if Univ.Instance.is_empty u then Constant.debug_print c + else str"(" ++ Constant.debug_print c ++ str ", " ++ + Univ.Instance.pr Univ.Level.pr u ++ str")" + | Cst_proj p -> + str".(" ++ Constant.debug_print (Projection.constant p) ++ str")" + + let empty = [] + + let append_app v s = + let le = Array.length v in + if Int.equal le 0 then s else App (0,v,pred le) :: s + + let decomp_node (i,l,j) sk = + if i < j then (l.(i), App (succ i,l,j) :: sk) + else (l.(i), sk) + + let decomp = function + | App node::s -> Some (decomp_node node s) + | _ -> None + + let decomp_node_last (i,l,j) sk = + if i < j then (l.(j), App (i,l,pred j) :: sk) + else (l.(j), sk) + + let equal f f_fix sk1 sk2 = + let equal_cst_member x y = + match x, y with + | Cst_const (c1,u1), Cst_const (c2, u2) -> + Constant.equal c1 c2 && Univ.Instance.equal u1 u2 + | Cst_proj p1, Cst_proj p2 -> Projection.repr_equal p1 p2 + | _, _ -> false + in + let rec equal_rec sk1 sk2 = + match sk1,sk2 with + | [],[] -> true + | App a1 :: s1, App a2 :: s2 -> + let t1,s1' = decomp_node_last a1 s1 in + let t2,s2' = decomp_node_last a2 s2 in + (f t1 t2) && (equal_rec s1' s2') + | Case (_,t1,a1,_) :: s1, Case (_,t2,a2,_) :: s2 -> + f t1 t2 && CArray.equal (fun x y -> f x y) a1 a2 && equal_rec s1 s2 + | (Proj (p,_)::s1, Proj(p2,_)::s2) -> + Projection.Repr.equal (Projection.repr p) (Projection.repr p2) + && equal_rec s1 s2 + | Fix (f1,s1,_) :: s1', Fix (f2,s2,_) :: s2' -> + f_fix f1 f2 + && equal_rec (List.rev s1) (List.rev s2) + && equal_rec s1' s2' + | Cst (c1,curr1,remains1,params1,_)::s1', Cst (c2,curr2,remains2,params2,_)::s2' -> + equal_cst_member c1 c2 + && equal_rec (List.rev params1) (List.rev params2) + && equal_rec s1' s2' + | ((App _|Case _|Proj _|Fix _|Cst _|Primitive _)::_|[]), _ -> false + in equal_rec (List.rev sk1) (List.rev sk2) + + exception IncompatibleFold2 + + let append_app_list l s = + let a = Array.of_list l in + append_app a s + + let rec args_size = function + | App (i,_,j)::s -> j + 1 - i + args_size s + | (Case _|Fix _|Proj _|Cst _|Primitive _)::_ | [] -> 0 + + let strip_app s = + let rec aux out = function + | ( App _ as e) :: s -> aux (e :: out) s + | s -> List.rev out,s + in aux [] s + let strip_n_app n s = + let rec aux n out = function + | App (i,a,j) as e :: s -> + let nb = j - i + 1 in + if n >= nb then + aux (n - nb) (e::out) s + else + let p = i+n in + Some (CList.rev + (if Int.equal n 0 then out else App (i,a,p-1) :: out), + a.(p), + if j > p then App(succ p,a,j)::s else s) + | s -> None + in aux n [] s + + let will_expose_iota args = + List.exists + (function (Fix (_,_,l) | Case (_,_,_,l) | + Proj (_,l) | Cst (_,_,_,_,l)) when Cst_stack.is_empty l -> true | _ -> false) + args + + let list_of_app_stack s = + let rec aux = function + | App (i,a,j) :: s -> + let (args',s') = aux s in + let a' = Array.sub a i (j - i + 1) in + (Array.fold_right (fun x y -> x::y) a' args', s') + | s -> ([],s) in + let (out,s') = aux s in + let init = match s' with [] -> true | _ -> false in + Option.init init out + + let tail n0 s0 = + let rec aux n s = + if Int.equal n 0 then s else + match s with + | App (i,a,j) :: s -> + let nb = j - i + 1 in + if n >= nb then + aux (n - nb) s + else + let p = i+n in + if j >= p then App(p,a,j)::s else s + | _ -> raise (Invalid_argument "Reductionops.Stack.tail") + in aux n0 s0 + + let nth s p = + match strip_n_app p s with + | Some (_,el,_) -> el + | None -> raise Not_found + + (** This function breaks the abstraction of Cst_stack ! *) + let best_state sigma (_,sk as s) l = + let rec aux sk def = function + |(cst, params, []) -> (cst, append_app_list (List.rev params) sk) + |(cst, params, (i,t)::q) -> match decomp sk with + | Some (el,sk') when EConstr.eq_constr sigma el t.(i) -> + if i = pred (Array.length t) + then aux sk' def (cst, params, q) + else aux sk' def (cst, params, (succ i,t)::q) + | _ -> def + in List.fold_left (aux sk) s l + + let constr_of_cst_member f sk = + match f with + | Cst_const (c, u) -> mkConstU (c, EInstance.make u), sk + | Cst_proj p -> + match decomp sk with + | Some (hd, sk) -> mkProj (p, hd), sk + | None -> assert false + + let zip ?(refold=false) sigma s = + let rec zip = function + | f, [] -> f + | f, (App (i,a,j) :: s) -> + let a' = if Int.equal i 0 && Int.equal j (Array.length a - 1) + then a + else Array.sub a i (j - i + 1) in + zip (mkApp (f, a'), s) + | f, (Case (ci,rt,br,cst_l)::s) when refold -> + zip (best_state sigma (mkCase (ci,rt,f,br), s) cst_l) + | f, (Case (ci,rt,br,_)::s) -> zip (mkCase (ci,rt,f,br), s) + | f, (Fix (fix,st,cst_l)::s) when refold -> + zip (best_state sigma (mkFix fix, st @ (append_app [|f|] s)) cst_l) + | f, (Fix (fix,st,_)::s) -> zip + (mkFix fix, st @ (append_app [|f|] s)) + | f, (Cst (cst,_,_,params,cst_l)::s) when refold -> + zip (best_state sigma (constr_of_cst_member cst (params @ (append_app [|f|] s))) cst_l) + | f, (Cst (cst,_,_,params,_)::s) -> + zip (constr_of_cst_member cst (params @ (append_app [|f|] s))) + | f, (Proj (p,cst_l)::s) when refold -> + zip (best_state sigma (mkProj (p,f),s) cst_l) + | f, (Proj (p,_)::s) -> zip (mkProj (p,f),s) + | f, (Primitive (p,c,args,kargs,cst_l)::s) -> + zip (mkConstU c, args @ append_app [|f|] s) + in + zip s + + (* Check if there is enough arguments on [stk] w.r.t. arity of [op] *) + let check_native_args op stk = + let nargs = CPrimitives.arity op in + let rargs = args_size stk in + nargs <= rargs + + let get_next_primitive_args kargs stk = + let rec nargs = function + | [] -> 0 + | CPrimitives.Kwhnf :: _ -> 0 + | _ :: s -> 1 + nargs s + in + let n = nargs kargs in + (List.skipn (n+1) kargs, strip_n_app n stk) + +end + +(** The type of (machine) states (= lambda-bar-calculus' cuts) *) + +(*************************************) +(*** Reduction Functions Operators ***) +(*************************************) + +let safe_meta_value sigma ev = + try Some (Evd.meta_value sigma ev) + with Not_found -> None + +(*************************************) +(*** Reduction using bindingss ***) +(*************************************) + +(* Beta Reduction tools *) + +let apply_subst recfun env sigma refold cst_l t stack = + let rec aux env cst_l t stack = + match (Stack.decomp stack, EConstr.kind sigma t) with + | Some (h,stacktl), Lambda (_,_,c) -> + let cst_l' = if refold then Cst_stack.add_param h cst_l else cst_l in + aux (h::env) cst_l' c stacktl + | _ -> recfun sigma cst_l (substl env t, stack) + in aux env cst_l t stack + +(* Iota reduction tools *) + +(** @return c if there is a constant c whose body is bd + @return bd else. + + It has only a meaning because internal representation of "Fixpoint f x + := t" is Definition f := fix f x => t + + Even more fragile that we could hope because do Module M. Fixpoint + f x := t. End M. Definition f := u. and say goodbye to any hope + of refolding M.f this way ... +*) +let magically_constant_of_fixbody env sigma reference bd = function + | Name.Anonymous -> bd + | Name.Name id -> + let open UnivProblem in + let (cst_mod,_) = Constant.repr2 reference in + let cst = Constant.make2 cst_mod (Label.of_id id) in + if not (Environ.mem_constant cst env) then bd + else + let (cst, u), ctx = UnivGen.fresh_constant_instance env cst in + match constant_opt_value_in env (cst,u) with + | None -> bd + | Some t -> + let csts = EConstr.eq_constr_universes env sigma (EConstr.of_constr t) bd in + begin match csts with + | Some csts -> + let subst = Set.fold (fun cst acc -> + let l, r = match cst with + | ULub (u, v) | UWeak (u, v) -> u, v + | UEq (u, v) | ULe (u, v) -> + let get u = Option.get (Universe.level u) in + get u, get v + in + Univ.LMap.add l r acc) + csts Univ.LMap.empty + in + let inst = Instance.subst_fn (fun u -> Univ.LMap.find u subst) u in + mkConstU (cst, EInstance.make inst) + | None -> bd + end + +let contract_cofix ?env sigma ?reference (bodynum,(names,types,bodies as typedbodies)) = + let nbodies = Array.length bodies in + let make_Fi j = + let ind = nbodies-j-1 in + if Int.equal bodynum ind then mkCoFix (ind,typedbodies) + else + let bd = mkCoFix (ind,typedbodies) in + match env with + | None -> bd + | Some e -> + match reference with + | None -> bd + | Some r -> magically_constant_of_fixbody e sigma r bd names.(ind).binder_name in + let closure = List.init nbodies make_Fi in + substl closure bodies.(bodynum) + +(** Similar to the "fix" case below *) +let reduce_and_refold_cofix recfun env sigma refold cst_l cofix sk = + let raw_answer = + let env = if refold then Some env else None in + contract_cofix ?env sigma ?reference:(Cst_stack.reference sigma cst_l) cofix in + apply_subst + (fun sigma x (t,sk') -> + let t' = + if refold then Cst_stack.best_replace sigma (mkCoFix cofix) cst_l t else t in + recfun x (t',sk')) + [] sigma refold Cst_stack.empty raw_answer sk + +(* contracts fix==FIX[nl;i](A1...Ak;[F1...Fk]{B1....Bk}) to produce + Bi[Fj --> FIX[nl;j](A1...Ak;[F1...Fk]{B1...Bk})] *) + +let contract_fix ?env sigma ?reference ((recindices,bodynum),(names,types,bodies as typedbodies)) = + let nbodies = Array.length recindices in + let make_Fi j = + let ind = nbodies-j-1 in + if Int.equal bodynum ind then mkFix ((recindices,ind),typedbodies) + else + let bd = mkFix ((recindices,ind),typedbodies) in + match env with + | None -> bd + | Some e -> + match reference with + | None -> bd + | Some r -> magically_constant_of_fixbody e sigma r bd names.(ind).binder_name in + let closure = List.init nbodies make_Fi in + substl closure bodies.(bodynum) + +(** First we substitute the Rel bodynum by the fixpoint and then we try to + replace the fixpoint by the best constant from [cst_l] + Other rels are directly substituted by constants "magically found from the + context" in contract_fix *) +let reduce_and_refold_fix recfun env sigma refold cst_l fix sk = + let raw_answer = + let env = if refold then Some env else None in + contract_fix ?env sigma ?reference:(Cst_stack.reference sigma cst_l) fix in + apply_subst + (fun sigma x (t,sk') -> + let t' = + if refold then + Cst_stack.best_replace sigma (mkFix fix) cst_l t + else t + in recfun x (t',sk')) + [] sigma refold Cst_stack.empty raw_answer sk + +module CredNative = Reductionops.CredNative + +(** Generic reduction function with environment + + Here is where unfolded constant are stored in order to be + eventually refolded. + + If tactic_mode is true, it uses ReductionBehaviour, prefers + refold constant instead of value and tries to infer constants + fix and cofix came from. + + It substitutes fix and cofix by the constant they come from in + contract_* in any case . +*) + +let debug_RAKAM = Reductionops.debug_RAKAM + +let equal_stacks sigma (x, l) (y, l') = + let f_equal x y = eq_constr sigma x y in + let eq_fix a b = f_equal (mkFix a) (mkFix b) in + Stack.equal f_equal eq_fix l l' && f_equal x y + +let rec whd_state_gen ?csts ~refold ~tactic_mode flags env sigma = + let open Context.Named.Declaration in + let open ReductionBehaviour in + let rec whrec cst_l (x, stack) = + let () = if debug_RAKAM () then + let open Pp in + let pr c = Termops.Internal.print_constr_env env sigma c in + Feedback.msg_debug + (h 0 (str "<<" ++ pr x ++ + str "|" ++ cut () ++ Cst_stack.pr env sigma cst_l ++ + str "|" ++ cut () ++ Stack.pr pr stack ++ + str ">>")) + in + let c0 = EConstr.kind sigma x in + let fold () = + let () = if debug_RAKAM () then + let open Pp in Feedback.msg_debug (str "<><><><><>") in + ((EConstr.of_kind c0, stack),cst_l) + in + match c0 with + | Rel n when CClosure.RedFlags.red_set flags CClosure.RedFlags.fDELTA -> + (match lookup_rel n env with + | LocalDef (_,body,_) -> whrec Cst_stack.empty (lift n body, stack) + | _ -> fold ()) + | Var id when CClosure.RedFlags.red_set flags (CClosure.RedFlags.fVAR id) -> + (match lookup_named id env with + | LocalDef (_,body,_) -> + whrec (if refold then Cst_stack.add_cst (mkVar id) cst_l else cst_l) (body, stack) + | _ -> fold ()) + | Evar ev -> fold () + | Meta ev -> + (match safe_meta_value sigma ev with + | Some body -> whrec cst_l (body, stack) + | None -> fold ()) + | Const (c,u as const) -> + Reductionops.reduction_effect_hook env sigma c + (lazy (EConstr.to_constr sigma (Stack.zip sigma (x,stack)))); + if CClosure.RedFlags.red_set flags (CClosure.RedFlags.fCONST c) then + let u' = EInstance.kind sigma u in + match constant_value_in env (c, u') with + | body -> + begin + let body = EConstr.of_constr body in + if not tactic_mode + then whrec (if refold then Cst_stack.add_cst (mkConstU const) cst_l else cst_l) + (body, stack) + else (* Looks for ReductionBehaviour *) + match ReductionBehaviour.get (GlobRef.ConstRef c) with + | None -> whrec (Cst_stack.add_cst (mkConstU const) cst_l) (body, stack) + | Some behavior -> + begin match behavior with + | NeverUnfold -> fold () + | (UnfoldWhen { nargs = Some n } | + UnfoldWhenNoMatch { nargs = Some n } ) + when Stack.args_size stack < n -> + fold () + | UnfoldWhenNoMatch { recargs } -> (* maybe unfolds *) + let app_sk,sk = Stack.strip_app stack in + let (tm',sk'),cst_l' = + whrec (Cst_stack.add_cst (mkConstU const) cst_l) (body, app_sk) + in + let rec is_case x = match EConstr.kind sigma x with + | Lambda (_,_, x) | LetIn (_,_,_, x) | Cast (x, _,_) -> is_case x + | App (hd, _) -> is_case hd + | Case _ -> true + | _ -> false in + if equal_stacks sigma (x, app_sk) (tm', sk') + || Stack.will_expose_iota sk' + || is_case tm' + then fold () + else whrec cst_l' (tm', sk' @ sk) + | UnfoldWhen { recargs } -> (* maybe unfolds *) + begin match recargs with + |[] -> (* if nargs has been specified *) + (* CAUTION : the constant is NEVER refold + (even when it hides a (co)fix) *) + whrec cst_l (body, stack) + |curr::remains -> match Stack.strip_n_app curr stack with + | None -> fold () + | Some (bef,arg,s') -> + whrec Cst_stack.empty + (arg,Stack.Cst(Stack.Cst_const (fst const, u'),curr,remains,bef,cst_l)::s') + end + end + end + | exception NotEvaluableConst (IsPrimitive p) when Stack.check_native_args p stack -> + let kargs = CPrimitives.kind p in + let (kargs,o) = Stack.get_next_primitive_args kargs stack in + (* Should not fail thanks to [check_native_args] *) + let (before,a,after) = Option.get o in + whrec Cst_stack.empty (a,Stack.Primitive(p,const,before,kargs,cst_l)::after) + | exception NotEvaluableConst _ -> fold () + else fold () + | Proj (p, c) when CClosure.RedFlags.red_projection flags p -> + (let npars = Projection.npars p in + if not tactic_mode then + let stack' = (c, Stack.Proj (p, Cst_stack.empty (*cst_l*)) :: stack) in + whrec Cst_stack.empty stack' + else match ReductionBehaviour.get (GlobRef.ConstRef (Projection.constant p)) with + | None -> + let stack' = (c, Stack.Proj (p, cst_l) :: stack) in + let stack'', csts = whrec Cst_stack.empty stack' in + if equal_stacks sigma stack' stack'' then fold () + else stack'', csts + | Some behavior -> + begin match behavior with + | NeverUnfold -> fold () + | (UnfoldWhen { nargs = Some n } + | UnfoldWhenNoMatch { nargs = Some n }) + when Stack.args_size stack < n - (npars + 1) -> fold () + | UnfoldWhen { recargs } + | UnfoldWhenNoMatch { recargs }-> (* maybe unfolds *) + let recargs = List.map_filter (fun x -> + let idx = x - npars in + if idx < 0 then None else Some idx) recargs + in + match recargs with + |[] -> (* if nargs has been specified *) + (* CAUTION : the constant is NEVER refold + (even when it hides a (co)fix) *) + let stack' = (c, Stack.Proj (p, cst_l) :: stack) in + whrec Cst_stack.empty(* cst_l *) stack' + | curr::remains -> + if curr == 0 then (* Try to reduce the record argument *) + whrec Cst_stack.empty + (c, Stack.Cst(Stack.Cst_proj p,curr,remains,Stack.empty,cst_l)::stack) + else + match Stack.strip_n_app curr stack with + | None -> fold () + | Some (bef,arg,s') -> + whrec Cst_stack.empty + (arg,Stack.Cst(Stack.Cst_proj p,curr,remains, + Stack.append_app [|c|] bef,cst_l)::s') + end) + + | LetIn (_,b,_,c) when CClosure.RedFlags.red_set flags CClosure.RedFlags.fZETA -> + apply_subst (fun _ -> whrec) [b] sigma refold cst_l c stack + | Cast (c,_,_) -> whrec cst_l (c, stack) + | App (f,cl) -> + whrec + (if refold then Cst_stack.add_args cl cst_l else cst_l) + (f, Stack.append_app cl stack) + | Lambda (na,t,c) -> + (match Stack.decomp stack with + | Some _ when CClosure.RedFlags.red_set flags CClosure.RedFlags.fBETA -> + apply_subst (fun _ -> whrec) [] sigma refold cst_l x stack + | None when CClosure.RedFlags.red_set flags CClosure.RedFlags.fETA -> + let env' = push_rel (LocalAssum (na, t)) env in + let whrec' = whd_state_gen ~refold ~tactic_mode flags env' sigma in + (match EConstr.kind sigma (Stack.zip ~refold sigma (fst (whrec' (c, Stack.empty)))) with + | App (f,cl) -> + let napp = Array.length cl in + if napp > 0 then + let (x', l'),_ = whrec' (Array.last cl, Stack.empty) in + match EConstr.kind sigma x', l' with + | Rel 1, [] -> + let lc = Array.sub cl 0 (napp-1) in + let u = if Int.equal napp 1 then f else mkApp (f,lc) in + if noccurn sigma 1 u then (pop u,Stack.empty),Cst_stack.empty else fold () + | _ -> fold () + else fold () + | _ -> fold ()) + | _ -> fold ()) + + | Case (ci,p,d,lf) -> + whrec Cst_stack.empty (d, Stack.Case (ci,p,lf,cst_l) :: stack) + + | Fix ((ri,n),_ as f) -> + (match Stack.strip_n_app ri.(n) stack with + |None -> fold () + |Some (bef,arg,s') -> + whrec Cst_stack.empty (arg, Stack.Fix(f,bef,cst_l)::s')) + + | Construct ((ind,c),u) -> + let use_match = CClosure.RedFlags.red_set flags CClosure.RedFlags.fMATCH in + let use_fix = CClosure.RedFlags.red_set flags CClosure.RedFlags.fFIX in + if use_match || use_fix then + match Stack.strip_app stack with + |args, (Stack.Case(ci, _, lf,_)::s') when use_match -> + whrec Cst_stack.empty (lf.(c-1), (Stack.tail ci.ci_npar args) @ s') + |args, (Stack.Proj (p,_)::s') when use_match -> + whrec Cst_stack.empty (Stack.nth args (Projection.npars p + Projection.arg p), s') + |args, (Stack.Fix (f,s',cst_l)::s'') when use_fix -> + let x' = Stack.zip sigma (x, args) in + let out_sk = s' @ (Stack.append_app [|x'|] s'') in + reduce_and_refold_fix whrec env sigma refold cst_l f out_sk + |args, (Stack.Cst (const,curr,remains,s',cst_l) :: s'') -> + let x' = Stack.zip sigma (x, args) in + begin match remains with + | [] -> + (match const with + | Stack.Cst_const const -> + (match constant_opt_value_in env const with + | None -> fold () + | Some body -> + let const = (fst const, EInstance.make (snd const)) in + let body = EConstr.of_constr body in + whrec (if refold then Cst_stack.add_cst (mkConstU const) cst_l else cst_l) + (body, s' @ (Stack.append_app [|x'|] s''))) + | Stack.Cst_proj p -> + let stack = s' @ (Stack.append_app [|x'|] s'') in + match Stack.strip_n_app 0 stack with + | None -> assert false + | Some (_,arg,s'') -> + whrec Cst_stack.empty (arg, Stack.Proj (p,cst_l) :: s'')) + | next :: remains' -> match Stack.strip_n_app (next-curr-1) s'' with + | None -> fold () + | Some (bef,arg,s''') -> + whrec Cst_stack.empty + (arg, + Stack.Cst (const,next,remains',s' @ (Stack.append_app [|x'|] bef),cst_l) :: s''') + end + |_, (Stack.App _)::_ -> assert false + |_, _ -> fold () + else fold () + + | CoFix cofix -> + if CClosure.RedFlags.red_set flags CClosure.RedFlags.fCOFIX then + match Stack.strip_app stack with + |args, ((Stack.Case _ |Stack.Proj _)::s') -> + reduce_and_refold_cofix whrec env sigma refold cst_l cofix stack + |_ -> fold () + else fold () + + | Int _ | Float _ -> + begin match Stack.strip_app stack with + | (_, Stack.Primitive(p,kn,rargs,kargs,cst_l')::s) -> + let more_to_reduce = List.exists (fun k -> CPrimitives.Kwhnf = k) kargs in + if more_to_reduce then + let (kargs,o) = Stack.get_next_primitive_args kargs s in + (* Should not fail because Primitive is put on the stack only if fully applied *) + let (before,a,after) = Option.get o in + whrec Cst_stack.empty (a,Stack.Primitive(p,kn,rargs @ Stack.append_app [|x|] before,kargs,cst_l')::after) + else + let n = List.length kargs in + let (args,s) = Stack.strip_app s in + let (args,extra_args) = + try List.chop n args + with List.IndexOutOfRange -> (args,[]) (* FIXME probably useless *) + in + let args = Array.of_list (Option.get (Stack.list_of_app_stack (rargs @ Stack.append_app [|x|] args))) in + begin match CredNative.red_prim env sigma p args with + | Some t -> whrec cst_l' (t,s) + | None -> ((mkApp (mkConstU kn, args), s), cst_l) + end + | _ -> fold () + end + + | Rel _ | Var _ | LetIn _ | Proj _ -> fold () + | Sort _ | Ind _ | Prod _ -> fold () + in + fun xs -> + let (s,cst_l as res) = whrec (Option.default Cst_stack.empty csts) xs in + if tactic_mode then (Stack.best_state sigma s cst_l,Cst_stack.empty) else res + +let whd_cbn flags env sigma t = + let (state,_) = + (whd_state_gen ~refold:true ~tactic_mode:true flags env sigma (t, Stack.empty)) + in + Stack.zip ~refold:true sigma state diff --git a/tactics/cbn.mli b/tactics/cbn.mli new file mode 100644 index 0000000000..af54771382 --- /dev/null +++ b/tactics/cbn.mli @@ -0,0 +1,13 @@ +(************************************************************************) +(* * The Coq Proof Assistant / The Coq Development Team *) +(* v * INRIA, CNRS and contributors - Copyright 1999-2019 *) +(* <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) *) +(************************************************************************) + +val whd_cbn : + CClosure.RedFlags.reds -> + Environ.env -> Evd.evar_map -> EConstr.constr -> EConstr.constr diff --git a/tactics/class_tactics.ml b/tactics/class_tactics.ml index 80c76bee60..195073d7aa 100644 --- a/tactics/class_tactics.ml +++ b/tactics/class_tactics.ml @@ -516,25 +516,21 @@ let make_resolve_hyp env sigma st flags only_classes pri decl = let is_class = iscl env cty in let keep = not only_classes || is_class in if keep then - let c = mkVar id in - let name = PathHints [GlobRef.VarRef id] in + let id = GlobRef.VarRef id in + let name = PathHints [id] in let hints = if is_class then - let hints = build_subclasses ~check:false env sigma (GlobRef.VarRef id) empty_hint_info in + let hints = build_subclasses ~check:false env sigma id empty_hint_info in (List.map_append (fun (path,info,c) -> let h = IsConstr (EConstr.of_constr c,Univ.ContextSet.empty) [@ocaml.warning "-3"] in make_resolves env sigma ~name:(PathHints path) - (true,false,not !Flags.quiet) info ~poly:false + (true,false,not !Flags.quiet) info ~check:true ~poly:false h) hints) else [] in - (hints @ List.map_filter - (fun f -> try Some (f (c, cty, Univ.ContextSet.empty)) - with Failure _ | UserError _ -> None) - [make_exact_entry ~name env sigma pri ~poly:false; - make_apply_entry ~name env sigma flags pri ~poly:false]) + (hints @ make_resolves env sigma flags pri ~name ~check:false ~poly:false (IsGlobRef id)) else [] let make_hints g (modes,st) only_classes sign = diff --git a/tactics/eauto.ml b/tactics/eauto.ml index 7d8620468d..c2eabffd44 100644 --- a/tactics/eauto.ml +++ b/tactics/eauto.ml @@ -59,32 +59,12 @@ let registered_e_assumption = (* PROLOG tactic *) (************************************************************************) -(*s Tactics handling a list of goals. *) - -(* first_goal : goal list sigma -> goal sigma *) - -let first_goal gls = - let gl = gls.Evd.it and sig_0 = gls.Evd.sigma in - if List.is_empty gl then user_err Pp.(str "first_goal"); - { Evd.it = List.hd gl; Evd.sigma = sig_0; } - -(* tactic -> tactic_list : Apply a tactic to the first goal in the list *) - -let apply_tac_list tac glls = - match glls.it with - | (g1::rest) -> - let pack = tac (re_sig g1 glls.sigma) in - re_sig (pack.it @ rest) pack.sigma - | _ -> user_err Pp.(str "apply_tac_list") - open Auto (***************************************************************************) (* A tactic similar to Auto, but using EApply, Assumption and e_give_exact *) (***************************************************************************) -let priority l = List.map snd (List.filter (fun (pr,_) -> Int.equal pr 0) l) - let unify_e_resolve poly flags (c,clenv) = Proofview.Goal.enter begin fun gl -> let clenv', c = connect_hint_clenv ~poly c clenv gl in @@ -94,7 +74,9 @@ let unify_e_resolve poly flags (c,clenv) = (Tactics.Simple.eapply c) end -let hintmap_of sigma secvars hdc concl = +let hintmap_of sigma secvars concl = + (* Warning: for computation sharing, we need to return a closure *) + let hdc = try Some (decompose_app_bound sigma concl) with Bound -> None in match hdc with | None -> fun db -> Hint_db.map_none ~secvars db | Some hdc -> @@ -125,13 +107,13 @@ let rec e_trivial_fail_db db_list local_db = let tacl = registered_e_assumption :: (Tacticals.New.tclTHEN Tactics.intro next) :: - (List.map fst (e_trivial_resolve (Tacmach.New.pf_env gl) (Tacmach.New.project gl) db_list local_db secvars (Tacmach.New.pf_concl gl))) + (e_trivial_resolve (Tacmach.New.pf_env gl) (Tacmach.New.project gl) db_list local_db secvars (Tacmach.New.pf_concl gl)) in Tacticals.New.tclSOLVE tacl end -and e_my_find_search env sigma db_list local_db secvars hdc concl = - let hint_of_db = hintmap_of sigma secvars hdc concl in +and e_my_find_search env sigma db_list local_db secvars concl = + let hint_of_db = hintmap_of sigma secvars concl in let hintl = List.map_append (fun db -> let flags = auto_flags_of_state (Hint_db.transparent_state db) in @@ -143,35 +125,40 @@ and e_my_find_search env sigma db_list local_db secvars hdc concl = | Unfold_nth _ -> 1 | _ -> b in - (b, - let tac = function - | Res_pf (term,cl) -> unify_resolve ~poly st (term,cl) - | ERes_pf (term,cl) -> unify_e_resolve poly st (term,cl) - | Give_exact (c,cl) -> e_exact poly st (c,cl) - | Res_pf_THEN_trivial_fail (term,cl) -> - Tacticals.New.tclTHEN (unify_e_resolve poly st (term,cl)) - (e_trivial_fail_db db_list local_db) - | Unfold_nth c -> reduce (Unfold [AllOccurrences,c]) onConcl - | Extern tacast -> conclPattern concl p tacast - in - let tac = run_hint t tac in - (tac, lazy (pr_hint env sigma t))) + let tac = function + | Res_pf (term,cl) -> unify_resolve ~poly st (term,cl) + | ERes_pf (term,cl) -> unify_e_resolve poly st (term,cl) + | Give_exact (c,cl) -> e_exact poly st (c,cl) + | Res_pf_THEN_trivial_fail (term,cl) -> + Tacticals.New.tclTHEN (unify_e_resolve poly st (term,cl)) + (e_trivial_fail_db db_list local_db) + | Unfold_nth c -> reduce (Unfold [AllOccurrences,c]) onConcl + | Extern tacast -> conclPattern concl p tacast + in + let tac = run_hint t tac in + (tac, b, lazy (pr_hint env sigma t)) in List.map tac_of_hint hintl and e_trivial_resolve env sigma db_list local_db secvars gl = - let hd = try Some (decompose_app_bound sigma gl) with Bound -> None in - try priority (e_my_find_search env sigma db_list local_db secvars hd gl) + let filter (tac, pr, _) = if Int.equal pr 0 then Some tac else None in + try List.map_filter filter (e_my_find_search env sigma db_list local_db secvars gl) with Not_found -> [] let e_possible_resolve env sigma db_list local_db secvars gl = - let hd = try Some (decompose_app_bound sigma gl) with Bound -> None in - try List.map (fun (b, (tac, pp)) -> (tac, b, pp)) - (e_my_find_search env sigma db_list local_db secvars hd gl) + try e_my_find_search env sigma db_list local_db secvars gl with Not_found -> [] +(*s Tactics handling a list of goals. *) + +(* first_goal : goal list sigma -> goal sigma *) + let find_first_goal gls = - try first_goal gls with UserError _ -> assert false + let gl = gls.Evd.it and sig_0 = gls.Evd.sigma in + match gl with + | [] -> assert false + | (gl, db) :: _ -> + { Evd.it = gl; Evd.sigma = sig_0; }, db (*s The following module [SearchProblem] is used to instantiate the generic exploration functor [Explore.Make]. *) @@ -179,10 +166,9 @@ let find_first_goal gls = type search_state = { priority : int; depth : int; (*r depth of search before failing *) - tacres : Goal.goal list sigma; + tacres : (Goal.goal * hint_db) list sigma; last_tactic : Pp.t Lazy.t; dblist : hint_db list; - localdb : hint_db list; prev : prev_search_state; local_lemmas : delayed_open_constr list; } @@ -200,7 +186,16 @@ module SearchProblem = struct (* let pr_ev evs ev = Printer.pr_constr_env (Evd.evar_env ev) (Evarutil.nf_evar evs ev.Evd.evar_concl) *) - let filter_tactics glls l = +(* tactic -> tactic_list : Apply a tactic to the first goal in the list *) + + let apply_tac_list tac mkdb glls = + match glls.it with + | ((g1, db) :: rest) -> + let pack = tac (re_sig g1 glls.sigma) in + List.map (fun gl -> gl, mkdb db (re_sig gl pack.sigma)) pack.it, re_sig rest pack.sigma + | _ -> user_err Pp.(str "apply_tac_list") + + let filter_tactics mkdb glls l = (* let _ = Proof_trees.db_pr_goal (List.hd (sig_it glls)) in *) (* let evars = Evarutil.nf_evars (Refiner.project glls) in *) (* msg (str"Goal:" ++ pr_ev evars (List.hd (sig_it glls)) ++ str"\n"); *) @@ -208,10 +203,10 @@ module SearchProblem = struct | [] -> [] | (tac, cost, pptac) :: tacl -> try - let lgls = apply_tac_list (Proofview.V82.of_tactic tac) glls in + let ngls, lgls = apply_tac_list (Proofview.V82.of_tactic tac) mkdb glls in (* let gl = Proof_trees.db_pr_goal (List.hd (sig_it glls)) in *) (* msg (hov 1 (pptac ++ str" gives: \n" ++ pr_goals lgls ++ str"\n")); *) - (lgls, cost, pptac) :: aux tacl + (ngls, lgls, cost, pptac) :: aux tacl with e when CErrors.noncritical e -> let e = Exninfo.capture e in Refiner.catch_failerror e; aux tacl @@ -233,63 +228,54 @@ module SearchProblem = struct else let ps = if s.prev == Unknown then Unknown else State s in let lg = s.tacres in - let nbgl = List.length (sig_it lg) in - assert (nbgl > 0); - let g = find_first_goal lg in + let g, db = find_first_goal lg in let hyps = pf_ids_of_hyps g in let secvars = secvars_of_hyps (pf_hyps g) in let map_assum id = (e_give_exact (mkVar id), (-1), lazy (str "exact" ++ spc () ++ Id.print id)) in let assumption_tacs = let tacs = List.map map_assum hyps in - let l = filter_tactics s.tacres tacs in - List.map (fun (res, cost, pp) -> { depth = s.depth; priority = cost; tacres = res; + let mkdb db gl = assert false in (* no goal can be generated *) + let l = filter_tactics mkdb s.tacres tacs in + List.map (fun (ngl, res, cost, pp) -> + let () = assert (List.is_empty ngl) in + { depth = s.depth; priority = cost; tacres = res; last_tactic = pp; dblist = s.dblist; - localdb = List.tl s.localdb; prev = ps; local_lemmas = s.local_lemmas}) l in let intro_tac = - let l = filter_tactics s.tacres [Tactics.intro, (-1), lazy (str "intro")] in + let mkdb db gl = + let hintl = make_resolve_hyp (pf_env gl) (project gl) (pf_last_hyp gl) in + Hint_db.add_list (pf_env gl) (project gl) hintl db + in + let l = filter_tactics mkdb s.tacres [Tactics.intro, (-1), lazy (str "intro")] in List.map - (fun (lgls, cost, pp) -> - let g' = first_goal lgls in - let hintl = - make_resolve_hyp (pf_env g') (project g') (pf_last_hyp g') - in - let ldb = Hint_db.add_list (pf_env g') (project g') - hintl (List.hd s.localdb) in + (fun (ngls, lgls, cost, pp) -> + let lgls = re_sig (ngls @ lgls.it) lgls.sigma in { depth = s.depth; priority = cost; tacres = lgls; last_tactic = pp; dblist = s.dblist; - localdb = ldb :: List.tl s.localdb; prev = ps; + prev = ps; local_lemmas = s.local_lemmas}) l in let rec_tacs = + let hyps = pf_hyps g in + let mkdb db gls = + let hyps' = pf_hyps gls in + if hyps' == hyps then db + else make_local_hint_db (pf_env gls) (project gls) ~ts:TransparentState.full true s.local_lemmas + in let l = let concl = Reductionops.nf_evar (project g) (pf_concl g) in - filter_tactics s.tacres - (e_possible_resolve (pf_env g) (project g) s.dblist (List.hd s.localdb) secvars concl) + filter_tactics mkdb s.tacres + (e_possible_resolve (pf_env g) (project g) s.dblist db secvars concl) in List.map - (fun (lgls, cost, pp) -> - let nbgl' = List.length (sig_it lgls) in - if nbgl' < nbgl then - { depth = s.depth; priority = cost; tacres = lgls; last_tactic = pp; - prev = ps; dblist = s.dblist; localdb = List.tl s.localdb; - local_lemmas = s.local_lemmas } - else - let newlocal = - let hyps = pf_hyps g in - List.map (fun gl -> - let gls = {Evd.it = gl; sigma = lgls.Evd.sigma } in - let hyps' = pf_hyps gls in - if hyps' == hyps then List.hd s.localdb - else make_local_hint_db (pf_env gls) (project gls) ~ts:TransparentState.full true s.local_lemmas) - (List.firstn ((nbgl'-nbgl) + 1) (sig_it lgls)) - in - { depth = pred s.depth; priority = cost; tacres = lgls; - dblist = s.dblist; last_tactic = pp; prev = ps; - localdb = newlocal @ List.tl s.localdb; - local_lemmas = s.local_lemmas }) + (fun (ngls, lgls, cost, pp) -> + let lgls = re_sig (ngls @ lgls.it) lgls.sigma in + let depth = if List.is_empty ngls then s.depth else pred s.depth in + { depth; priority = cost; tacres = lgls; last_tactic = pp; + prev = ps; dblist = s.dblist; + local_lemmas = s.local_lemmas }) l in List.sort compare (assumption_tacs @ intro_tac @ rec_tacs) @@ -353,15 +339,15 @@ let pr_info dbg s = let make_initial_state dbg n gl dblist localdb lems = { depth = n; priority = 0; - tacres = tclIDTAC gl; + tacres = re_sig [gl.it, localdb] gl.sigma; last_tactic = lazy (mt()); dblist = dblist; - localdb = [localdb]; prev = if dbg == Info then Init else Unknown; local_lemmas = lems; } -let e_search_auto debug (in_depth,p) lems db_list gl = +let e_search_auto debug (in_depth,p) lems db_list = + Proofview.V82.tactic ~nf_evars:false begin fun gl -> let local_db = make_local_hint_db (pf_env gl) (project gl) ~ts:TransparentState.full true lems in let d = mk_eauto_dbg debug in let tac = match in_depth,d with @@ -374,28 +360,29 @@ let e_search_auto debug (in_depth,p) lems db_list gl = pr_dbg_header d; let s = tac (make_initial_state d p gl db_list local_db lems) in pr_info d s; - s.tacres + re_sig (List.map fst s.tacres.it) s.tacres.sigma with Not_found -> pr_info_nop d; - user_err Pp.(str "eauto: search failed") + tclIDTAC gl + end (* let e_search_auto_key = CProfile.declare_profile "e_search_auto" *) (* let e_search_auto = CProfile.profile5 e_search_auto_key e_search_auto *) let eauto_with_bases ?(debug=Off) np lems db_list = - Hints.wrap_hint_warning (Proofview.V82.tactic (tclTRY (e_search_auto debug np lems db_list))) + Hints.wrap_hint_warning (e_search_auto debug np lems db_list) let eauto ?(debug=Off) np lems dbnames = let db_list = make_db_list dbnames in - tclTRY (e_search_auto debug np lems db_list) + e_search_auto debug np lems db_list -let full_eauto ?(debug=Off) n lems gl = +let full_eauto ?(debug=Off) n lems = let db_list = current_pure_db () in - tclTRY (e_search_auto debug n lems db_list) gl + e_search_auto debug n lems db_list let gen_eauto ?(debug=Off) np lems = function - | None -> Hints.wrap_hint_warning (Proofview.V82.tactic (full_eauto ~debug np lems)) - | Some l -> Hints.wrap_hint_warning (Proofview.V82.tactic (eauto ~debug np lems l)) + | None -> Hints.wrap_hint_warning (full_eauto ~debug np lems) + | Some l -> Hints.wrap_hint_warning (eauto ~debug np lems l) let make_depth = function | None -> !default_search_depth diff --git a/tactics/hints.ml b/tactics/hints.ml index a0670ef70d..0c23532e12 100644 --- a/tactics/hints.ml +++ b/tactics/hints.ml @@ -882,7 +882,7 @@ let fresh_global_or_constr env sigma poly cr = (c, Univ.ContextSet.empty) end -let make_resolves env sigma flags info ~poly ?name cr = +let make_resolves env sigma flags info ~check ~poly ?name cr = let c, ctx = fresh_global_or_constr env sigma poly cr in let cty = Retyping.get_type_of env sigma c in let try_apply f = @@ -891,7 +891,7 @@ let make_resolves env sigma flags info ~poly ?name cr = [make_exact_entry env sigma info ~poly ?name; make_apply_entry env sigma flags info ~poly ?name] in - if List.is_empty ents then + if check && List.is_empty ents then user_err ~hdr:"Hint" (pr_leconstr_env env sigma c ++ spc() ++ (if pi1 flags then str"cannot be used as a hint." @@ -1183,7 +1183,7 @@ let add_resolves env sigma clist ~local ~superglobal dbnames = let r = List.flatten (List.map (fun (pri, poly, hnf, path, gr) -> make_resolves env sigma (true,hnf,not !Flags.quiet) - pri ~poly ~name:path gr) clist) + pri ~check:true ~poly ~name:path gr) clist) in let hint = make_hint ~local dbname (AddHints { superglobal; hints = r }) in Lib.add_anonymous_leaf (inAutoHint hint)) @@ -1334,7 +1334,7 @@ let expand_constructor_hints env sigma lems = let constructor_hints env sigma eapply lems = let lems = expand_constructor_hints env sigma lems in List.map_append (fun (poly, lem) -> - make_resolves env sigma (eapply,true,false) empty_hint_info ~poly lem) lems + make_resolves env sigma (eapply,true,false) empty_hint_info ~check:true ~poly lem) lems let make_local_hint_db env sigma ts eapply lems = let map c = c env sigma in diff --git a/tactics/hints.mli b/tactics/hints.mli index f5fd3348e4..6c8b7fed75 100644 --- a/tactics/hints.mli +++ b/tactics/hints.mli @@ -206,36 +206,6 @@ val add_hints : locality:Goptions.option_locality -> hint_db_name list -> hints_ val prepare_hint : bool (* Check no remaining evars *) -> env -> evar_map -> evar_map * constr -> (constr * Univ.ContextSet.t) -(** [make_exact_entry info (c, ctyp, ctx)]. - [c] is the term given as an exact proof to solve the goal; - [ctyp] is the type of [c]. - [ctx] is its (refreshable) universe context. - In info: - [hint_priority] is the hint's desired priority, it is 0 if unspecified - [hint_pattern] is the hint's desired pattern, it is inferred if not specified -*) - -val make_exact_entry : env -> evar_map -> hint_info -> poly:bool -> ?name:hints_path_atom -> - (constr * types * Univ.ContextSet.t) -> hint_entry - -(** [make_apply_entry (eapply,hnf,verbose) info (c,cty,ctx))]. - [eapply] is true if this hint will be used only with EApply; - [hnf] should be true if we should expand the head of cty before searching for - products; - [c] is the term given as an exact proof to solve the goal; - [cty] is the type of [c]. - [ctx] is its (refreshable) universe context. - In info: - [hint_priority] is the hint's desired priority, it is computed as the number of products in [cty] - if unspecified - [hint_pattern] is the hint's desired pattern, it is inferred from the conclusion of [cty] - if not specified -*) - -val make_apply_entry : - env -> evar_map -> bool * bool * bool -> hint_info -> poly:bool -> ?name:hints_path_atom -> - (constr * types * Univ.ContextSet.t) -> hint_entry - (** A constr which is Hint'ed will be: - (1) used as an Exact, if it does not start with a product - (2) used as an Apply, if its HNF starts with a product, and @@ -244,7 +214,7 @@ val make_apply_entry : has missing arguments. *) val make_resolves : - env -> evar_map -> bool * bool * bool -> hint_info -> poly:bool -> ?name:hints_path_atom -> + env -> evar_map -> bool * bool * bool -> hint_info -> check:bool -> poly:bool -> ?name:hints_path_atom -> hint_term -> hint_entry list (** [make_resolve_hyp hname htyp]. diff --git a/tactics/redexpr.ml b/tactics/redexpr.ml index f681e4e99e..e000ddce74 100644 --- a/tactics/redexpr.ml +++ b/tactics/redexpr.ml @@ -44,11 +44,7 @@ let cbv_native env sigma c = (warn_native_compute_disabled (); cbv_vm env sigma c) -let whd_cbn flags env sigma t = - let (state,_) = - (whd_state_gen ~refold:true ~tactic_mode:true flags env sigma (t,Reductionops.Stack.empty)) - in - Reductionops.Stack.zip ~refold:true sigma state +let whd_cbn = Cbn.whd_cbn let strong_cbn flags = strong_with_flags whd_cbn flags diff --git a/tactics/tactics.mllib b/tactics/tactics.mllib index 36d61feed1..88025e3fb4 100644 --- a/tactics/tactics.mllib +++ b/tactics/tactics.mllib @@ -9,6 +9,7 @@ Eqschemes Elimschemes Genredexpr Redops +Cbn Redexpr Ppred Tactics |
