aboutsummaryrefslogtreecommitdiffstats
path: root/src/lfsc/lfsc.ml
blob: 906340d77a97b373435b320351922bce146df2c7 (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
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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
(**************************************************************************)
(*                                                                        *)
(*     SMTCoq                                                             *)
(*     Copyright (C) 2011 - 2022                                          *)
(*                                                                        *)
(*     See file "AUTHORS" for the list of authors                         *)
(*                                                                        *)
(*   This file is distributed under the terms of the CeCILL-C licence     *)
(*                                                                        *)
(**************************************************************************)


open Format

open SmtMisc
open CoqTerms
open SmtCertif
open SmtTrace
open SmtAtom


(******************************************************************************)
(* Given a lfsc trace build the corresponding certif and theorem             *)
(******************************************************************************)

(* Instantiate Converter with translator for SMTCoq *)
module C = Converter.Make (Tosmtcoq)

exception No_proof

(* Hard coded signatures *)
let signatures =
  let sigdir = try Sys.getenv "LFSCSIGS" with Not_found -> Sys.getcwd () in
  ["sat.plf";
   "smt.plf";
   "th_base.plf";
   "th_int.plf";
   "th_bv.plf";
   "th_bv_bitblast.plf";
   "th_bv_rewrites.plf";
   "th_arrays.plf" ]
  |> List.map (Filename.concat sigdir)


let process_signatures_once =
  let don = ref false in
  fun () ->
    if !don then ()
    else
      try
        (* don := true; *)
        List.iter (fun f ->
            let chan = open_in f in
            let lexbuf = Lexing.from_channel chan in
            LfscParser.ignore_commands LfscLexer.main lexbuf;
            close_in chan
          ) signatures
      with
      | Ast.TypingError (t1, t2) ->
        Structures.error
          (asprintf "@[<hov>LFSC typing error: expected %a, got %a@]@."
             Ast.print_term t1
             Ast.print_term t2)


let lfsc_parse_last lb =
  printf "Type-checking LFSC proof...@?";
  let t0 = Sys.time () in
  let r = LfscParser.last_command LfscLexer.main lb in
  let t1 = Sys.time () in
  printf " Done [%.3f s]@." (t1 -. t0);
  r

let lfsc_parse_one lb =
  printf "Type-checking LFSC proof...@?";
  let t0 = Sys.time () in
  let r = LfscParser.one_command LfscLexer.main lb in
  let t1 = Sys.time () in
  printf " Done [%.3f s]@." (t1 -. t0);
  r
  

let import_trace first parse lexbuf =
  Printexc.record_backtrace true;
  process_signatures_once ();
  try
    match parse lexbuf with

    | Some (Ast.Check p) ->
      (* Ast.flatten_term p; *)
      let confl_num = C.convert_pt p in
      (* Afterwards, the SMTCoq libraries will produce the remaining, you do
         not have to care *)
      let first =
        let aux = VeritSyntax.get_clause 1 in
        match first, aux.value with
        | Some (root,l), Some (fl::nil) ->
          (* Format.eprintf "Root: %a ,,,,,,\n\ *)
          (*                 input: %a@." *)
          (*   (Form.to_smt Atom.to_smt) l (Form.to_smt Atom.to_smt) fl; *)
          if Form.equal l fl then
            aux
          else (
            (* eprintf "ADDING Flatten rule@."; *)
            aux.kind <- Other (ImmFlatten(root,fl));
            SmtTrace.link root aux;
            root
          )
        | _,_ -> aux in
      let confl = VeritSyntax.get_clause confl_num in
      SmtTrace.select confl;
      occur confl;
      (alloc first, confl)

    | _ -> raise No_proof

  with
  | Ast.TypingError (t1, t2) ->
    Structures.error
      (asprintf "@[<hov>LFSC typing error: expected %a, got %a@]@."
         Ast.print_term t1
         Ast.print_term t2)



let import_trace_from_file first filename =
  let chan = open_in filename in
  let lexbuf = Lexing.from_channel chan in
  let p = import_trace first lfsc_parse_last lexbuf in
  close_in chan;
  p



let clear_all () =
  SmtTrace.clear ();
  SmtMaps.clear ();
  VeritSyntax.clear ();
  Tosmtcoq.clear ();
  C.clear ()


let import_all fsmt fproof =
  clear_all ();
  let rt = SmtBtype.create () in
  let ro = Op.create () in
  let ra = Tosmtcoq.ra in
  let rf = Tosmtcoq.rf in
  let roots = Smtlib2_genConstr.import_smtlib2 rt ro ra rf fsmt in
  let (max_id, confl) = import_trace_from_file None fproof in
  (rt, ro, ra, rf, roots, max_id, confl)


let parse_certif t_i t_func t_atom t_form root used_root trace fsmt fproof =
  SmtCommands.parse_certif t_i t_func t_atom t_form root used_root trace
    (import_all fsmt fproof)

let checker_debug fsmt fproof =
  SmtCommands.checker_debug (import_all fsmt fproof)

let theorem name fsmt fproof =
  SmtCommands.theorem name (import_all fsmt fproof)

(* let checker fsmt fproof =
 *   SmtCommands.checker (import_all fsmt fproof) *)

(* Same but print runtime *)
let checker fsmt fproof =
  let c = import_all fsmt fproof in
  printf "Coq checker...@.";
  let t0 = Sys.time () in
  let r = SmtCommands.checker c in
  let t1 = Sys.time () in
  printf "Done (Coq) [%.3f s]@." (t1 -. t0);
  r



(******************************************************************************)
(** Given a Coq formula build the proof                                       *)
(******************************************************************************)


(* module Form2 = struct *)
(*   (\* Just for printing *\) *)

(*   open Form *)

(*   let rec to_smt atom_to_smt fmt f = *)
(*     if is_pos f then to_smt_pform atom_to_smt fmt (pform f) *)
(*     else fprintf fmt "(not %a)" (to_smt_pform atom_to_smt) (pform f) *)

(*   and to_smt_pform atom_to_smt fmt = function *)
(*     | Fatom a -> atom_to_smt fmt a *)
(*     | Fapp (op,args) -> to_smt_op atom_to_smt op fmt (Array.to_list args) *)
(*     | _ -> assert false *)

(*   and to_smt_op atom_to_smt op fmt args = *)
(*     match op, args with *)
(*       | Ftrue, [] -> fprintf fmt "true" *)
(*       | Ffalse, [] -> fprintf fmt "false" *)
(*       | Fand, [x; y] -> *)
(*         fprintf fmt "(and %a %a)" (to_smt atom_to_smt) x (to_smt atom_to_smt) y *)
(*       | For, [x; y] -> *)
(*         fprintf fmt "(or %a %a)" (to_smt atom_to_smt) x (to_smt atom_to_smt) y *)
(*       | Fand, x :: rargs -> *)
(*         fprintf fmt "(and %a %a)" (to_smt atom_to_smt) x *)
(*           (to_smt_op atom_to_smt Fand) rargs *)
(*       | For, x :: rargs -> *)
(*         fprintf fmt "(or %a %a)" (to_smt atom_to_smt) x *)
(*           (to_smt_op atom_to_smt For) rargs *)
(*       (\* andb and orb are left-associative in Coq *\) *)
(*       (\* | Fand, _ -> left_assoc atom_to_smt Fand fmt (List.rev args) *\) *)
(*       (\* | For, _ -> left_assoc atom_to_smt For fmt (List.rev args) *\) *)
(*       | Fxor, _ -> *)
(*         fprintf fmt "(xor%a)" *)
(*           (fun fmt -> List.iter (fprintf fmt " %a" (to_smt atom_to_smt))) args *)
(*       | Fimp, _ -> *)
(*         fprintf fmt "(=>%a)" *)
(*           (fun fmt -> List.iter (fprintf fmt " %a" (to_smt atom_to_smt))) args *)
(*       | Fiff, _ -> *)
(*         fprintf fmt "(=%a)" *)
(*           (fun fmt -> List.iter (fprintf fmt " %a" (to_smt atom_to_smt))) args *)
(*       | Fite, _ -> *)
(*         fprintf fmt "(ite%a)" *)
(*           (fun fmt -> List.iter (fprintf fmt " %a" (to_smt atom_to_smt))) args *)
(*       | Fnot2 _, _ -> *)
(*         fprintf fmt "(not (not %a))" *)
(*           (fun fmt -> List.iter (fprintf fmt " %a" (to_smt atom_to_smt))) args *)
(*       | _ -> assert false *)

(*   and left_assoc atom_to_smt op fmt args = *)
(*     (\* args is reversed *\) *)
(*     match op, args with *)
(*     | Fand, [x; y] -> *)
(*       fprintf fmt "(and %a %a)" (to_smt atom_to_smt) y (to_smt atom_to_smt) x *)
(*     | For, [x; y] -> *)
(*       fprintf fmt "(or %a %a)" (to_smt atom_to_smt) y (to_smt atom_to_smt) x *)
(*     | Fand, last :: rargs -> *)
(*       fprintf fmt "(and %a %a)" *)
(*         (left_assoc atom_to_smt Fand) rargs (to_smt atom_to_smt) last *)
(*     | For, last :: rargs -> *)
(*       fprintf fmt "(or %a %a)" *)
(*         (left_assoc atom_to_smt For) rargs (to_smt atom_to_smt) last *)
(*     | _ -> assert false *)

(* end *)


(* module Atom2 = struct *)
(*   (\* Just for printing *\) *)

(*   open Atom *)
  
(*   let distrib x l = List.map (fun y -> (x,y)) l *)

(*   let rec cross acc l = match l with *)
(*     | [] | [_] -> List.rev acc *)
(*     | x :: r -> *)
(*       cross (List.rev_append (distrib x r) acc) r *)

(*   let cross = cross [] *)
  
(*   let rec compute_int = function *)
(*     | Acop c -> *)
(*       (match c with *)
(*        | CO_xH -> 1 *)
(*        | CO_Z0 -> 0 *)
(*        | CO_BV _ -> assert false) *)
(*     | Auop (op,h) -> *)
(*       (match op with *)
(*        | UO_xO -> 2*(compute_hint h) *)
(*        | UO_xI -> 2*(compute_hint h) + 1 *)
(*        | UO_Zpos -> compute_hint h *)
(*        | UO_Zneg -> - (compute_hint h) *)
(*        | _ -> assert false) *)
(*     | _ -> assert false *)

(*   and compute_hint h = compute_int (atom h) *)

(*   let to_smt_int fmt i = *)
(*     let s1 = if i < 0 then "(- " else "" in *)
(*     let s2 = if i < 0 then ")" else "" in *)
(*     let j = if i < 0 then -i else i in *)
(*     fprintf fmt "%s%i%s" s1 j s2 *)

(*   let rec to_smt fmt h = to_smt_atom fmt (atom h) *)

(*   and to_smt_atom fmt = function *)
(*     | Acop _ as a -> to_smt_int fmt (compute_int a) *)
(*     | Auop (UO_Zopp,h) -> *)
(*       fprintf fmt "(- "; *)
(*       to_smt fmt h; *)
(*       fprintf fmt ")" *)
(*     | Auop _ as a -> to_smt_int fmt (compute_int a) *)
(*     | Abop (op,h1,h2) -> to_smt_bop fmt op h1 h2 *)
(*     | Atop (op,h1,h2,h3) -> to_smt_bop fmt op h1 h2 h3 *)
(*     | Anop (op,a) -> to_smt_nop fmt op a *)
(*     | Aapp (op,a) -> *)
(*       if Array.length a = 0 then ( *)
(*         fprintf fmt "op_%i" (indexed_op_index op); *)
(*       ) else ( *)
(*         fprintf fmt "(op_%i" (indexed_op_index op); *)
(*         Array.iter (fun h -> fprintf fmt " "; to_smt fmt h) a; *)
(*         fprintf fmt ")" *)
(*       ) *)

(*   and str_op = function *)
(*       | BO_Zplus -> "+" *)
(*       | BO_Zminus -> "-" *)
(*       | BO_Zmult -> "*" *)
(*       | BO_Zlt -> "<" *)
(*       | BO_Zle -> "<=" *)
(*       | BO_Zge -> ">=" *)
(*       | BO_Zgt -> ">" *)
(*       | BO_eq _ -> "=" *)
  
(*   and to_smt_bop fmt op h1 h2 = *)
(*     match op with *)
(*     | BO_Zlt -> fprintf fmt "(not (>= %a %a)" to_smt h1 to_smt h2 *)
(*     | BO_Zle -> fprintf fmt "(not (>= %a (+ %a 1))" to_smt h1 to_smt h2 *)
(*     | BO_Zgt -> fprintf fmt "(>= %a (+ %a 1)" to_smt h1 to_smt h2 *)
(*     | _ -> fprintf fmt "(%s %a %a)" (str_op op) to_smt h1 to_smt h2 *)

(*   and to_smt_nop fmt op a = *)
(*     let rec pp fmt = function *)
(*       | [] -> assert false *)
(*       | [x, y] -> fprintf fmt "(not (= %a %a))" to_smt x to_smt y *)
(*       | (x, y) :: r -> *)
(*         fprintf fmt "(and (not (= %a %a)) %a)" to_smt x to_smt y pp r *)
(*     in *)
(*     let pairs = cross (Array.to_list a) in *)
(*     pp fmt pairs *)

(* end *)

let string_logic ro f =
  let l = SL.union (Op.logic_ro ro) (Form.logic f) in
  if SL.is_empty l then "QF_SAT"
  else
    sprintf "QF_%s%s%s%s"
    (if SL.mem LArrays l then "A" else "")
    (if SL.mem LUF l || SL.mem LLia l then "UF" else "")
    (if SL.mem LBitvectors l then "BV" else "")
    (if SL.mem LLia l then "LIA" else "")



let call_cvc4 env rt ro ra rf root _ =
  let open Smtlib2_solver in
  let fl = snd root in

  let cvc4 = create [|
      "cvc4";
      "--lang"; "smt2";
      "--proof";
      "--no-simplification"; "--fewer-preprocessing-holes";
      "--no-bv-eq"; "--no-bv-ineq"; "--no-bv-algebraic" |] in

  set_option cvc4 "print-success" true;
  set_option cvc4 "produce-assignments" true;
  set_option cvc4 "produce-proofs" true;
  set_logic cvc4 (string_logic ro fl);

  List.iter (fun (i,t) ->
    let s = "Tindex_"^(string_of_int i) in
    SmtMaps.add_btype s (SmtBtype.Tindex t);
    declare_sort cvc4 s 0;
  ) (SmtBtype.to_list rt);
  
  List.iter (fun (i,cod,dom,op) ->
    let s = "op_"^(string_of_int i) in
    SmtMaps.add_fun s op;
    let args =
      Array.fold_right
        (fun t acc -> asprintf "%a" SmtBtype.to_smt t :: acc) cod [] in
    let ret = asprintf "%a" SmtBtype.to_smt dom in
    declare_fun cvc4 s args ret
  ) (Op.to_list ro);

  assume cvc4 (asprintf "%a" (Form.to_smt ~debug:false) fl);

  let proof =
    match check_sat cvc4 with
    | Unsat ->
      begin
        try get_proof cvc4 (import_trace (Some root) lfsc_parse_one)
        with
        | Ast.CVC4Sat -> Structures.error "CVC4 returned SAT"
        | No_proof -> Structures.error "CVC4 did not generate a proof"
        | Failure s -> Structures.error ("Importing of proof failed: " ^ s)
      end
    | Sat ->
      let smodel = get_model cvc4 in
      Structures.error
        ("CVC4 returned sat. Here is the model:\n\n" ^
         SmtCommands.model_string env rt ro ra rf smodel)
        (* (asprintf "CVC4 returned sat. Here is the model:\n%a" SExpr.print smodel) *)
  in

  quit cvc4;
  proof



let export out_channel rt ro l =
  let fmt = formatter_of_out_channel out_channel in
  fprintf fmt "(set-logic %s)@." (string_logic ro l);

  List.iter (fun (i,t) ->
    let s = "Tindex_"^(string_of_int i) in
    SmtMaps.add_btype s (SmtBtype.Tindex t);
    fprintf fmt "(declare-sort %s 0)@." s
  ) (SmtBtype.to_list rt);

  List.iter (fun (i,cod,dom,op) ->
    let s = "op_"^(string_of_int i) in
    SmtMaps.add_fun s op;
    fprintf fmt "(declare-fun %s (" s;
    let is_first = ref true in
    Array.iter (fun t ->
        if !is_first then is_first := false
        else fprintf fmt " "; SmtBtype.to_smt fmt t
      ) cod;
    fprintf fmt ") %a)@." SmtBtype.to_smt dom;
  ) (Op.to_list ro);

  fprintf fmt "(assert %a)@\n(check-sat)@\n(exit)@."
    (Form.to_smt ~debug:false) l



let get_model_from_file filename =
  let chan = open_in filename in
  let lexbuf = Lexing.from_channel chan in
  match SExprParser.sexps SExprLexer.main lexbuf with
  | [SExpr.Atom "sat"; m] -> m
  | _ -> Structures.error "CVC4 returned SAT but no model"


let call_cvc4_file env rt ro ra rf root =
  let fl = snd root in
  let (filename, outchan) = Filename.open_temp_file "cvc4_coq" ".smt2" in
  export outchan rt ro fl;
  close_out outchan;
  let bf = Filename.chop_extension filename in
  let prooffilename = bf ^ ".lfsc" in

  (* let cvc4_cmd = *)
  (*   "cvc4 --proof --dump-proof -m --dump-model \ *)
  (*    --no-simplification --fewer-preprocessing-holes \ *)
  (*    --no-bv-eq --no-bv-ineq --no-bv-algebraic " *)
  (*   ^ filename ^ " > " ^ prooffilename in *)
  (* CVC4 crashes when asking for both models and proofs *)
  
  let cvc4_cmd =
    "cvc4 --proof --dump-proof \
     --no-simplification --fewer-preprocessing-holes \
     --no-bv-eq --no-bv-ineq --no-bv-algebraic "
    ^ filename ^ " > " ^ prooffilename in
  (* let clean_cmd = "sed -i -e '1d' " ^ prooffilename in *)
  eprintf "%s@." cvc4_cmd;
  let t0 = Sys.time () in
  let exit_code = Sys.command cvc4_cmd in
  
  let t1 = Sys.time () in
  eprintf "CVC4 = %.5f@." (t1-.t0);

  if exit_code <> 0 then
    Structures.error ("CVC4 crashed: return code "^string_of_int exit_code);

  (* ignore (Sys.command clean_cmd); *)

  try import_trace_from_file (Some root) prooffilename
  with
  | No_proof -> Structures.error "CVC4 did not generate a proof"
  | Failure s -> Structures.error ("Importing of proof failed: " ^ s)
  | Ast.CVC4Sat ->
    let smodel = get_model_from_file prooffilename in
    Structures.error
      ("CVC4 returned sat. Here is the model:\n\n" ^
       SmtCommands.model_string env rt ro ra rf smodel)


let cvc4_logic = 
  SL.of_list [LUF; LLia; LBitvectors; LArrays]


let tactic_gen vm_cast =
  clear_all ();
  let rt = SmtBtype.create () in
  let ro = Op.create () in
  let ra = Tosmtcoq.ra in
  let rf = Tosmtcoq.rf in
  let ra' = Tosmtcoq.ra in
  let rf' = Tosmtcoq.rf in
  SmtCommands.tactic call_cvc4 cvc4_logic rt ro ra rf ra' rf' vm_cast [] []
  (* (\* Currently, quantifiers are not handled by the cvc4 tactic: we pass
   *    the same ra and rf twice to have everything reifed *\)
   * SmtCommands.tactic call_cvc4 cvc4_logic rt ro ra rf ra rf vm_cast [] [] *)
let tactic () = tactic_gen vm_cast_true
let tactic_no_check () = tactic_gen (fun _ -> vm_cast_true_no_check)