summaryrefslogtreecommitdiff
path: root/tactics/tactics.ml
diff options
context:
space:
mode:
Diffstat (limited to 'tactics/tactics.ml')
-rw-r--r--tactics/tactics.ml787
1 files changed, 402 insertions, 385 deletions
diff --git a/tactics/tactics.ml b/tactics/tactics.ml
index 186b5c48..988d9f53 100644
--- a/tactics/tactics.ml
+++ b/tactics/tactics.ml
@@ -1,13 +1,12 @@
(************************************************************************)
(* v * The Coq Proof Assistant / The Coq Development Team *)
-(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2011 *)
+(* <O___,, * INRIA - CNRS - LIX - LRI - PPS - Copyright 1999-2010 *)
(* \VV/ **************************************************************)
(* // * This file is distributed under the terms of the *)
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
-(* $Id: tactics.ml 14641 2011-11-06 11:59:10Z herbelin $ *)
-
+open Compat
open Pp
open Util
open Names
@@ -25,9 +24,8 @@ open Libnames
open Evd
open Pfedit
open Tacred
-open Rawterm
+open Glob_term
open Tacmach
-open Proof_trees
open Proof_type
open Logic
open Evar_refiner
@@ -61,6 +59,8 @@ let inj_with_occurrences e = (all_occurrences_expr,e)
let dloc = dummy_loc
+let typ_of = Retyping.get_type_of
+
(* Option for 8.2 compatibility *)
open Goptions
let dependent_propositions_elimination = ref true
@@ -72,25 +72,15 @@ let use_dependent_propositions_elimination () =
let _ =
declare_bool_option
{ optsync = true;
+ optdepr = false;
optname = "dependent-propositions-elimination tactic";
optkey = ["Dependent";"Propositions";"Elimination"];
optread = (fun () -> !dependent_propositions_elimination) ;
optwrite = (fun b -> dependent_propositions_elimination := b) }
-let apply_in_side_conditions_come_first = ref true
-
-let use_apply_in_side_conditions_come_first () =
- !apply_in_side_conditions_come_first
- && Flags.version_strictly_greater Flags.V8_2
-
-let _ =
- declare_bool_option
- { optsync = true;
- optname = "apply-in side-conditions coming first";
- optkey = ["Side";"Conditions";"First";"For";"apply";"in"];
- optread = (fun () -> !dependent_propositions_elimination) ;
- optwrite = (fun b -> dependent_propositions_elimination := b) }
-
+let finish_evar_resolution env initial_sigma c =
+ snd (Pretyping.solve_remaining_evars true true solve_by_implicit_tactic
+ env initial_sigma c)
(*********************************************)
(* Tactics *)
@@ -165,7 +155,6 @@ let internal_cut_rev_replace = internal_cut_rev_gen true
(* Moving hypotheses *)
let move_hyp = Tacmach.move_hyp
-let order_hyps = Tacmach.order_hyps
(* Renaming hypotheses *)
let rename_hyp = Tacmach.rename_hyp
@@ -285,9 +274,12 @@ let reduct_in_hyp redfun (id,where) gl =
convert_hyp_no_check
(pf_reduce_decl redfun where (pf_get_hyp gl id) gl) gl
+let revert_cast (redfun,kind as r) =
+ if kind = DEFAULTcast then (redfun,REVERTcast) else r
+
let reduct_option redfun = function
| Some id -> reduct_in_hyp (fst redfun) id
- | None -> reduct_in_concl redfun
+ | None -> reduct_in_concl (revert_cast redfun)
(* Now we introduce different instances of the previous tacticals *)
let change_and_check cv_pb t env sigma c =
@@ -323,35 +315,25 @@ let change chg c cls gl =
cls gl
(* Pour usage interne (le niveau User est pris en compte par reduce) *)
-let try_red_in_concl = reduct_in_concl (try_red_product,DEFAULTcast)
-let red_in_concl = reduct_in_concl (red_product,DEFAULTcast)
+let try_red_in_concl = reduct_in_concl (try_red_product,REVERTcast)
+let red_in_concl = reduct_in_concl (red_product,REVERTcast)
let red_in_hyp = reduct_in_hyp red_product
-let red_option = reduct_option (red_product,DEFAULTcast)
-let hnf_in_concl = reduct_in_concl (hnf_constr,DEFAULTcast)
+let red_option = reduct_option (red_product,REVERTcast)
+let hnf_in_concl = reduct_in_concl (hnf_constr,REVERTcast)
let hnf_in_hyp = reduct_in_hyp hnf_constr
-let hnf_option = reduct_option (hnf_constr,DEFAULTcast)
-let simpl_in_concl = reduct_in_concl (simpl,DEFAULTcast)
+let hnf_option = reduct_option (hnf_constr,REVERTcast)
+let simpl_in_concl = reduct_in_concl (simpl,REVERTcast)
let simpl_in_hyp = reduct_in_hyp simpl
-let simpl_option = reduct_option (simpl,DEFAULTcast)
-let normalise_in_concl = reduct_in_concl (compute,DEFAULTcast)
+let simpl_option = reduct_option (simpl,REVERTcast)
+let normalise_in_concl = reduct_in_concl (compute,REVERTcast)
let normalise_in_hyp = reduct_in_hyp compute
-let normalise_option = reduct_option (compute,DEFAULTcast)
+let normalise_option = reduct_option (compute,REVERTcast)
let normalise_vm_in_concl = reduct_in_concl (Redexpr.cbv_vm,VMcast)
-let unfold_in_concl loccname = reduct_in_concl (unfoldn loccname,DEFAULTcast)
+let unfold_in_concl loccname = reduct_in_concl (unfoldn loccname,REVERTcast)
let unfold_in_hyp loccname = reduct_in_hyp (unfoldn loccname)
let unfold_option loccname = reduct_option (unfoldn loccname,DEFAULTcast)
let pattern_option l = reduct_option (pattern_occs l,DEFAULTcast)
-(* A function which reduces accordingly to a reduction expression,
- as the command Eval does. *)
-
-let checking_fun = function
- (* Expansion is not necessarily well-typed: e.g. expansion of t into x is
- not well-typed in [H:(P t); x:=t |- G] because x is defined after H *)
- | Fold _ -> with_check
- | Pattern _ -> with_check
- | _ -> (fun x -> x)
-
(* The main reduction function *)
let reduction_clause redexp cl =
@@ -433,31 +415,34 @@ let find_intro_names ctxt gl =
ctxt (pf_env gl , []) in
List.rev res
-let build_intro_tac id = function
- | MoveToEnd true -> introduction id
- | dest -> tclTHEN (introduction id) (move_hyp true id dest)
+let build_intro_tac id dest tac = match dest with
+ | MoveToEnd true -> tclTHEN (introduction id) (tac id)
+ | dest -> tclTHENLIST [introduction id; move_hyp true id dest; tac id]
-let rec intro_gen loc name_flag move_flag force_flag dep_flag gl =
+let rec intro_then_gen loc name_flag move_flag force_flag dep_flag tac gl =
match kind_of_term (pf_concl gl) with
| Prod (name,t,u) when not dep_flag or (dependent (mkRel 1) u) ->
- build_intro_tac (find_name loc (name,None,t) gl name_flag) move_flag gl
+ build_intro_tac (find_name loc (name,None,t) gl name_flag) move_flag tac gl
| LetIn (name,b,t,u) when not dep_flag or (dependent (mkRel 1) u) ->
- build_intro_tac (find_name loc (name,Some b,t) gl name_flag) move_flag
+ build_intro_tac (find_name loc (name,Some b,t) gl name_flag) move_flag tac
gl
| _ ->
if not force_flag then raise (RefinerError IntroNeedsProduct);
try
tclTHEN try_red_in_concl
- (intro_gen loc name_flag move_flag force_flag dep_flag) gl
+ (intro_then_gen loc name_flag move_flag force_flag dep_flag tac) gl
with Redelimination ->
user_err_loc(loc,"Intro",str "No product even after head-reduction.")
+let intro_gen loc n m f d = intro_then_gen loc n m f d (fun _ -> tclIDTAC)
let intro_mustbe_force id = intro_gen dloc (IntroMustBe id) no_move true false
let intro_using id = intro_gen dloc (IntroBasedOn (id,[])) no_move false false
+let intro_then = intro_then_gen dloc (IntroAvoid []) no_move false false
let intro = intro_gen dloc (IntroAvoid []) no_move false false
let introf = intro_gen dloc (IntroAvoid []) no_move true false
let intro_avoiding l = intro_gen dloc (IntroAvoid l) no_move false false
-let introf_move_name destopt = intro_gen dloc (IntroAvoid []) destopt true false
+
+let intro_then_force = intro_then_gen dloc (IntroAvoid []) no_move true false
(**** Multiple introduction tactics ****)
@@ -469,8 +454,13 @@ let intros = tclREPEAT intro
let intro_erasing id = tclTHEN (thin [id]) (introduction id)
-let intro_forthcoming_gen loc name_flag move_flag dep_flag =
- tclREPEAT (intro_gen loc name_flag move_flag false dep_flag)
+let intro_forthcoming_then_gen loc name_flag move_flag dep_flag tac =
+ let rec aux ids =
+ tclORELSE0
+ (intro_then_gen loc name_flag move_flag false dep_flag
+ (fun id -> aux (id::ids)))
+ (tac ids) in
+ aux []
let rec get_next_hyp_position id = function
| [] -> error ("No such hypothesis: " ^ string_of_id id)
@@ -517,8 +507,8 @@ let intro_move idopt hto = match idopt with
| Some id -> intro_gen dloc (IntroMustBe id) hto true false
let pf_lookup_hypothesis_as_renamed env ccl = function
- | AnonHyp n -> pf_lookup_index_as_renamed env ccl n
- | NamedHyp id -> pf_lookup_name_as_displayed env ccl id
+ | AnonHyp n -> Detyping.lookup_index_as_renamed env ccl n
+ | NamedHyp id -> Detyping.lookup_name_as_displayed env ccl id
let pf_lookup_hypothesis_as_renamed_gen red h gl =
let env = pf_env gl in
@@ -565,8 +555,13 @@ let intros_until = intros_until_gen true
let intros_until_n = intros_until_n_gen true
let intros_until_n_wored = intros_until_n_gen false
+let tclCHECKVAR id gl = ignore (pf_get_hyp gl id); tclIDTAC gl
+
+let try_intros_until_id_check id =
+ tclORELSE (intros_until_id id) (tclCHECKVAR id)
+
let try_intros_until tac = function
- | NamedHyp id -> tclTHEN (tclTRY (intros_until_id id)) (tac id)
+ | NamedHyp id -> tclTHEN (try_intros_until_id_check id) (tac id)
| AnonHyp n -> tclTHEN (intros_until_n n) (onLastHypId tac)
let rec intros_move = function
@@ -583,17 +578,32 @@ let dependent_in_decl a (_,c,t) =
(* Apply a tactic on a quantified hypothesis, an hypothesis in context
or a term with bindings *)
+let onOpenInductionArg tac = function
+ | ElimOnConstr cbl ->
+ tac cbl
+ | ElimOnAnonHyp n ->
+ tclTHEN
+ (intros_until_n n)
+ (onLastHyp (fun c -> tac (Evd.empty,(c,NoBindings))))
+ | ElimOnIdent (_,id) ->
+ (* A quantified hypothesis *)
+ tclTHEN
+ (try_intros_until_id_check id)
+ (tac (Evd.empty,(mkVar id,NoBindings)))
+
let onInductionArg tac = function
- | ElimOnConstr (c,lbindc as cbl) ->
- if isVar c & lbindc = NoBindings then
- tclTHEN (tclTRY (intros_until_id (destVar c))) (tac cbl)
- else
- tac cbl
+ | ElimOnConstr cbl ->
+ tac cbl
| ElimOnAnonHyp n ->
tclTHEN (intros_until_n n) (onLastHyp (fun c -> tac (c,NoBindings)))
| ElimOnIdent (_,id) ->
- (*Identifier apart because id can be quantified in goal and not typable*)
- tclTHEN (tclTRY (intros_until_id id)) (tac (mkVar id,NoBindings))
+ (* A quantified hypothesis *)
+ tclTHEN (try_intros_until_id_check id) (tac (mkVar id,NoBindings))
+
+let map_induction_arg f = function
+ | ElimOnConstr (sigma,(c,bl)) -> ElimOnConstr (f (sigma,c),bl)
+ | ElimOnAnonHyp n -> ElimOnAnonHyp n
+ | ElimOnIdent id -> ElimOnIdent id
(**************************)
(* Refinement tactics *)
@@ -615,9 +625,9 @@ let bring_hyps hyps =
let resolve_classes gl =
let env = pf_env gl and evd = project gl in
- if evd = Evd.empty then tclIDTAC gl
+ if Evd.is_empty evd then tclIDTAC gl
else
- let evd' = Typeclasses.resolve_typeclasses env (Evd.create_evar_defs evd) in
+ let evd' = Typeclasses.resolve_typeclasses env evd in
(tclTHEN (tclEVARS evd') tclNORMEVAR) gl
(**************************)
@@ -723,24 +733,15 @@ let index_of_ind_arg t =
| None -> error "Could not find inductive argument of elimination scheme."
in aux None 0 t
-let elim_flags = {
- modulo_conv_on_closed_terms = Some full_transparent_state;
- use_metas_eagerly = true;
- modulo_delta = empty_transparent_state;
- resolve_evars = false;
- use_evars_pattern_unification = true;
-}
-
-let elimination_clause_scheme with_evars allow_K i elimclause indclause gl =
+let elimination_clause_scheme with_evars ?(flags=elim_flags) i elimclause indclause gl =
let indmv =
(match kind_of_term (nth_arg i elimclause.templval.rebus) with
| Meta mv -> mv
| _ -> errorlabstrm "elimination_clause"
(str "The type of elimination clause is not well-formed."))
in
- let elimclause' = clenv_fchain ~flags:elim_flags indmv elimclause indclause in
- res_pf elimclause' ~with_evars:with_evars ~allow_K:allow_K ~flags:elim_flags
- gl
+ let elimclause' = clenv_fchain ~flags indmv elimclause indclause in
+ res_pf elimclause' ~with_evars:with_evars ~flags gl
(*
* Elimination tactic with bindings and using an arbitrary
@@ -769,8 +770,8 @@ let general_elim_clause elimtac (c,lbindc) elim gl =
let indclause = make_clenv_binding gl (c,t) lbindc in
general_elim_clause_gen elimtac indclause elim gl
-let general_elim with_evars c e ?(allow_K=true) =
- general_elim_clause (elimination_clause_scheme with_evars allow_K) c e
+let general_elim with_evars c e =
+ general_elim_clause (elimination_clause_scheme with_evars) c e
(* Elimination tactic with bindings but using the default elimination
* constant associated with the type. *)
@@ -786,15 +787,15 @@ let default_elim with_evars (c,_ as cx) gl =
let elim_in_context with_evars c = function
| Some elim ->
general_elim with_evars c {elimindex = Some (-1); elimbody = elim}
- ~allow_K:true
| None -> default_elim with_evars c
let elim with_evars (c,lbindc as cx) elim =
match kind_of_term c with
| Var id when lbindc = NoBindings ->
- tclTHEN (tclTRY (intros_until_id id))
+ tclTHEN (try_intros_until_id_check id)
(elim_in_context with_evars cx elim)
- | _ -> elim_in_context with_evars cx elim
+ | _ ->
+ elim_in_context with_evars cx elim
(* The simplest elimination tactic, with no substitutions at all. *)
@@ -810,13 +811,13 @@ let simplest_elim c = default_elim false (c,NoBindings)
(e.g. it could replace id:A->B->C by id:C, knowing A/\B)
*)
-let clenv_fchain_in id elim_flags mv elimclause hypclause =
- try clenv_fchain ~allow_K:false ~flags:elim_flags mv elimclause hypclause
- with PretypeError (env,NoOccurrenceFound (op,_)) ->
+let clenv_fchain_in id ?(flags=elim_flags) mv elimclause hypclause =
+ try clenv_fchain ~flags mv elimclause hypclause
+ with PretypeError (env,evd,NoOccurrenceFound (op,_)) ->
(* Set the hypothesis name in the message *)
- raise (PretypeError (env,NoOccurrenceFound (op,Some id)))
+ raise (PretypeError (env,evd,NoOccurrenceFound (op,Some id)))
-let elimination_in_clause_scheme with_evars id i elimclause indclause gl =
+let elimination_in_clause_scheme with_evars ?(flags=elim_flags) id i elimclause indclause gl =
let indmv = destMeta (nth_arg i elimclause.templval.rebus) in
let hypmv =
try match list_remove indmv (clenv_independent elimclause) with
@@ -824,12 +825,11 @@ let elimination_in_clause_scheme with_evars id i elimclause indclause gl =
| _ -> failwith ""
with Failure _ -> errorlabstrm "elimination_clause"
(str "The type of elimination clause is not well-formed.") in
- let elimclause' = clenv_fchain indmv elimclause indclause in
+ let elimclause' = clenv_fchain ~flags indmv elimclause indclause in
let hyp = mkVar id in
let hyp_typ = pf_type_of gl hyp in
let hypclause = mk_clenv_from_n gl (Some 0) (hyp, hyp_typ) in
- let elimclause'' =
- clenv_fchain_in id elim_flags hypmv elimclause' hypclause in
+ let elimclause'' = clenv_fchain_in id ~flags hypmv elimclause' hypclause in
let new_hyp_typ = clenv_type elimclause'' in
if eq_constr hyp_typ new_hyp_typ then
errorlabstrm "general_rewrite_in"
@@ -855,8 +855,8 @@ let general_case_analysis_in_context with_evars (c,lbindc) gl =
let general_case_analysis with_evars (c,lbindc as cx) =
match kind_of_term c with
| Var id when lbindc = NoBindings ->
- tclTHEN (tclTRY (intros_until_id id))
- (general_case_analysis_in_context with_evars cx)
+ tclTHEN (try_intros_until_id_check id)
+ (general_case_analysis_in_context with_evars cx)
| _ ->
general_case_analysis_in_context with_evars cx
@@ -871,6 +871,7 @@ type conjunction_status =
let make_projection sigma params cstr sign elim i n c =
let elim = match elim with
| NotADefinedRecordUseScheme elim ->
+ (* bugs: goes from right to left when i increases! *)
let (na,b,t) = List.nth cstr.cs_args i in
let b = match b with None -> mkRel (i+1) | Some b -> b in
let branch = it_mkLambda_or_LetIn b cstr.cs_args in
@@ -885,6 +886,7 @@ let make_projection sigma params cstr sign elim i n c =
else
None
| DefinedRecord l ->
+ (* goes from left to right when i increases! *)
match List.nth l i with
| Some proj ->
let t = Typeops.type_of_constant (Global.env()) proj in
@@ -919,7 +921,7 @@ let descend_in_conjunctions tac exit c gl =
| Some (p,pt) ->
tclTHENS
(internal_cut id pt)
- [refine_no_check p;
+ [refine p; (* Might be ill-typed due to forbidden elimination. *)
tclTHEN (tac (not isrec) (mkVar id)) (thin [id])] gl) n)
gl
| None ->
@@ -961,9 +963,9 @@ let general_apply with_delta with_destruct with_evars (loc,(c,lbind)) gl0 =
with PretypeError _|RefinerError _|UserError _|Failure _|Exit ->
if with_destruct then
descend_in_conjunctions
- try_main_apply (fun _ -> Stdpp.raise_with_loc loc exn) c gl
+ try_main_apply (fun _ -> Loc.raise loc exn) c gl
else
- Stdpp.raise_with_loc loc exn
+ Loc.raise loc exn
in try_red_apply thm_ty0
in
try_main_apply with_destruct c gl0
@@ -1023,8 +1025,7 @@ let apply_in_once_main flags innerclause (d,lbind) gl =
let apply_in_once sidecond_first with_delta with_destruct with_evars id
(loc,(d,lbind)) gl0 =
- let flags =
- if with_delta then default_unify_flags else default_no_delta_unify_flags in
+ let flags = if with_delta then elim_flags else elim_no_delta_flags in
let t' = pf_get_hyp_typ gl0 id in
let innerclause = mk_clenv_from_n gl0 (Some 0) (mkVar id,t') in
let rec aux with_destruct c gl =
@@ -1119,7 +1120,7 @@ let clear_wildcards ids =
try with_check (Tacmach.thin_no_check [id]) gl
with ClearDependencyError (id,err) ->
(* Intercept standard [thin] error message *)
- Stdpp.raise_with_loc loc
+ Loc.raise loc
(error_clear_dependency (pf_env gl) (id_of_string "_") err))
ids
@@ -1137,11 +1138,14 @@ let rec intros_clearing = function
(* Modifying/Adding an hypothesis *)
let specialize mopt (c,lbind) g =
- let term =
- if lbind = NoBindings then c
+ let tac, term =
+ if lbind = NoBindings then
+ let evd = Typeclasses.resolve_typeclasses (pf_env g) (project g) in
+ tclEVARS evd, nf_evar evd c
else
let clause = make_clenv_binding g (c,pf_type_of g c) lbind in
- let clause = clenv_unify_meta_types clause in
+ let flags = { default_unify_flags with resolve_evars = true } in
+ let clause = clenv_unify_meta_types ~flags clause in
let (thd,tstack) = whd_stack clause.evd (clenv_value clause) in
let nargs = List.length tstack in
let tstack = match mopt with
@@ -1158,16 +1162,18 @@ let specialize mopt (c,lbind) g =
errorlabstrm "" (str "Cannot infer an instance for " ++
pr_name (meta_name clause.evd (List.hd (collect_metas term))) ++
str ".");
- term
+ tclEVARS clause.evd, term
in
match kind_of_term (fst(decompose_app (snd(decompose_lam_assum c)))) with
| Var id when List.mem id (pf_ids_of_hyps g) ->
- tclTHENFIRST
+ tclTHEN tac
+ (tclTHENFIRST
(fun g -> internal_cut_replace id (pf_type_of g term) g)
- (exact_no_check term) g
- | _ -> tclTHENLAST
- (fun g -> cut (pf_type_of g term) g)
- (exact_no_check term) g
+ (exact_no_check term)) g
+ | _ -> tclTHEN tac
+ (tclTHENLAST
+ (fun g -> cut (pf_type_of g term) g)
+ (exact_no_check term)) g
(* Keeping only a few hypotheses *)
@@ -1245,12 +1251,20 @@ let simplest_split = split NoBindings
(* Decomposing introductions *)
(*****************************)
+(* Rewriting function for rewriting one hypothesis at the time *)
let forward_general_multi_rewrite =
ref (fun _ -> failwith "general_multi_rewrite undefined")
+(* Rewriting function for substitution (x=t) everywhere at the same time *)
+let forward_subst_one =
+ ref (fun _ -> failwith "subst_one undefined")
+
let register_general_multi_rewrite f =
forward_general_multi_rewrite := f
+let register_subst_one f =
+ forward_subst_one := f
+
let error_unexpected_extra_pattern loc nb pat =
let s1,s2,s3 = match pat with
| IntroIdentifier _ -> "name", (plural nb " introduction pattern"), "no"
@@ -1284,6 +1298,8 @@ let intro_or_and_pattern loc b ll l' tac id gl =
let rewrite_hyp l2r id gl =
let rew_on l2r =
!forward_general_multi_rewrite l2r false (mkVar id,NoBindings) in
+ let subst_on l2r x rhs =
+ !forward_subst_one true x (id,rhs,l2r) in
let clear_var_and_eq c =
tclTRY (tclTHEN (clear [id]) (tclTRY (clear [destVar c]))) in
let t = pf_whd_betadeltaiota gl (pf_type_of gl (mkVar id)) in
@@ -1291,9 +1307,9 @@ let rewrite_hyp l2r id gl =
match match_with_equality_type t with
| Some (hdcncl,[_;lhs;rhs]) ->
if l2r & isVar lhs & not (occur_var (pf_env gl) (destVar lhs) rhs) then
- tclTHEN (rew_on l2r allHypsAndConcl) (clear_var_and_eq lhs) gl
+ subst_on l2r (destVar lhs) rhs gl
else if not l2r & isVar rhs & not (occur_var (pf_env gl) (destVar rhs) lhs) then
- tclTHEN (rew_on l2r allHypsAndConcl) (clear_var_and_eq rhs) gl
+ subst_on l2r (destVar rhs) lhs gl
else
tclTHEN (rew_on l2r onConcl) (tclTRY (clear [id])) gl
| Some (hdcncl,[c]) ->
@@ -1315,57 +1331,65 @@ let rec explicit_intro_names = function
| [] ->
[]
+let wild_id = id_of_string "_tmp"
+
+let rec list_mem_assoc_right id = function
+ | [] -> false
+ | (x,id')::l -> id = id' || list_mem_assoc_right id l
+
+let check_thin_clash_then id thin avoid tac =
+ if list_mem_assoc_right id thin then
+ let newid = next_ident_away (add_suffix id "'") avoid in
+ let thin =
+ List.map (on_snd (fun id' -> if id = id' then newid else id')) thin in
+ tclTHEN (rename_hyp [id,newid]) (tac thin)
+ else
+ tac thin
+
(* We delay thinning until the completion of the whole intros tactic
to ensure that dependent hypotheses are cleared in the right
dependency order (see bug #1000); we use fresh names, not used in
the tactic, for the hyps to clear *)
-let rec intros_patterns b avoid thin destopt = function
+let rec intros_patterns b avoid ids thin destopt tac = function
| (loc, IntroWildcard) :: l ->
- tclTHEN
- (intro_gen loc (IntroAvoid(avoid@explicit_intro_names l))
- no_move true false)
- (onLastHypId (fun id ->
- tclORELSE
- (tclTHEN (clear [id]) (intros_patterns b avoid thin destopt l))
- (intros_patterns b avoid ((loc,id)::thin) destopt l)))
+ intro_then_gen loc (IntroBasedOn(wild_id,avoid@explicit_intro_names l))
+ no_move true false
+ (fun id -> intros_patterns b avoid ids ((loc,id)::thin) destopt tac l)
| (loc, IntroIdentifier id) :: l ->
- tclTHEN
- (intro_gen loc (IntroMustBe id) destopt true false)
- (intros_patterns b avoid thin destopt l)
+ check_thin_clash_then id thin avoid (fun thin ->
+ intro_then_gen loc (IntroMustBe id) destopt true false
+ (fun id -> intros_patterns b avoid (id::ids) thin destopt tac l))
| (loc, IntroAnonymous) :: l ->
- tclTHEN
- (intro_gen loc (IntroAvoid (avoid@explicit_intro_names l))
- destopt true false)
- (intros_patterns b avoid thin destopt l)
+ intro_then_gen loc (IntroAvoid (avoid@explicit_intro_names l))
+ destopt true false
+ (fun id -> intros_patterns b avoid (id::ids) thin destopt tac l)
| (loc, IntroFresh id) :: l ->
- tclTHEN
- (intro_gen loc (IntroBasedOn (id, avoid@explicit_intro_names l))
- destopt true false)
- (intros_patterns b avoid thin destopt l)
+ (* todo: avoid thinned names to interfere with generation of fresh name *)
+ intro_then_gen loc (IntroBasedOn (id, avoid@explicit_intro_names l))
+ destopt true false
+ (fun id -> intros_patterns b avoid (id::ids) thin destopt tac l)
| (loc, IntroForthcoming onlydeps) :: l ->
- tclTHEN
- (intro_forthcoming_gen loc (IntroAvoid (avoid@explicit_intro_names l))
- destopt onlydeps)
- (intros_patterns b avoid thin destopt l)
+ intro_forthcoming_then_gen loc (IntroAvoid (avoid@explicit_intro_names l))
+ destopt onlydeps
+ (fun ids -> intros_patterns b avoid ids thin destopt tac l)
| (loc, IntroOrAndPattern ll) :: l' ->
- tclTHEN
- introf
- (onLastHypId
- (intro_or_and_pattern loc b ll l'
- (intros_patterns b avoid thin destopt)))
+ intro_then_force
+ (intro_or_and_pattern loc b ll l'
+ (intros_patterns b avoid ids thin destopt tac))
| (loc, IntroRewrite l2r) :: l ->
- tclTHEN
- (intro_gen loc (IntroAvoid(avoid@explicit_intro_names l))
- no_move true false)
- (onLastHypId (fun id ->
+ intro_then_gen loc (IntroAvoid(avoid@explicit_intro_names l))
+ no_move true false
+ (fun id ->
tclTHENLAST (* Skip the side conditions of the rewriting step *)
(rewrite_hyp l2r id)
- (intros_patterns b avoid thin destopt l)))
- | [] -> clear_wildcards thin
+ (intros_patterns b avoid ids thin destopt tac l))
+ | [] -> tac ids thin
-let intros_pattern = intros_patterns false [] []
+let intros_pattern destopt =
+ intros_patterns false [] [] [] destopt (fun _ -> clear_wildcards)
-let intro_pattern destopt pat = intros_patterns false [] [] destopt [dloc,pat]
+let intro_pattern destopt pat =
+ intros_pattern destopt [dloc,pat]
let intro_patterns = function
| [] -> tclREPEAT intro
@@ -1390,7 +1414,7 @@ let prepare_intros s ipat gl = match ipat with
| IntroOrAndPattern ll -> make_id s gl,
onLastHypId
(intro_or_and_pattern loc true ll []
- (intros_patterns true [] [] no_move))
+ (intros_patterns true [] [] [] no_move (fun _ -> clear_wildcards)))
| IntroForthcoming _ -> user_err_loc
(loc,"",str "Introduction pattern for one hypothesis expected")
@@ -1400,7 +1424,8 @@ let ipat_of_name = function
let allow_replace c gl = function (* A rather arbitrary condition... *)
| Some (_, IntroIdentifier id) ->
- fst (decompose_app ((strip_lam_assum c))) = mkVar id
+ let c = fst (decompose_app ((strip_lam_assum c))) in
+ isVar c && destVar c = id
| _ ->
false
@@ -1422,7 +1447,8 @@ let as_tac id ipat = match ipat with
| Some (loc,IntroRewrite l2r) ->
!forward_general_multi_rewrite l2r false (mkVar id,NoBindings) allHypsAndConcl
| Some (loc,IntroOrAndPattern ll) ->
- intro_or_and_pattern loc true ll [] (intros_patterns true [] [] no_move)
+ intro_or_and_pattern loc true ll []
+ (intros_patterns true [] [] [] no_move (fun _ -> clear_wildcards))
id
| Some (loc,
(IntroIdentifier _ | IntroAnonymous | IntroFresh _ |
@@ -1484,7 +1510,7 @@ let generalize_goal gl i ((occs,c,b),na) cl =
let decls,cl = decompose_prod_n_assum i cl in
let dummy_prod = it_mkProd_or_LetIn mkProp decls in
let newdecls,_ = decompose_prod_n_assum i (subst_term c dummy_prod) in
- let cl' = subst_term_occ occs c (it_mkProd_or_LetIn cl newdecls) in
+ let cl' = subst_closed_term_occ occs c (it_mkProd_or_LetIn cl newdecls) in
let na = generalized_name c t (pf_ids_of_hyps gl) cl' na in
mkProd_or_LetIn (na,b,t) cl'
@@ -1659,14 +1685,48 @@ let letin_tac with_eq name c occs gl =
(* Implementation without generalisation: abbrev will be lost in hyps in *)
(* in the extracted proof *)
-let letin_abstract id c (occs,check_occs) gl =
+let default_matching_flags sigma = {
+ modulo_conv_on_closed_terms = Some empty_transparent_state;
+ use_metas_eagerly_in_conv_on_closed_terms = false;
+ modulo_delta = empty_transparent_state;
+ modulo_delta_types = full_transparent_state;
+ check_applied_meta_types = true;
+ resolve_evars = false;
+ use_pattern_unification = false;
+ use_meta_bound_pattern_unification = false;
+ frozen_evars =
+ fold_undefined (fun evk _ evars -> ExistentialSet.add evk evars)
+ sigma ExistentialSet.empty;
+ restrict_conv_on_strict_subterms = false;
+ modulo_betaiota = false;
+ modulo_eta = false;
+ allow_K_in_toplevel_higher_order_unification = false
+}
+
+let make_pattern_test env sigma0 (sigma,c) =
+ let flags = default_matching_flags sigma0 in
+ let matching_fun t =
+ try let sigma = w_unify env sigma Reduction.CONV ~flags c t in Some(sigma,t)
+ with _ -> raise NotUnifiable in
+ let merge_fun c1 c2 =
+ match c1, c2 with
+ | Some (_,c1), Some (_,c2) when not (is_fconv Reduction.CONV env sigma0 c1 c2) ->
+ raise NotUnifiable
+ | _ -> c1 in
+ { match_fun = matching_fun; merge_fun = merge_fun;
+ testing_state = None; last_found = None },
+ (fun test -> match test.testing_state with
+ | None -> finish_evar_resolution env sigma0 (sigma,c)
+ | Some (sigma,_) -> nf_evar sigma c)
+
+let letin_abstract id c (test,out) (occs,check_occs) gl =
let env = pf_env gl in
let compute_dependency _ (hyp,_,_ as d) depdecls =
match occurrences_of_hyp hyp occs with
| None -> depdecls
| Some occ ->
- let newdecl = subst_term_occ_decl occ c d in
- if occ = (all_occurrences,InHyp) & d = newdecl then
+ let newdecl = subst_closed_term_occ_decl_modulo occ test d in
+ if occ = (all_occurrences,InHyp) & eq_named_declaration d newdecl then
if check_occs & not (in_every_hyp occs)
then raise (RefinerError (DoesNotOccurIn (c,hyp)))
else depdecls
@@ -1675,19 +1735,21 @@ let letin_abstract id c (occs,check_occs) gl =
let depdecls = fold_named_context compute_dependency env ~init:[] in
let ccl = match occurrences_of_goal occs with
| None -> pf_concl gl
- | Some occ -> subst1 (mkVar id) (subst_term_occ occ c (pf_concl gl)) in
+ | Some occ ->
+ subst1 (mkVar id) (subst_closed_term_occ_modulo occ test None (pf_concl gl)) in
let lastlhyp =
if depdecls = [] then no_move else MoveAfter(pi1(list_last depdecls)) in
- (depdecls,lastlhyp,ccl)
+ (depdecls,lastlhyp,ccl,out test)
-let letin_tac_gen with_eq name c ty occs gl =
+let letin_tac_gen with_eq name (sigmac,c) test ty occs gl =
let id =
- let x = id_of_name_using_hdchar (Global.env()) (pf_type_of gl c) name in
+ let t = match ty with Some t -> t | None -> typ_of (pf_env gl) sigmac c in
+ let x = id_of_name_using_hdchar (Global.env()) t name in
if name = Anonymous then fresh_id [] x gl else
if not (mem_named_context x (pf_hyps gl)) then x else
error ("The variable "^(string_of_id x)^" is already declared.") in
- let (depdecls,lastlhyp,ccl)= letin_abstract id c occs gl in
- let t = match ty with Some t -> t | None -> pf_type_of gl c in
+ let (depdecls,lastlhyp,ccl,c) = letin_abstract id c test occs gl in
+ let t = match ty with Some t -> t | None -> pf_apply typ_of gl c in
let newcl,eq_tac = match with_eq with
| Some (lr,(loc,ido)) ->
let heq = match ido with
@@ -1711,8 +1773,15 @@ let letin_tac_gen with_eq name c ty occs gl =
tclMAP convert_hyp_no_check depdecls;
eq_tac ] gl
-let letin_tac with_eq name c ty occs =
- letin_tac_gen with_eq name c ty (occs,true)
+let make_eq_test c = (make_eq_test c,fun _ -> c)
+
+let letin_tac with_eq name c ty occs gl =
+ letin_tac_gen with_eq name (project gl,c) (make_eq_test c) ty (occs,true) gl
+
+let letin_pat_tac with_eq name c ty occs gl =
+ letin_tac_gen with_eq name c
+ (make_pattern_test (pf_env gl) (project gl) c)
+ ty (occs,true) gl
(* Tactics "pose proof" (usetac=None) and "assert" (otherwise) *)
let forward usetac ipat c gl =
@@ -1755,6 +1824,9 @@ let unfold_all x gl =
if xval <> None then tclTHEN (unfold_body x) (clear [x]) gl
else tclIDTAC gl
+(* Either unfold and clear if defined or simply clear if not a definition *)
+let expand_hyp id = tclTHEN (tclTRY (unfold_body id)) (clear [id])
+
(*****************************)
(* High-level induction *)
(*****************************)
@@ -1797,16 +1869,6 @@ let check_unused_names names =
(str"Unused introduction " ++ str (plural (List.length names) "pattern")
++ str": " ++ prlist_with_sep spc pr_intro_pattern names)
-let rec first_name_buggy avoid gl (loc,pat) = match pat with
- | IntroOrAndPattern [] -> no_move
- | IntroOrAndPattern ([]::l) ->
- first_name_buggy avoid gl (loc,IntroOrAndPattern l)
- | IntroOrAndPattern ((p::_)::_) -> first_name_buggy avoid gl p
- | IntroWildcard -> no_move
- | IntroRewrite _ -> no_move
- | IntroIdentifier id -> MoveAfter id
- | IntroAnonymous | IntroFresh _ | IntroForthcoming _ -> (* buggy *) no_move
-
let rec consume_pattern avoid id isdep gl = function
| [] -> ((dloc, IntroIdentifier (fresh_id avoid id gl)), [])
| (loc,IntroAnonymous)::names ->
@@ -1822,7 +1884,8 @@ let rec consume_pattern avoid id isdep gl = function
((loc,IntroIdentifier (fresh_id avoid id' gl)), names)
| pat::names -> (pat,names)
-let re_intro_dependent_hypotheses (lstatus,rstatus) tophyp =
+let re_intro_dependent_hypotheses (lstatus,rstatus) (_,tophyp) =
+ let tophyp = match tophyp with None -> MoveToEnd true | Some hyp -> MoveAfter hyp in
let newlstatus = (* if some IH has taken place at the top of hyps *)
List.map (function (hyp,MoveToEnd true) -> (hyp,tophyp) | x -> x) lstatus
in
@@ -1832,20 +1895,46 @@ let re_intro_dependent_hypotheses (lstatus,rstatus) tophyp =
let update destopt tophyp = if destopt = no_move then tophyp else destopt
-let safe_dest_intros_patterns avoid dest pat gl =
- try intros_patterns true avoid [] dest pat gl
+let safe_dest_intros_patterns avoid thin dest pat tac gl =
+ try intros_patterns true avoid [] thin dest tac pat gl
with UserError ("move_hyp",_) ->
- (* May happen if the lemma has dependent arguments that has resolved
- only after cook_sign is called, e.g. as in
+ (* May happen if the lemma has dependent arguments that are resolved
+ only after cook_sign is called, e.g. as in "destruct dec" in context
"dec:forall x, {x=0}+{x<>0}; a:A |- if dec a then True else False"
- for argument a of dec which will be found only lately *)
- intros_patterns true avoid [] no_move pat gl
+ where argument a of dec will be found only lately *)
+ intros_patterns true avoid [] [] no_move tac pat gl
type elim_arg_kind = RecArg | IndArg | OtherArg
-let induct_discharge destopt avoid' tac (avoid,ra) names gl =
+type recarg_position =
+ | AfterFixedPosition of identifier option (* None = top of context *)
+
+let update_dest (recargdests,tophyp as dests) = function
+ | [] -> dests
+ | hyp::_ ->
+ (match recargdests with
+ | AfterFixedPosition None -> AfterFixedPosition (Some hyp)
+ | x -> x),
+ (match tophyp with None -> Some hyp | x -> x)
+
+let get_recarg_dest (recargdests,tophyp) =
+ match recargdests with
+ | AfterFixedPosition None -> MoveToEnd true
+ | AfterFixedPosition (Some id) -> MoveAfter id
+
+(* Current policy re-introduces recursive arguments of destructed
+ variable at the place of the original variable while induction
+ hypothesese are introduced at the top of the context. Since in the
+ general case of an inductive scheme, the induction hypotheses can
+ arrive just after the recursive arguments (e.g. as in "forall
+ t1:tree, P t1 -> forall t2:tree, P t2 -> P (node t1 t2)", we need
+ to update the position for t2 after "P t1" is introduced if ever t2
+ had to be introduced at the top of the context).
+*)
+
+let induct_discharge dests avoid' tac (avoid,ra) names gl =
let avoid = avoid @ avoid' in
- let rec peel_tac ra names tophyp gl =
+ let rec peel_tac ra dests names thin gl =
match ra with
| (RecArg,deprec,recvarname) ::
(IndArg,depind,hyprecname) :: ra' ->
@@ -1855,37 +1944,33 @@ let induct_discharge destopt avoid' tac (avoid,ra) names gl =
(pat, [dloc, IntroIdentifier id'])
| _ -> consume_pattern avoid recvarname deprec gl names in
let hyprec,names = consume_pattern avoid hyprecname depind gl names in
- (* IH stays at top: we need to update tophyp *)
- (* This is buggy for intro-or-patterns with different first hypnames *)
- (* Would need to pass peel_tac as a continuation of intros_patterns *)
- (* (or to have hypotheses classified by blocks...) *)
- let newtophyp =
- if tophyp=no_move then first_name_buggy avoid gl hyprec else tophyp
- in
- tclTHENLIST
- [ safe_dest_intros_patterns avoid (update destopt tophyp) [recpat];
- safe_dest_intros_patterns avoid no_move [hyprec];
- peel_tac ra' names newtophyp] gl
+ let dest = get_recarg_dest dests in
+ safe_dest_intros_patterns avoid thin dest [recpat] (fun ids thin ->
+ safe_dest_intros_patterns avoid thin no_move [hyprec] (fun ids' thin ->
+ peel_tac ra' (update_dest dests ids') names thin))
+ gl
| (IndArg,dep,hyprecname) :: ra' ->
(* Rem: does not happen in Coq schemes, only in user-defined schemes *)
let pat,names = consume_pattern avoid hyprecname dep gl names in
- tclTHEN (safe_dest_intros_patterns avoid (update destopt tophyp) [pat])
- (peel_tac ra' names tophyp) gl
+ safe_dest_intros_patterns avoid thin no_move [pat] (fun ids thin ->
+ peel_tac ra' (update_dest dests ids) names thin) gl
| (RecArg,dep,recvarname) :: ra' ->
let pat,names = consume_pattern avoid recvarname dep gl names in
- tclTHEN (safe_dest_intros_patterns avoid (update destopt tophyp) [pat])
- (peel_tac ra' names tophyp) gl
+ let dest = get_recarg_dest dests in
+ safe_dest_intros_patterns avoid thin dest [pat] (fun ids thin ->
+ peel_tac ra' dests names thin) gl
| (OtherArg,_,_) :: ra' ->
let pat,names = match names with
| [] -> (dloc, IntroAnonymous), []
| pat::names -> pat,names in
- tclTHEN (safe_dest_intros_patterns avoid (update destopt tophyp) [pat])
- (peel_tac ra' names tophyp) gl
+ let dest = get_recarg_dest dests in
+ safe_dest_intros_patterns avoid thin dest [pat] (fun ids thin ->
+ peel_tac ra' dests names thin) gl
| [] ->
check_unused_names names;
- tac tophyp gl
+ tclTHEN (clear_wildcards thin) (tac dests) gl
in
- peel_tac ra names no_move gl
+ peel_tac ra dests names [] gl
(* - le recalcul de indtyp à chaque itération de atomize_one est pour ne pas
s'embêter à regarder si un letin_tac ne fait pas des
@@ -2063,8 +2148,13 @@ let cook_sign hyp0_opt indvars env =
fold_named_context_reverse compute_lstatus ~init:(MoveToEnd true) env in
raise (Shunt (MoveToEnd true)) (* ?? FIXME *)
with Shunt lhyp0 ->
+ let lhyp0 = match lhyp0 with
+ | MoveToEnd true -> None
+ | MoveAfter hyp -> Some hyp
+ | _ -> assert false in
let statuslists = (!lstatus,List.rev !rstatus) in
- (statuslists, (if hyp0_opt=None then MoveToEnd true else lhyp0),
+ let recargdests = AfterFixedPosition (if hyp0_opt=None then None else lhyp0) in
+ (statuslists, (recargdests,None),
!indhyps, !decldeps)
@@ -2167,23 +2257,6 @@ let make_up_names n ind_opt cname =
else avoid in
id_of_string base, hyprecname, avoid
-let is_indhyp p n t =
- let l, c = decompose_prod t in
- let c,_ = decompose_app c in
- let p = p + List.length l in
- match kind_of_term c with
- | Rel k when p < k & k <= p + n -> true
- | _ -> false
-
-let chop_context n l =
- let rec chop_aux acc = function
- | n, (_,Some _,_ as h :: t) -> chop_aux (h::acc) (n, t)
- | 0, l2 -> (List.rev acc, l2)
- | n, (h::t) -> chop_aux (h::acc) (n-1, t)
- | _, [] -> anomaly "chop_context"
- in
- chop_aux [] (n,l)
-
let error_ind_scheme s =
let s = if s <> "" then s^" " else s in
error ("Cannot recognize "^s^"an induction scheme.")
@@ -2194,8 +2267,6 @@ let coq_eq_refl = lazy ((Coqlib.build_coq_eq_data ()).Coqlib.refl)
let coq_heq = lazy (Coqlib.coq_constant "mkHEq" ["Logic";"JMeq"] "JMeq")
let coq_heq_refl = lazy (Coqlib.coq_constant "mkHEq" ["Logic";"JMeq"] "JMeq_refl")
-let coq_block = lazy (Coqlib.coq_constant "tactics" ["Program";"Equality"] "block")
-
let mkEq t x y =
mkApp (Lazy.force coq_eq, [| refresh_universes_strict t; x; y |])
@@ -2218,8 +2289,6 @@ let lift_togethern n l =
l ([], n)
in l'
-let lift_together l = lift_togethern 0 l
-
let lift_list l = List.map (lift 1) l
let ids_of_constr ?(all=false) vars c =
@@ -2267,11 +2336,11 @@ let make_abstract_generalize gl id concl dep ctx body c eqs args refls =
in
(* Abstract by equalitites *)
let eqs = lift_togethern 1 eqs in (* lift together and past genarg *)
- let abseqs = it_mkProd_or_LetIn ~init:(lift eqslen abshypeq) (List.map (fun x -> (Anonymous, None, x)) eqs) in
+ let abseqs = it_mkProd_or_LetIn (lift eqslen abshypeq) (List.map (fun x -> (Anonymous, None, x)) eqs) in
(* Abstract by the "generalized" hypothesis. *)
let genarg = mkProd_or_LetIn (Name id, body, c) abseqs in
(* Abstract by the extension of the context *)
- let genctyp = it_mkProd_or_LetIn ~init:genarg ctx in
+ let genctyp = it_mkProd_or_LetIn genarg ctx in
(* The goal will become this product. *)
let genc = mkCast (mkMeta meta, DEFAULTcast, genctyp) in
(* Apply the old arguments giving the proper instantiation of the hyp *)
@@ -2282,17 +2351,6 @@ let make_abstract_generalize gl id concl dep ctx body c eqs args refls =
let appeqs = mkApp (instc, Array.of_list refls) in
(* Finaly, apply the reflexivity proof for the original hyp, to get a term of type gl again. *)
mkApp (appeqs, abshypt)
-
-let deps_of_var id env =
- Environ.fold_named_context
- (fun _ (n,b,t) (acc : Idset.t) ->
- if Option.cata (occur_var env id) false b || occur_var env id t then
- Idset.add n acc
- else acc)
- env ~init:Idset.empty
-
-let idset_of_list =
- List.fold_left (fun s x -> Idset.add x s) Idset.empty
let hyps_of_vars env sign nogen hyps =
if Idset.is_empty hyps then []
@@ -2311,6 +2369,23 @@ let hyps_of_vars env sign nogen hyps =
sign
in lh
+exception Seen
+
+let linear vars args =
+ let seen = ref vars in
+ try
+ Array.iter (fun i ->
+ let rels = ids_of_constr ~all:true Idset.empty i in
+ let seen' =
+ Idset.fold (fun id acc ->
+ if Idset.mem id acc then raise Seen
+ else Idset.add id acc)
+ rels !seen
+ in seen := seen')
+ args;
+ true
+ with Seen -> false
+
let is_defined_variable env id =
pi2 (lookup_named id env) <> None
@@ -2337,6 +2412,7 @@ let abstract_args gl generalize_vars dep id defined f args =
in
let argty = pf_type_of gl arg in
let argty = refresh_universes_strict argty in
+ let ty = refresh_universes_strict ty in
let lenctx = List.length ctx in
let liftargty = lift lenctx argty in
let leq = constr_cmp Reduction.CUMUL liftargty ty in
@@ -2364,23 +2440,19 @@ let abstract_args gl generalize_vars dep id defined f args =
nongenvars, Idset.union argvars vars, env)
in
let f', args' = decompose_indapp f args in
- let parvars = ids_of_constr ~all:true Idset.empty f' in
- let seen = ref parvars in
let dogen, f', args' =
- let find i x = not (isVar x) ||
- let v = destVar x in
- if is_defined_variable env v || Idset.mem v !seen then true
- else (seen := Idset.add v !seen; false)
- in
- match array_find_i find args' with
- | None -> false, f', args'
- | Some nonvar ->
- let before, after = array_chop nonvar args' in
- true, mkApp (f', before), after
+ let parvars = ids_of_constr ~all:true Idset.empty f' in
+ if not (linear parvars args') then true, f, args
+ else
+ match array_find_i (fun i x -> not (isVar x) || is_defined_variable env (destVar x)) args' with
+ | None -> false, f', args'
+ | Some nonvar ->
+ let before, after = array_chop nonvar args' in
+ true, mkApp (f', before), after
in
if dogen then
- let arity, ctx, ctxenv, c', args, eqs, refls, nogen, vars, env =
- Array.fold_left aux (pf_type_of gl f',[],env,f',[],[],[],!seen,Idset.empty,env) args'
+ let arity, ctx, ctxenv, c', args, eqs, refls, nogen, vars, env =
+ Array.fold_left aux (pf_type_of gl f',[],env,f',[],[],[],Idset.empty,Idset.empty,env) args'
in
let args, refls = List.rev args, List.rev refls in
let vars =
@@ -2389,7 +2461,7 @@ let abstract_args gl generalize_vars dep id defined f args =
hyps_of_vars (pf_env gl) (pf_hyps gl) nogen vars
else []
in
- let body, c' = if defined then Some c', Retyping.get_type_of ctxenv Evd.empty c' else None, c' in
+ let body, c' = if defined then Some c', typ_of ctxenv Evd.empty c' else None, c' in
Some (make_abstract_generalize gl id concl dep ctx body c' eqs args refls,
dep, succ (List.length ctx), vars)
else None
@@ -2429,20 +2501,20 @@ let abstract_generalize ?(generalize_vars=true) ?(force_dep=false) id gl =
let specialize_eqs id gl =
let env = pf_env gl in
let ty = pf_get_hyp_typ gl id in
- let evars = ref (create_evar_defs (project gl)) in
+ let evars = ref (project gl) in
let unif env evars c1 c2 = Evarconv.e_conv env evars c2 c1 in
let rec aux in_eqs ctx acc ty =
match kind_of_term ty with
| Prod (na, t, b) ->
(match kind_of_term t with
- | App (eq, [| eqty; x; y |]) when in_eqs && eq_constr eq (Lazy.force coq_eq) ->
+ | App (eq, [| eqty; x; y |]) when eq_constr eq (Lazy.force coq_eq) ->
let c = if noccur_between 1 (List.length ctx) x then y else x in
let pt = mkApp (Lazy.force coq_eq, [| eqty; c; c |]) in
let p = mkApp (Lazy.force coq_eq_refl, [| eqty; c |]) in
if unif (push_rel_context ctx env) evars pt t then
aux true ctx (mkApp (acc, [| p |])) (subst1 p b)
else acc, in_eqs, ctx, ty
- | App (heq, [| eqty; x; eqty'; y |]) when in_eqs && eq_constr heq (Lazy.force coq_heq) ->
+ | App (heq, [| eqty; x; eqty'; y |]) when eq_constr heq (Lazy.force coq_heq) ->
let eqt, c = if noccur_between 1 (List.length ctx) x then eqty', y else eqty, x in
let pt = mkApp (Lazy.force coq_heq, [| eqt; c; eqt; c |]) in
let p = mkApp (Lazy.force coq_heq_refl, [| eqt; c |]) in
@@ -2454,10 +2526,8 @@ let specialize_eqs id gl =
else
let e = e_new_evar evars (push_rel_context ctx env) t in
aux false ((na, Some e, t) :: ctx) (mkApp (lift 1 acc, [| mkRel 1 |])) b)
- | App (f, args) when eq_constr f (Lazy.force coq_block) && not in_eqs ->
- aux true ctx acc args.(1)
| t -> acc, in_eqs, ctx, ty
- in
+ in
let acc, worked, ctx, ty = aux false [] (mkVar id) ty in
let ctx' = nf_rel_context_evar !evars ctx in
let ctx'' = List.map (fun (n,b,t as decl) ->
@@ -2485,33 +2555,6 @@ let occur_rel n c =
let res = not (noccurn n c) in
res
-let list_filter_firsts f l =
- let rec list_filter_firsts_aux f acc l =
- match l with
- | e::l' when f e -> list_filter_firsts_aux f (acc@[e]) l'
- | _ -> acc,l
- in
- list_filter_firsts_aux f [] l
-
-let count_rels_from n c =
- let rels = free_rels c in
- let cpt,rg = ref 0, ref n in
- while Intset.mem !rg rels do
- cpt:= !cpt+1; rg:= !rg+1;
- done;
- !cpt
-
-let count_nonfree_rels_from n c =
- let rels = free_rels c in
- if Intset.exists (fun x -> x >= n) rels then
- let cpt,rg = ref 0, ref n in
- while not (Intset.mem !rg rels) do
- cpt:= !cpt+1; rg:= !rg+1;
- done;
- !cpt
- else raise Not_found
-
-
(* cuts a list in two parts, first of size n. Size must be greater than n *)
let cut_list n l =
let rec cut_list_aux acc n l =
@@ -2671,83 +2714,62 @@ let compute_elim_sig ?elimc elimt =
let compute_scheme_signature scheme names_info ind_type_guess =
let f,l = decompose_app scheme.concl in
(* Vérifier que les arguments de Qi sont bien les xi. *)
- match scheme.indarg with
- | Some (_,Some _,_) -> error "Strange letin, cannot recognize an induction scheme."
- | None -> (* Non standard scheme *)
- let is_pred n c =
- let hd = fst (decompose_app c) in match kind_of_term hd with
- | Rel q when n < q & q <= n+scheme.npredicates -> IndArg
- | _ when hd = ind_type_guess & not scheme.farg_in_concl -> RecArg
- | _ -> OtherArg in
- let rec check_branch p c =
- match kind_of_term c with
- | Prod (_,t,c) ->
- (is_pred p t, dependent (mkRel 1) c) :: check_branch (p+1) c
- | LetIn (_,_,_,c) ->
- (OtherArg, dependent (mkRel 1) c) :: check_branch (p+1) c
- | _ when is_pred p c = IndArg -> []
- | _ -> raise Exit in
- let rec find_branches p lbrch =
- match lbrch with
- | (_,None,t)::brs ->
- (try
- let lchck_brch = check_branch p t in
- let n = List.fold_left
- (fun n (b,_) -> if b=RecArg then n+1 else n) 0 lchck_brch in
- let recvarname, hyprecname, avoid =
- make_up_names n scheme.indref names_info in
- let namesign =
- List.map (fun (b,dep) ->
- (b,dep,if b=IndArg then hyprecname else recvarname))
- lchck_brch in
- (avoid,namesign) :: find_branches (p+1) brs
- with Exit-> error_ind_scheme "the branches of")
- | (_,Some _,_)::_ -> error_ind_scheme "the branches of"
- | [] -> [] in
- Array.of_list (find_branches 0 (List.rev scheme.branches))
-
- | Some ( _,None,ind) -> (* Standard scheme from an inductive type *)
- let indhd,indargs = decompose_app ind in
- let is_pred n c =
- let hd = fst (decompose_app c) in match kind_of_term hd with
- | Rel q when n < q & q <= n+scheme.npredicates -> IndArg
- | _ when hd = indhd -> RecArg
- | _ -> OtherArg in
- let rec check_branch p c = match kind_of_term c with
- | Prod (_,t,c) ->
- (is_pred p t, dependent (mkRel 1) c) :: check_branch (p+1) c
- | LetIn (_,_,_,c) ->
- (OtherArg, dependent (mkRel 1) c) :: check_branch (p+1) c
- | _ when is_pred p c = IndArg -> []
- | _ -> raise Exit in
- let rec find_branches p lbrch =
- match lbrch with
- | (_,None,t)::brs ->
- (try
- let lchck_brch = check_branch p t in
- let n = List.fold_left
- (fun n (b,_) -> if b=RecArg then n+1 else n) 0 lchck_brch in
- let recvarname, hyprecname, avoid =
- make_up_names n scheme.indref names_info in
- let namesign =
- List.map (fun (b,dep) ->
- (b,dep,if b=IndArg then hyprecname else recvarname))
- lchck_brch in
- (avoid,namesign) :: find_branches (p+1) brs
- with Exit -> error_ind_scheme "the branches of")
- | (_,Some _,_)::_ -> error_ind_scheme "the branches of"
- | [] ->
- (* Check again conclusion *)
-
- let ccl_arg_ok = is_pred (p + scheme.nargs + 1) f = IndArg in
- let ind_is_ok =
- list_lastn scheme.nargs indargs
- = extended_rel_list 0 scheme.args in
- if not (ccl_arg_ok & ind_is_ok) then
- error_ind_scheme "the conclusion of";
- []
- in
- Array.of_list (find_branches 0 (List.rev scheme.branches))
+ let cond, check_concl =
+ match scheme.indarg with
+ | Some (_,Some _,_) ->
+ error "Strange letin, cannot recognize an induction scheme."
+ | None -> (* Non standard scheme *)
+ let cond hd = eq_constr hd ind_type_guess && not scheme.farg_in_concl
+ in (cond, fun _ _ -> ())
+ | Some ( _,None,ind) -> (* Standard scheme from an inductive type *)
+ let indhd,indargs = decompose_app ind in
+ let cond hd = eq_constr hd indhd in
+ let check_concl is_pred p =
+ (* Check again conclusion *)
+ let ccl_arg_ok = is_pred (p + scheme.nargs + 1) f = IndArg in
+ let ind_is_ok =
+ list_equal eq_constr
+ (list_lastn scheme.nargs indargs)
+ (extended_rel_list 0 scheme.args) in
+ if not (ccl_arg_ok & ind_is_ok) then
+ error_ind_scheme "the conclusion of"
+ in (cond, check_concl)
+ in
+ let is_pred n c =
+ let hd = fst (decompose_app c) in
+ match kind_of_term hd with
+ | Rel q when n < q & q <= n+scheme.npredicates -> IndArg
+ | _ when cond hd -> RecArg
+ | _ -> OtherArg
+ in
+ let rec check_branch p c =
+ match kind_of_term c with
+ | Prod (_,t,c) ->
+ (is_pred p t, dependent (mkRel 1) c) :: check_branch (p+1) c
+ | LetIn (_,_,_,c) ->
+ (OtherArg, dependent (mkRel 1) c) :: check_branch (p+1) c
+ | _ when is_pred p c = IndArg -> []
+ | _ -> raise Exit
+ in
+ let rec find_branches p lbrch =
+ match lbrch with
+ | (_,None,t)::brs ->
+ (try
+ let lchck_brch = check_branch p t in
+ let n = List.fold_left
+ (fun n (b,_) -> if b=RecArg then n+1 else n) 0 lchck_brch in
+ let recvarname, hyprecname, avoid =
+ make_up_names n scheme.indref names_info in
+ let namesign =
+ List.map (fun (b,dep) ->
+ (b,dep,if b=IndArg then hyprecname else recvarname))
+ lchck_brch in
+ (avoid,namesign) :: find_branches (p+1) brs
+ with Exit-> error_ind_scheme "the branches of")
+ | (_,Some _,_)::_ -> error_ind_scheme "the branches of"
+ | [] -> check_concl is_pred p; []
+ in
+ Array.of_list (find_branches 0 (List.rev scheme.branches))
(* Check that the elimination scheme has a form similar to the
elimination schemes built by Coq. Schemes may have the standard
@@ -2885,7 +2907,7 @@ let induction_tac_felim with_evars indvars nparams elim gl =
(* elimclause' is built from elimclause by instanciating all args and params. *)
let elimclause' = recolle_clenv nparams indvars elimclause gl in
(* one last resolution (useless?) *)
- let resolved = clenv_unique_resolver true elimclause' gl in
+ let resolved = clenv_unique_resolver ~flags:elim_flags elimclause' gl in
clenv_refine with_evars resolved gl
(* Apply induction "in place" replacing the hypothesis on which
@@ -2895,7 +2917,9 @@ let apply_induction_with_discharge induct_tac elim indhyps destopt avoid names t
let isrec, elim, indsign = get_eliminator elim gl in
let names = compute_induction_names (Array.length indsign) names in
(if isrec then tclTHENFIRSTn else tclTHENLASTn)
- (tclTHEN (induct_tac elim) (tclTRY (thin indhyps)))
+ (tclTHEN
+ (induct_tac elim)
+ (tclMAP (fun id -> tclTRY (expand_hyp id)) (List.rev indhyps)))
(array_map2 (induct_discharge destopt avoid tac) indsign names) gl
(* Apply induction "in place" taking into account dependent
@@ -2979,7 +3003,7 @@ let induction_tac with_evars elim (varname,lbind) typ gl =
let elimclause =
make_clenv_binding gl
(mkCast (elimc,DEFAULTcast,elimt),elimt) lbindelimc in
- elimination_clause_scheme with_evars true i elimclause indclause gl
+ elimination_clause_scheme with_evars i elimclause indclause gl
let induction_from_context isrec with_evars (indref,nparams,elim) (hyp0,lbind) names
inhyps gl =
@@ -3017,14 +3041,6 @@ let induction_without_atomization isrec with_evars elim names lid gl =
then error "Not the right number of induction arguments."
else induction_from_context_l with_evars elim_info lid names gl
-let enforce_eq_name id gl = function
- | (b,(loc,IntroAnonymous)) ->
- (b,(loc,IntroIdentifier (fresh_id [id] (add_prefix "Heq" id) gl)))
- | (b,(loc,IntroFresh heq_base)) ->
- (b,(loc,IntroIdentifier (fresh_id [id] heq_base gl)))
- | x ->
- x
-
let has_selected_occurrences = function
| None -> false
| Some cls ->
@@ -3054,7 +3070,7 @@ let clear_unselected_context id inhyps cls gl =
thin ids gl
| None -> tclIDTAC gl
-let new_induct_gen isrec with_evars elim (eqname,names) (c,lbind) cls gl =
+let new_induct_gen isrec with_evars elim (eqname,names) (sigma,(c,lbind)) cls gl =
let inhyps = match cls with
| Some {onhyps=Some hyps} -> List.map (fun ((_,id),_) -> id) hyps
| _ -> [] in
@@ -3067,14 +3083,17 @@ let new_induct_gen isrec with_evars elim (eqname,names) (c,lbind) cls gl =
(induction_with_atomization_of_ind_arg
isrec with_evars elim names (id,lbind) inhyps) gl
| _ ->
- let x = id_of_name_using_hdchar (Global.env()) (pf_type_of gl c)
+ let x = id_of_name_using_hdchar (Global.env()) (typ_of (pf_env gl) sigma c)
Anonymous in
let id = fresh_id [] x gl in
(* We need the equality name now *)
let with_eq = Option.map (fun eq -> (false,eq)) eqname in
(* TODO: if ind has predicate parameters, use JMeq instead of eq *)
tclTHEN
- (letin_tac_gen with_eq (Name id) c None (Option.default allHypsAndConcl cls,false))
+ (* Warning: letin is buggy when c is not of inductive type *)
+ (letin_tac_gen with_eq (Name id) (sigma,c)
+ (make_pattern_test (pf_env gl) (project gl) (sigma,c))
+ None (Option.default allHypsAndConcl cls,false))
(induction_with_atomization_of_ind_arg
isrec with_evars elim names (id,lbind) inhyps) gl
@@ -3131,7 +3150,7 @@ let induct_destruct isrec with_evars (lc,elim,names,cls) gl =
assert (List.length lc > 0); (* ensured by syntax, but if called inside caml? *)
if List.length lc = 1 && not (is_functional_induction elim gl) then
(* standard induction *)
- onInductionArg
+ onOpenInductionArg
(fun c -> new_induct_gen isrec with_evars elim names c cls)
(List.hd lc) gl
else begin
@@ -3143,6 +3162,8 @@ let induct_destruct isrec with_evars (lc,elim,names,cls) gl =
str "Example: induction x1 x2 x3 using my_scheme.");
if cls <> None then
error "'in' clause not supported here.";
+ let lc = List.map
+ (map_induction_arg (pf_apply finish_evar_resolution gl)) lc in
if List.length lc = 1 then
(* Hook to recover standard induction on non-standard induction schemes *)
(* will be removable when is_functional_induction will be more clever *)
@@ -3150,8 +3171,7 @@ let induct_destruct isrec with_evars (lc,elim,names,cls) gl =
(fun (c,lbind) ->
if lbind <> NoBindings then
error "'with' clause not supported here.";
- new_induct_gen_l isrec with_evars elim names [c])
- (List.hd lc) gl
+ new_induct_gen_l isrec with_evars elim names [c]) (List.hd lc) gl
else
let newlc =
List.map (fun x ->
@@ -3179,11 +3199,8 @@ let new_destruct ev lc e idl cls = induct_destruct false ev (lc,e,idl,cls)
(* Induction tactics *)
(* This was Induction before 6.3 (induction only in quantified premisses) *)
-let raw_induct s = tclTHEN (intros_until_id s) (onLastHyp simplest_elim)
-let raw_induct_nodep n = tclTHEN (intros_until_n n) (onLastHyp simplest_elim)
-
-let simple_induct_id hyp = raw_induct hyp
-let simple_induct_nodep = raw_induct_nodep
+let simple_induct_id s = tclTHEN (intros_until_id s) (onLastHyp simplest_elim)
+let simple_induct_nodep n = tclTHEN (intros_until_n n) (onLastHyp simplest_elim)
let simple_induct = function
| NamedHyp id -> simple_induct_id id
@@ -3213,9 +3230,9 @@ let elim_scheme_type elim t gl =
| Meta mv ->
let clause' =
(* t is inductive, then CUMUL or CONV is irrelevant *)
- clenv_unify true Reduction.CUMUL t
+ clenv_unify ~flags:elim_flags Reduction.CUMUL t
(clenv_meta_type clause mv) clause in
- res_pf clause' ~allow_K:true gl
+ res_pf clause' ~flags:elim_flags gl
| _ -> anomaly "elim_scheme_type"
let elim_type t gl =
@@ -3472,7 +3489,7 @@ let abstract_subproof id tac gl =
let const = Pfedit.build_constant_by_tactic id secsign concl
(tclCOMPLETE (tclTHEN (tclDO (List.length sign) intro) tac)) in
let cd = Entries.DefinitionEntry const in
- let lem = mkConst (Declare.declare_internal_constant id (cd,IsProof Lemma)) in
+ let lem = mkConst (Declare.declare_constant ~internal:Declare.KernelSilent id (cd,IsProof Lemma)) in
exact_no_check
(applist (lem,List.rev (Array.to_list (instance_from_named_context sign))))
gl
@@ -3501,8 +3518,9 @@ let admit_as_an_axiom gl =
let concl = it_mkNamedProd_or_LetIn (pf_concl gl) sign in
if occur_existential concl then error"\"admit\" cannot handle existentials.";
let axiom =
- let cd = Entries.ParameterEntry (concl,false) in
- let con = Declare.declare_internal_constant na (cd,IsAssumption Logical) in
+ let cd =
+ Entries.ParameterEntry (Pfedit.get_used_variables(),concl,None) in
+ let con = Declare.declare_constant ~internal:Declare.KernelSilent na (cd,IsAssumption Logical) in
constr_of_global (ConstRef con)
in
exact_no_check
@@ -3517,7 +3535,6 @@ let unify ?(state=full_transparent_state) x y gl =
modulo_delta = state;
modulo_conv_on_closed_terms = Some state}
in
- let evd = w_unify false (pf_env gl) Reduction.CONV
- ~flags x y (Evd.create_evar_defs (project gl))
+ let evd = w_unify (pf_env gl) (project gl) Reduction.CONV ~flags x y
in tclEVARS evd gl
with _ -> tclFAIL 0 (str"Not unifiable") gl