aboutsummaryrefslogtreecommitdiffhomepage
path: root/pretyping/tacred.ml
diff options
context:
space:
mode:
authorGravatar gareuselesinge <gareuselesinge@85f007b7-540e-0410-9357-904b9bb8a0f7>2011-11-21 17:03:42 +0000
committerGravatar gareuselesinge <gareuselesinge@85f007b7-540e-0410-9357-904b9bb8a0f7>2011-11-21 17:03:42 +0000
commitb1bfd9757d33d36b9fc009a97173ea7db2d5196d (patch)
tree0a6f2627ad5490a35679814b849a34378891f238 /pretyping/tacred.ml
parent5e62a6a476c925e58e169e43468ed0cee422bb1a (diff)
Configurable simpl tactic
The problem that this patch tries to solve is that sometimes the unfolding behaviour of simpl is too aggressive, frequently exposing match constructs. Moreover one may want some constants to never be unfolded by simpl (like in the Math-Comp library where the nosimpl hack is pervasive). Finally, one may want some constants to be volatile, always unfolded by simple when applied to enough arguments, even if they do not hide a fixpoint or a match. A new vernacular command to be indroduced in a following patch attaches to constants an extra-logical piece of information. It declares wich arguments must be checked to evaluate to a constructor before performing the unfolding of the constant (in the same spirit simpl considers as such the recursive argument of an inner fixpoint). Examples: Arguments minus !x !y. (* x and y must evaluate to a constructor for simpl to unfold minus *) (S x - y) --- simpl ---> (S x - y) (S x - S y) --- simpl ---> (x - y) Definition fcomp A B C (f : B -> C) (g : A -> B) x := f (g x). Arguments fcomp A B C f g x /. Infix "\o" := fcomp (at level 50, left associativity) : nat_scope. (* fcomp hides no fix, but simpl will unfold if applied to 6 arguments *) (fun x => (f \o g) x) = f \o g --- simpl ---> (fun x => f (g x)) = f \o g Arguments minus x y : simpl never. (* simpl will never unfold minus *) (S x - S y) --- simpl ---> (S x - S y) Definition nid (x : nat) := x. Arguments nid / x. (* nid is volatile, unfolded by simpl when applied to 0 or more arguments *) nid --- simpl ---> (fun x => x) Arguments minus x y : simpl nomatch. (* triggers for minus the "new" simpl heuristic I discussed with Hugo: if reducing the fix would leave a match in "head" position then don't unfold a suggestion for a better name (for "nomatch") would be appreciated *) (0 - y) --- simpl ---> 0 (S x - y) --- simpl ---> (S x - y) (S x - S y) --- simpl ---> (x - y) (minus 0) --- simpl ---> (minus 0) (* This last test does not work as one may expect, i.e. (minus 0) --- simpl ---> (fun _ => 0) The point is that simpl is implemented as "strong whd_simpl" and after unfolding minus you have (fun m => match 0 => 0 | S n' => ...n' - m'... end) that is already in whd and exposes a match, that of course "strong" would reduce away but at that stage we don't know, and reducing by hand under the lambda is against whd reduction. So further discussion on that idea is needed. *) git-svn-id: svn+ssh://scm.gforge.inria.fr/svn/coq/trunk@14716 85f007b7-540e-0410-9357-904b9bb8a0f7
Diffstat (limited to 'pretyping/tacred.ml')
-rw-r--r--pretyping/tacred.ml129
1 files changed, 120 insertions, 9 deletions
diff --git a/pretyping/tacred.ml b/pretyping/tacred.ml
index b6c67555d..4cf1e2345 100644
--- a/pretyping/tacred.ml
+++ b/pretyping/tacred.ml
@@ -16,6 +16,7 @@ open Termops
open Namegen
open Declarations
open Inductive
+open Libobject
open Environ
open Closure
open Reductionops
@@ -515,27 +516,127 @@ let special_red_case env sigma whfun (ci, p, c, lf) =
in
redrec (c, empty_stack)
+(* data structure to hold the map kn -> rec_args for simpl *)
+
+type behaviour = {
+ b_nargs: int;
+ b_recargs: int list;
+ b_dont_expose_case: bool;
+}
+
+let behaviour_table = ref (Refmap.empty : behaviour Refmap.t)
+
+let _ =
+ Summary.declare_summary "simplbehaviour"
+ { Summary.freeze_function = (fun () -> !behaviour_table);
+ Summary.unfreeze_function = (fun x -> behaviour_table := x);
+ Summary.init_function = (fun () -> behaviour_table := Refmap.empty) }
+
+type simpl_flag = [ `SimplDontExposeCase | `SimplNeverUnfold ]
+type req =
+ | ReqLocal
+ | ReqGlobal of global_reference * (int list * int * simpl_flag list)
+
+let load_simpl_behaviour _ (_,(_,(r, b))) =
+ behaviour_table := Refmap.add r b !behaviour_table
+
+let cache_simpl_behaviour o = load_simpl_behaviour 1 o
+
+let classify_simpl_behaviour = function
+ | ReqLocal, _ -> Dispose
+ | ReqGlobal _, _ as o -> Substitute o
+
+let subst_simpl_behaviour (subst, (_, (r,o as orig))) =
+ ReqLocal,
+ let r' = fst (subst_global subst r) in if r==r' then orig else (r',o)
+
+let discharge_simpl_behaviour = function
+ | _,(ReqGlobal (ConstRef c, req), (_, b)) ->
+ let c' = pop_con c in
+ let vars = Lib.section_segment_of_constant c in
+ let extra = List.length vars in
+ let nargs' = if b.b_nargs < 0 then b.b_nargs else b.b_nargs + extra in
+ let recargs' = List.map ((+) extra) b.b_recargs in
+ let b' = { b with b_nargs = nargs'; b_recargs = recargs' } in
+ Some (ReqGlobal (ConstRef c', req), (ConstRef c', b'))
+ | _ -> None
+
+let rebuild_simpl_behaviour = function
+ | req, (ConstRef c, _ as x) -> req, x
+ | _ -> assert false
+
+let inSimplBehaviour = declare_object { (default_object "SIMPLBEHAVIOUR") with
+ load_function = load_simpl_behaviour;
+ cache_function = cache_simpl_behaviour;
+ classify_function = classify_simpl_behaviour;
+ subst_function = subst_simpl_behaviour;
+ discharge_function = discharge_simpl_behaviour;
+ rebuild_function = rebuild_simpl_behaviour;
+}
+
+let set_simpl_behaviour local r (recargs, nargs, flags as req) =
+ let nargs = if List.mem `SimplNeverUnfold flags then max_int else nargs in
+ let nargs = List.fold_left max nargs recargs in
+ let behaviour = {
+ b_nargs = nargs; b_recargs = recargs;
+ b_dont_expose_case = List.mem `SimplDontExposeCase flags } in
+ let req = if local then ReqLocal else ReqGlobal (r, req) in
+ Lib.add_anonymous_leaf (inSimplBehaviour (req, (r, behaviour)))
+;;
+
+let get_simpl_behaviour r =
+ try
+ let b = Refmap.find r !behaviour_table in
+ let flags =
+ if b.b_nargs = max_int then [`SimplNeverUnfold]
+ else if b.b_dont_expose_case then [`SimplDontExposeCase] else [] in
+ Some (b.b_recargs, (if b.b_nargs = max_int then -1 else b.b_nargs), flags)
+ with Not_found -> None
+
+let get_behaviour = function
+ | EvalVar _ | EvalRel _ | EvalEvar _ -> raise Not_found
+ | EvalConst c -> Refmap.find (ConstRef c) !behaviour_table
+
+let recargs r =
+ try let b = get_behaviour r in Some (b.b_recargs, b.b_nargs)
+ with Not_found -> None
+
+let dont_expose_case r =
+ try (get_behaviour r).b_dont_expose_case with Not_found -> false
+
(* [red_elim_const] contracts iota/fix/cofix redexes hidden behind
constants by keeping the name of the constants in the recursive calls;
it fails if no redex is around *)
let rec red_elim_const env sigma ref largs =
- match reference_eval sigma env ref with
- | EliminationCases n when stack_args_size largs >= n ->
+ let nargs = stack_args_size largs in
+ let largs, unfold_anyway =
+ match recargs ref with
+ | None -> largs, false
+ | Some (_,n) when nargs < n -> raise Redelimination
+ | Some (l,n) ->
+ List.fold_left (fun stack i ->
+ let arg = stack_nth stack i in
+ let rarg = whd_construct_state env sigma (arg, empty_stack) in
+ match kind_of_term (fst rarg) with
+ | Construct _ -> stack_assign stack i (app_stack rarg)
+ | _ -> raise Redelimination)
+ largs l, n >= 0 && l = [] && nargs >= n in
+ try match reference_eval sigma env ref with
+ | EliminationCases n when nargs >= n ->
let c = reference_value sigma env ref in
let c', lrest = whd_betadelta_state env sigma (c,largs) in
let whfun = whd_simpl_state env sigma in
(special_red_case env sigma whfun (destCase c'), lrest)
- | EliminationFix (min,minfxargs,infos) when stack_args_size largs >=min ->
+ | EliminationFix (min,minfxargs,infos) when nargs >= min ->
let c = reference_value sigma env ref in
let d, lrest = whd_betadelta_state env sigma (c,largs) in
let f = make_elim_fun ([|Some (minfxargs,ref)|],infos) largs in
let whfun = whd_construct_state env sigma in
(match reduce_fix_use_function env sigma f whfun (destFix d) lrest with
| NotReducible -> raise Redelimination
- | Reduced (c,rest) -> (nf_beta sigma c, rest))
- | EliminationMutualFix (min,refgoal,refinfos)
- when stack_args_size largs >= min ->
+ | Reduced (c,rest) -> (nf_beta sigma c, rest))
+ | EliminationMutualFix (min,refgoal,refinfos) when nargs >= min ->
let rec descend ref args =
let c = reference_value sigma env ref in
if ref = refgoal then
@@ -551,6 +652,9 @@ let rec red_elim_const env sigma ref largs =
| NotReducible -> raise Redelimination
| Reduced (c,rest) -> (nf_beta sigma c, rest))
| _ -> raise Redelimination
+ with Redelimination when unfold_anyway ->
+ let c = reference_value sigma env ref in
+ whd_betaiotazeta sigma (app_stack (c, largs)), empty_stack
(* reduce to whd normal form or to an applied constant that does not hide
a reducible iota/fix/cofix redex (the "simpl" tactic) *)
@@ -578,7 +682,14 @@ and whd_simpl_state env sigma s =
| _ when isEvalRef env x ->
let ref = destEvalRef x in
(try
- redrec (red_elim_const env sigma ref stack)
+ let hd, _ as s' = redrec (red_elim_const env sigma ref stack) in
+ let rec is_case x = match kind_of_term x with
+ | Lambda (_,_, x) | LetIn (_,_,_, x) | Cast (x, _,_) -> is_case x
+ | App (hd, _) -> is_case hd
+ | Case _ -> true
+ | _ -> false in
+ if dont_expose_case ref && is_case hd then raise Redelimination
+ else s'
with Redelimination ->
s)
| _ -> s
@@ -919,7 +1030,7 @@ let one_step_reduce env sigma c =
| _ when isEvalRef env x ->
let ref = destEvalRef x in
(try
- red_elim_const env sigma ref stack
+ red_elim_const env sigma ref stack
with Redelimination ->
match reference_opt_value sigma env ref with
| Some d -> d, stack
@@ -959,7 +1070,7 @@ let reduce_to_ref_gen allow_product env sigma ref t =
with Not_found ->
try
let t' = nf_betaiota sigma (one_step_reduce env sigma t) in
- elimrec env t' l
+ elimrec env t' l
with NotStepReducible ->
errorlabstrm ""
(str "Cannot recognize a statement based on " ++