(************************************************************************) (* v * The Coq Proof Assistant / The Coq Development Team *) (* assert false) let register_replace f = replace := f type reflexive_relation = { refl_a: constr ; refl_aeq: constr; refl_refl: constr; refl_sym: constr option } type 'a relation_class = ACReflexive of 'a (* the relation of the reflexive_relation or the reflexive_relation*) | ACLeibniz of constr (* the carrier *) type 'a morphism = { args : 'a relation_class list; output : 'a relation_class; lem : constr; } type funct = { f_args : constr list; f_output : constr } type morphism_class = ACMorphism of reflexive_relation morphism | ACFunction of funct let subst_mps_in_relation_class subst = function ACReflexive t -> ACReflexive (subst_mps subst t) | ACLeibniz t -> ACLeibniz (subst_mps subst t) let constr_relation_class_of_relation_relation_class = function ACReflexive relation -> ACReflexive relation.refl_aeq | ACLeibniz t -> ACLeibniz t let constr_of c = Constrintern.interp_constr Evd.empty (Global.env()) c let constant dir s = Coqlib.gen_constant "Setoid_replace" ("Setoids"::dir) s let init_constant dir s = Coqlib.gen_constant "Setoid_replace" ("Init"::dir) s let reference dir s = Coqlib.gen_reference "Setoid_replace" ("Setoids"::dir) s let eval_reference dir s = EvalConstRef (destConst (constant dir s)) let eval_init_reference dir s = EvalConstRef (destConst (init_constant dir s)) let current_constant id = try global_reference id with Not_found -> anomaly ("Setoid: cannot find " ^ (string_of_id id)) (* From Setoid.v *) let coq_is_reflexive = lazy(constant ["Setoid"] "is_reflexive") let coq_Relation_Class = lazy(constant ["Setoid"] "Relation_Class") let coq_Setoid_Theory = lazy(constant ["Setoid"] "Setoid_Theory") let coq_Morphism_Theory = lazy(constant ["Setoid"] "Morphism_Theory") let coq_Build_Morphism_Theory= lazy(constant ["Setoid"] "Build_Morphism_Theory") let coq_Reflexive = lazy(constant ["Setoid"] "Reflexive") let coq_Leibniz = lazy(constant ["Setoid"] "Leibniz") let coq_seq_refl = lazy(constant ["Setoid"] "Seq_refl") let coq_seq_sym = lazy(constant ["Setoid"] "Seq_sym") let coq_seq_trans = lazy(constant ["Setoid"] "Seq_trans") let coq_singl = lazy(constant ["Setoid"] "singl") let coq_cons = lazy(constant ["Setoid"] "cons") let coq_equality_morphism_of_setoid_theory = lazy(constant ["Setoid"] "equality_morphism_of_setoid_theory") let coq_make_compatibility_goal = lazy(constant ["Setoid"] "make_compatibility_goal") let coq_make_compatibility_goal_ref = lazy(reference ["Setoid"] "make_compatibility_goal") let coq_make_compatibility_goal_aux_ref = lazy(reference ["Setoid"] "make_compatibility_goal_aux") let coq_App = lazy(constant ["Setoid"] "App") let coq_Toreplace = lazy(constant ["Setoid"] "Toreplace") let coq_Tokeep = lazy(constant ["Setoid"] "Tokeep") let coq_Imp = lazy(constant ["Setoid"] "Imp") 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_proj2 = lazy(init_constant ["Logic"] "proj2") let coq_morphism_theory_of_function = lazy(constant ["Setoid"] "morphism_theory_of_function") let coq_morphism_theory_of_predicate = lazy(constant ["Setoid"] "morphism_theory_of_predicate") let coq_relation_of_relation_class = lazy(eval_reference ["Setoid"] "relation_of_relation_class") let coq_interp = lazy(eval_reference ["Setoid"] "interp") let coq_Morphism_Context_rect2 = lazy(eval_reference ["Setoid"] "Morphism_Context_rect2") let coq_iff = lazy(eval_init_reference ["Logic"] "iff") let coq_prop_relation = lazy ( ACReflexive { refl_a = mkProp ; refl_aeq = init_constant ["Logic"] "iff" ; refl_refl = init_constant ["Logic"] "iff_refl"; refl_sym = Some (init_constant ["Logic"] "iff_sym") }) (************************* Table of declared relations **********************) (* Relations are stored in a table which is synchronised with the Reset mechanism. *) let relation_table = ref Gmap.empty let relation_table_add (s,th) = relation_table := Gmap.add s th !relation_table let relation_table_find s = Gmap.find s !relation_table let relation_table_mem s = Gmap.mem s !relation_table let prrelation s = str "(" ++ prterm s.refl_a ++ str "," ++ prterm s.refl_aeq ++ str ")" let prrelation_class = function ACReflexive eq -> (try prrelation (relation_table_find eq) with Not_found -> (*CSC: still "setoid" in the error message *) str "[[ Error: setoid on equality " ++ prterm eq ++ str " not found! ]]") | ACLeibniz ty -> prterm ty let prmorphism k m = prterm k ++ str ": " ++ prlist_with_sep (fun () -> str " -> ") prrelation_class m.args ++ str " -> " ++ prrelation_class m.output (* A function that gives back the only relation_class on a given carrier *) (*CSC: this implementation is really inefficient. I should define a new map to make it efficient. However, is this really worth of? *) let default_relation_for_carrier a = let rng = Gmap.rng !relation_table in match List.filter (fun {refl_a=refl_a} -> refl_a = a) rng with [] -> ACLeibniz a | relation::tl -> if tl <> [] then msg_warning (*CSC: still "setoid" in the error message *) (str "There are several setoids whose carrier is " ++ prterm a ++ str ". The setoid " ++ prrelation relation ++ str " is randomly chosen.") ; ACReflexive relation let relation_morphism_of_constr_morphism = let relation_relation_class_of_constr_relation_class = function ACLeibniz t -> ACLeibniz t | ACReflexive aeq -> ACReflexive (try relation_table_find aeq with Not_found -> assert false) in function mor -> let args' = List.map relation_relation_class_of_constr_relation_class mor.args in let output' = relation_relation_class_of_constr_relation_class mor.output in {mor with args=args' ; output=output'} let subst_relation subst relation = let refl_a' = subst_mps subst relation.refl_a in let refl_aeq' = subst_mps subst relation.refl_aeq in let refl_refl' = subst_mps subst relation.refl_refl in let refl_sym' = option_app (subst_mps subst) relation.refl_sym in if refl_a' == relation.refl_a && refl_aeq' == relation.refl_aeq && refl_refl' == relation.refl_refl && refl_sym' == relation.refl_sym then relation else { refl_a = refl_a' ; refl_aeq = refl_aeq' ; refl_refl = refl_refl' ; refl_sym = refl_sym' } let equiv_list () = List.map (fun x -> x.refl_aeq) (Gmap.rng !relation_table) let _ = Summary.declare_summary "relation-table" { Summary.freeze_function = (fun () -> !relation_table); Summary.unfreeze_function = (fun t -> relation_table := t); Summary.init_function = (fun () -> relation_table := Gmap .empty); Summary.survive_module = true; Summary.survive_section = false } (* Declare a new type of object in the environment : "relation-theory". *) let (relation_to_obj, obj_to_relation)= let cache_set (_,(s, th)) = let th' = if relation_table_mem s then begin let old_relation = relation_table_find s in let th' = {th with refl_sym = match th.refl_sym with None -> old_relation.refl_sym | Some t -> Some t} in (*CSC: still "setoid" in the error message *) msg_warning (str "The setoid " ++ prrelation th' ++ str " is redeclared. " ++ str "The new declaration (reflevity proved by " ++ prterm th'.refl_refl ++ (match th'.refl_sym with None -> str "" | Some t -> str " and symmetry proved by " ++ prterm t) ++ str ") replaces the old declaration (reflexivity proved by "++ prterm old_relation.refl_refl ++ (match old_relation.refl_sym with None -> str "" | Some t -> str " and symmetry proved by " ++ prterm t) ++ str ").") ; th' end else th in relation_table_add (s,th') and subst_set (_,subst,(s,th as obj)) = let s' = subst_mps subst s in let th' = subst_relation subst th in if s' == s && th' == th then obj else (s',th') and export_set x = Some x in declare_object {(default_object "relation-theory") with cache_function = cache_set; load_function = (fun i o -> cache_set o); subst_function = subst_set; classify_function = (fun (_,x) -> Substitute x); export_function = export_set} (******************************* Table of declared morphisms ********************) (* Setoids are stored in a table which is synchronised with the Reset mechanism. *) let morphism_table = ref Gmap.empty let morphism_table_find m = Gmap.find m !morphism_table let morphism_table_add (m,c) = let old = try morphism_table_find m with Not_found -> [] in try let old_morph = List.find (function mor -> mor.args = c.args && mor.output = c.output) old in msg_warning (str "The morphism " ++ prmorphism m old_morph ++ str " is redeclared. " ++ str "The new declaration whose compatibility is granted by " ++ prterm c.lem ++ str " replaces the old declaration whose" ++ str " compatibility was granted by " ++ prterm old_morph.lem ++ str ".") with Not_found -> morphism_table := Gmap.add m (c::old) !morphism_table let subst_morph subst morph = let lem' = subst_mps subst morph.lem in let args' = list_smartmap (subst_mps_in_relation_class subst) morph.args in let output' = subst_mps_in_relation_class subst morph.output in if lem' == morph.lem && args' == morph.args && output' == morph.output then morph else { args = args' ; output = output' ; lem = lem' } let _ = Summary.declare_summary "morphism-table" { Summary.freeze_function = (fun () -> !morphism_table); Summary.unfreeze_function = (fun t -> morphism_table := t); Summary.init_function = (fun () -> morphism_table := Gmap .empty); Summary.survive_module = true; Summary.survive_section = false } (* Declare a new type of object in the environment : "morphism-definition". *) let (morphism_to_obj, obj_to_morphism)= let cache_set (_,(m, c)) = morphism_table_add (m, c) and subst_set (_,subst,(m,c as obj)) = let m' = subst_mps subst m in let c' = subst_morph subst c in if m' == m && c' == c then obj else (m',c') and export_set x = Some x in declare_object {(default_object "morphism-definition") with cache_function = cache_set; load_function = (fun i o -> cache_set o); subst_function = subst_set; classify_function = (fun (_,x) -> Substitute x); export_function = export_set} (************************** Printing relations and morphisms **********************) (*CSC: still "setoids" in the name *) let print_setoids () = Gmap.iter (fun k relation -> assert (k=relation.refl_aeq) ; (*CSC: still "Setoid" in the error message *) ppnl (str"Setoid " ++ prrelation relation ++ str"; reflexivity granted by " ++ prterm relation.refl_refl ++ (match relation.refl_sym with None -> str "" | Some t -> str " symmetry granted by " ++ prterm t))) !relation_table ; Gmap.iter (fun k l -> List.iter (fun ({lem=lem} as mor) -> ppnl (str "Morphism " ++ prmorphism k mor ++ str ". Compatibility granted by " ++ prterm lem ++ str ".")) l) !morphism_table ;; (************************** Adding a relation to the database *********************) let int_add_relation a aeq refl sym = match refl with None -> assert false (*CSC: unimplemented yet*) | Some refl -> let env = Global.env () in if (is_conv env Evd.empty (Typing.type_of env Evd.empty refl) (mkApp ((Lazy.force coq_is_reflexive), [| a; aeq |]))) then Lib.add_anonymous_leaf (relation_to_obj (aeq, { refl_a = a; refl_aeq = aeq; refl_refl = refl; refl_sym = sym})) else errorlabstrm "Add Relation Class" (str "Not a valid proof of reflexivity") (* The vernac command "Add Relation ..." *) let add_relation a aeq refl sym = int_add_relation (constr_of a) (constr_of aeq) (option_app constr_of refl) (option_app constr_of sym) (***************** Adding a morphism to the database ****************************) (* We maintain a table of the currently edited proofs of morphism lemma in order to add them in the morphism_table when the user does Save *) let edited = ref Gmap.empty let new_edited id m = edited := Gmap.add id m !edited let is_edited id = Gmap.mem id !edited let no_more_edited id = edited := Gmap.remove id !edited let what_edited id = Gmap.find id !edited let check_is_dependent t n = let rec aux t i n = if (i mkApp ((Lazy.force coq_Reflexive), [| refl_a ; refl_aeq; refl_refl |]) | ACLeibniz t -> mkApp ((Lazy.force coq_Leibniz), [| t |]) let gen_compat_lemma_statement output args m = let rec mk_signature = function [] -> assert false | [last] -> mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Relation_Class; last |]) | he::tl -> mkApp ((Lazy.force coq_cons), [| Lazy.force coq_Relation_Class; he ; mk_signature tl |]) in let output = cic_relation_class_of_relation_class output in let args= mk_signature (List.map cic_relation_class_of_relation_class args) in args, output, mkApp ((Lazy.force coq_make_compatibility_goal), [| args ; output ; m |]) let new_morphism m id hook = let env = Global.env() in let typeofm = (Typing.type_of env Evd.empty m) in let typ = (nf_betaiota typeofm) in let (argsrev, output) = (decompose_prod typ) in let args_ty = (List.map snd (List.rev argsrev)) in if (args_ty=[]) then errorlabstrm "New Morphism" (str "The term " ++ prterm m ++ str " is not a product") else if (check_is_dependent typ (List.length args_ty)) then errorlabstrm "New Morphism" (str "The term " ++ prterm m ++ str" should not be a dependent product") else begin let args = List.map default_relation_for_carrier args_ty in let output = default_relation_for_carrier output in let argsconstr,outputconstr,lem = gen_compat_lemma_statement output args m in new_edited id (m,args,argsconstr,output,outputconstr); Pfedit.start_proof id (IsGlobal (Proof Lemma)) (Declare.clear_proofs (Global.named_context ())) lem hook; (* "unfold make_compatibility_goal" *) Pfedit.by (Tactics.unfold_constr (Lazy.force coq_make_compatibility_goal_ref)) ; (* "unfold make_compatibility_goal_aux" *) Pfedit.by (Tactics.unfold_constr (Lazy.force coq_make_compatibility_goal_aux_ref)) ; (* "simpl" *) Pfedit.by Tactics.simpl_in_concl ; Options.if_verbose msg (Pfedit.pr_open_subgoals ()); end let add_morphism lemma_infos mor_name (m,args,output) = let env = Global.env() in begin match lemma_infos with None -> () (* the Morphism_Theory object has alrady been created *) | Some (lem_name,argsconstr,outputconstr) -> (* only the compatibility has been proved; we need to declare the Morphism_Theory object *) let mext = current_constant lem_name in ignore ( Declare.declare_constant mor_name (DefinitionEntry {const_entry_body = mkApp ((Lazy.force coq_Build_Morphism_Theory), [| argsconstr; outputconstr; m ; mext |]); const_entry_type = None; const_entry_opaque = false}, IsDefinition)) end ; let mmor = current_constant mor_name in let args_constr = List.map constr_relation_class_of_relation_relation_class args in let output_constr = constr_relation_class_of_relation_relation_class output in Lib.add_anonymous_leaf (morphism_to_obj (m, { args = args_constr; output = output_constr; lem = mmor })); Options.if_verbose ppnl (prterm m ++ str " is registered as a morphism") let morphism_hook stre ref = let pf_id = id_of_global ref in let mor_id = id_of_string (string_of_id pf_id ^ "_morphism_theory") in let (m,args,argsconstr,output,outputconstr) = what_edited pf_id in if (is_edited pf_id) then add_morphism (Some (pf_id,argsconstr,outputconstr)) mor_id (m,args,output); no_more_edited pf_id let new_named_morphism id m = new_morphism (constr_of m) id morphism_hook (************************ Add Setoid ******************************************) (*CSC: I do not like this. I would prefer more self-describing constant names *) let gen_morphism_name = let i = ref 0 in function () -> incr i; make_ident "morphism_of_setoid_equality" (Some !i) (* The vernac command "Add Setoid" *) let add_setoid a aeq th = let a = constr_of a in let aeq = constr_of aeq in let th = constr_of th in let env = Global.env () in if is_conv env Evd.empty (Typing.type_of env Evd.empty th) (mkApp ((Lazy.force coq_Setoid_Theory), [| a; aeq |])) then begin let refl = mkApp ((Lazy.force coq_seq_refl), [| a; aeq; th |]) in let sym = mkApp ((Lazy.force coq_seq_sym), [| a; aeq; th |]) in int_add_relation a aeq (Some refl) (Some sym) ; let mor_name = gen_morphism_name () in let _ = Declare.declare_constant mor_name (DefinitionEntry {const_entry_body = mkApp ((Lazy.force coq_equality_morphism_of_setoid_theory), [| a; aeq; th |]) ; const_entry_type = None; const_entry_opaque = false}, IsDefinition) in let aeq_rel = ACReflexive {refl_a = a ; refl_aeq = aeq ; refl_refl = refl ; refl_sym = (Some sym)} in add_morphism None mor_name (aeq,[aeq_rel;aeq_rel],Lazy.force coq_prop_relation) end else errorlabstrm "Add Setoid" (str "Not a valid setoid theory") (****************************** The tactic itself *******************************) type constr_with_marks = | MApp of constr * morphism_class * constr_with_marks array | Toreplace | Tokeep of constr | Mimp of constr_with_marks * constr_with_marks let is_to_replace = function | Tokeep _ -> false | Toreplace -> true | MApp _ -> true | Mimp _ -> true let get_mark a = Array.fold_left (||) false (Array.map is_to_replace a) type argument = Toapply of constr (* apply the function to the argument *) | Toexpand of name * types (* beta-expand the function w.r.t. an argument of this type *) let beta_expand c args_rev = let rec to_expand = function [] -> [] | (Toapply _)::tl -> to_expand tl | (Toexpand (name,s))::tl -> (name,s)::(to_expand tl) in let rec aux n = function [] -> [] | (Toapply arg)::tl -> arg::(aux n tl) | (Toexpand _)::tl -> (mkRel n)::(aux (n + 1) tl) in compose_lam (to_expand args_rev) (mkApp (c, Array.of_list (List.rev (aux 1 args_rev)))) let relation_of_hypothesis_and_term_to_rewrite gl (_,h) t = let hypt = pf_type_of gl h in let (heq, hargs) = decompose_app hypt in let rec get_all_but_last_two = function [] | [_] -> assert false | [_;_] -> [] | he::tl -> he::(get_all_but_last_two tl) in let aeq = mkApp (heq,(Array.of_list (get_all_but_last_two hargs))) in try relation_table_find aeq with Not_found -> (*CSC: still "setoid" in the error message *) errorlabstrm "Setoid_rewrite" (prterm aeq ++ str " is not a setoid equality.") let mark_occur t in_c = let rec aux t in_c = if eq_constr t in_c then Toreplace else match kind_of_term in_c with | App (c,al) -> let a = Array.map (aux t) al in if not (get_mark a) then Tokeep in_c 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)) | Prod (_, c1, c2) -> if (dependent (mkRel 1) c2) then Tokeep in_c else let c1m = aux t c1 in let c2m = aux t c2 in if ((is_to_replace c1m)||(is_to_replace c2m)) then (Mimp (c1m, c2m)) else Tokeep in_c | _ -> Tokeep in_c in aux t in_c let cic_morphism_context_list_of_list hole_relation = let rec aux = function [] -> assert false | [out,value] -> mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Relation_Class ; out |]), mkApp ((Lazy.force coq_fcl_singl), [| hole_relation; out ; value |]) | (out,value)::tl -> let outtl, valuetl = aux tl in mkApp ((Lazy.force coq_cons), [| Lazy.force coq_Relation_Class ; out ; outtl |]), mkApp ((Lazy.force coq_fcl_cons), [| hole_relation ; out ; outtl ; value ; valuetl |]) in aux let rec cic_type_nelist_of_list = function [] -> assert false | [value] -> mkApp ((Lazy.force coq_singl), [| mkType (Termops.new_univ ()) ; value |]) | value::tl -> 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 rec aux out = function MApp (f, m, args) -> 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 = if eq_constr out (cic_relation_class_of_relation_class (Lazy.force coq_prop_relation)) then mkApp ((Lazy.force coq_morphism_theory_of_predicate), [| cic_type_nelist_of_list f_args; f|]) else mkApp ((Lazy.force coq_morphism_theory_of_function), [| cic_type_nelist_of_list f_args; f_output; f|]) in mt,List.map (fun x -> ACLeibniz x) f_args in let cic_relations = List.map cic_relation_class_of_relation_class relations in let cic_args_relations,argst = cic_morphism_context_list_of_list hole_relation (List.combine cic_relations (List.map2 (fun t v -> aux t v) cic_relations (Array.to_list args))) in mkApp ((Lazy.force coq_App), [|hole_relation ; cic_args_relations ; out ; morphism_theory ; argst|]) | Toreplace -> mkApp ((Lazy.force coq_Toreplace), [| hole_relation |]) | Tokeep c -> mkApp ((Lazy.force coq_Tokeep), [| hole_relation ; out ; c |]) | Mimp (source, target) -> mkApp ((Lazy.force coq_Imp), [| hole_relation ; aux out source ; aux out target |]) in aux let apply_coq_setoid_rewrite hole_relation c1 c2 (lft2rgt,h) m = let {refl_aeq=hole_equality; refl_sym=hole_symmetry} = hole_relation in let hole_relation = cic_relation_class_of_relation_class (ACReflexive hole_relation) in let prop_relation = cic_relation_class_of_relation_class (Lazy.force coq_prop_relation) in let hyp = if lft2rgt then h else match hole_symmetry with Some sym -> mkApp (sym, [| c2 ; c1 ; h |]) | None -> errorlabstrm "Setoid_rewrite" (str "Rewriting from right to left not permitted since the " ++ 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.") in mkApp ((Lazy.force coq_setoid_rewrite), [| hole_relation ; prop_relation ; c1 ; c2 ; syntactic_but_representation_of_marked_but hole_relation prop_relation m ; hyp |]) let relation_rewrite c1 c2 (lft2rgt,cl) gl = let but = pf_concl gl in let (hyp,c1,c2) = let (cl',_) = Clenv.unify_to_subterm cl (c1,but) in let c1 = Clenv.clenv_instance_term cl' c1 in let c2 = Clenv.clenv_instance_term cl' c2 in (lft2rgt,Clenv.clenv_instance_template 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 thty = pf_type_of gl th in let simplified_thty = pf_unfoldn [[], Lazy.force coq_iff] gl (pf_unfoldn [[], Lazy.force coq_relation_of_relation_class] gl thty) in let ty1,ty2 = match destApplication simplified_thty with (_ (*and*),[|ty1;ty2|]) -> ty1,ty2 | _ -> assert false in let th' = mkApp ((Lazy.force coq_proj2), [|ty1; ty2; th|]) in let hyp2 = Termops.replace_term c1 c2 but in let simplified_thty' = mkProd (Anonymous, hyp2, lift 1 but) in (*CSC: the next line does not work because simplified_thty' is not used to generate the type of the new goals ;-( Tactics.apply (mkCast (th',simplified_thty')) gl Thus I am using the following longer (and less accurate) code *) tclTHENS (Tactics.apply (mkCast (th',simplified_thty'))) [Tactics.change_in_concl None hyp2] gl (*CSC: this is another way to proceed, but the generated proof_term would be much bigger than possible Tactics.forward true Anonymous (mkCast (th',simplified_thty')) gl (... followed by the application of the introduced hypothesis ...) *) let general_s_rewrite lft2rgt c gl = let (wc,_) = Evar_refiner.startWalk gl in let ctype = pf_type_of gl c in let eqclause = Clenv.make_clenv_binding wc (c,ctype) Rawterm.NoBindings in let (equiv, args) = decompose_app (Clenv.clenv_instance_template_type eqclause) in let rec get_last_two = function | [c1;c2] -> (c1, c2) | 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 exception Use_replace (*CSC: the name should be changed *) let setoid_replace c1 c2 gl = try let relation = match default_relation_for_carrier (pf_type_of gl c1) with ACReflexive sa -> sa | ACLeibniz _ -> raise Use_replace in let eq = mkApp (relation.refl_aeq, [| c1 ; c2 |]) in tclTHENS (assert_tac false Anonymous eq) [onLastHyp (fun id -> tclTHEN (general_s_rewrite true (mkVar id)) (clear [id])); Tacticals.tclIDTAC] gl with Use_replace -> (!replace c1 c2) gl let setoid_rewriteLR = general_s_rewrite true let setoid_rewriteRL = general_s_rewrite false