aboutsummaryrefslogtreecommitdiff
path: root/tactics/class_setoid.ml4
blob: b05b7ec8c28267d5875a1996afe9618801038fdf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
(************************************************************************)
(*  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: eauto.ml4 10346 2007-12-05 21:11:19Z aspiwack $ *)

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

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)

let morphism_class = lazy (class_info (id_of_string "Morphism"))
let morphism2_class = lazy (class_info (id_of_string "BinaryMorphism"))
let morphism3_class = lazy (class_info (id_of_string "TernaryMorphism"))

let get_respect cl = 
  Option.get (List.hd (Recordops.lookup_projections cl.cl_impl))

let respect_proj = lazy (get_respect (Lazy.force morphism_class))
let respect2_proj = lazy (get_respect (Lazy.force morphism2_class))
let respect3_proj = lazy (get_respect (Lazy.force morphism3_class))

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 iff_setoid = lazy (gen_constant ["Classes"; "SetoidClass"] "iff_setoid")
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"] "equiv_refl")
  
let arrow_morphism a b = 
  mkLambda (Name (id_of_string "A"), a, 
	   mkLambda (Name (id_of_string "B"), b, 
		    mkProd (Anonymous, mkRel 2, mkRel 2)))
    
let setoid_refl pars x = 
  applistc (Lazy.force setoid_refl_proj) (pars @ [x])
      
let class_one = lazy (Lazy.force morphism_class, Lazy.force respect_proj)
let class_two = lazy (Lazy.force morphism2_class, Lazy.force respect2_proj)
let class_three = lazy (Lazy.force morphism3_class, Lazy.force respect3_proj)

exception Found of (constr * constant * constr list * constr * constr array *
		       (constr * (constr * constr * constr * constr * constr)) option array)

let resolve_morphism_evd env evd app = 
  let ev = Evarutil.e_new_evar evd env app in
  let evd' = resolve_typeclasses ~check:false env (Evd.evars_of !evd) !evd in
  let evm' = Evd.evars_of evd' in
    match Evd.evar_body (Evd.find evm' (fst (destEvar ev))) with
	Evd.Evar_empty -> raise Not_found
      | Evd.Evar_defined c -> evd := Evarutil.nf_evar_defs evd'; c

let is_equiv env sigma t = 
  isConst t && Reductionops.is_conv env sigma (Lazy.force setoid_equiv) t

let resolve_morphism env sigma direction oldt m args args' = 
  let evars = ref (Evd.create_evar_defs Evd.empty) in
  let morph_instance, proj, subst, m', args, args' = 
    if is_equiv env sigma m then 
      let params, rest = array_chop 3 args in
      let a, r, s = params.(0), params.(1), params.(2) in
      let params', rest' = array_chop 3 args' in
      let inst = mkApp (Lazy.force setoid_morphism, params) in
	(* Equiv gives a binary morphism *)
      let (cl, proj) = Lazy.force class_two in
      let ctxargs = [ a; r; s; a; r; s; mkProp; Lazy.force iff; Lazy.force iff_setoid; ] in
      let m' = mkApp (m, [| a; r; s |]) in
	inst, proj, ctxargs, m', rest, rest'
    else
      let cls =
	match Array.length args with
	    1 -> [Lazy.force class_one, 1]
	  | 2 -> [Lazy.force class_two, 2; Lazy.force class_one, 1]
	  | n -> [Lazy.force class_three, 3; Lazy.force class_two, 2; Lazy.force class_one, 1]
      in
	try 
	  List.iter (fun ((cl, proj), n) ->
	    evars := Evd.create_evar_defs Evd.empty;
	    let cl_param, cl_context = match cl.cl_context with hd :: tl -> hd, tl | [] -> assert false in
	    let ctxevs = substitution_of_named_context evars env cl.cl_name 0 [] (List.map snd cl_context) in
	    let morphargs, morphobjs = array_chop (Array.length args - n) args in
	    let morphargs', morphobjs' = array_chop (Array.length args - n) args' in
	    let args = List.rev_map (fun (_, c) -> c) ctxevs in
	    let appm = mkApp(m, morphargs) in
	    let appmtype = Typing.type_of env sigma appm in
	    let app = applistc (mkInd cl.cl_impl) (args @ [appm]) in
	    let mtype = replace_vars ctxevs (pi3 (snd cl_param)) in
	      try 
		evars := Evarconv.the_conv_x env appmtype mtype !evars;
		evars := Evarutil.nf_evar_defs !evars;
		let app = Evarutil.nf_isevar !evars app in
		  raise (Found (resolve_morphism_evd env evars app, proj, args, appm, morphobjs, morphobjs'))
	      with Not_found -> ()
		| Reduction.NotConvertible -> ()
		| Stdpp.Exc_located (_, Pretype_errors.PretypeError _) 
		| Pretype_errors.PretypeError _ -> ())
	    cls; 
	  raise Not_found
	with Found x -> x
  in 
  evars := Evarutil.nf_evar_defs !evars;
  let evm = Evd.evars_of !evars in
  let ctxargs = List.map (Reductionops.nf_evar evm) subst in
(*   let ctx, sup = Util.list_chop len ctxargs in *)
  let m' = Reductionops.nf_evar evm m' in
  let appproj = applistc (mkConst proj) (ctxargs @ [m' ; morph_instance]) in
  let projargs, respars, typeargs = 
    array_fold_left2 
      (fun (acc, ctxargs, typeargs') x y -> 
	let par, ctx = list_chop 3 ctxargs in
	  match y with
	      None ->
		let refl_proof = setoid_refl par x in
		  [ refl_proof ; x ; x ] @ acc, ctx, x :: typeargs'
	    | Some (p, (_, _, _, _, t')) ->
		if direction then
		  [ p ; t'; x ] @ acc, ctx, t' :: typeargs'
		else [ p ; x; t' ] @ acc, ctx, t' :: typeargs')
      ([], ctxargs, []) args args'
  in
  let proof = applistc appproj (List.rev projargs) in
  let newt = applistc m' (List.rev typeargs) in
    match respars with
	[ a ; r ; s ] -> (proof, (a, r, s, oldt, newt))
      | _ -> assert(false)
      
let build_new gl env setoid direction origt newt hyp hypinfo concl =
  let rec aux t = 
    match kind_of_term t with
      | _ when eq_constr t origt -> 
	  Some (hyp, hypinfo)
      | App (m, args) ->
	  let args' = Array.map aux args in
	    if array_for_all (fun x -> x = None) args' then None
	    else 
	      (try Some (resolve_morphism env (project gl) direction t m args args')
		with Not_found -> None)
      | Prod (_, x, b) -> 
	  let x', b' = aux x, aux b in
	    if x' = None && b' = None then None
	    else 
	      (try Some (resolve_morphism env (project gl) direction t (arrow_morphism (pf_type_of gl x) (pf_type_of gl b)) [| x ; b |] [| x' ; b' |])
		with Not_found -> None)
		
      | _ -> None
  in aux concl

let decompose_setoid_eqhyp env sigma c dir t = 
  match kind_of_term t with
    | App (equiv, [| a; r; s; x; y |]) ->
	if dir then (c, (a, r, s, x, y))
	else (c, (a, r, s, y, x))
    | App (r, args) when Array.length args >= 2 -> 
	(try 
	    let (p, (a, r, s, _, _)) = resolve_morphism env sigma dir t r args (Array.map (fun _ -> None) args) in
	    let _, args = array_chop (Array.length args - 2) args in
	      if dir then (c, (a, r, s, args.(0), args.(1)))
	      else (c, (a, r, s, args.(1), args.(0)))
	  with Not_found -> error "Not a (declared) setoid equality")
    | _ -> error "Not a setoid equality"

let cl_rewrite c left2right gl =
  let env = pf_env gl in
  let sigma = project gl in
  let hyp = pf_type_of gl c in
  let hypt, (typ, rel, setoid, origt, newt as hypinfo) = decompose_setoid_eqhyp env sigma c left2right hyp in
  let concl = pf_concl gl in
  let _concltyp = pf_type_of gl concl in
  let eq = build_new gl env setoid left2right origt newt hypt hypinfo concl in
    match eq with  
	Some (p, (_, _, _, _, t)) -> 
	  let proj = 
	    if left2right then 
	      applistc (Lazy.force coq_proj2)
		[ mkProd (Anonymous, concl, t) ; mkProd (Anonymous, t, concl) ; p ] 
	    else 
	      applistc (Lazy.force coq_proj1)
		[ mkProd (Anonymous, t, concl) ; mkProd (Anonymous, concl, t) ; p ] 
	  in
	    (Tactics.apply proj) gl
      | None -> tclIDTAC gl
	  
open Extraargs

TACTIC EXTEND class_rewrite
| [ "clrewrite" orient(o) constr(c) ] -> [ cl_rewrite c o ]
END