aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES6
-rw-r--r--doc/refman/RefMan-gal.tex14
-rw-r--r--lib/util.ml8
-rw-r--r--lib/util.mli3
-rw-r--r--pretyping/pretyping.ml66
-rw-r--r--pretyping/pretyping.mli5
-rw-r--r--toplevel/command.ml57
7 files changed, 114 insertions, 45 deletions
diff --git a/CHANGES b/CHANGES
index 7917e72953..efbe41c5eb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,12 @@
Changes from V8.1 to V8.2
=========================
+Language
+
+- If a fixpoint is not written with an explicit { struct ... }, then
+ all arguments are tried successively (from left to right) until one is
+ found that satisfies the structural decreasing condition.
+
Commands
- Added option Global to "Implicit Arguments" and "Arguments Scope" for
diff --git a/doc/refman/RefMan-gal.tex b/doc/refman/RefMan-gal.tex
index 424e5047da..8ee30b8fb8 100644
--- a/doc/refman/RefMan-gal.tex
+++ b/doc/refman/RefMan-gal.tex
@@ -1329,9 +1329,8 @@ syntactical constraints on a special argument called the decreasing
argument. They are needed to ensure that the {\tt Fixpoint} definition
always terminates. The point of the {\tt \{struct \ident {\tt \}}}
annotation is to let the user tell the system which argument decreases
-along the recursive calls. This annotation may be left implicit for
-fixpoints where only one argument has an inductive type. For instance,
-one can define the addition function as :
+along the recursive calls. For instance, one can define the addition
+function as :
\begin{coq_example}
Fixpoint add (n m:nat) {struct n} : nat :=
@@ -1341,6 +1340,15 @@ Fixpoint add (n m:nat) {struct n} : nat :=
end.
\end{coq_example}
+The {\tt \{struct \ident {\tt \}}} annotation may be left implicit, in
+this case the system try successively arguments from left to right
+until it finds one that satisfies the decreasing condition. Note that
+some fixpoints may have several arguments that fit as decreasing
+arguments, and this choice influences the reduction of the
+fixpoint. Hence an explicit annotation must be used if the leftmost
+decreasing argument is not the desired one. Writing explicit
+annotations can also speed up type-checking of large mutual fixpoints.
+
The {\tt match} operator matches a value (here \verb:n:) with the
various constructors of its (inductive) type. The remaining arguments
give the respective values to be returned, as functions of the
diff --git a/lib/util.ml b/lib/util.ml
index a1c011ce1b..590d649931 100644
--- a/lib/util.ml
+++ b/lib/util.ml
@@ -445,6 +445,14 @@ let list_fold_map' f l e =
let list_map_assoc f = List.map (fun (x,a) -> (x,f a))
+(* list_combinations [[a;b];[c;d]] gives [[a;c];[a;d];[b;c];[b;d]] *)
+
+let rec list_combinations = function
+ | [] -> [[]]
+ | l::ll ->
+ let res = list_combinations ll in
+ list_map_append (fun x -> List.map (fun l -> x::l) res) l
+
(* Arrays *)
let array_exists f v =
diff --git a/lib/util.mli b/lib/util.mli
index 5c59708211..c315ecd0ac 100644
--- a/lib/util.mli
+++ b/lib/util.mli
@@ -138,6 +138,9 @@ val list_join_map : ('a -> 'b list) -> 'a list -> 'b list
val list_fold_map : ('a -> 'b -> 'a * 'c) -> 'a -> 'b list -> 'a * 'c list
val list_fold_map' : ('b -> 'a -> 'c * 'a) -> 'b list -> 'a -> 'c list * 'a
val list_map_assoc : ('a -> 'b) -> ('c * 'a) list -> ('c * 'b) list
+(* list_combinations [[a;b];[c;d]] gives [[a;c];[a;d];[b;c];[b;d]] *)
+val list_combinations : 'a list list -> 'a list list
+
(*s Arrays. *)
diff --git a/pretyping/pretyping.ml b/pretyping/pretyping.ml
index 3aefeeb077..c5742268b1 100644
--- a/pretyping/pretyping.ml
+++ b/pretyping/pretyping.ml
@@ -43,6 +43,34 @@ open Inductiveops
(************************************************************************)
+(* An auxiliary function for searching for fixpoint guard indexes *)
+
+exception Found of int array
+
+let search_guard loc env possible_indexes fixdefs =
+ (* Standard situation with only one possibility for each fix. *)
+ (* We treat it separately in order to get proper error msg. *)
+ if List.for_all (fun l->1=List.length l) possible_indexes then
+ let indexes = Array.of_list (List.map List.hd possible_indexes) in
+ let fix = ((indexes, 0),fixdefs) in
+ (try check_fix env fix with
+ | e -> if loc = dummy_loc then raise e else Stdpp.raise_with_loc loc e);
+ indexes
+ else
+ (* we now search recursively amoungst all combinations *)
+ (try
+ List.iter
+ (fun l ->
+ let indexes = Array.of_list l in
+ let fix = ((indexes, 0),fixdefs) in
+ try check_fix env fix; raise (Found indexes)
+ with TypeError _ -> ())
+ (list_combinations possible_indexes);
+ let errmsg = "cannot guess decreasing argument of fix" in
+ if loc = dummy_loc then error errmsg else
+ user_err_loc (loc,"search_guard", Pp.str errmsg)
+ with Found indexes -> indexes)
+
(* To embed constr in rawconstr *)
let ((constr_in : constr -> Dyn.t),
(constr_out : Dyn.t -> constr)) = create "constr"
@@ -266,6 +294,8 @@ module Pretyping_F (Coercion : Coercion.S) = struct
| RProp c -> judge_of_prop_contents c
| RType _ -> judge_of_new_Type ()
+ exception Found of fixpoint
+
(* [pretype tycon env isevars lvar lmeta cstr] attempts to type [cstr] *)
(* in environment [env], with existential variables [(evars_of isevars)] and *)
(* the type constraint tycon *)
@@ -342,28 +372,22 @@ module Pretyping_F (Coercion : Coercion.S) = struct
evar_type_fixpoint loc env isevars names ftys vdefj;
let fixj = match fixkind with
| RFix (vn,i) ->
- let guard_indexes = Array.mapi
+ (* First, let's find the guard indexes. *)
+ (* If recursive argument was not given by user, we try all args.
+ An earlier approach was to look only for inductive arguments,
+ but doing it properly involves delta-reduction, and it finally
+ doesn't seem to worth the effort (except for huge mutual
+ fixpoints ?) *)
+ let possible_indexes = Array.to_list (Array.mapi
(fun i (n,_) -> match n with
- | Some n -> n
- | None ->
- (* Recursive argument was not given by the user : We
- check that there is only one inductive argument *)
- let ctx = ctxtv.(i) in
- let isIndApp t =
- isInd (fst (decompose_app (strip_head_cast t))) in
- (* This could be more precise (e.g. do some delta) *)
- let lb = List.rev_map (fun (_,_,t) -> isIndApp t) ctx in
- try (list_unique_index true lb) - 1
- with Not_found ->
- Util.user_err_loc
- (loc,"pretype",
- Pp.str "cannot guess decreasing argument of fix"))
- vn
- in
- let fix = ((guard_indexes, i),(names,ftys,Array.map j_val vdefj)) in
- (try check_fix env fix with e -> Stdpp.raise_with_loc loc e);
- make_judge (mkFix fix) ftys.(i)
- | RCoFix i ->
+ | Some n -> [n]
+ | None -> list_map_i (fun i _ -> i) 0 ctxtv.(i))
+ vn)
+ in
+ let fixdecls = (names,ftys,Array.map j_val vdefj) in
+ let indexes = search_guard loc env possible_indexes fixdecls in
+ make_judge (mkFix ((indexes,i),fixdecls)) ftys.(i)
+ | RCoFix i ->
let cofix = (i,(names,ftys,Array.map j_val vdefj)) in
(try check_cofix env cofix with e -> Stdpp.raise_with_loc loc e);
make_judge (mkCoFix cofix) ftys.(i) in
diff --git a/pretyping/pretyping.mli b/pretyping/pretyping.mli
index 0b9b47b7a7..64144f9a4d 100644
--- a/pretyping/pretyping.mli
+++ b/pretyping/pretyping.mli
@@ -18,6 +18,11 @@ open Rawterm
open Evarutil
(*i*)
+(* An auxiliary function for searching for fixpoint guard indexes *)
+
+val search_guard :
+ Util.loc -> env -> int list list -> rec_declaration -> int array
+
type typing_constraint = OfType of types option | IsType
type var_map = (identifier * unsafe_judgment) list
diff --git a/toplevel/command.ml b/toplevel/command.ml
index 6c4fe20571..05166b21fa 100644
--- a/toplevel/command.ml
+++ b/toplevel/command.ml
@@ -452,13 +452,27 @@ let build_mutual l finite =
(* 3c| Fixpoints and co-fixpoints *)
-let recursive_message = function
+let pr_rank = function
+ | 0 -> str "1st"
+ | 1 -> str "2nd"
+ | 2 -> str "3rd"
+ | n -> str ((string_of_int n)^"th")
+
+let recursive_message indexes = function
| [] -> anomaly "no recursive definition"
- | [id] -> pr_id id ++ str " is recursively defined"
+ | [id] -> pr_id id ++ str " is recursively defined" ++
+ (match indexes with
+ | Some [|i|] -> str " (decreasing on "++pr_rank i++str " argument)"
+ | _ -> mt ())
| l -> hov 0 (prlist_with_sep pr_coma pr_id l ++
- spc () ++ str "are recursively defined")
-
-let corecursive_message = function
+ spc () ++ str "are recursively defined" ++
+ match indexes with
+ | Some a -> spc () ++ str "(decreasing respectively on " ++
+ prlist_with_sep pr_coma pr_rank (Array.to_list a) ++
+ str " arguments)"
+ | None -> mt ())
+
+let corecursive_message _ = function
| [] -> error "no corecursive definition"
| [id] -> pr_id id ++ str " is corecursively defined"
| l -> hov 0 (prlist_with_sep pr_coma pr_id l ++
@@ -572,19 +586,18 @@ let prepare_recursive_declaration fixnames fixtypes fixdefs =
let names = List.map (fun id -> Name id) fixnames in
(Array.of_list names, Array.of_list fixtypes, Array.of_list defs)
-let compute_guardness_evidence (n,_) fixl fixtype =
+let compute_possible_guardness_evidences (n,_) fixl fixtype =
match n with
- | Some n -> n
+ | Some n -> [n]
| None ->
- (* Recursive argument was not given by the user :
- We check that there is only one inductive argument *)
+ (* If recursive argument was not given by user, we try all args.
+ An earlier approach was to look only for inductive arguments,
+ but doing it properly involves delta-reduction, and it finally
+ doesn't seem to worth the effort (except for huge mutual
+ fixpoints ?) *)
let m = local_binders_length fixl.fix_binders in
let ctx = fst (Sign.decompose_prod_n_assum m fixtype) in
- let isIndApp t = isInd (fst (decompose_app (strip_head_cast t))) in
- (* This could be more precise (e.g. do some delta) *)
- let lb = List.rev_map (fun (_,_,t) -> isIndApp t) ctx in
- try (list_unique_index true lb) - 1
- with Not_found -> error "the recursive argument needs to be specified"
+ list_map_i (fun i _ -> i) 0 ctx
let interp_recursive fixkind l boxed =
let env = Global.env() in
@@ -618,18 +631,20 @@ let interp_recursive fixkind l boxed =
(* Build the fix declaration block *)
let fixdecls = prepare_recursive_declaration fixnames fixtypes fixdefs in
- let fixdecls =
+ let indexes, fixdecls =
match fixkind with
| IsFixpoint wfl ->
- let fixwf = list_map3 compute_guardness_evidence wfl fixl fixtypes in
- list_map_i (fun i _ -> mkFix ((Array.of_list fixwf,i),fixdecls)) 0 l
+ let possible_indexes =
+ list_map3 compute_possible_guardness_evidences wfl fixl fixtypes in
+ let indexes = search_guard dummy_loc env possible_indexes fixdecls in
+ Some indexes, list_map_i (fun i _ -> mkFix ((indexes,i),fixdecls)) 0 l
| IsCoFixpoint ->
- list_map_i (fun i _ -> mkCoFix (i,fixdecls)) 0 l
+ None, list_map_i (fun i _ -> mkCoFix (i,fixdecls)) 0 l
in
(* Declare the recursive definitions *)
ignore (list_map3 (declare_fix boxed kind) fixnames fixdecls fixtypes);
- if_verbose ppnl (recursive_message kind fixnames);
+ if_verbose ppnl (recursive_message kind indexes fixnames);
(* Declare notations *)
List.iter (declare_interning_data ([],[])) notations
@@ -680,7 +695,7 @@ let build_induction_scheme lnamedepindsort =
ConstRef kn :: lrecref
in
let _ = List.fold_right2 declare listdecl lrecnames [] in
- if_verbose ppnl (recursive_message Fixpoint lrecnames)
+ if_verbose ppnl (recursive_message Fixpoint None lrecnames)
let build_scheme l =
let ischeme,escheme = split_scheme l in
@@ -745,7 +760,7 @@ let build_combined_scheme name schemes =
const_entry_opaque = false;
const_entry_boxed = Options.boxed_definitions() } in
let _ = declare_constant (snd name) (DefinitionEntry ce, IsDefinition Scheme) in
- if_verbose ppnl (recursive_message Fixpoint [snd name])
+ if_verbose ppnl (recursive_message Fixpoint None [snd name])
(* 4| Goal declaration *)