From 3ef7797ef6fc605dfafb32523261fe1b023aeecb Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Fri, 28 Apr 2006 14:59:16 +0000 Subject: Imported Upstream version 8.0pl3+8.1alpha --- tactics/setoid_replace.ml | 2366 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 1818 insertions(+), 548 deletions(-) (limited to 'tactics/setoid_replace.ml') diff --git a/tactics/setoid_replace.ml b/tactics/setoid_replace.ml index 74b062e0..a6331927 100644 --- a/tactics/setoid_replace.ml +++ b/tactics/setoid_replace.ml @@ -6,7 +6,7 @@ (* * GNU Lesser General Public License Version 2.1 *) (************************************************************************) -(* $Id: setoid_replace.ml,v 1.31.2.1 2004/07/16 19:30:55 herbelin Exp $ *) +(* $Id: setoid_replace.ml 8683 2006-04-05 15:47:39Z letouzey $ *) open Tacmach open Proof_type @@ -22,6 +22,8 @@ open Util open Pp open Printer open Environ +open Clenv +open Unification open Tactics open Tacticals open Vernacexpr @@ -29,106 +31,365 @@ open Safe_typing open Nametab open Decl_kinds open Constrintern - -type setoid = - { set_a : constr; - set_aeq : constr; - set_th : constr +open Mod_subst + +let replace = ref (fun _ _ -> assert false) +let register_replace f = replace := f + +let general_rewrite = ref (fun _ _ -> assert false) +let register_general_rewrite f = general_rewrite := f + +(* util function; it should be in util.mli *) +let prlist_with_sepi sep elem = + let rec aux n = + function + | [] -> mt () + | [h] -> elem n h + | h::t -> + let e = elem n h and s = sep() and r = aux (n+1) t in + e ++ s ++ r + in + aux 1 + +type relation = + { rel_a: constr ; + rel_aeq: constr; + rel_refl: constr option; + rel_sym: constr option; + rel_trans : constr option; + rel_quantifiers_no: int (* it helps unification *); + rel_X_relation_class: constr; + rel_Xreflexive_relation_class: constr + } + +type 'a relation_class = + Relation of 'a (* the rel_aeq of the relation or the relation *) + | Leibniz of constr option (* the carrier (if eq is partially instantiated) *) + +type 'a morphism = + { args : (bool option * 'a relation_class) list; + output : 'a relation_class; + lem : constr; + morphism_theory : constr } -type morphism = - { lem : constr; - profil : bool list; - arg_types : constr list; - lem2 : constr option +type funct = + { f_args : constr list; + f_output : constr } +type morphism_class = + ACMorphism of relation morphism + | ACFunction of funct + +let subst_mps_in_relation_class subst = + function + Relation t -> Relation (subst_mps subst t) + | Leibniz t -> Leibniz (option_app (subst_mps subst) t) + +let subst_mps_in_argument_class subst (variance,rel) = + variance, subst_mps_in_relation_class subst rel + +let constr_relation_class_of_relation_relation_class = + function + Relation relation -> Relation relation.rel_aeq + | Leibniz t -> Leibniz 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 global_constant dir s =Coqlib.gen_constant "Setoid_replace" ("Init"::dir) s +let gen_constant dir s = Coqlib.gen_constant "Setoid_replace" 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 (gen_constant ("Init"::dir) s)) let current_constant id = try global_reference id with Not_found -> - anomaly ("Setoid: cannot find "^(string_of_id id)) - -(* Setoid_theory *) - -let coq_Setoid_Theory = lazy(constant ["Setoid"] "Setoid_Theory") - -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_fleche = lazy(constant ["Setoid"] "fleche") - -(* Coq constants *) + anomaly ("Setoid: cannot find " ^ (string_of_id id)) -let coqeq = lazy(global_constant ["Logic"] "eq") +(* From Setoid.v *) -let coqconj = lazy(global_constant ["Logic"] "conj") -let coqand = lazy(global_constant ["Logic"] "and") -let coqproj1 = lazy(global_constant ["Logic"] "proj1") -let coqproj2 = lazy(global_constant ["Logic"] "proj2") - -(************************* Table of declared setoids **********************) +let coq_reflexive = + lazy(gen_constant ["Relations"; "Relation_Definitions"] "reflexive") +let coq_symmetric = + lazy(gen_constant ["Relations"; "Relation_Definitions"] "symmetric") +let coq_transitive = + lazy(gen_constant ["Relations"; "Relation_Definitions"] "transitive") +let coq_relation = + lazy(gen_constant ["Relations"; "Relation_Definitions"] "relation") +let coq_Relation_Class = lazy(constant ["Setoid"] "Relation_Class") +let coq_Argument_Class = lazy(constant ["Setoid"] "Argument_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_Compat = lazy(constant ["Setoid"] "Compat") -(* Setoids are stored in a table which is synchronised with the Reset mechanism. *) +let coq_AsymmetricReflexive = lazy(constant ["Setoid"] "AsymmetricReflexive") +let coq_SymmetricReflexive = lazy(constant ["Setoid"] "SymmetricReflexive") +let coq_SymmetricAreflexive = lazy(constant ["Setoid"] "SymmetricAreflexive") +let coq_AsymmetricAreflexive = lazy(constant ["Setoid"] "AsymmetricAreflexive") +let coq_Leibniz = lazy(constant ["Setoid"] "Leibniz") -module Cmap = Map.Make(struct type t = constr let compare = compare end) +let coq_RAsymmetric = lazy(constant ["Setoid"] "RAsymmetric") +let coq_RSymmetric = lazy(constant ["Setoid"] "RSymmetric") +let coq_RLeibniz = lazy(constant ["Setoid"] "RLeibniz") -let setoid_table = ref Gmap.empty +let coq_ASymmetric = lazy(constant ["Setoid"] "ASymmetric") +let coq_AAsymmetric = lazy(constant ["Setoid"] "AAsymmetric") -let setoid_table_add (s,th) = setoid_table := Gmap.add s th !setoid_table -let setoid_table_find s = Gmap.find s !setoid_table -let setoid_table_mem s = Gmap.mem s !setoid_table +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 subst_setoid subst setoid = - let set_a' = subst_mps subst setoid.set_a in - let set_aeq' = subst_mps subst setoid.set_aeq in - let set_th' = subst_mps subst setoid.set_th in - if set_a' == setoid.set_a - && set_aeq' == setoid.set_aeq - && set_th' == setoid.set_th +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") + +let coq_singl = lazy(constant ["Setoid"] "singl") +let coq_cons = lazy(constant ["Setoid"] "cons") + +let coq_equality_morphism_of_asymmetric_areflexive_transitive_relation = + lazy(constant ["Setoid"] + "equality_morphism_of_asymmetric_areflexive_transitive_relation") +let coq_equality_morphism_of_symmetric_areflexive_transitive_relation = + lazy(constant ["Setoid"] + "equality_morphism_of_symmetric_areflexive_transitive_relation") +let coq_equality_morphism_of_asymmetric_reflexive_transitive_relation = + lazy(constant ["Setoid"] + "equality_morphism_of_asymmetric_reflexive_transitive_relation") +let coq_equality_morphism_of_symmetric_reflexive_transitive_relation = + lazy(constant ["Setoid"] + "equality_morphism_of_symmetric_reflexive_transitive_relation") +let coq_make_compatibility_goal = + lazy(constant ["Setoid"] "make_compatibility_goal") +let coq_make_compatibility_goal_eval_ref = + lazy(eval_reference ["Setoid"] "make_compatibility_goal") +let coq_make_compatibility_goal_aux_eval_ref = + lazy(eval_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_ProperElementToKeep = lazy(constant ["Setoid"] "ProperElementToKeep") +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") +let coq_eq = lazy(gen_constant ["Init"; "Logic"] "eq") + +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_directed_relation_of_relation_class = + lazy(eval_reference ["Setoid"] "directed_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(gen_constant ["Init";"Logic"] "iff") +let coq_impl = lazy(constant ["Setoid"] "impl") + + +(************************* 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 "(" ++ pr_lconstr s.rel_a ++ str "," ++ pr_lconstr s.rel_aeq ++ str ")" + +let prrelation_class = + function + Relation eq -> + (try prrelation (relation_table_find eq) + with Not_found -> + str "[[ Error: " ++ pr_lconstr eq ++ + str " is not registered as a relation ]]") + | Leibniz (Some ty) -> pr_lconstr ty + | Leibniz None -> str "_" + +let prmorphism_argument_gen prrelation (variance,rel) = + prrelation rel ++ + match variance with + None -> str " ==> " + | Some true -> str " ++> " + | Some false -> str " --> " + +let prargument_class = prmorphism_argument_gen prrelation_class + +let pr_morphism_signature (l,c) = + prlist (prmorphism_argument_gen Ppconstr.pr_constr_expr) l ++ + Ppconstr.pr_constr_expr c + +let prmorphism k m = + pr_lconstr k ++ str ": " ++ + prlist prargument_class m.args ++ + 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 ?(filter=fun _ -> true) a = + let rng = Gmap.rng !relation_table in + match List.filter (fun ({rel_a=rel_a} as r) -> rel_a = a && filter r) rng with + [] -> Leibniz (Some a) + | relation::tl -> + if tl <> [] then + ppnl + (str "Warning: There are several relations on the carrier \"" ++ + pr_lconstr a ++ str "\". The relation " ++ prrelation relation ++ + str " is chosen.") ; + Relation relation + +let find_relation_class rel = + try Relation (relation_table_find rel) + with + Not_found -> + let rel = Reduction.whd_betadeltaiota (Global.env ()) rel in + match kind_of_term rel with + | App (eq,[|ty|]) when eq_constr eq (Lazy.force coq_eq) -> Leibniz (Some ty) + | _ when eq_constr rel (Lazy.force coq_eq) -> Leibniz None + | _ -> raise Not_found + +let coq_iff_relation = lazy (find_relation_class (Lazy.force coq_iff)) +let coq_impl_relation = lazy (find_relation_class (Lazy.force coq_impl)) + +let relation_morphism_of_constr_morphism = + let relation_relation_class_of_constr_relation_class = + function + Leibniz t -> Leibniz t + | Relation aeq -> + Relation (try relation_table_find aeq with Not_found -> assert false) + in + function mor -> + let args' = + List.map + (fun (variance,rel) -> + variance, relation_relation_class_of_constr_relation_class rel + ) 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 rel_a' = subst_mps subst relation.rel_a in + let rel_aeq' = subst_mps subst relation.rel_aeq in + let rel_refl' = option_app (subst_mps subst) relation.rel_refl in + let rel_sym' = option_app (subst_mps subst) relation.rel_sym in + let rel_trans' = option_app (subst_mps subst) relation.rel_trans in + let rel_X_relation_class' = subst_mps subst relation.rel_X_relation_class in + let rel_Xreflexive_relation_class' = + subst_mps subst relation.rel_Xreflexive_relation_class + in + if rel_a' == relation.rel_a + && rel_aeq' == relation.rel_aeq + && rel_refl' == relation.rel_refl + && rel_sym' == relation.rel_sym + && rel_trans' == relation.rel_trans + && rel_X_relation_class' == relation.rel_X_relation_class + && rel_Xreflexive_relation_class'==relation.rel_Xreflexive_relation_class then - setoid + relation else - { set_a = set_a' ; - set_aeq = set_aeq' ; - set_th = set_th' ; + { rel_a = rel_a' ; + rel_aeq = rel_aeq' ; + rel_refl = rel_refl' ; + rel_sym = rel_sym'; + rel_trans = rel_trans'; + rel_quantifiers_no = relation.rel_quantifiers_no; + rel_X_relation_class = rel_X_relation_class'; + rel_Xreflexive_relation_class = rel_Xreflexive_relation_class' } -let equiv_list () = List.map (fun x -> x.set_aeq) (Gmap.rng !setoid_table) +let equiv_list () = List.map (fun x -> x.rel_aeq) (Gmap.rng !relation_table) let _ = - Summary.declare_summary "setoid-table" - { Summary.freeze_function = (fun () -> !setoid_table); - Summary.unfreeze_function = (fun t -> setoid_table := t); - Summary.init_function = (fun () -> setoid_table := Gmap .empty); + 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 = false; Summary.survive_section = false } -(* Declare a new type of object in the environment : "setoid-theory". *) - -let (setoid_to_obj, obj_to_setoid)= - let cache_set (_,(s, th)) = setoid_table_add (s,th) +(* 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 rel_sym = + match th.rel_sym with + None -> old_relation.rel_sym + | Some t -> Some t} in + ppnl + (str "Warning: The relation " ++ prrelation th' ++ + str " is redeclared. The new declaration" ++ + (match th'.rel_refl with + None -> str "" + | Some t -> str " (reflevity proved by " ++ pr_lconstr t) ++ + (match th'.rel_sym with + None -> str "" + | Some t -> + (if th'.rel_refl = None then str " (" else str " and ") ++ + str "symmetry proved by " ++ pr_lconstr t) ++ + (if th'.rel_refl <> None && th'.rel_sym <> None then + str ")" else str "") ++ + str " replaces the old declaration" ++ + (match old_relation.rel_refl with + None -> str "" + | Some t -> str " (reflevity proved by " ++ pr_lconstr t) ++ + (match old_relation.rel_sym with + None -> str "" + | Some t -> + (if old_relation.rel_refl = None then + str " (" else str " and ") ++ + str "symmetry proved by " ++ pr_lconstr t) ++ + (if old_relation.rel_refl <> None && old_relation.rel_sym <> None + then str ")" else str "") ++ + 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_setoid subst th in + let th' = subst_relation subst th in if s' == s && th' == th then obj else - (s',th') + (s',th') and export_set x = Some x in - declare_object {(default_object "setoid-theory") with - cache_function = cache_set; - open_function = (fun i o -> if i=1 then cache_set o); - subst_function = subst_set; - classify_function = (fun (_,x) -> Substitute x); - export_function = export_set} + 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 ********************) @@ -136,24 +397,56 @@ let (setoid_to_obj, obj_to_setoid)= let morphism_table = ref Gmap.empty -let morphism_table_add (m,c) = morphism_table := Gmap.add m c !morphism_table let morphism_table_find m = Gmap.find m !morphism_table -let morphism_table_mem m = Gmap.mem 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 + ppnl + (str "Warning: The morphism " ++ prmorphism m old_morph ++ + str " is redeclared. " ++ + str "The new declaration whose compatibility is proved by " ++ + pr_lconstr c.lem ++ str " replaces the old declaration whose" ++ + str " compatibility was proved by " ++ + pr_lconstr old_morph.lem ++ str ".") + with + Not_found -> morphism_table := Gmap.add m (c::old) !morphism_table + +let default_morphism ?(filter=fun _ -> true) m = + match List.filter filter (morphism_table_find m) with + [] -> raise Not_found + | m1::ml -> + if ml <> [] then + ppnl + (str "Warning: There are several morphisms associated to \"" ++ + pr_lconstr m ++ str"\". Morphism " ++ prmorphism m m1 ++ + str " is randomly chosen."); + relation_morphism_of_constr_morphism m1 let subst_morph subst morph = let lem' = subst_mps subst morph.lem in - let arg_types' = list_smartmap (subst_mps subst) morph.arg_types in - let lem2' = option_smartmap (subst_mps subst) morph.lem2 in + let args' = list_smartmap (subst_mps_in_argument_class subst) morph.args in + let output' = subst_mps_in_relation_class subst morph.output in + let morphism_theory' = subst_mps subst morph.morphism_theory in if lem' == morph.lem - && arg_types' == morph.arg_types - && lem2' == morph.lem2 + && args' == morph.args + && output' == morph.output + && morphism_theory' == morph.morphism_theory then morph else - { lem = lem' ; - profil = morph.profil ; - arg_types = arg_types' ; - lem2 = lem2' ; + { args = args' ; + output = output' ; + lem = lem' ; + morphism_theory = morphism_theory' } @@ -173,139 +466,42 @@ let (morphism_to_obj, obj_to_morphism)= let m' = subst_mps subst m in let c' = subst_morph subst c in if m' == m && c' == c then obj else - (m',c') + (m',c') and export_set x = Some x in declare_object {(default_object "morphism-definition") with - cache_function = cache_set; - open_function = (fun i o -> if i=1 then cache_set o); - subst_function = subst_set; - classify_function = (fun (_,x) -> Substitute x); - export_function = export_set} - -(************************** Adding a setoid to the database *********************) - -(* Find the setoid theory associated with a given type A. -This implies that only one setoid theory can be declared for -a given type A. *) - -let find_theory a = - try - setoid_table_find a - with Not_found -> - errorlabstrm "Setoid" - (str "No Declared Setoid Theory for " ++ - prterm a ++ fnl () ++ - str "Use Add Setoid to declare it") - -(* Add a Setoid to the database after a type verification. *) - -let eq_lem_common_sign env a eq = - let na = named_hd env a Anonymous in - let ne = named_hd env eq Anonymous in - [(ne,None,mkApp (eq, [|(mkRel 3);(mkRel 2)|])); - (ne,None,mkApp (eq, [|(mkRel 4);(mkRel 3)|])); - (na,None,a);(na,None,a);(na,None,a);(na,None,a)] - -(* Proof of (a,b,c,d:A)(eq a b)->(eq c d)->(eq a c)->(eq b d) *) -let eq_lem_proof env a eq sym trans = - let sign = eq_lem_common_sign env a eq in - let ne = named_hd env eq Anonymous in - let sign = (ne,None,mkApp (eq, [|(mkRel 6);(mkRel 4)|]))::sign in - let ccl = mkApp (eq, [|(mkRel 6);(mkRel 4)|]) in - let body = - mkApp (trans, - [|(mkRel 6);(mkRel 7);(mkRel 4); - (mkApp (sym, [|(mkRel 7);(mkRel 6);(mkRel 3)|])); - (mkApp (trans, - [|(mkRel 7);(mkRel 5);(mkRel 4);(mkRel 1);(mkRel 2)|]))|]) in - let p = it_mkLambda_or_LetIn body sign in - let t = it_mkProd_or_LetIn ccl sign in - (p,t) - -(* Proof of (a,b,c,d:A)(eq a b)->(eq c d)->((eq a c)<->(eq b d)) *) -let eq_lem2_proof env a eq sym trans = - let sign = eq_lem_common_sign env a eq in - let ccl1 = - mkArrow - (mkApp (eq, [|(mkRel 6);(mkRel 4)|])) - (mkApp (eq, [|(mkRel 6);(mkRel 4)|])) in - let ccl2 = - mkArrow - (mkApp (eq, [|(mkRel 5);(mkRel 3)|])) - (mkApp (eq, [|(mkRel 7);(mkRel 5)|])) in - let ccl = mkApp (Lazy.force coqand, [|ccl1;ccl2|]) in - let body = - mkApp ((Lazy.force coqconj), - [|ccl1;ccl2; - lambda_create env - (mkApp (eq, [|(mkRel 6);(mkRel 4)|]), - (mkApp (trans, - [|(mkRel 6);(mkRel 7);(mkRel 4); - (mkApp (sym, [|(mkRel 7);(mkRel 6);(mkRel 3)|])); - (mkApp (trans, - [|(mkRel 7);(mkRel 5);(mkRel 4);(mkRel 1);(mkRel 2)|]))|]))); - lambda_create env - (mkApp (eq, [|(mkRel 5);(mkRel 3)|]), - (mkApp (trans, - [|(mkRel 7);(mkRel 6);(mkRel 5);(mkRel 3); - (mkApp (trans, - [|(mkRel 6);(mkRel 4);(mkRel 5);(mkRel 1); - (mkApp (sym, [|(mkRel 5);(mkRel 4);(mkRel 2)|]))|]))|])))|]) - in - let p = it_mkLambda_or_LetIn body sign in - let t = it_mkProd_or_LetIn ccl sign in - (p,t) - -let gen_eq_lem_name = - let i = ref 0 in - function () -> - incr i; - make_ident "setoid_eq_ext" (Some !i) - -let add_setoid a aeq th = - if setoid_table_mem a - then errorlabstrm "Add Setoid" - (str "A Setoid Theory is already declared for " ++ prterm a) - else 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 (Lib.add_anonymous_leaf - (setoid_to_obj - (a, { set_a = a; - set_aeq = aeq; - set_th = th})); - let sym = mkApp ((Lazy.force coq_seq_sym), [|a; aeq; th|]) in - let trans = mkApp ((Lazy.force coq_seq_trans), [|a; aeq; th|]) in - let (eq_morph, eq_morph_typ) = eq_lem_proof env a aeq sym trans in - let (eq_morph2, eq_morph2_typ) = eq_lem2_proof env a aeq sym trans in - Options.if_verbose ppnl (prterm a ++str " is registered as a setoid"); - let eq_ext_name = gen_eq_lem_name () in - let eq_ext_name2 = gen_eq_lem_name () in - let _ = Declare.declare_constant eq_ext_name - ((DefinitionEntry {const_entry_body = eq_morph; - const_entry_type = Some eq_morph_typ; - const_entry_opaque = true}), - IsProof Lemma) in - let _ = Declare.declare_constant eq_ext_name2 - ((DefinitionEntry {const_entry_body = eq_morph2; - const_entry_type = Some eq_morph2_typ; - const_entry_opaque = true}), - IsProof Lemma) in - let eqmorph = (current_constant eq_ext_name) in - let eqmorph2 = (current_constant eq_ext_name2) in - (Lib.add_anonymous_leaf - (morphism_to_obj (aeq, - { lem = eqmorph; - profil = [true; true]; - arg_types = [a;a]; - lem2 = (Some eqmorph2)}))); - Options.if_verbose ppnl (prterm aeq ++str " is registered as a morphism")) - else errorlabstrm "Add Setoid" (str "Not a valid setoid theory") - -(* The vernac command "Add Setoid" *) -let add_setoid a aeq th = - add_setoid (constr_of a) (constr_of aeq) (constr_of th) + 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 **********************) + +let print_setoids () = + Gmap.iter + (fun k relation -> + assert (k=relation.rel_aeq) ; + ppnl (str"Relation " ++ prrelation relation ++ str";" ++ + (match relation.rel_refl with + None -> str "" + | Some t -> str" reflexivity proved by " ++ pr_lconstr t) ++ + (match relation.rel_sym with + None -> str "" + | Some t -> str " symmetry proved by " ++ pr_lconstr t) ++ + (match relation.rel_trans with + None -> str "" + | Some t -> str " transitivity proved by " ++ pr_lconstr t))) + !relation_table ; + Gmap.iter + (fun k l -> + List.iter + (fun ({lem=lem} as mor) -> + ppnl (str "Morphism " ++ prmorphism k mor ++ + str ". Compatibility proved by " ++ + pr_lconstr lem ++ str ".")) + l) !morphism_table +;; (***************** Adding a morphism to the database ****************************) @@ -314,8 +510,8 @@ let add_setoid a aeq th = let edited = ref Gmap.empty -let new_edited id m profil = - edited := Gmap.add id (m,profil) !edited +let new_edited id m = + edited := Gmap.add id m !edited let is_edited id = Gmap.mem id !edited @@ -326,361 +522,1435 @@ let no_more_edited id = let what_edited id = Gmap.find id !edited -let check_is_dependent t n = - let rec aux t i n = - if (i add_suffix id "_ext" - | Const kn -> add_suffix (id_of_label (label kn)) "_ext" - | Ind (kn, i) -> add_suffix (id_of_label (label kn)) ((string_of_int i)^"_ext") - | Construct ((kn,i),j) -> add_suffix - (id_of_label (label kn)) ((string_of_int i)^(string_of_int j)^"_ext") - | _ -> errorlabstrm "New Morphism" (str "The term " ++ prterm m ++ str "is not a known name") - -let gen_lemma_tail m lisset body n = - let l = (List.length lisset) in - let a1 = Array.create l (mkRel 0) in - let a2 = Array.create l (mkRel 0) in - let rec aux i n = function - | true::q -> - a1.(i) <- (mkRel n); - a2.(i) <- (mkRel (n-1)); - aux (i+1) (n-2) q - | false::q -> - a1.(i) <- (mkRel n); - a2.(i) <- (mkRel n); - aux (i+1) (n-1) q - | [] -> () in - aux 0 n lisset; - if (eq_constr body mkProp) - then mkArrow (mkApp (m,a1)) (lift 1 (mkApp (m, a2))) - else if (setoid_table_mem body) - then mkApp ((setoid_table_find body).set_aeq, [|(mkApp (m, a1)); (mkApp (m, a2))|]) - else mkApp ((Lazy.force coqeq), [|body; (mkApp (m, a1)); (mkApp (m, a2))|]) - -let gen_lemma_middle m larg lisset body n = - let rec aux la li i n = match (la, li) with - | ([], []) -> gen_lemma_tail m lisset body n - | (t::q, true::lq) -> - mkArrow (mkApp ((setoid_table_find t).set_aeq, - [|(mkRel i); (mkRel (i-1))|])) (aux q lq (i-1) (n+1)) - | (t::q, false::lq) -> aux q lq (i-1) n - | _ -> assert false - in aux larg lisset n n - -let gen_compat_lemma env m body larg lisset = - let rec aux la li n = match (la, li) with - | (t::q, true::lq) -> - prod_create env (t,(prod_create env (t, (aux q lq (n+2))))) - | (t::q, false::lq) -> - prod_create env (t, (aux q lq (n+1))) - | ([],[]) -> gen_lemma_middle m larg lisset body n - | _ -> assert false - in aux larg lisset 0 - -let new_morphism m id hook = - if morphism_table_mem m - then errorlabstrm "New Morphism" - (str "The term " ++ prterm m ++ str " is already declared as a morphism") - else - let env = Global.env() in - let typeofm = (Typing.type_of env Evd.empty m) in - let typ = (nf_betaiota typeofm) in (* nf_bdi avant, mais bug *) - let (argsrev, body) = (decompose_prod typ) in - let args = (List.rev argsrev) in - if (args=[]) - then errorlabstrm "New Morphism" - (str "The term " ++ prterm m ++ str " is not a product") - else if (check_is_dependent typ (List.length args)) - then errorlabstrm "New Morphism" - (str "The term " ++ prterm m ++ str " should not be a dependent product") - else ( - let args_t = (List.map snd args) in - let poss = (List.map setoid_table_mem args_t) in - let lem = (gen_compat_lemma env m body args_t poss) in - new_edited id m poss; - Pfedit.start_proof id (IsGlobal (Proof Lemma)) - (Declare.clear_proofs (Global.named_context ())) - lem hook; - (Options.if_verbose msg (Pfedit.pr_open_subgoals ()))) - -let rec sub_bool l1 n = function - | [] -> [] - | true::q -> ((List.hd l1), n)::(sub_bool (List.tl l1) (n-2) q) - | false::q -> (sub_bool (List.tl l1) (n-1) q) - -let gen_lemma_iff_tail m mext larg lisset n k = - let a1 = Array.create k (mkRel 0) in - let a2 = Array.create k (mkRel 0) in - let nb = List.length lisset in - let b1 = Array.create nb (mkRel 0) in - let b2 = Array.create nb (mkRel 0) in - let rec aux i j = function - |[] -> () - |true::q -> - (a1.(i) <- (mkRel j); - a1.(i+1) <- (mkRel (j-1)); - a2.(i) <- (mkRel (j-1)); - a2.(i+1) <- (mkRel j); - aux (i+2) (j-2) q) - |false::q -> - (a1.(i) <- (mkRel j); - a2.(i) <- (mkRel j); - aux (i+1) (j-1) q) in - let rec aux2 i j = function - | (t,p)::q -> - let th = (setoid_table_find t).set_th - and equiv = (setoid_table_find t).set_aeq in - a1.(i) <- (mkRel j); - a2.(i) <- mkApp ((Lazy.force coq_seq_sym), - [|t; equiv; th; (mkRel p); (mkRel (p-1)); (mkRel j)|]); - aux2 (i+1) (j-1) q - | [] -> () in - let rec aux3 i j = function - | true::q -> - b1.(i) <- (mkRel j); - b2.(i) <- (mkRel (j-1)); - aux3 (i+1) (j-2) q - | false::q -> - b1.(i) <- (mkRel j); - b2.(i) <- (mkRel j); - aux3 (i+1) (j-1) q - | [] -> () in - aux 0 k lisset; - aux2 n (k-n) (sub_bool larg k lisset); - aux3 0 k lisset; - mkApp ((Lazy.force coqconj), - [|(mkArrow (mkApp (m,b1)) (lift 1 (mkApp (m, b2)))); - (mkArrow (mkApp (m,b2)) (lift 1 (mkApp (m, b1)))); - (mkApp (mext, a1));(mkApp (mext, a2))|]) - -let gen_lemma_iff_middle env m mext larg lisset n = - let rec aux la li i k = match (la, li) with - | ([], []) -> gen_lemma_iff_tail m mext larg lisset n k - | (t::q, true::lq) -> - lambda_create env ((mkApp ((setoid_table_find t).set_aeq, [|(mkRel i); (mkRel (i-1))|])), - (aux q lq (i-1) (k+1))) - | (t::q, false::lq) -> aux q lq (i-1) k - | _ -> assert false - in aux larg lisset n n - -let gen_lem_iff env m mext larg lisset = - let rec aux la li n = match (la, li) with - | (t::q, true::lq) -> - lambda_create env (t,(lambda_create env (t, (aux q lq (n+2))))) - | (t::q, false::lq) -> - lambda_create env (t, (aux q lq (n+1))) - | ([],[]) -> gen_lemma_iff_middle env m mext larg lisset n - | _ -> assert false - in aux larg lisset 0 - -let add_morphism lem_name (m,profil) = - if morphism_table_mem m - then errorlabstrm "New Morphism" - (str "The term " ++ prterm m ++ str " is already declared as a morpism") - else - let env = Global.env() in - let mext = (current_constant lem_name) in - let typeofm = (Typing.type_of env Evd.empty m) in - let typ = (nf_betaiota typeofm) in - let (argsrev, body) = (decompose_prod typ) in - let args = List.rev argsrev in - let args_t = (List.map snd args) in - let poss = (List.map setoid_table_mem args_t) in - let _ = assert (poss=profil) in - (if (eq_constr body mkProp) - then - (let lem_2 = gen_lem_iff env m mext args_t poss in - let lem2_name = add_suffix lem_name "2" in - let _ = Declare.declare_constant lem2_name - ((DefinitionEntry {const_entry_body = lem_2; - const_entry_type = None; - const_entry_opaque = true}), - IsProof Lemma) in - let lem2 = (current_constant lem2_name) in - (Lib.add_anonymous_leaf - (morphism_to_obj (m, - { lem = mext; - profil = poss; - arg_types = args_t; - lem2 = (Some lem2)}))); - Options.if_verbose message ((string_of_id lem2_name) ^ " is defined")) +(* also returns the triple (args_ty_quantifiers_rev,real_args_ty,real_output) + where the args_ty and the output are delifted *) +let check_is_dependent n args_ty output = + let m = List.length args_ty - n in + let args_ty_quantifiers, args_ty = Util.list_chop n args_ty in + let rec aux m t = + match kind_of_term t with + Prod (n,s,t) when m > 0 -> + if not (dependent (mkRel 1) t) then + let args,out = aux (m - 1) (subst1 (mkRel 1) (* dummy *) t) in + s::args,out + else + errorlabstrm "New Morphism" + (str "The morphism is not a quantified non dependent product.") + | _ -> [],t + in + let ty = compose_prod (List.rev args_ty) output in + let args_ty, output = aux m ty in + List.rev args_ty_quantifiers, args_ty, output + +let cic_relation_class_of_X_relation typ value = + function + {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=Some refl; rel_sym=None} -> + mkApp ((Lazy.force coq_AsymmetricReflexive), + [| typ ; value ; rel_a ; rel_aeq; refl |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=Some refl; rel_sym=Some sym} -> + mkApp ((Lazy.force coq_SymmetricReflexive), + [| typ ; rel_a ; rel_aeq; sym ; refl |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=None; rel_sym=None} -> + mkApp ((Lazy.force coq_AsymmetricAreflexive), + [| typ ; value ; rel_a ; rel_aeq |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=None; rel_sym=Some sym} -> + mkApp ((Lazy.force coq_SymmetricAreflexive), + [| typ ; rel_a ; rel_aeq; sym |]) + +let cic_relation_class_of_X_relation_class typ value = + function + Relation {rel_X_relation_class=x_relation_class} -> + mkApp (x_relation_class, [| typ ; value |]) + | Leibniz (Some t) -> + mkApp ((Lazy.force coq_Leibniz), [| typ ; t |]) + | Leibniz None -> assert false + + +let cic_precise_relation_class_of_relation = + function + {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=Some refl; rel_sym=None} -> + mkApp ((Lazy.force coq_RAsymmetric), [| rel_a ; rel_aeq; refl |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=Some refl; rel_sym=Some sym} -> + mkApp ((Lazy.force coq_RSymmetric), [| rel_a ; rel_aeq; sym ; refl |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=None; rel_sym=None} -> + mkApp ((Lazy.force coq_AAsymmetric), [| rel_a ; rel_aeq |]) + | {rel_a=rel_a; rel_aeq=rel_aeq; rel_refl=None; rel_sym=Some sym} -> + mkApp ((Lazy.force coq_ASymmetric), [| rel_a ; rel_aeq; sym |]) + +let cic_precise_relation_class_of_relation_class = + function + Relation + {rel_aeq=rel_aeq; rel_Xreflexive_relation_class=lem; rel_refl=rel_refl } + -> + rel_aeq,lem,not(rel_refl=None) + | Leibniz (Some t) -> + mkApp ((Lazy.force coq_eq), [| t |]), + mkApp ((Lazy.force coq_RLeibniz), [| t |]), true + | Leibniz None -> assert false + +let cic_relation_class_of_relation_class rel = + cic_relation_class_of_X_relation_class + (Lazy.force coq_unit) (Lazy.force coq_tt) rel + +let cic_argument_class_of_argument_class (variance,arg) = + let coq_variant_value = + match variance with + None -> (Lazy.force coq_Covariant) (* dummy value, it won't be used *) + | Some true -> (Lazy.force coq_Covariant) + | Some false -> (Lazy.force coq_Contravariant) + in + cic_relation_class_of_X_relation_class (Lazy.force coq_variance) + coq_variant_value arg + +let cic_arguments_of_argument_class_list args = + let rec aux = + function + [] -> assert false + | [last] -> + mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Argument_Class; last |]) + | he::tl -> + mkApp ((Lazy.force coq_cons), + [| Lazy.force coq_Argument_Class; he ; aux tl |]) + in + aux (List.map cic_argument_class_of_argument_class args) + +let gen_compat_lemma_statement quantifiers_rev output args m = + let output = cic_relation_class_of_relation_class output in + let args = cic_arguments_of_argument_class_list args in + args, output, + compose_prod quantifiers_rev + (mkApp ((Lazy.force coq_make_compatibility_goal), [| args ; output ; m |])) + +let morphism_theory_id_of_morphism_proof_id id = + id_of_string (string_of_id id ^ "_morphism_theory") + +(* apply_to_rels c [l1 ; ... ; ln] returns (c Rel1 ... reln) *) +let apply_to_rels c l = + if l = [] then c + else + let len = List.length l in + applistc c (Util.list_map_i (fun i _ -> mkRel (len - i)) 0 l) + +let apply_to_relation subst rel = + if Array.length subst = 0 then rel + else + let new_quantifiers_no = rel.rel_quantifiers_no - Array.length subst in + assert (new_quantifiers_no >= 0) ; + { rel_a = mkApp (rel.rel_a, subst) ; + rel_aeq = mkApp (rel.rel_aeq, subst) ; + rel_refl = option_app (fun c -> mkApp (c,subst)) rel.rel_refl ; + rel_sym = option_app (fun c -> mkApp (c,subst)) rel.rel_sym; + rel_trans = option_app (fun c -> mkApp (c,subst)) rel.rel_trans; + rel_quantifiers_no = new_quantifiers_no; + rel_X_relation_class = mkApp (rel.rel_X_relation_class, subst); + rel_Xreflexive_relation_class = + mkApp (rel.rel_Xreflexive_relation_class, subst) } + +let add_morphism lemma_infos mor_name (m,quantifiers_rev,args,output) = + let lem = + match lemma_infos with + None -> + (* the Morphism_Theory object has already been created *) + let applied_args = + let len = List.length quantifiers_rev in + let subst = + Array.of_list + (Util.list_map_i (fun i _ -> mkRel (len - i)) 0 quantifiers_rev) + in + List.map + (fun (v,rel) -> + match rel with + Leibniz (Some t) -> + assert (subst=[||]); + v, Leibniz (Some t) + | Leibniz None -> + assert (Array.length subst = 1); + v, Leibniz (Some (subst.(0))) + | Relation rel -> v, Relation (apply_to_relation subst rel)) args + in + compose_lam quantifiers_rev + (mkApp (Lazy.force coq_Compat, + [| cic_arguments_of_argument_class_list applied_args; + cic_relation_class_of_relation_class output; + apply_to_rels (current_constant mor_name) quantifiers_rev |])) + | 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_internal_constant mor_name + (DefinitionEntry + {const_entry_body = + compose_lam quantifiers_rev + (mkApp ((Lazy.force coq_Build_Morphism_Theory), + [| argsconstr; outputconstr; apply_to_rels m quantifiers_rev ; + apply_to_rels mext quantifiers_rev |])); + const_entry_type = None; + const_entry_opaque = false; + const_entry_boxed = Options.boxed_definitions()}, + IsDefinition Definition)) ; + mext + in + let mmor = current_constant mor_name in + let args_constr = + List.map + (fun (variance,arg) -> + variance, constr_relation_class_of_relation_relation_class arg) 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 = lem; + morphism_theory = mmor })); + Options.if_verbose ppnl (pr_lconstr m ++ str " is registered as a morphism") + +(* first order matching with a bit of conversion *) +let unify_relation_carrier_with_type env rel t = + let raise_error quantifiers_no = + errorlabstrm "New Morphism" + (str "One morphism argument or its output has type " ++ pr_lconstr t ++ + str " but the signature requires an argument of type \"" ++ + pr_lconstr rel.rel_a ++ str " " ++ prvect_with_sep pr_spc (fun _ -> str "?") + (Array.make quantifiers_no 0) ++ str "\"") in + let args = + match kind_of_term t with + App (he',args') -> + let argsno = Array.length args' - rel.rel_quantifiers_no in + let args1 = Array.sub args' 0 argsno in + let args2 = Array.sub args' argsno rel.rel_quantifiers_no in + if is_conv env Evd.empty rel.rel_a (mkApp (he',args1)) then + args2 else - (Lib.add_anonymous_leaf - (morphism_to_obj (m, - { lem = mext; - profil = poss; - arg_types = args_t; - lem2 = None})))); - Options.if_verbose ppnl (prterm m ++str " is registered as a morphism") -let morphism_hook stre ref = + raise_error rel.rel_quantifiers_no + | _ -> + if rel.rel_quantifiers_no = 0 && is_conv env Evd.empty rel.rel_a t then + [||] + else + begin + let evars,args,instantiated_rel_a = + let ty = Typing.type_of env Evd.empty rel.rel_a in + let evd = Evd.create_evar_defs Evd.empty in + let evars,args,concl = + Clenv.clenv_environments_evars env evd + (Some rel.rel_quantifiers_no) ty + in + evars, args, + nf_betaiota + (match args with [] -> rel.rel_a | _ -> applist (rel.rel_a,args)) + in + let evars' = + w_unify true (*??? or false? *) env Reduction.CONV (*??? or cumul? *) + ~mod_delta:true (*??? or true? *) t instantiated_rel_a evars in + let args' = + List.map (Reductionops.nf_evar (Evd.evars_of evars')) args + in + Array.of_list args' + end + in + apply_to_relation args rel + +let unify_relation_class_carrier_with_type env rel t = + match rel with + Leibniz (Some t') -> + if is_conv env Evd.empty t t' then + rel + else + errorlabstrm "New Morphism" + (str "One morphism argument or its output has type " ++ pr_lconstr t ++ + str " but the signature requires an argument of type " ++ + pr_lconstr t') + | Leibniz None -> Leibniz (Some t) + | Relation rel -> Relation (unify_relation_carrier_with_type env rel t) + +(* first order matching with a bit of conversion *) +(* Note: the type checking operations performed by the function could *) +(* be done once and for all abstracting the morphism structure using *) +(* the quantifiers. Would the new structure be more suited than the *) +(* existent one for other tasks to? (e.g. pretty printing would expose *) +(* much more information: is it ok or is it too much information?) *) +let unify_morphism_with_arguments gl (c,av) + {args=args; output=output; lem=lem; morphism_theory=morphism_theory} t += + let al = Array.to_list av in + let argsno = List.length args in + let quantifiers,al' = Util.list_chop (List.length al - argsno) al in + let quantifiersv = Array.of_list quantifiers in + let c' = mkApp (c,quantifiersv) in + if dependent t c' then None else ( + (* these are pf_type_of we could avoid *) + let al'_type = List.map (Tacmach.pf_type_of gl) al' in + let args' = + List.map2 + (fun (var,rel) ty -> + var,unify_relation_class_carrier_with_type (pf_env gl) rel ty) + args al'_type in + (* this is another pf_type_of we could avoid *) + let ty = Tacmach.pf_type_of gl (mkApp (c,av)) in + let output' = unify_relation_class_carrier_with_type (pf_env gl) output ty in + let lem' = mkApp (lem,quantifiersv) in + let morphism_theory' = mkApp (morphism_theory,quantifiersv) in + Some + ({args=args'; output=output'; lem=lem'; morphism_theory=morphism_theory'}, + c',Array.of_list al')) + +let new_morphism m signature id hook = + if Nametab.exists_cci (Lib.make_path id) or is_section_variable id then + errorlabstrm "New Morphism" (pr_id id ++ str " already exists") + else + let env = Global.env() in + let typeofm = Typing.type_of env Evd.empty m in + let typ = clos_norm_flags Closure.betaiotazeta empty_env Evd.empty typeofm in + let argsrev, output = + match signature with + None -> decompose_prod typ + | Some (_,output') -> + (* the carrier of the relation output' can be a Prod ==> + we must uncurry on the fly output. + E.g: A -> B -> C vs A -> (B -> C) + args output args output + *) + let rel = find_relation_class output' in + let rel_a,rel_quantifiers_no = + match rel with + Relation rel -> rel.rel_a, rel.rel_quantifiers_no + | Leibniz (Some t) -> t, 0 + | Leibniz None -> assert false in + let rel_a_n = + clos_norm_flags Closure.betaiotazeta empty_env Evd.empty rel_a in + try + let _,output_rel_a_n = decompose_lam_n rel_quantifiers_no rel_a_n in + let argsrev,_ = decompose_prod output_rel_a_n in + let n = List.length argsrev in + let argsrev',_ = decompose_prod typ in + let m = List.length argsrev' in + decompose_prod_n (m - n) typ + with UserError(_,_) -> + (* decompose_lam_n failed. This may happen when rel_a is an axiom, + a constructor, an inductive type, etc. *) + decompose_prod typ + in + let args_ty = List.rev argsrev in + let args_ty_len = List.length (args_ty) in + let args_ty_quantifiers_rev,args,args_instance,output,output_instance = + match signature with + None -> + if args_ty = [] then + errorlabstrm "New Morphism" + (str "The term " ++ pr_lconstr m ++ str " has type " ++ + pr_lconstr typeofm ++ str " that is not a product.") ; + ignore (check_is_dependent 0 args_ty output) ; + let args = + List.map + (fun (_,ty) -> None,default_relation_for_carrier ty) args_ty in + let output = default_relation_for_carrier output in + [],args,args,output,output + | Some (args,output') -> + assert (args <> []); + let number_of_arguments = List.length args in + let number_of_quantifiers = args_ty_len - number_of_arguments in + if number_of_quantifiers < 0 then + errorlabstrm "New Morphism" + (str "The morphism " ++ pr_lconstr m ++ str " has type " ++ + pr_lconstr typeofm ++ str " that attends at most " ++ int args_ty_len ++ + str " arguments. The signature that you specified requires " ++ + int number_of_arguments ++ str " arguments.") + else + begin + (* the real_args_ty returned are already delifted *) + let args_ty_quantifiers_rev, real_args_ty, real_output = + check_is_dependent number_of_quantifiers args_ty output in + let quantifiers_rel_context = + List.map (fun (n,t) -> n,None,t) args_ty_quantifiers_rev in + let env = push_rel_context quantifiers_rel_context env in + let find_relation_class t real_t = + try + let rel = find_relation_class t in + rel, unify_relation_class_carrier_with_type env rel real_t + with Not_found -> + errorlabstrm "Add Morphism" + (str "Not a valid signature: " ++ pr_lconstr t ++ + str " is neither a registered relation nor the Leibniz " ++ + str " equality.") + in + let find_relation_class_v (variance,t) real_t = + let relation,relation_instance = find_relation_class t real_t in + match relation, variance with + Leibniz _, None + | Relation {rel_sym = Some _}, None + | Relation {rel_sym = None}, Some _ -> + (variance, relation), (variance, relation_instance) + | Relation {rel_sym = None},None -> + errorlabstrm "Add Morphism" + (str "You must specify the variance in each argument " ++ + str "whose relation is asymmetric.") + | Leibniz _, Some _ + | Relation {rel_sym = Some _}, Some _ -> + errorlabstrm "Add Morphism" + (str "You cannot specify the variance of an argument " ++ + str "whose relation is symmetric.") + in + let args, args_instance = + List.split + (List.map2 find_relation_class_v args real_args_ty) in + let output,output_instance= find_relation_class output' real_output in + args_ty_quantifiers_rev, args, args_instance, output, output_instance + end + in + let argsconstr,outputconstr,lem = + gen_compat_lemma_statement args_ty_quantifiers_rev output_instance + args_instance (apply_to_rels m args_ty_quantifiers_rev) in + (* "unfold make_compatibility_goal" *) + let lem = + Reductionops.clos_norm_flags + (Closure.unfold_red (Lazy.force coq_make_compatibility_goal_eval_ref)) + env Evd.empty lem in + (* "unfold make_compatibility_goal_aux" *) + let lem = + Reductionops.clos_norm_flags + (Closure.unfold_red(Lazy.force coq_make_compatibility_goal_aux_eval_ref)) + env Evd.empty lem in + (* "simpl" *) + let lem = Tacred.nf env Evd.empty lem in + if Lib.is_modtype () then + begin + ignore + (Declare.declare_internal_constant id + (ParameterEntry lem, IsAssumption Logical)) ; + let mor_name = morphism_theory_id_of_morphism_proof_id id in + let lemma_infos = Some (id,argsconstr,outputconstr) in + add_morphism lemma_infos mor_name + (m,args_ty_quantifiers_rev,args,output) + end + else + begin + new_edited id + (m,args_ty_quantifiers_rev,args,argsconstr,output,outputconstr); + Pfedit.start_proof id (Global, Proof Lemma) + (Declare.clear_proofs (Global.named_context ())) + lem hook; + Options.if_verbose msg (Printer.pr_open_subgoals ()); + end + +let morphism_hook _ ref = let pf_id = id_of_global ref in + let mor_id = morphism_theory_id_of_morphism_proof_id pf_id in + let (m,quantifiers_rev,args,argsconstr,output,outputconstr) = + what_edited pf_id in if (is_edited pf_id) then - (add_morphism pf_id (what_edited pf_id); no_more_edited pf_id) + begin + add_morphism (Some (pf_id,argsconstr,outputconstr)) mor_id + (m,quantifiers_rev,args,output) ; + no_more_edited pf_id + end + +type morphism_signature = + (bool option * Topconstr.constr_expr) list * Topconstr.constr_expr + +let new_named_morphism id m sign = + let sign = + match sign with + None -> None + | Some (args,out) -> + Some + (List.map (fun (variance,ty) -> variance, constr_of ty) args, + constr_of out) + in + new_morphism (constr_of m) sign id morphism_hook + +(************************** Adding a relation to the database *********************) + +let check_a env a = + let typ = Typing.type_of env Evd.empty a in + let a_quantifiers_rev,_ = Reduction.dest_arity env typ in + a_quantifiers_rev + +let check_eq env a_quantifiers_rev a aeq = + let typ = + Sign.it_mkProd_or_LetIn + (mkApp ((Lazy.force coq_relation),[| apply_to_rels a a_quantifiers_rev |])) + a_quantifiers_rev in + if + not + (is_conv env Evd.empty (Typing.type_of env Evd.empty aeq) typ) + then + errorlabstrm "Add Relation Class" + (pr_lconstr aeq ++ str " should have type (" ++ pr_lconstr typ ++ str ")") + +let check_property env a_quantifiers_rev a aeq strprop coq_prop t = + if + not + (is_conv env Evd.empty (Typing.type_of env Evd.empty t) + (Sign.it_mkProd_or_LetIn + (mkApp ((Lazy.force coq_prop), + [| apply_to_rels a a_quantifiers_rev ; + apply_to_rels aeq a_quantifiers_rev |])) a_quantifiers_rev)) + then + errorlabstrm "Add Relation Class" + (str "Not a valid proof of " ++ str strprop ++ str ".") + +let check_refl env a_quantifiers_rev a aeq refl = + check_property env a_quantifiers_rev a aeq "reflexivity" coq_reflexive refl + +let check_sym env a_quantifiers_rev a aeq sym = + check_property env a_quantifiers_rev a aeq "symmetry" coq_symmetric sym + +let check_trans env a_quantifiers_rev a aeq trans = + check_property env a_quantifiers_rev a aeq "transitivity" coq_transitive trans + +let check_setoid_theory env a_quantifiers_rev a aeq th = + if + not + (is_conv env Evd.empty (Typing.type_of env Evd.empty th) + (Sign.it_mkProd_or_LetIn + (mkApp ((Lazy.force coq_Setoid_Theory), + [| apply_to_rels a a_quantifiers_rev ; + apply_to_rels aeq a_quantifiers_rev |])) a_quantifiers_rev)) + then + errorlabstrm "Add Relation Class" + (str "Not a valid proof of symmetry") + +let int_add_relation id a aeq refl sym trans = + let env = Global.env () in + let a_quantifiers_rev = check_a env a in + check_eq env a_quantifiers_rev a aeq ; + option_iter (check_refl env a_quantifiers_rev a aeq) refl ; + option_iter (check_sym env a_quantifiers_rev a aeq) sym ; + option_iter (check_trans env a_quantifiers_rev a aeq) trans ; + let quantifiers_no = List.length a_quantifiers_rev in + let aeq_rel = + { rel_a = a; + rel_aeq = aeq; + rel_refl = refl; + rel_sym = sym; + rel_trans = trans; + rel_quantifiers_no = quantifiers_no; + rel_X_relation_class = mkProp; (* dummy value, overwritten below *) + rel_Xreflexive_relation_class = mkProp (* dummy value, overwritten below *) + } in + let x_relation_class = + let subst = + let len = List.length a_quantifiers_rev in + Array.of_list + (Util.list_map_i (fun i _ -> mkRel (len - i + 2)) 0 a_quantifiers_rev) in + cic_relation_class_of_X_relation + (mkRel 2) (mkRel 1) (apply_to_relation subst aeq_rel) in + let _ = + Declare.declare_internal_constant id + (DefinitionEntry + {const_entry_body = + Sign.it_mkLambda_or_LetIn x_relation_class + ([ Name (id_of_string "v"),None,mkRel 1; + Name (id_of_string "X"),None,mkType (Termops.new_univ ())] @ + a_quantifiers_rev); + const_entry_type = None; + const_entry_opaque = false; + const_entry_boxed = Options.boxed_definitions()}, + IsDefinition Definition) in + let id_precise = id_of_string (string_of_id id ^ "_precise_relation_class") in + let xreflexive_relation_class = + let subst = + let len = List.length a_quantifiers_rev in + Array.of_list + (Util.list_map_i (fun i _ -> mkRel (len - i)) 0 a_quantifiers_rev) + in + cic_precise_relation_class_of_relation (apply_to_relation subst aeq_rel) in + let _ = + Declare.declare_internal_constant id_precise + (DefinitionEntry + {const_entry_body = + Sign.it_mkLambda_or_LetIn xreflexive_relation_class a_quantifiers_rev; + const_entry_type = None; + const_entry_opaque = false; + const_entry_boxed = Options.boxed_definitions() }, + IsDefinition Definition) in + let aeq_rel = + { aeq_rel with + rel_X_relation_class = current_constant id; + rel_Xreflexive_relation_class = current_constant id_precise } in + Lib.add_anonymous_leaf (relation_to_obj (aeq, aeq_rel)) ; + Options.if_verbose ppnl (pr_lconstr aeq ++ str " is registered as a relation"); + match trans with + None -> () + | Some trans -> + let mor_name = id_of_string (string_of_id id ^ "_morphism") in + let a_instance = apply_to_rels a a_quantifiers_rev in + let aeq_instance = apply_to_rels aeq a_quantifiers_rev in + let sym_instance = + option_app (fun x -> apply_to_rels x a_quantifiers_rev) sym in + let refl_instance = + option_app (fun x -> apply_to_rels x a_quantifiers_rev) refl in + let trans_instance = apply_to_rels trans a_quantifiers_rev in + let aeq_rel_class_and_var1, aeq_rel_class_and_var2, lemma, output = + match sym_instance, refl_instance with + None, None -> + (Some false, Relation aeq_rel), + (Some true, Relation aeq_rel), + mkApp + ((Lazy.force + coq_equality_morphism_of_asymmetric_areflexive_transitive_relation), + [| a_instance ; aeq_instance ; trans_instance |]), + Lazy.force coq_impl_relation + | None, Some refl_instance -> + (Some false, Relation aeq_rel), + (Some true, Relation aeq_rel), + mkApp + ((Lazy.force + coq_equality_morphism_of_asymmetric_reflexive_transitive_relation), + [| a_instance ; aeq_instance ; refl_instance ; trans_instance |]), + Lazy.force coq_impl_relation + | Some sym_instance, None -> + (None, Relation aeq_rel), + (None, Relation aeq_rel), + mkApp + ((Lazy.force + coq_equality_morphism_of_symmetric_areflexive_transitive_relation), + [| a_instance ; aeq_instance ; sym_instance ; trans_instance |]), + Lazy.force coq_iff_relation + | Some sym_instance, Some refl_instance -> + (None, Relation aeq_rel), + (None, Relation aeq_rel), + mkApp + ((Lazy.force + coq_equality_morphism_of_symmetric_reflexive_transitive_relation), + [| a_instance ; aeq_instance ; refl_instance ; sym_instance ; + trans_instance |]), + Lazy.force coq_iff_relation in + let _ = + Declare.declare_internal_constant mor_name + (DefinitionEntry + {const_entry_body = Sign.it_mkLambda_or_LetIn lemma a_quantifiers_rev; + const_entry_type = None; + const_entry_opaque = false; + const_entry_boxed = Options.boxed_definitions()}, + IsDefinition Definition) + in + let a_quantifiers_rev = + List.map (fun (n,b,t) -> assert (b = None); n,t) a_quantifiers_rev in + add_morphism None mor_name + (aeq,a_quantifiers_rev,[aeq_rel_class_and_var1; aeq_rel_class_and_var2], + output) + +(* The vernac command "Add Relation ..." *) +let add_relation id a aeq refl sym trans = + int_add_relation id (constr_of a) (constr_of aeq) (option_app constr_of refl) + (option_app constr_of sym) (option_app constr_of trans) + +(************************ Add Setoid ******************************************) + +(* The vernac command "Add Setoid" *) +let add_setoid id 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 + let a_quantifiers_rev = check_a env a in + check_eq env a_quantifiers_rev a aeq ; + check_setoid_theory env a_quantifiers_rev a aeq th ; + let a_instance = apply_to_rels a a_quantifiers_rev in + let aeq_instance = apply_to_rels aeq a_quantifiers_rev in + let th_instance = apply_to_rels th a_quantifiers_rev in + let refl = + Sign.it_mkLambda_or_LetIn + (mkApp ((Lazy.force coq_seq_refl), + [| a_instance; aeq_instance; th_instance |])) a_quantifiers_rev in + let sym = + Sign.it_mkLambda_or_LetIn + (mkApp ((Lazy.force coq_seq_sym), + [| a_instance; aeq_instance; th_instance |])) a_quantifiers_rev in + let trans = + Sign.it_mkLambda_or_LetIn + (mkApp ((Lazy.force coq_seq_trans), + [| a_instance; aeq_instance; th_instance |])) a_quantifiers_rev in + int_add_relation id a aeq (Some refl) (Some sym) (Some trans) -let new_named_morphism id m = new_morphism (constr_of m) id morphism_hook (****************************** The tactic itself *******************************) +type direction = + Left2Right + | Right2Left + +let prdirection = + function + Left2Right -> str "->" + | Right2Left -> str "<-" + type constr_with_marks = - | MApp of constr_with_marks array - | Toreplace - | Tokeep - | Mimp of constr_with_marks * constr_with_marks + | MApp of constr * morphism_class * constr_with_marks array * direction + | ToReplace + | ToKeep of constr * relation relation_class * direction let is_to_replace = function - | Tokeep -> false - | Toreplace -> true - | MApp _ -> true - | Mimp _ -> true + | ToKeep _ -> false + | ToReplace -> true + | MApp _ -> true let get_mark a = Array.fold_left (||) false (Array.map is_to_replace a) -let rec mark_occur t in_c = - if (eq_constr t in_c) then Toreplace else +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 + 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)))) + +exception Optimize (* used to fall-back on the tactic for Leibniz equality *) + +let relation_class_that_matches_a_constr caller_name new_goals hypt = + let (heq, hargs) = decompose_app hypt in + let rec get_all_but_last_two = + function + [] + | [_] -> + errorlabstrm caller_name (pr_lconstr hypt ++ + str " is not a registered relation.") + | [_;_] -> [] + | he::tl -> he::(get_all_but_last_two tl) in + let all_aeq_args = get_all_but_last_two hargs in + let rec find_relation l subst = + let aeq = mkApp (heq,(Array.of_list l)) in + try + let rel = find_relation_class aeq in + match rel,new_goals with + Leibniz _,[] -> + assert (subst = []); + raise Optimize (* let's optimize the proof term size *) + | Leibniz (Some _), _ -> + assert (subst = []); + rel + | Leibniz None, _ -> + (* for well-typedness reasons it should have been catched by the + previous guard in the previous iteration. *) + assert false + | Relation rel,_ -> Relation (apply_to_relation (Array.of_list subst) rel) + with Not_found -> + if l = [] then + errorlabstrm caller_name + (pr_lconstr (mkApp (aeq, Array.of_list all_aeq_args)) ++ + str " is not a registered relation.") + else + let last,others = Util.list_sep_last l in + find_relation others (last::subst) + in + find_relation all_aeq_args [] + +(* rel1 is a subrelation of rel2 whenever + forall x1 x2, rel1 x1 x2 -> rel2 x1 x2 + The Coq part of the tactic, however, needs rel1 == rel2. + Hence the third case commented out. + Note: accepting user-defined subtrelations seems to be the last + useful generalization that does not go against the original spirit of + the tactic. +*) +let subrelation gl rel1 rel2 = + match rel1,rel2 with + Relation {rel_aeq=rel_aeq1}, Relation {rel_aeq=rel_aeq2} -> + Tacmach.pf_conv_x gl rel_aeq1 rel_aeq2 + | Leibniz (Some t1), Leibniz (Some t2) -> + Tacmach.pf_conv_x gl t1 t2 + | Leibniz None, _ + | _, Leibniz None -> assert false +(* This is the commented out case (see comment above) + | Leibniz (Some t1), Relation {rel_a=t2; rel_refl = Some _} -> + Tacmach.pf_conv_x gl t1 t2 +*) + | _,_ -> false + +(* this function returns the list of new goals opened by a constr_with_marks *) +let rec collect_new_goals = + function + MApp (_,_,a,_) -> List.concat (List.map collect_new_goals (Array.to_list a)) + | ToReplace + | ToKeep (_,Leibniz _,_) + | ToKeep (_,Relation {rel_refl=Some _},_) -> [] + | ToKeep (c,Relation {rel_aeq=aeq; rel_refl=None},_) -> [mkApp(aeq,[|c ; c|])] + +(* two marked_constr are equivalent if they produce the same set of new goals *) +let marked_constr_equiv_or_more_complex to_marked_constr gl c1 c2 = + let glc1 = collect_new_goals (to_marked_constr c1) in + let glc2 = collect_new_goals (to_marked_constr c2) in + List.for_all (fun c -> List.exists (fun c' -> pf_conv_x gl c c') glc1) glc2 + +let pr_new_goals i c = + let glc = collect_new_goals c in + str " " ++ int i ++ str ") side conditions:" ++ + (if glc = [] then str " no side conditions" + else + (pr_fnl () ++ str " " ++ + prlist_with_sep (fun () -> str "\n ") + (fun c -> str " ... |- " ++ pr_lconstr c) glc)) + +(* given a list of constr_with_marks, it returns the list where + constr_with_marks than open more goals than simpler ones in the list + are got rid of *) +let elim_duplicates gl to_marked_constr = + let rec aux = + function + [] -> [] + | he:: tl -> + if List.exists + (marked_constr_equiv_or_more_complex to_marked_constr gl he) tl + then aux tl + else he::aux tl + in + aux + +let filter_superset_of_new_goals gl new_goals l = + List.filter + (fun (_,_,c) -> + List.for_all + (fun g -> List.exists (pf_conv_x gl g) (collect_new_goals c)) new_goals) l + +(* given the array of lists [| l1 ; ... ; ln |] it returns the list of arrays + [ c1 ; ... ; cn ] that is the cartesian product of the sets l1, ..., ln *) +let cartesian_product gl a = + let rec aux = + function + [] -> assert false + | [he] -> List.map (fun e -> [e]) he + | he::tl -> + let tl' = aux tl in + List.flatten + (List.map (function e -> List.map (function l -> e :: l) tl') he) + in + List.map Array.of_list + (aux (List.map (elim_duplicates gl identity) (Array.to_list a))) + +let mark_occur gl ~new_goals t in_c input_relation input_direction = + let rec aux output_relation output_direction in_c = + if eq_constr t in_c then + if input_direction = output_direction + && subrelation gl input_relation output_relation then + [ToReplace] + else [] + else match kind_of_term in_c with | App (c,al) -> - let a = Array.map (mark_occur t) al - in if (get_mark a) then (MApp a) else Tokeep + let mors_and_cs_and_als = + let mors_and_cs_and_als = + let morphism_table_find c = + try morphism_table_find c with Not_found -> [] in + let rec aux acc = + function + [] -> + let c' = mkApp (c, Array.of_list acc) in + let al' = [||] in + List.map (fun m -> m,c',al') (morphism_table_find c') + | (he::tl) as l -> + let c' = mkApp (c, Array.of_list acc) in + let al' = Array.of_list l in + let acc' = acc @ [he] in + (List.map (fun m -> m,c',al') (morphism_table_find c')) @ + (aux acc' tl) + in + aux [] (Array.to_list al) in + let mors_and_cs_and_als = + List.map + (function (m,c,al) -> + relation_morphism_of_constr_morphism m, c, al) + mors_and_cs_and_als in + let mors_and_cs_and_als = + List.fold_left + (fun l (m,c,al) -> + match unify_morphism_with_arguments gl (c,al) m t with + Some res -> res::l + | None -> l + ) [] mors_and_cs_and_als + in + List.filter + (fun (mor,_,_) -> subrelation gl mor.output output_relation) + mors_and_cs_and_als + in + (* First we look for well typed morphisms *) + let res_mors = + List.fold_left + (fun res (mor,c,al) -> + let a = + let arguments = Array.of_list mor.args in + let apply_variance_to_direction default_dir = + function + None -> default_dir + | Some true -> output_direction + | Some false -> opposite_direction output_direction + in + Util.array_map2 + (fun a (variance,relation) -> + (aux relation + (apply_variance_to_direction Left2Right variance) a) @ + (aux relation + (apply_variance_to_direction Right2Left variance) a) + ) al arguments + in + let a' = cartesian_product gl a in + (List.map + (function a -> + if not (get_mark a) then + ToKeep (in_c,output_relation,output_direction) + else + MApp (c,ACMorphism mor,a,output_direction)) a') @ res + ) [] mors_and_cs_and_als in + (* Then we look for well typed functions *) + let res_functions = + (* 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 typeofc = Tacmach.pf_type_of gl 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 + [] -> + if a_rev = [] then + [ToKeep (in_c,output_relation,output_direction)] + else + let a' = + cartesian_product gl (Array.of_list (List.rev a_rev)) + in + List.fold_left + (fun res a -> + if not (get_mark a) then + (ToKeep (in_c,output_relation,output_direction))::res + else + let err = + match output_relation with + Leibniz (Some typ') when pf_conv_x gl typ typ' -> + false + | Leibniz None -> assert false + | _ when output_relation = Lazy.force coq_iff_relation + -> false + | _ -> true + in + if err then res + else + 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,a,output_direction))::res + ) [] a' + | (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 + let he = + (aux (Leibniz (Some s)) Left2Right he) @ + (aux (Leibniz (Some s)) Right2Left he) in + if he = [] then [] + else + let he0 = List.hd he in + begin + match noccurn 1 t, he0 with + _, ToKeep (arg,_,_) -> + (* invariant: if he0 = ToKeep (t,_,_) then every + element in he is = ToKeep (t,_,_) *) + assert + (List.for_all + (function + ToKeep(arg',_,_) when pf_conv_x gl arg arg' -> + true + | _ -> false) he) ; + (* 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 *) + (* 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 author.") + end + | _ -> assert false + in + find_non_dependent_function (Tacmach.pf_env gl) c [] typ [] [] + (Array.to_list al) + in + elim_duplicates gl identity (res_functions @ res_mors) | Prod (_, c1, c2) -> - if (dependent (mkRel 1) c2) - then Tokeep - else - let c1m = mark_occur t c1 in - let c2m = mark_occur t c2 in - if ((is_to_replace c1m)||(is_to_replace c2m)) - then (Mimp (c1m, c2m)) - else Tokeep - | _ -> Tokeep - -let create_args ca ma bl c1 c2 = - let rec aux i = function - | [] -> [] - | true::q -> - if (is_to_replace ma.(i)) - then (replace_term c1 c2 ca.(i))::ca.(i)::(aux (i+1) q) - else ca.(i)::ca.(i)::(aux (i+1) q) - | false::q -> ca.(i)::(aux (i+1) q) + if (dependent (mkRel 1) c2) + then + errorlabstrm "Setoid_replace" + (str "Cannot rewrite in the type of a variable bound " ++ + str "in a dependent product.") + else + let typeofc1 = Tacmach.pf_type_of gl c1 in + if not (Tacmach.pf_conv_x gl typeofc1 mkProp) then + (* to avoid this error we should introduce an impl relation + whose first argument is Type instead of Prop. However, + the type of the new impl would be Type -> Prop -> Prop + that is no longer a Relation_Definitions.relation. Thus + the Coq part of the tactic should be heavily modified. *) + errorlabstrm "Setoid_replace" + (str "Rewriting in a product A -> B is possible only when A " ++ + str "is a proposition (i.e. A is of type Prop). The type " ++ + pr_lconstr c1 ++ str " has type " ++ pr_lconstr typeofc1 ++ + str " that is not convertible to Prop.") + else + aux output_relation output_direction + (mkApp ((Lazy.force coq_impl), + [| c1 ; subst1 (mkRel 1 (*dummy*)) c2 |])) + | _ -> + if occur_term t in_c then + errorlabstrm "Setoid_replace" + (str "Trying to replace " ++ pr_lconstr t ++ str " in " ++ pr_lconstr in_c ++ + str " that is not an applicative context.") + else + [ToKeep (in_c,output_relation,output_direction)] + in + let aux2 output_relation output_direction = + List.map + (fun res -> output_relation,output_direction,res) + (aux output_relation output_direction in_c) in + let res = + (aux2 (Lazy.force coq_iff_relation) Right2Left) @ + (* This is the case of a proposition of signature A ++> iff or B --> iff *) + (aux2 (Lazy.force coq_iff_relation) Left2Right) @ + (aux2 (Lazy.force coq_impl_relation) Right2Left) in + let res = elim_duplicates gl (function (_,_,t) -> t) res in + let res' = filter_superset_of_new_goals gl new_goals res in + match res' with + [] when res = [] -> + errorlabstrm "Setoid_rewrite" + (str "Either the term " ++ pr_lconstr t ++ str " that must be " ++ + str "rewritten occurs in a covariant position or the goal is not " ++ + str "made of morphism applications only. You can replace only " ++ + str "occurrences that are in a contravariant position and such that " ++ + str "the context obtained by abstracting them is made of morphism " ++ + str "applications only.") + | [] -> + errorlabstrm "Setoid_rewrite" + (str "No generated set of side conditions is a superset of those " ++ + str "requested by the user. The generated sets of side conditions " ++ + str "are: " ++ + pr_fnl () ++ + prlist_with_sepi pr_fnl + (fun i (_,_,mc) -> pr_new_goals i mc) res) + | [he] -> he + | he::_ -> + ppnl + (str "Warning: The application of the tactic is subject to one of " ++ + str "the \nfollowing set of side conditions that the user needs " ++ + str "to prove:" ++ + pr_fnl () ++ + prlist_with_sepi pr_fnl + (fun i (_,_,mc) -> pr_new_goals i mc) res' ++ pr_fnl () ++ + str "The first set is randomly chosen. Use the syntax " ++ + str "\"setoid_rewrite ... generate side conditions ...\" to choose " ++ + str "a different set.") ; + he + +let cic_morphism_context_list_of_list hole_relation hole_direction out_direction += + let check = + function + (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,direction)] -> + mkApp ((Lazy.force coq_singl), [| Lazy.force coq_Argument_Class ; out |]), + mkApp ((Lazy.force coq_fcl_singl), + [| 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 ; hole_direction ; out ; outtl ; + direction ; out_direction ; + check (variance,direction,out_direction) ; + 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 hole_direction = + let rec aux out (rel_out,precise_out,is_reflexive) = + function + MApp (f, m, args, direction) -> + let direction = cic_direction_of_direction direction in + let morphism_theory, relations = + match m with + ACMorphism { args = args ; morphism_theory = morphism_theory } -> + morphism_theory,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_iff_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 -> None,Leibniz (Some x)) f_args in + let cic_relations = + List.map + (fun (variance,r) -> + variance, + r, + cic_relation_class_of_relation_class r, + cic_precise_relation_class_of_relation_class r + ) relations in + let cic_args_relations,argst = + 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, + direction_of_constr_with_marks hole_direction v) + ) cic_relations (Array.to_list args)) + in + mkApp ((Lazy.force coq_App), + [|hole_relation ; hole_direction ; + cic_args_relations ; out ; direction ; + morphism_theory ; argst|]) + | ToReplace -> + 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 ; hole_direction ; precise_out ; direction ; c |]) + else + let c_is_proper = + let typ = mkApp (rel_out, [| c ; c |]) in + mkCast (Evarutil.mk_new_meta (),DEFAULTcast, typ) + in + mkApp ((Lazy.force coq_ProperElementToKeep), + [| hole_relation ; hole_direction; precise_out ; + direction; c ; c_is_proper |]) + in aux + +let apply_coq_setoid_rewrite hole_relation prop_relation c1 c2 (direction,h) + prop_direction m += + let hole_relation = cic_relation_class_of_relation_class hole_relation in + let hyp,hole_direction = h,cic_direction_of_direction direction 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 ; 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 check_evar_map_of_evars_defs evd = + let metas = Evd.meta_list evd in + let check_freemetas_is_empty rebus = + Evd.Metaset.iter + (fun m -> + if Evd.meta_defined evd m then () else + raise (Logic.RefinerError (Logic.OccurMetaGoal rebus))) + in + List.iter + (fun (_,binding) -> + match binding with + Evd.Cltyp (_,{Evd.rebus=rebus; Evd.freemetas=freemetas}) -> + check_freemetas_is_empty rebus freemetas + | Evd.Clval (_,{Evd.rebus=rebus1; Evd.freemetas=freemetas1}, + {Evd.rebus=rebus2; Evd.freemetas=freemetas2}) -> + check_freemetas_is_empty rebus1 freemetas1 ; + check_freemetas_is_empty rebus2 freemetas2 + ) metas + +(* For a correct meta-aware "rewrite in", we split unification + apart from the actual rewriting (Pierre L, 05/04/06) *) + +(* [unification_rewrite] searchs a match for [c1] in [but] and then + returns the modified objects (in particular [c1] and [c2]) *) + +let unification_rewrite c1 c2 cl but gl = + let (env',c1) = + try + (* ~mod_delta:false to allow to mark occurences that must not be + rewritten simply by replacing them with let-defined definitions + in the context *) + w_unify_to_subterm ~mod_delta:false (pf_env gl) (c1,but) cl.env + with + Pretype_errors.PretypeError _ -> + (* ~mod_delta:true to make Ring work (since it really + exploits conversion) *) + w_unify_to_subterm ~mod_delta:true (pf_env gl) (c1,but) cl.env in - aux 0 bl - - -let res_tac c a hyp = - let sa = setoid_table_find a in - let fin = match hyp with - | None -> Auto.full_trivial - | Some h -> - tclORELSE (tclTHEN (tclTRY (apply h)) (tclFAIL 0 "")) - (tclORELSE (tclTHEN (tclTRY (tclTHEN (apply (mkApp ((Lazy.force coq_seq_sym), [|sa.set_a; sa.set_aeq; sa.set_th|]))) (apply h))) (tclFAIL 0 "")) - Auto.full_trivial) in - tclORELSE (tclTHEN (tclTRY (apply (mkApp ((Lazy.force coq_seq_refl), [|sa.set_a; sa.set_aeq; sa.set_th;c|])))) (tclFAIL 0 "")) - (tclORELSE assumption - (tclORELSE (tclTHEN (tclTRY (apply (mkApp ((Lazy.force coq_seq_sym), [|sa.set_a; sa.set_aeq; sa.set_th|])))) assumption) - fin)) - -let id_res_tac c a = - let sa = setoid_table_find a in - (tclTRY (apply (mkApp ((Lazy.force coq_seq_refl), [|sa.set_a; sa.set_aeq; sa.set_th; c|])))) - -(* An exception to catchs errors *) - -exception Nothing_found of constr;; - -let rec create_tac_list i a al c1 c2 hyp args_t = function - | [] -> [] - | false::q -> create_tac_list (i+1) a al c1 c2 hyp args_t q - | true::q -> - if (is_to_replace a.(i)) - then (zapply false al.(i) a.(i) c1 c2 hyp)::(create_tac_list (i+1) a al c1 c2 hyp args_t q) - else (id_res_tac al.(i) (List.nth args_t i))::(create_tac_list (i+1) a al c1 c2 hyp args_t q) -(* else tclIDTAC::(create_tac_list (i+1) a al c1 c2 hyp q) *) - -and zapply is_r gl gl_m c1 c2 hyp glll = (match ((kind_of_term gl), gl_m) with - | ((App (c,al)),(MApp a)) -> ( - try - let m = morphism_table_find c in - let args = Array.of_list (create_args al a m.profil c1 c2) in - if is_r - then tclTHENS (apply (mkApp (m.lem, args))) - ((create_tac_list 0 a al c1 c2 hyp m.arg_types m.profil)@[tclIDTAC]) - else (match m.lem2 with - | None -> - tclTHENS (apply (mkApp (m.lem, args))) (create_tac_list 0 a al c1 c2 hyp m.arg_types m.profil) - | Some xom -> - tclTHENS (apply (mkApp (xom, args))) (create_tac_list 0 a al c1 c2 hyp m.arg_types m.profil)) - with Not_found -> errorlabstrm "Setoid_replace" - (str "The term " ++ prterm c ++ str " has not been declared as a morphism")) - | ((Prod (_,hh, cc)),(Mimp (hhm, ccm))) -> - let al = [|hh; cc|] in - let a = [|hhm; ccm|] in - let fleche_constr = (Lazy.force coq_fleche) in - let fleche_cp = destConst fleche_constr in - let new_concl = (mkApp (fleche_constr, al)) in - if is_r - then - let m = morphism_table_find fleche_constr in - let args = Array.of_list (create_args al a m.profil c1 c2) in - tclTHEN (change_in_concl None new_concl) - (tclTHENS (apply (mkApp (m.lem, args))) - ((create_tac_list 0 a al c1 c2 hyp m.arg_types m.profil)@[unfold_constr (ConstRef fleche_cp)])) -(* ((create_tac_list 0 a al c1 c2 hyp m.arg_types m.profil)@[tclIDTAC])) *) - else (zapply is_r new_concl (MApp a) c1 c2 hyp) -(* let args = Array.of_list (create_args [|hh; cc|] [|hhm; ccm|] [true;true] c1 c2) in - if is_r - then tclTHENS (apply (mkApp ((Lazy.force coq_fleche_ext), args))) - ((create_tac_list 0 [|hhm; ccm|] [|hh; cc|] c1 c2 hyp [mkProp; mkProp] [true;true])@[tclIDTAC]) - else tclTHENS (apply (mkApp ((Lazy.force coq_fleche_ext2), args))) - ((create_tac_list 0 [|hhm; ccm|] [|hh; cc|] c1 c2 hyp [mkProp; mkProp] [true;true])@[tclIDTAC]) -*) - | (_, Toreplace) -> - if is_r - then (match hyp with - | None -> errorlabstrm "Setoid_replace" - (str "You should use the tactic Replace here") - | Some h -> - let hypt = pf_type_of glll h in - let (heq, hargs) = decompose_app hypt in - let rec get_last_two = function - | [c1;c2] -> (c1, c2) - | x::y::z -> get_last_two (y::z) - | _ -> assert false in - let (hc1,hc2) = get_last_two hargs in - if c1 = hc1 - then - apply (mkApp (Lazy.force coqproj2,[|(mkArrow hc1 hc2);(mkArrow hc2 hc1);h|])) - else - apply (mkApp (Lazy.force coqproj1,[|(mkArrow hc1 hc2);(mkArrow hc2 hc1);h|])) - ) - else (res_tac gl (pf_type_of glll gl) hyp) (* tclORELSE Auto.full_trivial tclIDTAC *) - | (_, Tokeep) -> (match hyp with - | None -> errorlabstrm "Setoid_replace" - (str "No replacable occurence of " ++ prterm c1 ++ str " found") - | Some _ ->errorlabstrm "Setoid_replace" - (str "No rewritable occurence of " ++ prterm c1 ++ str " found")) - | _ -> anomaly ("Bug in Setoid_replace")) glll - -let setoid_replace c1 c2 hyp gl = - let but = (pf_concl gl) in - (zapply true but (mark_occur c1 but) c1 c2 hyp) gl - -let general_s_rewrite lft2rgt c gl = - let ctype = pf_type_of gl c in - let (equiv, args) = decompose_app ctype 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 setoid_replace c1 c2 (Some c) gl - else setoid_replace c2 c1 (Some c) gl - -let setoid_rewriteLR = general_s_rewrite true - -let setoid_rewriteRL = general_s_rewrite false + let cl' = {cl with env = env' } in + let c2 = Clenv.clenv_nf_meta cl' c2 in + check_evar_map_of_evars_defs env' ; + env',Clenv.clenv_value cl', c1, c2 + +(* no unification is performed in this function. [sigma] is the + substitution obtained from an earlier unification. *) + +let relation_rewrite_no_unif c1 c2 hyp ~new_goals sigma gl = + let but = pf_concl gl in + try + let input_relation = + relation_class_that_matches_a_constr "Setoid_rewrite" + new_goals (Typing.mtype_of (pf_env gl) sigma (snd hyp)) in + let output_relation,output_direction,marked_but = + mark_occur gl ~new_goals c1 but input_relation (fst hyp) in + let cic_output_direction = cic_direction_of_direction output_direction in + let if_output_relation_is_iff gl = + let th = + apply_coq_setoid_rewrite input_relation output_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(), DEFAULTcast, new_but)|])) + gl in + let if_output_relation_is_if gl = + let th = + apply_coq_setoid_rewrite input_relation output_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(), DEFAULTcast, new_but)|])) + gl in + if output_relation = (Lazy.force coq_iff_relation) then + if_output_relation_is_iff gl + else + if_output_relation_is_if gl + with + Optimize -> + !general_rewrite (fst hyp = Left2Right) (snd hyp) gl + +let relation_rewrite c1 c2 (input_direction,cl) ~new_goals gl = + let (sigma,cl,c1,c2) = unification_rewrite c1 c2 cl (pf_concl gl) gl in + relation_rewrite_no_unif c1 c2 (input_direction,cl) ~new_goals sigma gl + +let analyse_hypothesis gl c = + 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 + let rec split_last_two = function + | [c1;c2] -> [],(c1, c2) + | x::y::z -> + let l,res = split_last_two (y::z) in x::l, res + | _ -> error "The term provided is not an equivalence" in + let others,(c1,c2) = split_last_two args in + eqclause,mkApp (equiv, Array.of_list others),c1,c2 + +let general_s_rewrite lft2rgt c ~new_goals gl = + let eqclause,_,c1,c2 = analyse_hypothesis gl c in + if lft2rgt then + relation_rewrite c1 c2 (Left2Right,eqclause) ~new_goals gl + else + relation_rewrite c2 c1 (Right2Left,eqclause) ~new_goals gl + +let relation_rewrite_in id c1 c2 (direction,eqclause) ~new_goals gl = + let hyp = pf_type_of gl (mkVar id) in + (* first, we find a match for c1 in the hyp *) + let (sigma,cl,c1,c2) = unification_rewrite c1 c2 eqclause hyp gl in + (* since we will actually rewrite in the opposite direction, we also need + to replace every occurrence of c2 (resp. c1) in hyp with something that + is convertible but not syntactically equal. To this aim we introduce a + let-in and then we will use the intro tactic to get rid of it *) + let let_in_abstract t in_t = + let t' = lift 1 t in + let in_t' = lift 1 in_t in + mkLetIn (Anonymous,t,pf_type_of gl t,subst_term t' in_t') in + let mangled_new_hyp = Termops.replace_term c1 c2 (let_in_abstract c2 hyp) in + let new_hyp = Termops.replace_term c1 c2 hyp in + let oppdir = opposite_direction direction in + cut_replacing id new_hyp + (tclTHENLAST + (tclTHEN (change_in_concl None mangled_new_hyp) + (tclTHEN intro + (relation_rewrite_no_unif c2 c1 (oppdir,cl) ~new_goals sigma)))) + gl + +let general_s_rewrite_in id lft2rgt c ~new_goals gl = + let eqclause,_,c1,c2 = analyse_hypothesis gl c in + if lft2rgt then + relation_rewrite_in id c1 c2 (Left2Right,eqclause) ~new_goals gl + else + relation_rewrite_in id c2 c1 (Right2Left,eqclause) ~new_goals gl + +let setoid_replace relation c1 c2 ~new_goals gl = + try + let relation = + match relation with + Some rel -> + (try + match find_relation_class rel with + Relation sa -> sa + | Leibniz _ -> raise Optimize + with + Not_found -> + errorlabstrm "Setoid_rewrite" + (pr_lconstr rel ++ str " is not a registered relation.")) + | None -> + match default_relation_for_carrier (pf_type_of gl c1) with + Relation sa -> sa + | Leibniz _ -> raise Optimize + in + let eq_left_to_right = mkApp (relation.rel_aeq, [| c1 ; c2 |]) in + let eq_right_to_left = mkApp (relation.rel_aeq, [| c2 ; c1 |]) in + let replace dir eq = + tclTHENS (assert_tac false Anonymous eq) + [onLastHyp (fun id -> + tclTHEN + (general_s_rewrite dir (mkVar id) ~new_goals) + (clear [id])); + Tacticals.tclIDTAC] + in + tclORELSE + (replace true eq_left_to_right) (replace false eq_right_to_left) gl + with + Optimize -> (!replace c1 c2) gl + +let setoid_replace_in id relation c1 c2 ~new_goals gl = + let hyp = pf_type_of gl (mkVar id) in + let new_hyp = Termops.replace_term c1 c2 hyp in + cut_replacing id new_hyp + (fun exact -> tclTHENLASTn + (setoid_replace relation c2 c1 ~new_goals) + [| exact; tclIDTAC |]) gl + +(* [setoid_]{reflexivity,symmetry,transitivity} tactics *) + +let setoid_reflexivity gl = + try + let relation_class = + relation_class_that_matches_a_constr "Setoid_reflexivity" + [] (pf_concl gl) in + match relation_class with + Leibniz _ -> assert false (* since [] is empty *) + | Relation rel -> + match rel.rel_refl with + None -> + errorlabstrm "Setoid_reflexivity" + (str "The relation " ++ prrelation rel ++ str " is not reflexive.") + | Some refl -> apply refl gl + with + Optimize -> reflexivity gl + +let setoid_symmetry gl = + try + let relation_class = + relation_class_that_matches_a_constr "Setoid_symmetry" + [] (pf_concl gl) in + match relation_class with + Leibniz _ -> assert false (* since [] is empty *) + | Relation rel -> + match rel.rel_sym with + None -> + errorlabstrm "Setoid_symmetry" + (str "The relation " ++ prrelation rel ++ str " is not symmetric.") + | Some sym -> apply sym gl + with + Optimize -> symmetry gl + +let setoid_symmetry_in id gl = + let new_hyp = + let _,he,c1,c2 = analyse_hypothesis gl (mkVar id) in + mkApp (he, [| c2 ; c1 |]) + in + cut_replacing id new_hyp (tclTHEN setoid_symmetry) gl + +let setoid_transitivity c gl = + try + let relation_class = + relation_class_that_matches_a_constr "Setoid_transitivity" + [] (pf_concl gl) in + match relation_class with + Leibniz _ -> assert false (* since [] is empty *) + | Relation rel -> + let ctyp = pf_type_of gl c in + let rel' = unify_relation_carrier_with_type (pf_env gl) rel ctyp in + match rel'.rel_trans with + None -> + errorlabstrm "Setoid_transitivity" + (str "The relation " ++ prrelation rel ++ str " is not transitive.") + | Some trans -> + let transty = nf_betaiota (pf_type_of gl trans) in + let argsrev, _ = + Reductionops.decomp_n_prod (pf_env gl) Evd.empty 2 transty in + let binder = + match List.rev argsrev with + _::(Name n2,None,_)::_ -> Rawterm.NamedHyp n2 + | _ -> assert false + in + apply_with_bindings + (trans, Rawterm.ExplicitBindings [ dummy_loc, binder, c ]) gl + with + Optimize -> transitivity c gl +;; + +Tactics.register_setoid_reflexivity setoid_reflexivity;; +Tactics.register_setoid_symmetry setoid_symmetry;; +Tactics.register_setoid_symmetry_in setoid_symmetry_in;; +Tactics.register_setoid_transitivity setoid_transitivity;; -- cgit v1.2.3