Source file why3_tactic.ml
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
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
(** Implementation of the why3 tactic. *)
open Lplib
open Timed
open Common open Error
open Core open Term open Print
open Fol
open Proof
(** Logging function for external prover calling with Why3. *)
let log = Logger.make 'y' "why3" "why3 provers"
let log = log.pp
(** [default_prover] contains the name of the current prover. Note that it can
be changed by using the "set prover <string>" command. *)
let default_prover : string ref = ref "Alt-Ergo"
(** [timeout] is the current time limit (in seconds) for a Why3 prover to find
a proof. It can be changed with "set prover <int>". *)
let timeout : int ref = ref 2
(** [why3_config] is the Why3 configuration read from the configuration file
(["~/.why3.conf"] usually). For more information, visit the Why3
documentation at http://why3.lri.fr/api/Whyconf.html. *)
let why3_config : Why3.Whyconf.config =
let cfg = Why3.Whyconf.init_config None in
let provers = Why3.Whyconf.get_provers cfg in
Console.out 2 "provers available for why3:";
let prover p _ = Console.out 2 "%a" Why3.Whyconf.print_prover p in
Why3.Whyconf.Mprover.iter prover provers;
cfg
(** [why3_main] is the main section of the Why3 configuration. *)
let why3_main : Why3.Whyconf.main = Why3.Whyconf.get_main why3_config
(** [why3_env] is the initialized Why3 environment. *)
let why3_env : Why3.Env.env =
Why3.Env.create_env (Why3.Whyconf.loadpath why3_main)
(** Data type used to record how Lambdapi symbols, variables and terms are
mapped to Why3 symbols or variables. *)
type l2y =
{ s2ts : (sym * Why3.Ty.tysymbol) list
; t2ts : (term * Why3.Ty.tysymbol) list
; v2tv : (tvar * Why3.Ty.tvsymbol) list
; s2ls : (sym * (Why3.Term.lsymbol * bool)) list
; v2ls : (tvar * (Why3.Term.lsymbol * bool)) list
; t2ls : (term * Why3.Term.lsymbol) list
; v2vs : (tvar * Why3.Term.vsymbol) list
}
let empty_l2y =
{s2ts=[]; v2tv=[]; t2ts=[]; s2ls=[]; v2ls=[]; v2vs=[]; t2ls=[]}
let l2y ppf m =
let open Debug.D in
Base.out ppf
"{s2ts=%a; v2tv=%a; t2ts=%a; s2ls=%a; v2ls=%a; v2vs=%a; t2ls=%a}"
(list (pair sym Why3.Pretty.print_ts)) m.s2ts
(list (pair var Why3.Pretty.print_tv)) m.v2tv
(list (pair term Why3.Pretty.print_ts)) m.t2ts
(list (pair sym (pair Why3.Pretty.print_ls bool))) m.s2ls
(list (pair var (pair Why3.Pretty.print_ls bool))) m.v2ls
(list (pair var Why3.Pretty.print_vs)) m.v2vs
(list (pair term Why3.Pretty.print_ls)) m.t2ls
(** Translation functions below raise Exit if a term cannot be
translated. They return an update l2y map as well because mappings are
done while translating terms. *)
(** [translate_set m t] tries to translate a Lambdapi term [t:Set] to a Why3
type. *)
let rec translate_set m t =
if Logger.log_enabled() then log "translate_set %a [%a]" l2y m term t;
match get_args t with
| Symb s, ts ->
let m, ts = translate_sets m ts in
let m, s =
match List.assoc_opt s m.s2ts with
| Some s' -> m, s'
| None ->
let id = Why3.Ident.id_fresh s.sym_name in
let s' = Why3.Ty.create_tysymbol id [] Why3.Ty.NoDef in
if Logger.log_enabled() then
log "add tysymbol %a" Why3.Pretty.print_ts s';
{m with s2ts=(s,s')::m.s2ts}, s'
in
m, Why3.Ty.ty_app s ts
| Vari x, [] ->
begin
match List.assoc_eq_opt Bindlib.eq_vars x m.v2tv with
| Some tv -> m, Why3.Ty.ty_var tv
| None -> raise Exit
end
| _ -> raise Exit
and translate_sets m ts =
List.fold_right
(fun t (m,ts) -> let m,t = translate_set m t in m, t::ts)
ts (m,[])
(** [translate_type m t] tries to translate a Lambdapi term [t:TYPE] of the
form [P _] to a Why3 type. *)
let translate_type cfg m t =
if Logger.log_enabled() then log "translate_type %a [%a]" l2y m term t;
match get_args t with
| Symb s, [t] when s == cfg.symb_T -> translate_set m t
| _ -> raise Exit
(** [translate_pred_type cfg m t] tries to translate a Lambdapi term [t] of
the form [T a1 → .. → T an → Prop] to a Why3 type. *)
let translate_pred_type cfg =
let rec aux acc m t =
if Logger.log_enabled() then
log "translate_pred_type %a [%a]" l2y m term t;
match unfold t with
| Symb s -> if s == cfg.symb_Prop then m, List.rev acc else raise Exit
| Prod(a,b) ->
let m,a = translate_type cfg m a in
let _,b = Bindlib.unbind b in
aux (a::acc) m b
| _ -> raise Exit
in aux []
(** [translate_fun_type cfg m t] tries to translate a Lambdapi term [t] of the
form [T a1 → .. → T an → T a] to a Why3 type. *)
let translate_fun_type cfg =
let rec aux acc m t =
if Logger.log_enabled() then
log "translate_fun_type %a [%a]" l2y m term t;
match unfold t with
| Prod(a,b) ->
let m,a = translate_type cfg m a in
let _,b = Bindlib.unbind b in
aux (a::acc) m b
| _ ->
let m,a = translate_type cfg m t in
m, List.rev acc, a
in aux []
(** [translate_term cfg m t] tries to translate a Lambdapi term [t:T _] to a
Why3 term. *)
let rec translate_term cfg m t =
if Logger.log_enabled() then log "translate_term %a [%a]" l2y m term t;
match get_args t with
| Symb s, ts ->
begin
match List.assoc_eq_opt (==) s m.s2ls with
| Some(_, true) -> assert false
| Some(s, false) ->
let m, ts = translate_terms cfg m ts in
m, Why3.Term.t_app_infer s ts
| None ->
let m, tys, a = translate_fun_type cfg m !(s.sym_type) in
let id = Why3.Ident.id_fresh s.sym_name in
let f = Why3.Term.create_fsymbol id tys a in
if Logger.log_enabled() then
log "add psymbol %a : %a → %a" Why3.Pretty.print_ls f
(Debug.D.list Why3.Pretty.print_ty) tys
Why3.Pretty.print_ty a;
let m = {m with s2ls = (s,(f,false))::m.s2ls} in
let m, ts = translate_terms cfg m ts in
m, Why3.Term.t_app_infer f ts
end
| Vari x, ts ->
begin
match List.assoc_eq_opt Bindlib.eq_vars x m.v2vs with
| Some v ->
if ts = [] then m, Why3.Term.t_var v else raise Exit
| None ->
match List.assoc_eq_opt Bindlib.eq_vars x m.v2ls with
| Some(_, true) -> assert false
| Some(s, false) ->
let m, ts = translate_terms cfg m ts in
m, Why3.Term.t_app_infer s ts
| None -> assert false
end
| _ -> raise Exit
and translate_terms cfg m ts =
List.fold_right
(fun t (m,ts) -> let m,t = translate_term cfg m t in m, t::ts)
ts (m,[])
(** [translate_prop cfg m t] tries to translation a Lambdapi term [t:Prop] to
a Why3 proposition. *)
let rec translate_prop : config -> l2y -> term -> l2y * Why3.Term.term =
let default m t =
try
let sym = List.assoc_eq (Eval.eq_modulo []) t m.t2ls in
m, Why3.Term.ps_app sym []
with Not_found ->
let sym = Why3.Term.create_psymbol (Why3.Ident.id_fresh "X") [] in
if Logger.log_enabled() then
log "abstract [%a] as psymbol %a" term t Why3.Pretty.print_ls sym;
{m with t2ls = (t,sym)::m.t2ls}, Why3.Term.ps_app sym []
in
fun cfg m t ->
if Logger.log_enabled() then log "translate_prop %a [%a]" l2y m term t;
match get_args t with
| Symb s, [t1;t2] when s == cfg.symb_and ->
let m, t1 = translate_prop cfg m t1 in
let m, t2 = translate_prop cfg m t2 in
m, Why3.Term.t_and t1 t2
| Symb s, [t1;t2] when s == cfg.symb_or ->
let m, t1 = translate_prop cfg m t1 in
let m, t2 = translate_prop cfg m t2 in
m, Why3.Term.t_or t1 t2
| Symb s, [t1;t2] when s == cfg.symb_imp ->
let m, t1 = translate_prop cfg m t1 in
let m, t2 = translate_prop cfg m t2 in
m, Why3.Term.t_implies t1 t2
| Symb s, [t1;t2] when s == cfg.symb_eqv ->
let m, t1 = translate_prop cfg m t1 in
let m, t2 = translate_prop cfg m t2 in
m, Why3.Term.t_iff t1 t2
| Symb s, [t] when s == cfg.symb_not ->
let m, t = translate_prop cfg m t in
m, Why3.Term.t_not t
| Symb s, [] when s == cfg.symb_bot ->
m, Why3.Term.t_false
| Symb s, [] when s == cfg.symb_top ->
m, Why3.Term.t_true
| Symb s, [a;Abst(_,t)] when s == cfg.symb_ex ->
let m,a = translate_set m a
and x,t = Bindlib.unbind t in
let id = Why3.Ident.id_fresh (Bindlib.name_of x) in
let v = Why3.Term.create_vsymbol id a in
if Logger.log_enabled() then
log "add vsymbol %a" Why3.Pretty.print_vs v;
let m = {m with v2vs = (x,v)::m.v2vs} in
let m,t = translate_prop cfg m t in
m, Why3.Term.t_exists_close [v] [] t
| Symb s, [a;Abst(_,t)] when s == cfg.symb_all ->
let m,a = translate_set m a
and x,t = Bindlib.unbind t in
let id = Why3.Ident.id_fresh (Bindlib.name_of x) in
let v = Why3.Term.create_vsymbol id a in
if Logger.log_enabled() then
log "add vsymbol %a" Why3.Pretty.print_vs v;
let m = {m with v2vs = (x,v)::m.v2vs} in
let m,t = translate_prop cfg m t in
m, Why3.Term.t_forall_close [v] [] t
| Vari x, ts ->
begin
match List.assoc_eq_opt Bindlib.eq_vars x m.v2ls with
| Some(_, false) -> assert false
| Some(s, true) ->
begin
try
let m, ts = translate_terms cfg m ts in
m, Why3.Term.ps_app s ts
with Exit -> default m t
end
| None -> default m t
end
| Symb s, ts ->
begin
match List.assoc_eq_opt (==) s m.s2ls with
| Some(_, false) -> assert false
| Some(s, true) ->
begin
try
let m, ts = translate_terms cfg m ts in
m, Why3.Term.ps_app s ts
with Exit -> default m t
end
| None ->
begin
try
let m, tys = translate_pred_type cfg m !(s.sym_type) in
let id = Why3.Ident.id_fresh s.sym_name in
let f = Why3.Term.create_psymbol id tys in
if Logger.log_enabled() then
log "add psymbol %a : %a" Why3.Pretty.print_ls f
(Debug.D.list Why3.Pretty.print_ty) tys;
let m = {m with s2ls = (s,(f,true))::m.s2ls} in
let m, ts = translate_terms cfg m ts in
m, Why3.Term.ps_app f ts
with Exit -> default m t
end
end
| _ -> default m t
(** [translate_goal ss pos env g] translates the hypotheses [env] and the goal
[g] into a Why3 task. *)
let translate_goal :
Sig_state.t -> Pos.popt -> Env.env -> term -> Why3.Task.task =
fun ss pos env g ->
let cfg = get_config ss pos in
let translate_env_elt (name,(x,a,_)) (m,hyps) =
let a = Bindlib.unbox a in
match get_args a with
| Symb s, [] when s == cfg.symb_Set ->
let id = Why3.Ident.id_fresh name in
let tv = Why3.Ty.create_tvsymbol id in
if Logger.log_enabled() then
log "add tvsymbol %a" Why3.Pretty.print_tv tv;
{m with v2tv = (x,tv)::m.v2tv}, hyps
| Symb s, [t] when s == cfg.symb_T ->
let m,t = translate_set m t in
let id = Why3.Ident.id_fresh name in
let f = Why3.Term.create_fsymbol id [] t in
if Logger.log_enabled() then
log "add fsymbol %a" Why3.Pretty.print_ls f;
{m with v2ls = (x,(f,false))::m.v2ls}, hyps
| Symb s, [t] when s == cfg.symb_P ->
let m,t = translate_prop cfg m t in
m, (name,t)::hyps
| _ ->
let m, tys = translate_pred_type cfg m a in
let id = Why3.Ident.id_fresh name in
let f = Why3.Term.create_psymbol id tys in
if Logger.log_enabled() then
log "add psymbol %a" Why3.Pretty.print_ls f;
{m with v2ls = (x,(f,true))::m.v2ls}, hyps
in
let translate_env_elt b m = try translate_env_elt b m with Exit -> m in
let m, hyps = List.fold_right translate_env_elt env (empty_l2y, []) in
if Logger.log_enabled() then log "hyps: %a"
Debug.D.(list (pair string Why3.Pretty.print_term)) hyps;
let m, g =
match get_args g with
| Symb s, [t] when s == cfg.symb_P ->
begin
try translate_prop cfg m t
with Exit -> fatal pos "The goal cannot be translated to Why3."
end
| _ -> fatal pos "The goal is not of the form [%a _]." sym cfg.symb_P
in
if Logger.log_enabled() then log "goal: %a" Why3.Pretty.print_term g;
if Logger.log_enabled() then log "map: %a" l2y m;
let tsk = None in
let add_s2ts_decl tsk (_,tsym) = Why3.Task.add_ty_decl tsk tsym in
let tsk = List.fold_left add_s2ts_decl tsk m.s2ts in
let add_t2ts_decl tsk (_,tsym) = Why3.Task.add_ty_decl tsk tsym in
let tsk = List.fold_left add_t2ts_decl tsk m.t2ts in
if Logger.log_enabled() then log "%a" Why3.Pretty.print_task tsk;
let add_s2ls_decl tsk (_,(lsym,_)) = Why3.Task.add_param_decl tsk lsym in
let tsk = List.fold_left add_s2ls_decl tsk m.s2ls in
let add_v2ls_decl tsk (_,(lsym,_)) = Why3.Task.add_param_decl tsk lsym in
let tsk = List.fold_left add_v2ls_decl tsk m.v2ls in
let add_t2ls_decl tsk (_,lsym) = Why3.Task.add_param_decl tsk lsym in
let tsk = List.fold_left add_t2ls_decl tsk m.t2ls in
let add_hyp_decl tsk (name,prop) =
let axiom = Why3.Decl.create_prsymbol (Why3.Ident.id_fresh name) in
Why3.Task.add_prop_decl tsk Why3.Decl.Paxiom axiom prop
in
let tsk = List.fold_left add_hyp_decl tsk hyps in
let goal = Why3.Decl.create_prsymbol (Why3.Ident.id_fresh "G") in
let tsk = Why3.Task.add_prop_decl tsk Why3.Decl.Pgoal goal g in
if Logger.log_enabled() then log "%a" Why3.Pretty.print_task tsk;
tsk
(** [run tsk pos prover_name] sends [tsk] to [prover_name]. *)
let run : Why3.Task.task -> Pos.popt -> string -> bool =
fun tsk pos prover_name ->
let filter = Why3.Whyconf.parse_filter_prover prover_name in
let provers = Why3.Whyconf.filter_provers why3_config filter in
if Why3.Whyconf.Mprover.is_empty provers then
begin
fatal_msg "Your prover might not be installed or detected.\n";
fatal_msg "Remember to run \"why3 config detect\" \
after installing a prover.";
fatal_msg "Why3 configuration read from \"%s\".\n"
(Why3.Whyconf.get_conf_file why3_config);
let provers = Why3.Whyconf.get_provers why3_config in
if Why3.Whyconf.Mprover.is_empty provers then
fatal_msg "There are no available provers.@."
else
begin
fatal_msg "The available provers are:@.";
let f p _ = fatal_msg " - %a@." Why3.Whyconf.print_prover p in
Why3.Whyconf.Mprover.iter f provers
end;
fatal pos "prover \"%s\" not found.@." prover_name;
end;
let prover = snd (Why3.Whyconf.Mprover.max_binding provers) in
let driver =
try Why3.Driver.load_driver_for_prover why3_main why3_env prover
with e -> fatal pos "Failed to load driver for \"%s\": %a"
prover.prover.prover_name Why3.Exn_printer.exn_printer e
in
let command = prover.Why3.Whyconf.command
and limits = {Why3.Call_provers.empty_limits
with limit_time = float_of_int !timeout} in
let call =
Why3.Driver.prove_task ~command ~config:why3_main ~limits driver tsk in
Why3.Call_provers.((wait_on_call call).pr_answer = Valid)
(** [handle ss pos prover_name gt] runs the Why3 prover corresponding to
[prover_name] (if given or a default one otherwise) on the goal type [gt],
and fails if no proof is found. *)
let handle :
Sig_state.t -> Pos.popt -> string option -> goal_typ -> unit =
fun ss pos prover_name
({goal_meta = _; goal_hyps = hyps; goal_type = t} as gt) ->
let g = Typ gt in
if Logger.log_enabled() then log "%a%a" Goal.hyps g Goal.pp g;
let tsk = translate_goal ss pos hyps t in
let prover_name = Option.get !default_prover prover_name in
if Logger.log_enabled() then log "running with prover \"%s\"" prover_name;
if not (run tsk pos prover_name) then
fatal pos "\"%s\" did not find a proof" prover_name