summaryrefslogtreecommitdiff
path: root/tactics/class_tactics.ml4
diff options
context:
space:
mode:
Diffstat (limited to 'tactics/class_tactics.ml4')
-rw-r--r--tactics/class_tactics.ml41692
1 files changed, 1692 insertions, 0 deletions
diff --git a/tactics/class_tactics.ml4 b/tactics/class_tactics.ml4
new file mode 100644
index 00000000..9a1a3042
--- /dev/null
+++ b/tactics/class_tactics.ml4
@@ -0,0 +1,1692 @@
+(* -*- compile-command: "make -C .. bin/coqtop.byte" -*- *)
+(************************************************************************)
+(* v * The Coq Proof Assistant / The Coq Development Team *)
+(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
+(* \VV/ **************************************************************)
+(* // * This file is distributed under the terms of the *)
+(* * GNU Lesser General Public License Version 2.1 *)
+(************************************************************************)
+
+(*i camlp4deps: "parsing/grammar.cma" i*)
+
+(* $Id: class_tactics.ml4 11150 2008-06-19 11:38:27Z msozeau $ *)
+
+open Pp
+open Util
+open Names
+open Nameops
+open Term
+open Termops
+open Sign
+open Reduction
+open Proof_type
+open Proof_trees
+open Declarations
+open Tacticals
+open Tacmach
+open Evar_refiner
+open Tactics
+open Pattern
+open Clenv
+open Auto
+open Rawterm
+open Hiddentac
+open Typeclasses
+open Typeclasses_errors
+open Classes
+open Topconstr
+open Pfedit
+open Command
+open Libnames
+open Evd
+
+let default_eauto_depth = 100
+let typeclasses_db = "typeclass_instances"
+
+let check_imported_library d =
+ let d' = List.map id_of_string d in
+ let dir = make_dirpath (List.rev d') in
+ if not (Library.library_is_loaded dir) then
+ error ("Library "^(list_last d)^" has to be imported first")
+
+let classes_dirpath =
+ make_dirpath (List.map id_of_string ["Classes";"Coq"])
+
+let init_setoid () =
+ if is_dirpath_prefix_of classes_dirpath (Lib.cwd ()) then ()
+ else check_imported_library ["Coq";"Setoids";"Setoid"]
+
+(** Typeclasses instance search tactic / eauto *)
+
+open Auto
+
+let e_give_exact c gl =
+ let t1 = (pf_type_of gl c) and t2 = pf_concl gl in
+ if occur_existential t1 or occur_existential t2 then
+ tclTHEN (Clenvtac.unify t1) (exact_check c) gl
+ else exact_check c gl
+
+let assumption id = e_give_exact (mkVar id)
+
+open Unification
+
+let auto_unif_flags = {
+ modulo_conv_on_closed_terms = Some full_transparent_state;
+ use_metas_eagerly = true;
+ modulo_delta = var_full_transparent_state;
+}
+
+let unify_e_resolve st (c,clenv) gls =
+ let clenv' = connect_clenv gls clenv in
+ let clenv' = clenv_unique_resolver false
+ ~flags:{auto_unif_flags with modulo_delta = st} clenv' gls
+ in
+ Clenvtac.clenv_refine true clenv' gls
+
+let unify_resolve st (c,clenv) gls =
+ let clenv' = connect_clenv gls clenv in
+ let clenv' = clenv_unique_resolver false
+ ~flags:{auto_unif_flags with modulo_delta = st} clenv' gls
+ in
+ Clenvtac.clenv_refine false clenv' gls
+
+let rec e_trivial_fail_db db_list local_db goal =
+ let tacl =
+ Eauto.registered_e_assumption ::
+ (tclTHEN Tactics.intro
+ (function g'->
+ let d = pf_last_hyp g' in
+ let hintl = make_resolve_hyp (pf_env g') (project g') d in
+ (e_trivial_fail_db db_list
+ (add_hint_list hintl local_db) g'))) ::
+ (List.map pi1 (e_trivial_resolve db_list local_db (pf_concl goal)) )
+ in
+ tclFIRST (List.map tclCOMPLETE tacl) goal
+
+and e_my_find_search db_list local_db hdc concl =
+ let hdc = head_of_constr_reference hdc in
+ let hintl =
+ if occur_existential concl then
+ list_map_append
+ (fun (st, db) -> List.map (fun x -> (st, x)) (Hint_db.map_all hdc db))
+ (local_db::db_list)
+ else
+ list_map_append
+ (fun (st, db) -> List.map (fun x -> (st, x)) (Hint_db.map_auto (hdc,concl) db))
+ (local_db::db_list)
+ in
+ let tac_of_hint =
+ fun (st, {pri=b; pat = p; code=t}) ->
+ let tac =
+ match t with
+ | Res_pf (term,cl) -> unify_resolve st (term,cl)
+ | ERes_pf (term,cl) -> unify_e_resolve st (term,cl)
+ | Give_exact (c) -> e_give_exact c
+ | Res_pf_THEN_trivial_fail (term,cl) ->
+ tclTHEN (unify_e_resolve st (term,cl))
+ (e_trivial_fail_db db_list local_db)
+ | Unfold_nth c -> unfold_in_concl [all_occurrences,c]
+ | Extern tacast -> conclPattern concl
+ (Option.get p) tacast
+ in
+ (tac,b,fmt_autotactic t)
+ in
+ List.map tac_of_hint hintl
+
+and e_trivial_resolve db_list local_db gl =
+ try
+ e_my_find_search db_list local_db
+ (List.hd (head_constr_bound gl [])) gl
+ with Bound | Not_found -> []
+
+let e_possible_resolve db_list local_db gl =
+ try
+ e_my_find_search db_list local_db
+ (List.hd (head_constr_bound gl [])) gl
+ with Bound | Not_found -> []
+
+let find_first_goal gls =
+ try first_goal gls with UserError _ -> assert false
+
+
+type search_state = {
+ depth : int; (*r depth of search before failing *)
+ tacres : goal list sigma * validation;
+ pri : int;
+ last_tactic : std_ppcmds;
+ dblist : Auto.hint_db list;
+ localdb : Auto.hint_db list }
+
+let filter_hyp t =
+ match kind_of_term t with
+ | Evar _ | Meta _ | Sort _ -> false
+ | _ -> true
+
+let rec catchable = function
+ | Refiner.FailError _ -> true
+ | Stdpp.Exc_located (_, e) -> catchable e
+ | e -> Logic.catchable_exception e
+
+module SearchProblem = struct
+
+ type state = search_state
+
+ let debug = ref false
+
+ let success s = (sig_it (fst s.tacres)) = []
+
+ let pr_ev evs ev = Printer.pr_constr_env (Evd.evar_env ev) (Evarutil.nf_evar evs ev.Evd.evar_concl)
+
+ let pr_goals gls =
+ let evars = Evarutil.nf_evars (Refiner.project gls) in
+ prlist (pr_ev evars) (sig_it gls)
+
+ let filter_tactics (glls,v) l =
+(* if !debug then *)
+(* (let _ = Proof_trees.db_pr_goal (List.hd (sig_it glls)) in *)
+(* let evars = Evarutil.nf_evars (Refiner.project glls) in *)
+(* msg (str"Goal: " ++ pr_ev evars (List.hd (sig_it glls)) ++ str"\n")); *)
+ let rec aux = function
+ | [] -> []
+ | (tac,pri,pptac) :: tacl ->
+ try
+(* if !debug then msg (str"\nTrying tactic: " ++ pptac ++ str"\n"); *)
+ let (lgls,ptl) = apply_tac_list tac glls in
+ let v' p = v (ptl p) in
+(* if !debug then *)
+(* begin *)
+(* let evars = Evarutil.nf_evars (Refiner.project glls) in *)
+(* msg (str"\nOn goal: " ++ pr_ev evars (List.hd (sig_it glls)) ++ str"\n"); *)
+(* msg (hov 1 (pptac ++ str" gives: \n" ++ pr_goals lgls ++ str"\n")) *)
+(* end; *)
+ ((lgls,v'),pri,pptac) :: aux tacl
+ with e when catchable e -> aux tacl
+ in aux l
+
+ let nb_empty_evars s =
+ Evd.fold (fun ev evi acc -> if evi.evar_body = Evar_empty then succ acc else acc) s 0
+
+ (* Ordering of states is lexicographic on depth (greatest first) then
+ priority (lowest pri means higher priority), then number of remaining goals. *)
+ let compare s s' =
+ let d = s'.depth - s.depth in
+ let nbgoals s =
+ List.length (sig_it (fst s.tacres)) +
+ nb_empty_evars (sig_sig (fst s.tacres))
+ in
+ if d <> 0 && d <> 1 then d else
+ let pri = s.pri - s'.pri in
+ if pri <> 0 then pri
+ else nbgoals s - nbgoals s'
+
+ let branching s =
+ if s.depth = 0 then
+ []
+ else
+ let lg = fst s.tacres in
+ let nbgl = List.length (sig_it lg) in
+ assert (nbgl > 0);
+ let g = find_first_goal lg in
+ let assumption_tacs =
+ let l =
+ filter_tactics s.tacres
+ (List.map
+ (fun id -> (Eauto.e_give_exact_constr (mkVar id), 0,
+ (str "exact" ++ spc () ++ pr_id id)))
+ (List.filter (fun id -> filter_hyp (pf_get_hyp_typ g id))
+ (pf_ids_of_hyps g)))
+ in
+ List.map (fun (res,pri,pp) -> { s with tacres = res; pri = 0;
+ last_tactic = pp; localdb = List.tl s.localdb }) l
+ in
+(* let intro_tac = *)
+(* List.map *)
+(* (fun ((lgls,_) as res,pri,pp) -> *)
+(* let g' = first_goal lgls in *)
+(* let hintl = *)
+(* make_resolve_hyp (pf_env g') (project g') (pf_last_hyp g') *)
+(* in *)
+(* let ldb = Hint_db.add_list hintl (match s.localdb with [] -> assert false | hd :: _ -> hd) in *)
+(* { s with tacres = res; *)
+(* last_tactic = pp; *)
+(* pri = pri; *)
+(* localdb = ldb :: List.tl s.localdb }) *)
+(* (filter_tactics s.tacres [Tactics.intro,1,(str "intro")]) *)
+(* in *)
+ let possible_resolve ((lgls,_) as res, pri, pp) =
+ let nbgl' = List.length (sig_it lgls) in
+ if nbgl' < nbgl then
+ { s with tacres = res; last_tactic = pp; pri = pri;
+ localdb = List.tl s.localdb }
+ else
+ { s with
+ depth = pred s.depth; tacres = res;
+ last_tactic = pp; pri = pri;
+ localdb =
+ list_addn (nbgl'-nbgl) (List.hd s.localdb) s.localdb }
+ in
+ let rec_tacs =
+ let l =
+ filter_tactics s.tacres (e_possible_resolve s.dblist (List.hd s.localdb) (pf_concl g))
+ in
+ List.map possible_resolve l
+ in
+ List.sort compare (assumption_tacs (* @intro_tac @ custom_tac *) @ rec_tacs)
+
+ let pp s =
+ msg (hov 0 (str " depth=" ++ int s.depth ++ spc () ++
+ s.last_tactic ++ str "\n"))
+
+end
+
+module Search = Explore.Make(SearchProblem)
+
+
+let filter_pat c =
+ try
+ let morg = Nametab.global (Qualid (dummy_loc, qualid_of_string "Coq.Classes.Morphisms.Morphism")) in
+ let morc = constr_of_global morg in
+ match kind_of_term c with
+ | App(morph, [| t; r; m |]) when eq_constr morph morc ->
+ (fun y ->
+ (match y.pat with
+ Some (PApp (PRef mor, [| t'; r'; m' |])) when mor = morg ->
+ (match m' with
+ | PRef c -> if isConst m then eq_constr (constr_of_global c) m else false
+ | _ -> true)
+ | _ -> true))
+ | _ -> fun _ -> true
+ with _ -> fun _ -> true
+
+let morphism_class =
+ lazy (class_info (Nametab.global (Qualid (dummy_loc, qualid_of_string "Coq.Classes.Morphisms.Morphism"))))
+
+let morphism_proxy_class =
+ lazy (class_info (Nametab.global (Qualid (dummy_loc, qualid_of_string "Coq.Classes.Morphisms.MorphismProxy"))))
+
+let filter c =
+ try let morc = constr_of_global (Nametab.global (Qualid (dummy_loc, qualid_of_string "Coq.Classes.Morphisms.Morphism"))) in
+ match kind_of_term c with
+ | App(morph, [| t; r; m |]) when eq_constr morph morc ->
+ (fun y ->
+ let (_, r) = decompose_prod y in
+ (match kind_of_term r with
+ App (morph', [| t'; r'; m' |]) when eq_constr morph' morc ->
+ (match kind_of_term m' with
+ | Rel n -> true
+ | Const c -> eq_constr m m'
+ | App _ -> true
+ | _ -> false)
+ | _ -> false))
+ | _ -> fun _ -> true
+ with _ -> fun _ -> true
+
+let make_initial_state n gls dblist localdbs =
+ { depth = n;
+ tacres = gls;
+ pri = 0;
+ last_tactic = (mt ());
+ dblist = dblist;
+ localdb = localdbs }
+
+let e_depth_search debug s =
+ let tac = if debug then
+ (SearchProblem.debug := true; Search.debug_depth_first) else Search.depth_first in
+ let s = tac s in
+ s.tacres
+
+let e_breadth_search debug s =
+ try
+ let tac =
+ if debug then Search.debug_breadth_first else Search.breadth_first
+ in let s = tac s in s.tacres
+ with Not_found -> error "EAuto: breadth first search failed"
+
+let e_search_auto debug (in_depth,p) lems db_list gls =
+ let sigma = Evd.sig_sig (fst gls) and gls' = Evd.sig_it (fst gls) in
+ let local_dbs = List.map (fun gl -> make_local_hint_db true lems ({it = gl; sigma = sigma})) gls' in
+ let state = make_initial_state p gls db_list local_dbs in
+ if in_depth then
+ e_depth_search debug state
+ else
+ e_breadth_search debug state
+
+let full_eauto debug n lems gls =
+ let dbnames = current_db_names () in
+ let dbnames = list_subtract dbnames ["v62"] in
+ let db_list = List.map searchtable_map dbnames in
+ e_search_auto debug n lems db_list gls
+
+let nf_goal (gl, valid) =
+ { gl with sigma = Evarutil.nf_evars gl.sigma }, valid
+
+let typeclasses_eauto debug n lems gls =
+ let dbnames = [typeclasses_db] in
+ let db_list = List.map
+ (fun x ->
+ try searchtable_map x
+ with Not_found -> (empty_transparent_state, Hint_db.empty))
+ dbnames
+ in
+ e_search_auto debug n lems db_list gls
+
+exception Found of evar_map
+
+let valid goals p res_sigma l =
+ let evm =
+ List.fold_left2
+ (fun sigma (ev, evi) prf ->
+ let cstr, obls = Refiner.extract_open_proof !res_sigma prf in
+ if not (Evd.is_defined sigma ev) then
+ Evd.define sigma ev cstr
+ else sigma)
+ !res_sigma goals l
+ in raise (Found evm)
+
+let resolve_all_evars_once debug (mode, depth) env p evd =
+ let evm = Evd.evars_of evd in
+ let goals, evm' =
+ Evd.fold
+ (fun ev evi (gls, evm) ->
+ if evi.evar_body = Evar_empty
+ && Typeclasses.is_resolvable evi
+ && p ev evi then ((ev,evi) :: gls, Evd.add evm ev (Typeclasses.mark_unresolvable evi)) else
+ (gls, Evd.add evm ev evi))
+ evm ([], Evd.empty)
+ in
+ let goals = List.rev goals in
+ let gls = { it = List.map snd goals; sigma = evm' } in
+ let res_sigma = ref evm' in
+ let gls', valid' = typeclasses_eauto debug (mode, depth) [] (gls, valid goals p res_sigma) in
+ res_sigma := Evarutil.nf_evars (sig_sig gls');
+ try ignore(valid' []); assert(false)
+ with Found evm' -> Evarutil.nf_evar_defs (Evd.evars_reset_evd evm' evd)
+
+exception FoundTerm of constr
+
+let resolve_one_typeclass env gl =
+ let gls = { it = [ Evd.make_evar (Environ.named_context_val env) gl ] ; sigma = Evd.empty } in
+ let valid x = raise (FoundTerm (fst (Refiner.extract_open_proof Evd.empty (List.hd x)))) in
+ let gls', valid' = typeclasses_eauto false (true, default_eauto_depth) [] (gls, valid) in
+ try ignore(valid' []); assert false with FoundTerm t ->
+ let term = Evarutil.nf_evar (sig_sig gls') t in
+ if occur_existential term then raise Not_found else term
+
+let has_undefined p oevd evd =
+ Evd.fold (fun ev evi has -> has ||
+ (evi.evar_body = Evar_empty && p ev evi &&
+ (try Typeclasses.is_resolvable (Evd.find oevd ev) with _ -> true)))
+ (Evd.evars_of evd) false
+
+let evars_of_term init c =
+ let rec evrec acc c =
+ match kind_of_term c with
+ | Evar (n, _) -> Intset.add n acc
+ | _ -> fold_constr evrec acc c
+ in
+ evrec init c
+
+let intersects s t =
+ Intset.exists (fun el -> Intset.mem el t) s
+
+let rec merge_deps deps = function
+ | [] -> [deps]
+ | hd :: tl ->
+ if intersects deps hd then
+ merge_deps (Intset.union deps hd) tl
+ else hd :: merge_deps deps tl
+
+let split_evars evm =
+ Evd.fold (fun ev evi acc ->
+ let deps = evars_of_term (Intset.singleton ev) evi.evar_concl in
+ merge_deps deps acc)
+ evm []
+
+let select_evars evs evm =
+ Evd.fold (fun ev evi acc ->
+ if Intset.mem ev evs then Evd.add acc ev evi else acc)
+ evm Evd.empty
+
+let resolve_all_evars debug m env p oevd do_split fail =
+ let oevm = Evd.evars_of oevd in
+ let split = if do_split then split_evars (Evd.evars_of (Evd.undefined_evars oevd)) else [Intset.empty] in
+ let p = if do_split then
+ fun comp ev evi -> (Intset.mem ev comp || not (Evd.mem oevm ev)) && p ev evi
+ else fun _ -> p
+ in
+ let rec aux n p evd =
+ if has_undefined p oevm evd then
+ if n > 0 then
+ let evd' = resolve_all_evars_once debug m env p evd in
+ aux (pred n) p evd'
+ else None
+ else Some evd
+ in
+ let rec docomp evd = function
+ | [] -> evd
+ | comp :: comps ->
+ let res = try aux 3 (p comp) evd with Not_found -> None in
+ match res with
+ | None ->
+ if fail then
+ (* Unable to satisfy the constraints. *)
+ let evm = Evd.evars_of evd in
+ let evm = if do_split then select_evars comp evm else evm in
+ let _, ev = Evd.fold
+ (fun ev evi (b,acc) ->
+ (* focus on one instance if only one was searched for *)
+ if class_of_constr evi.evar_concl <> None then
+ if not b then
+ true, Some ev
+ else b, None
+ else b, acc) evm (false, None)
+ in
+ Typeclasses_errors.unsatisfiable_constraints env (Evd.evars_reset_evd evm evd) ev
+ else (* Best effort: do nothing *) oevd
+ | Some evd' -> docomp evd' comps
+ in docomp oevd split
+
+(* let resolve_all_evars debug m env p oevd = *)
+(* let oevm = Evd.evars_of oevd in *)
+(* try *)
+(* let rec aux n evd = *)
+(* if has_undefined p oevm evd then *)
+(* if n > 0 then *)
+(* let evd' = resolve_all_evars_once debug m env p evd in *)
+(* aux (pred n) evd' *)
+(* else None *)
+(* else Some evd *)
+(* in aux 3 oevd *)
+(* with Not_found -> None *)
+
+VERNAC COMMAND EXTEND Typeclasses_Unfold_Settings
+| [ "Typeclasses" "unfold" reference_list(cl) ] -> [
+ add_hints false [typeclasses_db] (Vernacexpr.HintsUnfold cl)
+ ]
+END
+
+(** Typeclass-based rewriting. *)
+
+let respect_proj = lazy (mkConst (snd (List.hd (Lazy.force morphism_class).cl_projs)))
+
+let make_dir l = make_dirpath (List.map id_of_string (List.rev l))
+
+let try_find_reference dir s =
+ let sp = Libnames.make_path (make_dir ("Coq"::dir)) (id_of_string s) in
+ constr_of_global (Nametab.absolute_reference sp)
+
+let gen_constant dir s = Coqlib.gen_constant "Class_setoid" dir s
+let coq_proj1 = lazy(gen_constant ["Init"; "Logic"] "proj1")
+let coq_proj2 = lazy(gen_constant ["Init"; "Logic"] "proj2")
+let iff = lazy (gen_constant ["Init"; "Logic"] "iff")
+let coq_all = lazy (gen_constant ["Init"; "Logic"] "all")
+let impl = lazy (gen_constant ["Program"; "Basics"] "impl")
+let arrow = lazy (gen_constant ["Program"; "Basics"] "arrow")
+let coq_id = lazy (gen_constant ["Program"; "Basics"] "id")
+
+let reflexive_type = lazy (try_find_reference ["Classes"; "RelationClasses"] "Reflexive")
+let reflexive_proof = lazy (try_find_reference ["Classes"; "RelationClasses"] "reflexivity")
+
+let symmetric_type = lazy (try_find_reference ["Classes"; "RelationClasses"] "Symmetric")
+let symmetric_proof = lazy (try_find_reference ["Classes"; "RelationClasses"] "symmetry")
+
+let transitive_type = lazy (try_find_reference ["Classes"; "RelationClasses"] "Transitive")
+let transitive_proof = lazy (try_find_reference ["Classes"; "RelationClasses"] "transitivity")
+
+let coq_inverse = lazy (gen_constant (* ["Classes"; "RelationClasses"] "inverse" *)
+ ["Program"; "Basics"] "flip")
+
+let inverse car rel = mkApp (Lazy.force coq_inverse, [| car ; car; mkProp; rel |])
+
+let complement = lazy (gen_constant ["Classes"; "RelationClasses"] "complement")
+let pointwise_relation = lazy (gen_constant ["Classes"; "RelationClasses"] "pointwise_relation")
+
+let respectful_dep = lazy (gen_constant ["Classes"; "Morphisms"] "respectful_dep")
+let respectful = lazy (gen_constant ["Classes"; "Morphisms"] "respectful")
+
+let equivalence = lazy (gen_constant ["Classes"; "RelationClasses"] "Equivalence")
+let default_relation = lazy (gen_constant ["Classes"; "SetoidTactics"] "DefaultRelation")
+
+let coq_relation = lazy (gen_constant ["Relations";"Relation_Definitions"] "relation")
+let mk_relation a = mkApp (Lazy.force coq_relation, [| a |])
+let coq_relationT = lazy (gen_constant ["Classes";"Relations"] "relationT")
+
+let setoid_refl_proj = lazy (gen_constant ["Classes"; "SetoidClass"] "Equivalence_Reflexive")
+
+let setoid_equiv = lazy (gen_constant ["Classes"; "SetoidClass"] "equiv")
+let setoid_morphism = lazy (gen_constant ["Classes"; "SetoidClass"] "setoid_morphism")
+let setoid_refl_proj = lazy (gen_constant ["Classes"; "SetoidClass"] "Equivalence_Reflexive")
+
+let arrow_morphism a b =
+ if isprop a && isprop b then
+ Lazy.force impl
+ else
+ mkApp(Lazy.force arrow, [|a;b|])
+
+let setoid_refl pars x =
+ applistc (Lazy.force setoid_refl_proj) (pars @ [x])
+
+let morphism_type = lazy (constr_of_global (Lazy.force morphism_class).cl_impl)
+
+let morphism_proxy_type = lazy (constr_of_global (Lazy.force morphism_proxy_class).cl_impl)
+
+exception Found of (constr * constr * (types * types) list * constr * constr array *
+ (constr * (constr * constr * constr * constr)) option array)
+
+let is_equiv env sigma t =
+ isConst t && Reductionops.is_conv env sigma (Lazy.force setoid_equiv) t
+
+let split_head = function
+ hd :: tl -> hd, tl
+ | [] -> assert(false)
+
+exception DependentMorphism
+
+let build_signature isevars env m (cstrs : 'a option list) (finalcstr : 'a Lazy.t option) (f : 'a -> constr) =
+ let new_evar isevars env t =
+ Evarutil.e_new_evar isevars env
+ (* ~src:(dummy_loc, ImplicitArg (ConstRef (Lazy.force respectful), (n, Some na))) *) t
+ in
+ let mk_relty ty obj =
+ match obj with
+ | None ->
+ let relty = mk_relation ty in
+ new_evar isevars env relty
+ | Some x -> f x
+ in
+ let rec aux env ty l =
+ let t = Reductionops.whd_betadeltaiota env (Evd.evars_of !isevars) ty in
+ match kind_of_term t, l with
+ | Prod (na, ty, b), obj :: cstrs ->
+ if dependent (mkRel 1) ty then raise DependentMorphism;
+ let (b, arg, evars) = aux (Environ.push_rel (na, None, ty) env) b cstrs in
+ let ty = Reductionops.nf_betaiota ty in
+ let relty = mk_relty ty obj in
+ let arg' = mkApp (Lazy.force respectful, [| ty ; b ; relty ; arg |]) in
+ mkProd(na, ty, b), arg', (ty, relty) :: evars
+ | _, obj :: _ -> anomaly "build_signature: not enough products"
+ | _, [] ->
+ (match finalcstr with
+ None ->
+ let t = Reductionops.nf_betaiota ty in
+ let rel = mk_relty t None in
+ t, rel, [t, rel]
+ | Some codom -> let (t, rel) = Lazy.force codom in
+ t, rel, [t, rel])
+ in aux env m cstrs
+
+let morphism_proof env evars carrier relation x =
+ let goal =
+ mkApp (Lazy.force morphism_proxy_type, [| carrier ; relation; x |])
+ in Evarutil.e_new_evar evars env goal
+
+let find_class_proof proof_type proof_method env carrier relation =
+ try
+ let goal =
+ mkApp (Lazy.force proof_type, [| carrier ; relation |])
+ in
+ let inst = resolve_one_typeclass env goal in
+ mkApp (Lazy.force proof_method, [| carrier ; relation ; inst |])
+ with e when Logic.catchable_exception e -> raise Not_found
+
+let reflexive_proof env = find_class_proof reflexive_type reflexive_proof env
+let symmetric_proof env = find_class_proof symmetric_type symmetric_proof env
+let transitive_proof env = find_class_proof transitive_type transitive_proof env
+
+exception FoundInt of int
+
+let array_find (arr: 'a array) (pred: int -> 'a -> bool): int =
+ try
+ for i=0 to Array.length arr - 1 do if pred i (arr.(i)) then raise (FoundInt i) done;
+ raise Not_found
+ with FoundInt i -> i
+
+let resolve_morphism env sigma oldt m ?(fnewt=fun x -> x) args args' cstr evars =
+ let morph_instance, proj, sigargs, m', args, args' =
+ let first = try (array_find args' (fun i b -> b <> None)) with Not_found -> raise (Invalid_argument "resolve_morphism") in
+ let morphargs, morphobjs = array_chop first args in
+ let morphargs', morphobjs' = array_chop first args' in
+ let appm = mkApp(m, morphargs) in
+ let appmtype = Typing.type_of env sigma appm in
+ let cstrs = List.map (function None -> None | Some (_, (a, r, _, _)) -> Some (a, r)) (Array.to_list morphobjs') in
+ let appmtype', signature, sigargs = build_signature evars env appmtype cstrs cstr (fun (a, r) -> r) in
+ let cl_args = [| appmtype' ; signature ; appm |] in
+ let app = mkApp (Lazy.force morphism_type, cl_args) in
+ let morph = Evarutil.e_new_evar evars env app in
+ let proj =
+ mkApp (Lazy.force respect_proj,
+ Array.append cl_args [|morph|])
+ in
+ morph, proj, sigargs, appm, morphobjs, morphobjs'
+ in
+ let projargs, respars, typeargs =
+ array_fold_left2
+ (fun (acc, sigargs, typeargs') x y ->
+ let (carrier, relation), sigargs = split_head sigargs in
+ match y with
+ None ->
+ let proof = morphism_proof env evars carrier relation x in
+ [ proof ; x ; x ] @ acc, sigargs, x :: typeargs'
+ | Some (p, (_, _, _, t')) ->
+ [ p ; t'; x ] @ acc, sigargs, t' :: typeargs')
+ ([], sigargs, []) args args'
+ in
+ let proof = applistc proj (List.rev projargs) in
+ let newt = applistc m' (List.rev typeargs) in
+ match respars with
+ [ a, r ] -> (proof, (a, r, oldt, fnewt newt))
+ | _ -> assert(false)
+
+(* Adapted from setoid_replace. *)
+
+type hypinfo = {
+ cl : clausenv;
+ prf : constr;
+ car : constr;
+ rel : constr;
+ l2r : bool;
+ c1 : constr;
+ c2 : constr;
+ c : constr option;
+ abs : (constr * types) option;
+}
+
+let evd_convertible env evd x y =
+ try ignore(Evarconv.the_conv_x env x y evd); true
+ with _ -> false
+
+let decompose_setoid_eqhyp env sigma c left2right =
+ let ctype = Typing.type_of env sigma c in
+ let eqclause = Clenv.mk_clenv_from_env env sigma None (c,ctype) 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 applied relation" in
+ let others,(c1,c2) = split_last_two args in
+ let ty1, ty2 =
+ Typing.mtype_of env eqclause.evd c1, Typing.mtype_of env eqclause.evd c2
+ in
+ if not (evd_convertible env eqclause.evd ty1 ty2) then
+ error "The term does not end with an applied homogeneous relation"
+ else
+ { cl=eqclause; prf=(Clenv.clenv_value eqclause);
+ car=ty1; rel=mkApp (equiv, Array.of_list others);
+ l2r=left2right; c1=c1; c2=c2; c=Some c; abs=None }
+
+let rewrite_unif_flags = {
+ Unification.modulo_conv_on_closed_terms = None;
+ Unification.use_metas_eagerly = true;
+ Unification.modulo_delta = empty_transparent_state;
+}
+
+let conv_transparent_state = (Idpred.empty, Cpred.full)
+
+let rewrite2_unif_flags = {
+ Unification.modulo_conv_on_closed_terms = Some conv_transparent_state;
+ Unification.use_metas_eagerly = true;
+ Unification.modulo_delta = empty_transparent_state;
+}
+
+let convertible env evd x y =
+ Reductionops.is_conv env (Evd.evars_of evd) x y
+
+let allowK = true
+
+let refresh_hypinfo env sigma hypinfo =
+ if !hypinfo.abs = None then
+ let {l2r=l2r; c = c} = !hypinfo in
+ match c with
+ | Some c ->
+ (* Refresh the clausenv to not get the same meta twice in the goal. *)
+ hypinfo := decompose_setoid_eqhyp env sigma c l2r;
+ | _ -> ()
+ else ()
+
+let unify_eqn env sigma hypinfo t =
+ try
+ let {cl=cl; prf=prf; car=car; rel=rel; l2r=l2r; c1=c1; c2=c2; c=c; abs=abs} = !hypinfo in
+ let env', prf, c1, c2, car, rel =
+ let left = if l2r then c1 else c2 in
+ match abs with
+ Some (absprf, absprfty) ->
+ (*if convertible env cl.evd left t then
+ cl, prf, c1, c2, car, rel
+ else raise Not_found*)
+ let env' = clenv_unify allowK ~flags:rewrite2_unif_flags CONV left t cl in
+ let env' =
+ let mvs = clenv_dependent false env' in
+ clenv_pose_metas_as_evars env' mvs
+ in
+ let c1 = Clenv.clenv_nf_meta env' c1
+ and c2 = Clenv.clenv_nf_meta env' c2
+ and car = Clenv.clenv_nf_meta env' car
+ and rel = Clenv.clenv_nf_meta env' rel in
+ hypinfo := { !hypinfo with
+ cl = env';
+ abs = Some (Clenv.clenv_value env', Clenv.clenv_type env') };
+ env', prf, c1, c2, car, rel
+ | None ->
+ let env' =
+ try clenv_unify allowK ~flags:rewrite_unif_flags
+ CONV left t cl
+ with Pretype_errors.PretypeError _ ->
+ (* For Ring essentially, only when doing setoid_rewrite *)
+ clenv_unify allowK ~flags:rewrite2_unif_flags
+ CONV left t cl
+ in
+ let env' =
+ let mvs = clenv_dependent false env' in
+ clenv_pose_metas_as_evars env' mvs
+ in
+ let c1 = Clenv.clenv_nf_meta env' c1
+ and c2 = Clenv.clenv_nf_meta env' c2
+ and car = Clenv.clenv_nf_meta env' car
+ and rel = Clenv.clenv_nf_meta env' rel in
+ let prf = Clenv.clenv_value env' in
+ let ty1 = Typing.mtype_of env'.env env'.evd c1
+ and ty2 = Typing.mtype_of env'.env env'.evd c2
+ in
+ if convertible env env'.evd ty1 ty2 then (
+ if occur_meta prf then refresh_hypinfo env sigma hypinfo;
+ env', prf, c1, c2, car, rel)
+ else raise Reduction.NotConvertible
+ in
+ let res =
+ if l2r then (prf, (car, rel, c1, c2))
+ else
+ try (mkApp (symmetric_proof env car rel, [| c1 ; c2 ; prf |]), (car, rel, c2, c1))
+ with Not_found ->
+ (prf, (car, inverse car rel, c2, c1))
+ in Some (env', res)
+ with _ -> None
+
+let unfold_impl t =
+ match kind_of_term t with
+ | App (arrow, [| a; b |])(* when eq_constr arrow (Lazy.force impl) *) ->
+ mkProd (Anonymous, a, lift 1 b)
+ | _ -> assert false
+
+let unfold_id t =
+ match kind_of_term t with
+ | App (id, [| a; b |]) (* when eq_constr id (Lazy.force coq_id) *) -> b
+ | _ -> assert false
+
+let unfold_all t =
+ match kind_of_term t with
+ | App (id, [| a; b |]) (* when eq_constr id (Lazy.force coq_all) *) ->
+ (match kind_of_term b with
+ | Lambda (n, ty, b) -> mkProd (n, ty, b)
+ | _ -> assert false)
+ | _ -> assert false
+
+let decomp_prod env evm n c =
+ snd (Reductionops.decomp_n_prod env evm n c)
+
+let rec decomp_pointwise n c =
+ if n = 0 then c
+ else
+ match kind_of_term c with
+ | App (pointwise, [| a; b; relb |]) -> decomp_pointwise (pred n) relb
+ | _ -> raise Not_found
+
+let lift_cstr env sigma evars args cstr =
+ let cstr () =
+ let start =
+ match cstr with
+ | Some codom -> Lazy.force codom
+ | None -> let car = Evarutil.e_new_evar evars env (new_Type ()) in
+ let rel = Evarutil.e_new_evar evars env (mk_relation car) in
+ (car, rel)
+ in
+ Array.fold_right
+ (fun arg (car, rel) ->
+ let ty = Typing.type_of env sigma arg in
+ let car' = mkProd (Anonymous, ty, car) in
+ let rel' = mkApp (Lazy.force pointwise_relation, [| ty; car; rel |]) in
+ (car', rel'))
+ args start
+ in Some (Lazy.lazy_from_fun cstr)
+
+let unlift_cstr env sigma = function
+ | None -> None
+ | Some codom ->
+ let cstr () =
+ let car, rel = Lazy.force codom in
+ decomp_prod env sigma 1 car, decomp_pointwise 1 rel
+ in Some (Lazy.lazy_from_fun cstr)
+
+type rewrite_flags = { under_lambdas : bool; on_morphisms : bool }
+
+let default_flags = { under_lambdas = true; on_morphisms = true; }
+
+let build_new gl env sigma flags loccs hypinfo concl cstr evars =
+ let (nowhere_except_in,occs) = loccs in
+ let is_occ occ =
+ if nowhere_except_in then List.mem occ occs else not (List.mem occ occs) in
+ let rec aux env t occ cstr =
+ let unif = unify_eqn env sigma hypinfo t in
+ let occ = if unif = None then occ else succ occ in
+ match unif with
+ | Some (env', (prf, hypinfo as x)) when is_occ occ ->
+ begin
+ evars := Evd.evar_merge !evars
+ (Evd.evars_of (Evd.undefined_evars (Evarutil.nf_evar_defs env'.evd)));
+ match cstr with
+ | None -> Some x, occ
+ | Some _ ->
+ let (car, r, orig, dest) = hypinfo in
+ let res =
+ resolve_morphism env sigma t ~fnewt:unfold_id
+ (mkApp (Lazy.force coq_id, [| car |]))
+ [| orig |] [| Some x |] cstr evars
+ in Some res, occ
+ end
+ | _ ->
+ match kind_of_term t with
+ | App (m, args) ->
+ let rewrite_args occ =
+ let args', occ =
+ Array.fold_left
+ (fun (acc, occ) arg -> let res, occ = aux env arg occ None in (res :: acc, occ))
+ ([], occ) args
+ in
+ let res =
+ if List.for_all (fun x -> x = None) args' then None
+ else
+ let args' = Array.of_list (List.rev args') in
+ (Some (resolve_morphism env sigma t m args args' cstr evars))
+ in res, occ
+ in
+ if flags.on_morphisms then
+ let m', occ = aux env m occ (lift_cstr env sigma evars args cstr) in
+ match m' with
+ | None -> rewrite_args occ (* Standard path, try rewrite on arguments *)
+ | Some (prf, (car, rel, c1, c2)) ->
+ (* We rewrote the function and get a proof of pointwise rel for the arguments.
+ We just apply it. *)
+ let nargs = Array.length args in
+ let res =
+ mkApp (prf, args),
+ (decomp_prod env (Evd.evars_of !evars) nargs car,
+ decomp_pointwise nargs rel, mkApp(c1, args), mkApp(c2, args))
+ in Some res, occ
+ else rewrite_args occ
+
+ | Prod (n, x, b) when not (dependent (mkRel 1) b) ->
+ let x', occ = aux env x occ None in
+(* if x' = None && flags.under_lambdas then *)
+(* let lam = mkLambda (n, x, b) in *)
+(* let lam', occ = aux env lam occ None in *)
+(* let res = *)
+(* match lam' with *)
+(* | None -> None *)
+(* | Some (prf, (car, rel, c1, c2)) -> *)
+(* Some (resolve_morphism env sigma t *)
+(* ~fnewt:unfold_all *)
+(* (Lazy.force coq_all) [| x ; lam |] [| None; lam' |] *)
+(* cstr evars) *)
+(* in res, occ *)
+(* else *)
+ let b = subst1 mkProp b in
+ let b', occ = aux env b occ None in
+ let res =
+ if x' = None && b' = None then None
+ else
+ Some (resolve_morphism env sigma t
+ ~fnewt:unfold_impl
+ (arrow_morphism (Typing.type_of env sigma x) (Typing.type_of env sigma b))
+ [| x ; b |] [| x' ; b' |]
+ cstr evars)
+ in res, occ
+
+ | Prod (n, ty, b) ->
+ let lam = mkLambda (n, ty, b) in
+ let lam', occ = aux env lam occ None in
+ let res =
+ match lam' with
+ | None -> None
+ | Some (prf, (car, rel, c1, c2)) ->
+ Some (resolve_morphism env sigma t
+ ~fnewt:unfold_all
+ (Lazy.force coq_all) [| ty ; lam |] [| None; lam' |]
+ cstr evars)
+ in res, occ
+
+ | Lambda (n, t, b) when flags.under_lambdas ->
+ let env' = Environ.push_rel (n, None, t) env in
+ refresh_hypinfo env' sigma hypinfo;
+ let b', occ = aux env' b occ (unlift_cstr env sigma cstr) in
+ let res =
+ match b' with
+ | None -> None
+ | Some (prf, (car, rel, c1, c2)) ->
+ let prf' = mkLambda (n, t, prf) in
+ let car' = mkProd (n, t, car) in
+ let rel' = mkApp (Lazy.force pointwise_relation, [| t; car; rel |]) in
+ let c1' = mkLambda(n, t, c1) and c2' = mkLambda (n, t, c2) in
+ Some (prf', (car', rel', c1', c2'))
+ in res, occ
+ | _ -> None, occ
+ in
+ let eq,nbocc_min_1 = aux env concl 0 cstr in
+ let rest = List.filter (fun o -> o > nbocc_min_1) occs in
+ if rest <> [] then error_invalid_occurrence rest;
+ eq
+
+let resolve_typeclass_evars d p env evd onlyargs split fail =
+ let pred =
+ if onlyargs then
+ (fun ev evi -> Typeclasses.is_implicit_arg (snd (Evd.evar_source ev evd)) &&
+ class_of_constr evi.Evd.evar_concl <> None)
+ else
+ (fun ev evi -> class_of_constr evi.Evd.evar_concl <> None)
+ in resolve_all_evars d p env pred evd split fail
+
+let cl_rewrite_clause_aux ?(flags=default_flags) hypinfo goal_meta occs clause gl =
+ let concl, is_hyp =
+ match clause with
+ Some ((_, id), _) -> pf_get_hyp_typ gl id, Some id
+ | None -> pf_concl gl, None
+ in
+ let cstr =
+ match is_hyp with
+ None -> (mkProp, inverse mkProp (Lazy.force impl))
+ | Some _ -> (mkProp, Lazy.force impl)
+ in
+ let evars = ref (Evd.create_evar_defs Evd.empty) in
+ let env = pf_env gl in
+ let sigma = project gl in
+ let eq = build_new gl env sigma flags occs hypinfo concl (Some (Lazy.lazy_from_val cstr)) evars
+ in
+ match eq with
+ | Some (p, (_, _, oldt, newt)) ->
+ (try
+ evars := Typeclasses.resolve_typeclasses env ~split:false ~fail:true !evars;
+ let p = Evarutil.nf_isevar !evars p in
+ let newt = Evarutil.nf_isevar !evars newt in
+ let undef = Evd.undefined_evars !evars in
+ let abs = Option.map (fun (x, y) -> Evarutil.nf_isevar !evars x,
+ Evarutil.nf_isevar !evars y) !hypinfo.abs in
+ let rewtac =
+ match is_hyp with
+ | Some id ->
+ let term =
+ match abs with
+ | None -> p
+ | Some (t, ty) ->
+ mkApp (mkLambda (Name (id_of_string "lemma"), ty, p), [| t |])
+ in
+ cut_replacing id newt
+ (fun x -> Tactics.refine (mkApp (term, [| mkVar id |])))
+ | None ->
+ (match abs with
+ | None ->
+ let name = next_name_away_with_default "H" Anonymous (pf_ids_of_hyps gl) in
+ tclTHENLAST
+ (Tacmach.internal_cut_no_check name newt)
+ (tclTHEN (Tactics.revert [name]) (Tactics.refine p))
+ | Some (t, ty) ->
+ Tactics.refine
+ (mkApp (mkLambda (Name (id_of_string "newt"), newt,
+ mkLambda (Name (id_of_string "lemma"), ty,
+ mkApp (p, [| mkRel 2 |]))),
+ [| mkMeta goal_meta; t |])))
+ in
+ let evartac =
+ let evd = Evd.evars_of undef in
+ if not (evd = Evd.empty) then Refiner.tclEVARS (Evd.merge sigma evd)
+ else tclIDTAC
+ in tclTHENLIST [evartac; rewtac] gl
+ with
+ | Stdpp.Exc_located (_, TypeClassError (env, (UnsatisfiableConstraints _ as e)))
+ | TypeClassError (env, (UnsatisfiableConstraints _ as e)) ->
+ tclFAIL 0 (str" setoid rewrite failed: unable to satisfy the rewriting constraints."
+ ++ fnl () ++ Himsg.explain_typeclass_error env e) gl)
+ (* | Not_found -> *)
+ (* tclFAIL 0 (str" setoid rewrite failed: unable to satisfy the rewriting constraints.") gl) *)
+ | None ->
+ let {l2r=l2r; c1=x; c2=y} = !hypinfo in
+ raise (Pretype_errors.PretypeError
+ (pf_env gl,
+ Pretype_errors.NoOccurrenceFound ((if l2r then x else y), is_hyp)))
+ (* tclFAIL 1 (str"setoid rewrite failed") gl *)
+
+let cl_rewrite_clause_aux ?(flags=default_flags) hypinfo goal_meta occs clause gl =
+ try cl_rewrite_clause_aux ~flags hypinfo goal_meta occs clause gl
+ with DependentMorphism -> tclFAIL 0 (str " setoid rewrite failed: cannot handle dependent morphisms") gl
+
+let cl_rewrite_clause c left2right occs clause gl =
+ init_setoid ();
+ let meta = Evarutil.new_meta() in
+ let gl = { gl with sigma = Typeclasses.mark_unresolvables gl.sigma } in
+ let hypinfo = ref (decompose_setoid_eqhyp (pf_env gl) (project gl) c left2right) in
+ cl_rewrite_clause_aux hypinfo meta occs clause gl
+
+open Genarg
+open Extraargs
+
+let occurrences_of = function
+ | n::_ as nl when n < 0 -> (false,List.map abs nl)
+ | nl ->
+ if List.exists (fun n -> n < 0) nl then
+ error "Illegal negative occurrence number";
+ (true,nl)
+
+TACTIC EXTEND class_rewrite
+| [ "clrewrite" orient(o) constr(c) "in" hyp(id) "at" occurrences(occ) ] -> [ cl_rewrite_clause c o (occurrences_of occ) (Some (([],id), [])) ]
+| [ "clrewrite" orient(o) constr(c) "at" occurrences(occ) "in" hyp(id) ] -> [ cl_rewrite_clause c o (occurrences_of occ) (Some (([],id), [])) ]
+| [ "clrewrite" orient(o) constr(c) "in" hyp(id) ] -> [ cl_rewrite_clause c o all_occurrences (Some (([],id), [])) ]
+| [ "clrewrite" orient(o) constr(c) "at" occurrences(occ) ] -> [ cl_rewrite_clause c o (occurrences_of occ) None ]
+| [ "clrewrite" orient(o) constr(c) ] -> [ cl_rewrite_clause c o all_occurrences None ]
+END
+
+
+let clsubstitute o c =
+ let is_tac id = match kind_of_term c with Var id' when id' = id -> true | _ -> false in
+ Tacticals.onAllClauses
+ (fun cl ->
+ match cl with
+ | Some ((_,id),_) when is_tac id -> tclIDTAC
+ | _ -> tclTRY (cl_rewrite_clause c o all_occurrences cl))
+
+TACTIC EXTEND substitute
+| [ "substitute" orient(o) constr(c) ] -> [ clsubstitute o c ]
+END
+
+let pr_debug _prc _prlc _prt b =
+ if b then Pp.str "debug" else Pp.mt()
+
+ARGUMENT EXTEND debug TYPED AS bool PRINTED BY pr_debug
+| [ "debug" ] -> [ true ]
+| [ ] -> [ false ]
+END
+
+let pr_mode _prc _prlc _prt m =
+ match m with
+ Some b ->
+ if b then Pp.str "depth-first" else Pp.str "breadth-fist"
+ | None -> Pp.mt()
+
+ARGUMENT EXTEND search_mode TYPED AS bool option PRINTED BY pr_mode
+| [ "dfs" ] -> [ Some true ]
+| [ "bfs" ] -> [ Some false ]
+| [] -> [ None ]
+END
+
+let pr_depth _prc _prlc _prt = function
+ Some i -> Util.pr_int i
+ | None -> Pp.mt()
+
+ARGUMENT EXTEND depth TYPED AS int option PRINTED BY pr_depth
+| [ int_or_var_opt(v) ] -> [ match v with Some (ArgArg i) -> Some i | _ -> None ]
+END
+
+let solve_inst debug mode depth env evd onlyargs split fail =
+ resolve_typeclass_evars debug (mode, depth) env evd onlyargs split fail
+
+let _ =
+ Typeclasses.solve_instanciations_problem :=
+ solve_inst false true default_eauto_depth
+
+VERNAC COMMAND EXTEND Typeclasses_Settings
+ | [ "Typeclasses" "eauto" ":=" debug(d) search_mode(s) depth(depth) ] -> [
+ let mode = match s with Some t -> t | None -> true in
+ let depth = match depth with Some i -> i | None -> default_eauto_depth in
+ Typeclasses.solve_instanciations_problem :=
+ solve_inst d mode depth
+ ]
+END
+
+TACTIC EXTEND typeclasses_eauto
+| [ "typeclasses" "eauto" debug(d) search_mode(s) depth(depth) ] -> [
+ let mode = match s with Some t -> t | None -> true in
+ let depth = match depth with Some i -> i | None -> default_eauto_depth in
+ fun gl ->
+ let gls = {it = [sig_it gl]; sigma = project gl} in
+ let vals v = List.hd v in
+ typeclasses_eauto d (mode, depth) [] (gls, vals) ]
+END
+
+
+(* fun gl -> *)
+(* let env = pf_env gl in *)
+(* let sigma = project gl in *)
+(* let proj = sig_it gl in *)
+(* let evd = Evd.create_evar_defs (Evd.add Evd.empty 1 proj) in *)
+(* let mode = match s with Some t -> t | None -> true in *)
+(* let depth = match depth with Some i -> i | None -> default_eauto_depth in *)
+(* match resolve_typeclass_evars d (mode, depth) env evd false with *)
+(* | Some evd' -> *)
+(* let goal = Evd.find (Evd.evars_of evd') 1 in *)
+(* (match goal.evar_body with *)
+(* | Evar_empty -> tclIDTAC gl *)
+(* | Evar_defined b -> refine b gl) *)
+(* | None -> tclIDTAC gl *)
+(* ] *)
+
+let _ =
+ Classes.refine_ref := Refine.refine
+
+(* Compatibility with old Setoids *)
+
+TACTIC EXTEND setoid_rewrite
+ [ "setoid_rewrite" orient(o) constr(c) ]
+ -> [ cl_rewrite_clause c o all_occurrences None ]
+ | [ "setoid_rewrite" orient(o) constr(c) "in" hyp(id) ] ->
+ [ cl_rewrite_clause c o all_occurrences (Some (([],id), []))]
+ | [ "setoid_rewrite" orient(o) constr(c) "at" occurrences(occ) ] ->
+ [ cl_rewrite_clause c o (occurrences_of occ) None]
+ | [ "setoid_rewrite" orient(o) constr(c) "at" occurrences(occ) "in" hyp(id)] ->
+ [ cl_rewrite_clause c o (occurrences_of occ) (Some (([],id), []))]
+ | [ "setoid_rewrite" orient(o) constr(c) "in" hyp(id) "at" occurrences(occ)] ->
+ [ cl_rewrite_clause c o (occurrences_of occ) (Some (([],id), []))]
+END
+
+(* let solve_obligation lemma = *)
+(* tclTHEN (Tacinterp.interp (Tacexpr.TacAtom (dummy_loc, Tacexpr.TacAnyConstructor None))) *)
+(* (eapply_with_bindings (Constrintern.interp_constr Evd.empty (Global.env()) lemma, NoBindings)) *)
+
+let mkappc s l = CAppExpl (dummy_loc,(None,(Libnames.Ident (dummy_loc,id_of_string s))),l)
+
+let declare_an_instance n s args =
+ ((dummy_loc,Name n), Explicit,
+ CAppExpl (dummy_loc, (None, Qualid (dummy_loc, qualid_of_string s)),
+ args))
+
+let declare_instance a aeq n s = declare_an_instance n s [a;aeq]
+
+let anew_instance binders instance fields =
+ new_instance binders instance fields
+ ~on_free_vars:Classes.fail_on_free_vars
+ None
+
+let require_library dirpath =
+ let qualid = (dummy_loc, Libnames.qualid_of_dirpath (Libnames.dirpath_of_string dirpath)) in
+ Library.require_library [qualid] (Some false)
+
+let declare_instance_refl binders a aeq n lemma =
+ let instance = declare_instance a aeq (add_suffix n "_Reflexive") "Coq.Classes.RelationClasses.Reflexive"
+ in anew_instance binders instance
+ [((dummy_loc,id_of_string "reflexivity"),[],lemma)]
+
+let declare_instance_sym binders a aeq n lemma =
+ let instance = declare_instance a aeq (add_suffix n "_Symmetric") "Coq.Classes.RelationClasses.Symmetric"
+ in anew_instance binders instance
+ [((dummy_loc,id_of_string "symmetry"),[],lemma)]
+
+let declare_instance_trans binders a aeq n lemma =
+ let instance = declare_instance a aeq (add_suffix n "_Transitive") "Coq.Classes.RelationClasses.Transitive"
+ in anew_instance binders instance
+ [((dummy_loc,id_of_string "transitivity"),[],lemma)]
+
+let constr_tac = Tacinterp.interp (Tacexpr.TacAtom (dummy_loc, Tacexpr.TacAnyConstructor (false,None)))
+
+let declare_relation ?(binders=[]) a aeq n refl symm trans =
+ init_setoid ();
+ match (refl,symm,trans) with
+ (None, None, None) ->
+ let instance = declare_instance a aeq n "Coq.Classes.SetoidTactics.DefaultRelation"
+ in ignore(anew_instance binders instance [])
+ | (Some lemma1, None, None) ->
+ ignore (declare_instance_refl binders a aeq n lemma1)
+ | (None, Some lemma2, None) ->
+ ignore (declare_instance_sym binders a aeq n lemma2)
+ | (None, None, Some lemma3) ->
+ ignore (declare_instance_trans binders a aeq n lemma3)
+ | (Some lemma1, Some lemma2, None) ->
+ ignore (declare_instance_refl binders a aeq n lemma1);
+ ignore (declare_instance_sym binders a aeq n lemma2)
+ | (Some lemma1, None, Some lemma3) ->
+ let _lemma_refl = declare_instance_refl binders a aeq n lemma1 in
+ let _lemma_trans = declare_instance_trans binders a aeq n lemma3 in
+ let instance = declare_instance a aeq n "Coq.Classes.RelationClasses.PreOrder"
+ in ignore(
+ anew_instance binders instance
+ [((dummy_loc,id_of_string "PreOrder_Reflexive"), [], lemma1);
+ ((dummy_loc,id_of_string "PreOrder_Transitive"),[], lemma3)])
+ | (None, Some lemma2, Some lemma3) ->
+ let _lemma_sym = declare_instance_sym binders a aeq n lemma2 in
+ let _lemma_trans = declare_instance_trans binders a aeq n lemma3 in
+ let instance = declare_instance a aeq n "Coq.Classes.RelationClasses.PER"
+ in ignore(
+ anew_instance binders instance
+ [((dummy_loc,id_of_string "PER_Symmetric"), [], lemma2);
+ ((dummy_loc,id_of_string "PER_Transitive"),[], lemma3)])
+ | (Some lemma1, Some lemma2, Some lemma3) ->
+ let _lemma_refl = declare_instance_refl binders a aeq n lemma1 in
+ let _lemma_sym = declare_instance_sym binders a aeq n lemma2 in
+ let _lemma_trans = declare_instance_trans binders a aeq n lemma3 in
+ let instance = declare_instance a aeq n "Coq.Classes.RelationClasses.Equivalence"
+ in ignore(
+ anew_instance binders instance
+ [((dummy_loc,id_of_string "Equivalence_Reflexive"), [], lemma1);
+ ((dummy_loc,id_of_string "Equivalence_Symmetric"), [], lemma2);
+ ((dummy_loc,id_of_string "Equivalence_Transitive"),[], lemma3)])
+
+type 'a binders_let_argtype = (local_binder list, 'a) Genarg.abstract_argument_type
+
+let (wit_binders_let : Genarg.tlevel binders_let_argtype),
+ (globwit_binders_let : Genarg.glevel binders_let_argtype),
+ (rawwit_binders_let : Genarg.rlevel binders_let_argtype) =
+ Genarg.create_arg "binders_let"
+
+open Pcoq.Constr
+
+VERNAC COMMAND EXTEND AddRelation
+ | [ "Add" "Relation" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "symmetry" "proved" "by" constr(lemma2) "as" ident(n) ] ->
+ [ declare_relation a aeq n (Some lemma1) (Some lemma2) None ]
+
+ | [ "Add" "Relation" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "as" ident(n) ] ->
+ [ declare_relation a aeq n (Some lemma1) None None ]
+ | [ "Add" "Relation" constr(a) constr(aeq) "as" ident(n) ] ->
+ [ declare_relation a aeq n None None None ]
+END
+
+VERNAC COMMAND EXTEND AddRelation2
+ [ "Add" "Relation" constr(a) constr(aeq) "symmetry" "proved" "by" constr(lemma2)
+ "as" ident(n) ] ->
+ [ declare_relation a aeq n None (Some lemma2) None ]
+ | [ "Add" "Relation" constr(a) constr(aeq) "symmetry" "proved" "by" constr(lemma2) "transitivity" "proved" "by" constr(lemma3) "as" ident(n) ] ->
+ [ declare_relation a aeq n None (Some lemma2) (Some lemma3) ]
+END
+
+VERNAC COMMAND EXTEND AddRelation3
+ [ "Add" "Relation" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "transitivity" "proved" "by" constr(lemma3) "as" ident(n) ] ->
+ [ declare_relation a aeq n (Some lemma1) None (Some lemma3) ]
+ | [ "Add" "Relation" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "symmetry" "proved" "by" constr(lemma2) "transitivity" "proved" "by" constr(lemma3)
+ "as" ident(n) ] ->
+ [ declare_relation a aeq n (Some lemma1) (Some lemma2) (Some lemma3) ]
+ | [ "Add" "Relation" constr(a) constr(aeq) "transitivity" "proved" "by" constr(lemma3)
+ "as" ident(n) ] ->
+ [ declare_relation a aeq n None None (Some lemma3) ]
+END
+
+VERNAC COMMAND EXTEND AddParametricRelation
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq)
+ "reflexivity" "proved" "by" constr(lemma1)
+ "symmetry" "proved" "by" constr(lemma2) "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n (Some lemma1) (Some lemma2) None ]
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq)
+ "reflexivity" "proved" "by" constr(lemma1)
+ "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n (Some lemma1) None None ]
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n None None None ]
+END
+
+VERNAC COMMAND EXTEND AddParametricRelation2
+ [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "symmetry" "proved" "by" constr(lemma2)
+ "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n None (Some lemma2) None ]
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "symmetry" "proved" "by" constr(lemma2) "transitivity" "proved" "by" constr(lemma3) "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n None (Some lemma2) (Some lemma3) ]
+END
+
+VERNAC COMMAND EXTEND AddParametricRelation3
+ [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "transitivity" "proved" "by" constr(lemma3) "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n (Some lemma1) None (Some lemma3) ]
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "reflexivity" "proved" "by" constr(lemma1)
+ "symmetry" "proved" "by" constr(lemma2) "transitivity" "proved" "by" constr(lemma3)
+ "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n (Some lemma1) (Some lemma2) (Some lemma3) ]
+ | [ "Add" "Parametric" "Relation" binders_let(b) ":" constr(a) constr(aeq) "transitivity" "proved" "by" constr(lemma3)
+ "as" ident(n) ] ->
+ [ declare_relation ~binders:b a aeq n None None (Some lemma3) ]
+END
+
+let mk_qualid s =
+ Libnames.Qualid (dummy_loc, Libnames.qualid_of_string s)
+
+let cHole = CHole (dummy_loc, None)
+
+open Entries
+open Libnames
+
+let respect_projection r ty =
+ let ctx, inst = Sign.decompose_prod_assum ty in
+ let mor, args = destApp inst in
+ let instarg = mkApp (r, rel_vect 0 (List.length ctx)) in
+ let app = mkApp (Lazy.force respect_proj,
+ Array.append args [| instarg |]) in
+ it_mkLambda_or_LetIn app ctx
+
+let declare_projection n instance_id r =
+ let ty = Global.type_of_global r in
+ let c = constr_of_global r in
+ let term = respect_projection c ty in
+ let typ = Typing.type_of (Global.env ()) Evd.empty term in
+ let ctx, typ = Sign.decompose_prod_assum typ in
+ let typ =
+ let n =
+ let rec aux t =
+ match kind_of_term t with
+ App (f, [| a ; a' ; rel; rel' |]) when eq_constr f (Lazy.force respectful) ->
+ succ (aux rel')
+ | _ -> 0
+ in
+ let init =
+ match kind_of_term typ with
+ App (f, args) when eq_constr f (Lazy.force respectful) ->
+ mkApp (f, fst (array_chop (Array.length args - 2) args))
+ | _ -> typ
+ in aux init
+ in
+ let ctx,ccl = Reductionops.decomp_n_prod (Global.env()) Evd.empty (3 * n) typ
+ in it_mkProd_or_LetIn ccl ctx
+ in
+ let typ = it_mkProd_or_LetIn typ ctx in
+ let cst =
+ { const_entry_body = term;
+ const_entry_type = Some typ;
+ const_entry_opaque = false;
+ const_entry_boxed = false }
+ in
+ ignore(Declare.declare_constant n (Entries.DefinitionEntry cst, Decl_kinds.IsDefinition Decl_kinds.Definition))
+
+let build_morphism_signature m =
+ let env = Global.env () in
+ let m = Constrintern.interp_constr Evd.empty env m in
+ let t = Typing.type_of env Evd.empty m in
+ let isevars = ref (Evd.create_evar_defs Evd.empty) in
+ let cstrs =
+ let rec aux t =
+ match kind_of_term t with
+ | Prod (na, a, b) ->
+ None :: aux b
+ | _ -> []
+ in aux t
+ in
+ let t', sig_, evars = build_signature isevars env t cstrs None snd in
+ let _ = List.iter
+ (fun (ty, rel) ->
+ let default = mkApp (Lazy.force default_relation, [| ty; rel |]) in
+ ignore (Evarutil.e_new_evar isevars env default))
+ evars
+ in
+ let morph =
+ mkApp (Lazy.force morphism_type, [| t; sig_; m |])
+ in
+ let evd =
+ Typeclasses.resolve_typeclasses ~fail:true ~onlyargs:false env !isevars in
+ let m = Evarutil.nf_isevar evd morph in
+ Evarutil.check_evars env Evd.empty evd m; m
+
+let default_morphism sign m =
+ let env = Global.env () in
+ let isevars = ref (Evd.create_evar_defs Evd.empty) in
+ let t = Typing.type_of env Evd.empty m in
+ let _, sign, evars =
+ try build_signature isevars env t (fst sign) (snd sign) (fun (ty, rel) -> rel)
+ with DependentMorphism -> error "Cannot infer the signature of dependent morphisms"
+ in
+ let morph =
+ mkApp (Lazy.force morphism_type, [| t; sign; m |])
+ in
+ let mor = resolve_one_typeclass env morph in
+ mor, respect_projection mor morph
+
+let add_setoid binders a aeq t n =
+ init_setoid ();
+ let _lemma_refl = declare_instance_refl binders a aeq n (mkappc "Seq_refl" [a;aeq;t]) in
+ let _lemma_sym = declare_instance_sym binders a aeq n (mkappc "Seq_sym" [a;aeq;t]) in
+ let _lemma_trans = declare_instance_trans binders a aeq n (mkappc "Seq_trans" [a;aeq;t]) in
+ let instance = declare_instance a aeq n "Coq.Classes.RelationClasses.Equivalence"
+ in ignore(
+ anew_instance binders instance
+ [((dummy_loc,id_of_string "Equivalence_Reflexive"), [], mkappc "Seq_refl" [a;aeq;t]);
+ ((dummy_loc,id_of_string "Equivalence_Symmetric"), [], mkappc "Seq_sym" [a;aeq;t]);
+ ((dummy_loc,id_of_string "Equivalence_Transitive"),[], mkappc "Seq_trans" [a;aeq;t])])
+
+let add_morphism_infer m n =
+ init_setoid ();
+ let instance_id = add_suffix n "_Morphism" in
+ let instance = try build_morphism_signature m
+ with DependentMorphism -> error "Cannot infer the signature of dependent morphisms"
+ in
+ if Lib.is_modtype () then
+ let cst = Declare.declare_internal_constant instance_id
+ (Entries.ParameterEntry (instance,false), Decl_kinds.IsAssumption Decl_kinds.Logical)
+ in
+ add_instance (Typeclasses.new_instance (Lazy.force morphism_class) None false cst);
+ declare_projection n instance_id (ConstRef cst)
+ else
+ let kind = Decl_kinds.Global, Decl_kinds.DefinitionBody Decl_kinds.Instance in
+ Flags.silently
+ (fun () ->
+ Command.start_proof instance_id kind instance
+ (fun _ -> function
+ Libnames.ConstRef cst ->
+ add_instance (Typeclasses.new_instance (Lazy.force morphism_class) None false cst);
+ declare_projection n instance_id (ConstRef cst)
+ | _ -> assert false);
+ Pfedit.by (Tacinterp.interp <:tactic< Coq.Classes.SetoidTactics.add_morphism_tactic>>)) ();
+ Flags.if_verbose (fun x -> msg (Printer.pr_open_subgoals x)) ()
+
+let add_morphism binders m s n =
+ init_setoid ();
+ let instance_id = add_suffix n "_Morphism" in
+ let instance =
+ ((dummy_loc,Name instance_id), Explicit,
+ CAppExpl (dummy_loc,
+ (None, Qualid (dummy_loc, Libnames.qualid_of_string "Coq.Classes.Morphisms.Morphism")),
+ [cHole; s; m]))
+ in
+ let tac = Tacinterp.interp <:tactic<add_morphism_tactic>> in
+ ignore(new_instance binders instance []
+ ~on_free_vars:Classes.fail_on_free_vars
+ ~tac ~hook:(fun cst -> declare_projection n instance_id (ConstRef cst))
+ None)
+
+VERNAC COMMAND EXTEND AddSetoid1
+ [ "Add" "Setoid" constr(a) constr(aeq) constr(t) "as" ident(n) ] ->
+ [ add_setoid [] a aeq t n ]
+ | [ "Add" "Parametric" "Setoid" binders_let(binders) ":" constr(a) constr(aeq) constr(t) "as" ident(n) ] ->
+ [ add_setoid binders a aeq t n ]
+ | [ "Add" "Morphism" constr(m) ":" ident(n) ] ->
+ [ add_morphism_infer m n ]
+ | [ "Add" "Morphism" constr(m) "with" "signature" lconstr(s) "as" ident(n) ] ->
+ [ add_morphism [] m s n ]
+ | [ "Add" "Parametric" "Morphism" binders_let(binders) ":" constr(m) "with" "signature" lconstr(s) "as" ident(n) ] ->
+ [ add_morphism binders m s n ]
+END
+
+(** Bind to "rewrite" too *)
+
+(** Taken from original setoid_replace, to emulate the old rewrite semantics where
+ lemmas are first instantiated and then rewrite proceeds. *)
+
+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.UnresolvedBindings [Evd.meta_name evd m])))
+ 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
+
+let unification_rewrite l2r c1 c2 cl car rel but gl =
+ let (env',c') =
+ try
+ (* ~flags:(false,true) to allow to mark occurrences that must not be
+ rewritten simply by replacing them with let-defined definitions
+ in the context *)
+ Unification.w_unify_to_subterm ~flags:rewrite_unif_flags (pf_env gl) ((if l2r then c1 else c2),but) cl.evd
+ with
+ Pretype_errors.PretypeError _ ->
+ (* ~flags:(true,true) to make Ring work (since it really
+ exploits conversion) *)
+ Unification.w_unify_to_subterm ~flags:rewrite2_unif_flags
+ (pf_env gl) ((if l2r then c1 else c2),but) cl.evd
+ in
+ let cl' = {cl with evd = env'} in
+ let c1 = Clenv.clenv_nf_meta cl' c1
+ and c2 = Clenv.clenv_nf_meta cl' c2 in
+ check_evar_map_of_evars_defs env';
+ let prf = Clenv.clenv_value cl' in
+ let prfty = Clenv.clenv_type cl' in
+ let cl' = { cl' with templval = mk_freelisted prf ; templtyp = mk_freelisted prfty } in
+ {cl=cl'; prf=(mkRel 1); car=car; rel=rel; l2r=l2r; c1=c1; c2=c2; c=None; abs=Some (prf, prfty)}
+
+let get_hyp gl c clause l2r =
+ let hi = decompose_setoid_eqhyp (pf_env gl) (project gl) c l2r in
+ let but = match clause with Some id -> pf_get_hyp_typ gl id | None -> pf_concl gl in
+ unification_rewrite hi.l2r hi.c1 hi.c2 hi.cl hi.car hi.rel but gl
+
+let general_rewrite_flags = { under_lambdas = false; on_morphisms = false }
+
+let general_s_rewrite l2r occs c ~new_goals gl =
+ let meta = Evarutil.new_meta() in
+ let hypinfo = ref (get_hyp gl c None l2r) in
+ cl_rewrite_clause_aux ~flags:general_rewrite_flags hypinfo meta occs None gl
+
+let general_s_rewrite_in id l2r occs c ~new_goals gl =
+ let meta = Evarutil.new_meta() in
+ let hypinfo = ref (get_hyp gl c (Some id) l2r) in
+ cl_rewrite_clause_aux ~flags:general_rewrite_flags hypinfo meta occs (Some (([],id), [])) gl
+
+let general_s_rewrite_clause x =
+ init_setoid ();
+ match x with
+ | None -> general_s_rewrite
+ | Some id -> general_s_rewrite_in id
+
+let _ = Equality.register_general_setoid_rewrite_clause general_s_rewrite_clause
+
+(* [setoid_]{reflexivity,symmetry,transitivity} tactics *)
+
+let relation_of_constr c =
+ match kind_of_term c with
+ | App (f, args) when Array.length args >= 2 ->
+ let relargs, args = array_chop (Array.length args - 2) args in
+ mkApp (f, relargs), args
+ | _ -> error "Not an applied relation"
+
+let is_loaded d =
+ let d' = List.map id_of_string d in
+ let dir = make_dirpath (List.rev d') in
+ Library.library_is_loaded dir
+
+let try_loaded f gl =
+ if is_loaded ["Coq";"Classes";"RelationClasses"] then f gl
+ else tclFAIL 0 (str"You need to require Coq.Classes.RelationClasses first") gl
+
+let setoid_reflexivity gl =
+ let env = pf_env gl in
+ let rel, args = relation_of_constr (pf_concl gl) in
+ try
+ apply (reflexive_proof env (pf_type_of gl args.(0)) rel) gl
+ with Not_found ->
+ tclFAIL 0 (str" The relation " ++ Printer.pr_constr_env env rel ++ str" is not a declared reflexive relation")
+ gl
+
+let setoid_symmetry gl =
+ let env = pf_env gl in
+ let rel, args = relation_of_constr (pf_concl gl) in
+ try
+ apply (symmetric_proof env (pf_type_of gl args.(0)) rel) gl
+ with Not_found ->
+ tclFAIL 0 (str" The relation " ++ Printer.pr_constr_env env rel ++ str" is not a declared symmetric relation")
+ gl
+
+let setoid_transitivity c gl =
+ let env = pf_env gl in
+ let rel, args = relation_of_constr (pf_concl gl) in
+ try
+ apply_with_bindings
+ ((transitive_proof env (pf_type_of gl args.(0)) rel),
+ Rawterm.ExplicitBindings [ dummy_loc, Rawterm.NamedHyp (id_of_string "y"), c ]) gl
+ with Not_found ->
+ tclFAIL 0
+ (str" The relation " ++ Printer.pr_constr_env env rel ++ str" is not a declared transitive relation") gl
+
+let setoid_symmetry_in id gl =
+ let ctype = pf_type_of gl (mkVar id) in
+ let binders,concl = Sign.decompose_prod_assum ctype in
+ let (equiv, args) = decompose_app concl 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
+ let he,c1,c2 = mkApp (equiv, Array.of_list others),c1,c2 in
+ let new_hyp' = mkApp (he, [| c2 ; c1 |]) in
+ let new_hyp = it_mkProd_or_LetIn new_hyp' binders in
+ tclTHENS (cut new_hyp)
+ [ intro_replacing id;
+ tclTHENLIST [ intros; setoid_symmetry; apply (mkVar id); Tactics.assumption ] ]
+ gl
+
+let _ = Tactics.register_setoid_reflexivity setoid_reflexivity
+let _ = Tactics.register_setoid_symmetry setoid_symmetry
+let _ = Tactics.register_setoid_symmetry_in setoid_symmetry_in
+let _ = Tactics.register_setoid_transitivity setoid_transitivity
+
+TACTIC EXTEND setoid_symmetry
+ [ "setoid_symmetry" ] -> [ setoid_symmetry ]
+ | [ "setoid_symmetry" "in" hyp(n) ] -> [ setoid_symmetry_in n ]
+END
+
+TACTIC EXTEND setoid_reflexivity
+ [ "setoid_reflexivity" ] -> [ setoid_reflexivity ]
+END
+
+TACTIC EXTEND setoid_transitivity
+ [ "setoid_transitivity" constr(t) ] -> [ setoid_transitivity t ]
+END
+
+let try_classes t gls =
+ try t gls
+ with (Pretype_errors.PretypeError _) as e -> raise e
+
+TACTIC EXTEND try_classes
+ [ "try_classes" tactic(t) ] -> [ try_classes (snd t) ]
+END
+
+open Rawterm
+
+let constrexpr = Pcoq.Tactic.open_constr
+type 'a constr_expr_argtype = (open_constr_expr, 'a) Genarg.abstract_argument_type
+
+let (wit_constrexpr : Genarg.tlevel constr_expr_argtype),
+ (globwit_constrexpr : Genarg.glevel constr_expr_argtype),
+ (rawwit_const_expr : Genarg.rlevel constr_expr_argtype) =
+ Genarg.create_arg "constrexpr"
+
+open Environ
+open Refiner
+
+TACTIC EXTEND apply_typeclasses
+ [ "typeclass_app" raw(t) ] -> [ fun gl ->
+ let nprod = nb_prod (pf_concl gl) in
+ let env = pf_env gl in
+ let evars = ref (create_evar_defs (project gl)) in
+ let j = Pretyping.Default.understand_judgment_tcc evars env t in
+ let n = nb_prod j.uj_type - nprod in
+ if n<0 then error "Apply_tc: theorem has not enough premisses.";
+ Refiner.tclTHEN (Refiner.tclEVARS (Evd.evars_of !evars))
+ (fun gl ->
+ let clause = make_clenv_binding_apply gl (Some n) (j.uj_val,j.uj_type) NoBindings in
+ let cl' = evar_clenv_unique_resolver true ~flags:default_unify_flags clause gl in
+ let evd' = Typeclasses.resolve_typeclasses cl'.env ~fail:true cl'.evd in
+ Clenvtac.clenv_refine true {cl' with evd = evd'} gl) gl
+ ]
+END