aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--tactics/setoid_replace.ml393
1 files changed, 253 insertions, 140 deletions
diff --git a/tactics/setoid_replace.ml b/tactics/setoid_replace.ml
index 9ce587a7cb..7b6ebd7143 100644
--- a/tactics/setoid_replace.ml
+++ b/tactics/setoid_replace.ml
@@ -124,6 +124,7 @@ let coq_variance = lazy(constant ["Setoid"] "variance")
let coq_Covariant = lazy(constant ["Setoid"] "Covariant")
let coq_Contravariant = lazy(constant ["Setoid"] "Contravariant")
let coq_Left2Right = lazy(constant ["Setoid"] "Left2Right")
+let coq_Right2Left = lazy(constant ["Setoid"] "Right2Left")
let coq_MSNone = lazy(constant ["Setoid"] "MSNone")
let coq_MSCovariant = lazy(constant ["Setoid"] "MSCovariant")
let coq_MSContravariant = lazy(constant ["Setoid"] "MSContravariant")
@@ -148,6 +149,7 @@ let coq_fcl_singl = lazy(constant ["Setoid"] "fcl_singl")
let coq_fcl_cons = lazy(constant ["Setoid"] "fcl_cons")
let coq_setoid_rewrite = lazy(constant ["Setoid"] "setoid_rewrite")
+let coq_proj1 = lazy(gen_constant ["Init"; "Logic"] "proj1")
let coq_proj2 = lazy(gen_constant ["Init"; "Logic"] "proj2")
let coq_unit = lazy(gen_constant ["Init"; "Datatypes"] "unit")
let coq_tt = lazy(gen_constant ["Init"; "Datatypes"] "tt")
@@ -176,6 +178,15 @@ let coq_prop_relation =
rel_sym = Some (gen_constant ["Init"; "Logic"] "iff_sym")
})
+let coq_prop_relation2 =
+ lazy (
+ Relation {
+ rel_a = mkProp ;
+ rel_aeq = Lazy.force coq_impl ;
+ rel_refl = None;
+ rel_sym = None
+ })
+
(************************* Table of declared relations **********************)
@@ -723,6 +734,7 @@ let add_setoid a aeq th =
Relation
{rel_a = a ; rel_aeq = aeq ; rel_refl = Some refl ; rel_sym = Some sym}
in
+ (*CSC: coq_prop_relation used here *)
add_morphism None mor_name
(aeq,[aeq_rel;aeq_rel],Lazy.force coq_prop_relation)
end
@@ -732,10 +744,14 @@ let add_setoid a aeq th =
(****************************** The tactic itself *******************************)
+type direction =
+ Left2Right
+ | Right2Left
+
type constr_with_marks =
- | MApp of constr * morphism_class * constr_with_marks array
+ | MApp of constr * morphism_class * constr_with_marks array * direction
| ToReplace
- | ToKeep of constr
+ | ToKeep of constr * direction
let is_to_replace = function
| ToKeep _ -> false
@@ -745,6 +761,22 @@ let is_to_replace = function
let get_mark a =
Array.fold_left (||) false (Array.map is_to_replace a)
+let cic_direction_of_direction =
+ function
+ Left2Right -> Lazy.force coq_Left2Right
+ | Right2Left -> Lazy.force coq_Right2Left
+
+let opposite_direction =
+ function
+ Left2Right -> Right2Left
+ | Right2Left -> Left2Right
+
+let direction_of_constr_with_marks hole_direction =
+ function
+ MApp (_,_,_,dir) -> cic_direction_of_direction dir
+ | ToReplace -> hole_direction
+ | ToKeep (_,dir) -> cic_direction_of_direction dir
+
type argument =
Toapply of constr (* apply the function to the argument *)
| Toexpand of name * types (* beta-expand the function w.r.t. an argument
@@ -781,130 +813,168 @@ let relation_of_hypothesis_and_term_to_rewrite gl (_,h) t =
errorlabstrm "Setoid_rewrite"
(prterm aeq ++ str " is not a setoid equality.")
-let mark_occur t in_c =
- let rec aux in_c =
+exception BackTrack
+
+let mark_occur t in_c input_direction direction =
+ let rec aux direction in_c =
if eq_constr t in_c then
+(*CSC: The following if should be enabled as soon as backtracking is activated.
+ CSC: Right now it would reject also cases of right-to-left rewriting in
+ CSC: symmetric arguments
+*)
+ if input_direction = direction then
ToReplace
+ else
+ raise BackTrack
+(*CSC: nice error message, incompatible with backtracking
+ errorlabstrm "Setoid_rewrite"
+ (str "The term " ++ prterm in_c ++ str " that must be rewritten occurs " ++
+ str " in a covariant position. You can replace only occurrences that " ++
+ str " are in a contravariant position.")
+*)
else
match kind_of_term in_c with
| App (c,al) ->
- let a = Array.map aux al in
- if not (get_mark a) then ToKeep in_c
+ let mor =
+ try
+ begin
+ match morphism_table_find c with
+ [] -> assert false
+ | morphism::tl ->
+ if tl <> [] then
+ msg_warning
+ (str "There are several morphisms declared for " ++
+ prterm c ++
+ str ". The morphism " ++ prmorphism c morphism ++
+ str " is randomly chosen.") ;
+ Some (relation_morphism_of_constr_morphism morphism)
+ end
+ with Not_found -> None
+ in
+ let a =
+ match mor with
+ None ->
+ (try
+ Array.map (aux Left2Right) al
+ with BackTrack ->
+ Array.map (aux Right2Left) al)
+ | Some mor ->
+ let variances = Array.of_list (List.map fst mor.args) in
+ let apply_variance_to_direction default_dir =
+ function
+ None -> default_dir
+ | Some true -> direction
+ | Some false -> opposite_direction direction
+ in
+ Util.array_map2
+ (fun a variance ->
+ try
+ aux (apply_variance_to_direction Left2Right variance) a
+ with BackTrack ->
+ aux (apply_variance_to_direction Right2Left variance) a
+ ) al variances
+ in
+ if not (get_mark a) then ToKeep (in_c,direction)
else
- let mor =
- try
- begin
- match morphism_table_find c with
- [] -> assert false
- | morphism::tl ->
- if tl <> [] then
- msg_warning
- (str "There are several morphisms declared for " ++
- prterm c ++
- str ". The morphism " ++ prmorphism c morphism ++
- str " is randomly chosen.") ;
- Some
- (ACMorphism
- (relation_morphism_of_constr_morphism morphism))
- end
- with Not_found -> None
- in
- (match mor with
- Some mor -> MApp (c,mor,a)
- | None ->
- (* the tactic works only if the function type is
- made of non-dependent products only. However, here we
- can cheat a bit by partially istantiating c to match
- the requirement when the arguments to be replaced are
- bound by non-dependent products only. *)
- let env = Global.env() in
- let typeofc = (Typing.type_of env Evd.empty c) in
- let typ = nf_betaiota typeofc in
- let rec find_non_dependent_function env c c_args_rev typ
- f_args_rev a_rev=
- function
- [] ->
- let mor =
- ACFunction {f_args=List.rev f_args_rev ; f_output=typ} in
- let func = beta_expand c c_args_rev in
- MApp (func,mor,Array.of_list (List.rev a_rev))
- | (he::tl) as a->
- let typnf = Reduction.whd_betadeltaiota env typ in
- match kind_of_term typnf with
- Cast (typ,_) ->
- find_non_dependent_function env c c_args_rev typ
- f_args_rev a_rev a
- | Prod (name,s,t) ->
- let env' = push_rel (name,None,s) env in
- begin
- match noccurn 1 t, he with
- _, ToKeep arg ->
- (* generic product, to keep *)
- find_non_dependent_function
- env' c ((Toapply arg)::c_args_rev)
- (subst1 arg t) f_args_rev a_rev tl
- | true, _ ->
- (* non-dependent product, to replace *)
- find_non_dependent_function
- env' c ((Toexpand (name,s))::c_args_rev)
- (lift 1 t) (s::f_args_rev) (he::a_rev) tl
- | false, _ ->
- (* dependent product, to replace *)
- (*CSC: this limitation is due to the reflexive
- implementation and it is hard to lift *)
- errorlabstrm "Setoid_replace"
- (str "Cannot rewrite in the argument of a " ++
- str "dependent product. If you need this " ++
- str "feature, please report to the authors.")
- end
- | _ -> assert false
- in
- find_non_dependent_function env c [] typ [] []
- (Array.to_list a))
+ (match mor with
+ Some mor -> MApp (c,ACMorphism mor,a,direction)
+ | None ->
+ (* the tactic works only if the function type is
+ made of non-dependent products only. However, here we
+ can cheat a bit by partially istantiating c to match
+ the requirement when the arguments to be replaced are
+ bound by non-dependent products only. *)
+ let env = Global.env() in
+ let typeofc = (Typing.type_of env Evd.empty c) in
+ let typ = nf_betaiota typeofc in
+ let rec find_non_dependent_function env c c_args_rev typ
+ f_args_rev a_rev
+ =
+ function
+ [] ->
+ let mor =
+ ACFunction {f_args=List.rev f_args_rev ; f_output=typ} in
+ let func = beta_expand c c_args_rev in
+ MApp (func,mor,Array.of_list (List.rev a_rev),direction)
+ | (he::tl) as a->
+ let typnf = Reduction.whd_betadeltaiota env typ in
+ match kind_of_term typnf with
+ Cast (typ,_) ->
+ find_non_dependent_function env c c_args_rev typ
+ f_args_rev a_rev a
+ | Prod (name,s,t) ->
+ let env' = push_rel (name,None,s) env in
+ begin
+ match noccurn 1 t, he with
+ _, ToKeep (arg,_) ->
+ (* generic product, to keep *)
+ find_non_dependent_function
+ env' c ((Toapply arg)::c_args_rev)
+ (subst1 arg t) f_args_rev a_rev tl
+ | true, _ ->
+ (* non-dependent product, to replace *)
+ find_non_dependent_function
+ env' c ((Toexpand (name,s))::c_args_rev)
+ (lift 1 t) (s::f_args_rev) (he::a_rev) tl
+ | false, _ ->
+ (* dependent product, to replace *)
+ (*CSC: this limitation is due to the reflexive
+ implementation and it is hard to lift *)
+ errorlabstrm "Setoid_replace"
+ (str "Cannot rewrite in the argument of a " ++
+ str "dependent product. If you need this " ++
+ str "feature, please report to the authors.")
+ end
+ | _ -> assert false
+ in
+ find_non_dependent_function env c [] typ [] []
+ (Array.to_list a))
| Prod (_, c1, c2) ->
if (dependent (mkRel 1) c2)
- then ToKeep in_c
+ then ToKeep (in_c,direction)
else
- let c1m = aux c1 in
- let c2m = aux c2 in
+ let c1m = aux (opposite_direction direction) c1 in
+ let c2m = aux direction c2 in
if ((is_to_replace c1m)||(is_to_replace c2m))
then
(* this can be optimized since c1 and c2 will be *)
(* processed again *)
- aux (mkApp ((Lazy.force coq_impl), [| c1 ; c2 |]))
- else ToKeep in_c
- | _ -> ToKeep in_c
- in aux in_c
+ aux direction (mkApp ((Lazy.force coq_impl), [| c1 ; c2 |]))
+ else ToKeep (in_c,direction)
+ | _ -> ToKeep (in_c,direction)
+ in
+ aux direction in_c
-let cic_morphism_context_list_of_list hole_relation =
+let cic_morphism_context_list_of_list hole_relation hole_direction out_direction
+=
let check =
function
- None ->
- mkApp ((Lazy.force coq_MSNone),
- [| Lazy.force coq_Left2Right ; Lazy.force coq_Left2Right |])
- | Some true ->
- mkApp ((Lazy.force coq_MSCovariant),
- [| Lazy.force coq_Left2Right |])
- | Some false ->
- mkApp ((Lazy.force coq_MSContravariant),
- [| Lazy.force coq_Left2Right |]) in
+ (None,dir,dir') ->
+ mkApp ((Lazy.force coq_MSNone), [| dir ; dir' |])
+ | (Some true,dir,dir') ->
+ assert (dir = dir');
+ mkApp ((Lazy.force coq_MSCovariant), [| dir |])
+ | (Some false,dir,dir') ->
+ assert (dir <> dir');
+ mkApp ((Lazy.force coq_MSContravariant), [| dir |]) in
let rec aux =
function
[] -> assert false
- | [(variance,out),value] ->
+ | [(variance,out),(value,direction)] ->
mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Argument_Class ; out |]),
mkApp ((Lazy.force coq_fcl_singl),
- [| hole_relation; Lazy.force coq_Left2Right ; out ;
- Lazy.force coq_Left2Right ; Lazy.force coq_Left2Right ;
- check variance ; value |])
- | ((variance,out),value)::tl ->
+ [| hole_relation; hole_direction ; out ;
+ direction ; out_direction ;
+ check (variance,direction,out_direction) ; value |])
+ | ((variance,out),(value,direction))::tl ->
let outtl, valuetl = aux tl in
mkApp ((Lazy.force coq_cons),
[| Lazy.force coq_Argument_Class ; out ; outtl |]),
mkApp ((Lazy.force coq_fcl_cons),
- [| hole_relation ; Lazy.force coq_Left2Right ; out ; outtl ;
- Lazy.force coq_Left2Right ; Lazy.force coq_Left2Right ;
- check variance ; value ; valuetl |])
+ [| hole_relation ; hole_direction ; out ; outtl ;
+ direction ; out_direction ;
+ check (variance,direction,out_direction) ;
+ value ; valuetl |])
in aux
let rec cic_type_nelist_of_list =
@@ -916,15 +986,17 @@ let rec cic_type_nelist_of_list =
mkApp ((Lazy.force coq_cons),
[| mkType (Termops.new_univ ()); value; cic_type_nelist_of_list tl |])
-let syntactic_but_representation_of_marked_but hole_relation =
+let syntactic_but_representation_of_marked_but hole_relation hole_direction =
let rec aux out (rel_out,precise_out,is_reflexive) =
function
- MApp (f, m, args) ->
+ MApp (f, m, args, direction) ->
+ let direction = cic_direction_of_direction direction in
let morphism_theory, relations =
match m with
ACMorphism { args = args ; lem = lem } -> lem,args
| ACFunction { f_args = f_args ; f_output = f_output } ->
let mt =
+ (*CSC: coq_prop_relation used here *)
if eq_constr out (cic_relation_class_of_relation_class
(Lazy.force coq_prop_relation))
then
@@ -944,46 +1016,46 @@ let syntactic_but_representation_of_marked_but hole_relation =
cic_precise_relation_class_of_relation_class r
) relations in
let cic_args_relations,argst =
- cic_morphism_context_list_of_list hole_relation
+ cic_morphism_context_list_of_list hole_relation hole_direction direction
(List.map2
(fun (variance,trel,t,precise_t) v ->
(variance,cic_argument_class_of_argument_class (variance,trel)),
- aux t precise_t v
+ (aux t precise_t v,
+ direction_of_constr_with_marks hole_direction v)
) cic_relations (Array.to_list args))
in
mkApp ((Lazy.force coq_App),
- [|hole_relation ; Lazy.force coq_Left2Right ;
- cic_args_relations ; out ; Lazy.force coq_Left2Right ;
+ [|hole_relation ; hole_direction ;
+ cic_args_relations ; out ; direction ;
morphism_theory ; argst|])
| ToReplace ->
- mkApp ((Lazy.force coq_ToReplace),
- [| hole_relation ; Lazy.force coq_Left2Right |])
- | ToKeep c ->
+ mkApp ((Lazy.force coq_ToReplace), [| hole_relation ; hole_direction |])
+ | ToKeep (c,direction) ->
+ let direction = cic_direction_of_direction direction in
if is_reflexive then
mkApp ((Lazy.force coq_ToKeep),
- [| hole_relation ; Lazy.force coq_Left2Right ; precise_out ;
- Lazy.force coq_Left2Right ; c |])
+ [| hole_relation ; hole_direction ; precise_out ; direction ; c |])
else
let c_is_proper =
let typ = mkApp (rel_out, [| c ; c |]) in
mkCast (Evarutil.mk_new_meta (),typ)
in
mkApp ((Lazy.force coq_ProperElementToKeep),
- [| hole_relation ; Lazy.force coq_Left2Right; precise_out ;
- Lazy.force coq_Left2Right; c ; c_is_proper |])
+ [| hole_relation ; hole_direction; precise_out ;
+ direction; c ; c_is_proper |])
in aux
-let apply_coq_setoid_rewrite hole_relation c1 c2 (lft2rgt,h) m =
+let apply_coq_setoid_rewrite hole_relation prop_relation c1 c2 (direction,h)
+ prop_direction m
+=
let {rel_aeq=hole_equality; rel_sym=hole_symmetry} = hole_relation in
let hole_relation =
cic_relation_class_of_relation_class (Relation hole_relation) in
- let prop_relation =
- cic_relation_class_of_relation_class (Lazy.force coq_prop_relation) in
- let precise_prop_relation =
- cic_precise_relation_class_of_relation_class
- (Lazy.force coq_prop_relation) in
- let hyp =
- if lft2rgt then h else
+ let hyp,hole_direction = h,cic_direction_of_direction direction in
+(*CSC: this old code seems interesting and allows to rewrite in one direction
+ both arguments in covariant and contravariant position. However, this
+ would mean extending a bit also the Coq part of the tactic and I do not
+ know if it is worth the effort.
match hole_symmetry with
Some sym ->
mkApp (sym, [| c2 ; c1 ; h |])
@@ -993,35 +1065,76 @@ let apply_coq_setoid_rewrite hole_relation c1 c2 (lft2rgt,h) m =
str "relation " ++ prterm hole_equality ++ str " is not known " ++
str "to be symmetric. Either declare it as a symmetric " ++
str "relation or use setoid_replace or (setoid_)rewrite from " ++
- str "left to right only.")
+ str "left to right only.") in
+*)
+ let cic_prop_relation = cic_relation_class_of_relation_class prop_relation in
+ let precise_prop_relation =
+ cic_precise_relation_class_of_relation_class prop_relation
in
mkApp ((Lazy.force coq_setoid_rewrite),
- [| hole_relation ; Lazy.force coq_Left2Right ; prop_relation ;
- Lazy.force coq_Left2Right ; c1 ; c2 ;
- syntactic_but_representation_of_marked_but hole_relation
- prop_relation precise_prop_relation m ; hyp |])
+ [| hole_relation ; hole_direction ; cic_prop_relation ;
+ prop_direction ; c1 ; c2 ;
+ syntactic_but_representation_of_marked_but hole_relation hole_direction
+ cic_prop_relation precise_prop_relation m ; hyp |])
-let relation_rewrite c1 c2 (lft2rgt,cl) gl =
+let relation_rewrite c1 c2 (input_direction,cl) gl =
let but = pf_concl gl in
let (hyp,c1,c2) =
let cl' =
{cl with env = fst (w_unify_to_subterm (pf_env gl) (c1,but) cl.env)} in
let c1 = Clenv.clenv_nf_meta cl' c1 in
let c2 = Clenv.clenv_nf_meta cl' c2 in
- (lft2rgt,Clenv.clenv_value cl'), c1, c2
+ (input_direction,Clenv.clenv_value cl'), c1, c2
in
let input_relation = relation_of_hypothesis_and_term_to_rewrite gl hyp c1 in
- let marked_but = mark_occur c1 but in
- let th = apply_coq_setoid_rewrite input_relation c1 c2 hyp marked_but in
- let hyp1 = Termops.replace_term c1 c2 but in
- let hyp2 = but in
- let impl1 = mkProd (Anonymous, hyp2, lift 1 hyp1) in
- let impl2 = mkProd (Anonymous, hyp1, lift 1 hyp2) in
- let th' = mkApp ((Lazy.force coq_proj2), [|impl1; impl2; th|]) in
+ let output_direction,marked_but =
+ (*CSC: this code must be moved into mark_occur, as well as the choice of
+ the output relation *)
+ try
+ Right2Left, mark_occur c1 but input_direction Right2Left
+ with BackTrack -> try
+ Left2Right, mark_occur c1 but input_direction Left2Right
+ with BackTrack ->
+ (*CSC: the error is too detailed. The tactic can now fail also for other
+ CSC: more stupid reasons (i.e. randomly not choosing the right morphism *)
+ errorlabstrm "Setoid_rewrite"
+ (str "The term " ++ prterm c1 ++ str " that must be rewritten occurs " ++
+ str "in a covariant position. You can replace only occurrences that " ++
+ str "are in a contravariant position.")
+ in
+ let cic_output_direction = cic_direction_of_direction output_direction in
+let temporary_solution prop_relation gl =
+ let th =
+ apply_coq_setoid_rewrite input_relation prop_relation c1 c2 hyp
+ cic_output_direction marked_but
+ in
+ let new_but = Termops.replace_term c1 c2 but in
+ let hyp1,hyp2,proj =
+ match output_direction with
+ Right2Left -> new_but, but, Lazy.force coq_proj1
+ | Left2Right -> but, new_but, Lazy.force coq_proj2
+ in
+ let impl1 = mkProd (Anonymous, hyp2, lift 1 hyp1) in
+ let impl2 = mkProd (Anonymous, hyp1, lift 1 hyp2) in
+ let th' = mkApp (proj, [|impl2; impl1; th|]) in
+ Tactics.refine
+ (mkApp (th', [| mkCast (Evarutil.mk_new_meta(), new_but) |])) gl
+in
+let temporary_solution2 prop_relation gl =
+ let th =
+ apply_coq_setoid_rewrite input_relation prop_relation c1 c2 hyp
+ cic_output_direction marked_but
+ in
+ let new_but = Termops.replace_term c1 c2 but in
Tactics.refine
- (mkApp (th', [| mkCast (Evarutil.mk_new_meta(), hyp1) |])) gl
+ (mkApp (th, [| mkCast (Evarutil.mk_new_meta(), new_but) |])) gl
+in
+ (*CSC: the prop_relation will be returned by mark_occur *)
+ Tacticals.tclORELSE (temporary_solution (Lazy.force coq_prop_relation))
+ (temporary_solution2 (Lazy.force coq_prop_relation2)) gl
let general_s_rewrite lft2rgt c gl =
+ let direction = if lft2rgt then Left2Right else Right2Left in
let ctype = pf_type_of gl c in
let eqclause = Clenv.make_clenv_binding gl (c,ctype) Rawterm.NoBindings in
let (equiv, args) = decompose_app (Clenv.clenv_type eqclause) in
@@ -1030,9 +1143,9 @@ let general_s_rewrite lft2rgt c gl =
| x::y::z -> get_last_two (y::z)
| _ -> error "The term provided is not an equivalence" in
let (c1,c2) = get_last_two args in
- if lft2rgt
- then relation_rewrite c1 c2 (lft2rgt,eqclause) gl
- else relation_rewrite c2 c1 (lft2rgt,eqclause) gl
+ match direction with
+ Left2Right -> relation_rewrite c1 c2 (direction,eqclause) gl
+ | Right2Left -> relation_rewrite c2 c1 (direction,eqclause) gl
exception Use_replace