liquid-fixpoint-0.2.0.0: external/fixpoint/toSmtLib.ml
(*
* Copyright © 2009 The Regents of the University of California. All rights reserved.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
* FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION
* TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONAst.Symbol.
*
*)
(* This module implements the IMP language and translation from fixpoint constraints *)
module F = Format
module H = Hashtbl
module A = Ast
module E = A.Expression
module P = A.Predicate
module Sy = A.Symbol
module So = A.Sort
module SM = Sy.SMap
module SS = Sy.SSet
module C = FixConstraint
module Cg = FixConfig
module Co = Constants
(*module BS = BNstats*)
module Misc = FixMisc open Misc.Ops
let mydebug = false
(*************************************************************************)
(************* Datatypes for SMTLIB Representation ***********************)
(*************************************************************************)
(*
type sort = Int | Bool | Func of (sort list * sort)
*)
type rpred
= A.pred (* Bexp (App (k, es)) *)
type vdef
= Sy.t * So.t (* variable, sort *)
type kdef
= Sy.t * (vdef list)
type cstr
= { lhs : A.pred
; rhs : rpred option
; id : int
}
type smtlib
= { vars : vdef list
; kvars : kdef list
; cstrs : cstr list
; consts : vdef list
}
type kmap
= kdef SM.t
let is_kvar x = Misc.is_prefix "k_" (Sy.to_string x)
(*************************************************************************)
(************* Rendering SMTLIB to String ********************************)
(*************************************************************************)
(* Printing as C syntax *)
let print_brel ppf = function
| A.Eq -> F.fprintf ppf "="
| A.Ne -> F.fprintf ppf "!="
| A.Gt -> F.fprintf ppf ">"
| A.Ge -> F.fprintf ppf ">="
| A.Lt -> F.fprintf ppf "<"
| A.Le -> F.fprintf ppf "<="
let print_bop ppf = function
| A.Plus -> F.fprintf ppf "+"
| A.Minus -> F.fprintf ppf "-"
| A.Times -> F.fprintf ppf "*"
| A.Div -> F.fprintf ppf "/"
| A.Mod -> F.fprintf ppf "mod"
let rec print_pred ppf pred =
match P.unwrap pred with
| A.True ->
F.fprintf ppf "true"
| A.False ->
F.fprintf ppf "false"
| A.Atom (e1, A.Ne, e2) ->
F.fprintf ppf "(not (= %a %a))"
print_expr e1
print_expr e2
| A.Atom (e1, r, e2) ->
F.fprintf ppf "(%a %a %a)"
print_brel r
print_expr e1
print_expr e2
| A.And ps ->
Misc.pprint_many_prefix "and" A.pTrue print_pred ppf ps
| A.Or ps ->
Misc.pprint_many_prefix "or" A.pFalse print_pred ppf ps
| A.Not p ->
F.fprintf ppf "(not %a)" print_pred p
| A.Imp (p1, p2) ->
F.fprintf ppf "(=> %a %a)"
print_pred p1
print_pred p2
| A.Iff (p1, p2) ->
F.fprintf ppf "(= %a %a)"
print_pred p1
print_pred p2
| A.Bexp e ->
print_expr ppf e
| _ -> assertf "ERROR: ToSmtLib.print_pred %s" (P.to_string pred)
and print_expr_app f ppf = function
| [e] ->
F.fprintf ppf "(select %a %a)"
Sy.print f
print_expr e
| e::es ->
F.fprintf ppf "(select %a %a)"
(print_expr_app f) es
print_expr e
and print_expr ppf expr =
match E.unwrap expr with
| A.Con c ->
F.fprintf ppf "%a" A.Constant.print c
| A.App (v, [])
| A.Var v ->
F.fprintf ppf "%a" Sy.print v
| A.App (f, es) when is_kvar f ->
F.fprintf ppf "(%a %a)"
Sy.print f
(Misc.pprint_many false " " print_expr) es
| A.App (f, es) ->
print_expr_app f ppf (List.rev es)
(*
print_expr_app f ppf (List.rev es)
F.fprintf ppf "(%a %a)"
Sy.print f
(Misc.pprint_many false " " print_expr) es *)
| A.Bin (e1, op, e2) ->
F.fprintf ppf "(%a %a %a)"
print_bop op
print_expr e1
print_expr e2
| A.Ite (p, e1, e2) ->
F.fprintf ppf "(ite %a %a %a)"
print_pred p
print_expr e1
print_expr e2
| A.Cst (e, _) ->
F.fprintf ppf "%a" print_expr e
| _ -> assertf "ERROR: ToSmtLib.print_expr %s" (E.to_string expr)
let print_sort_base ppf t =
if So.is_bool t then
Format.fprintf ppf "Bool"
else
Format.fprintf ppf "Int"
let rec print_sort ppf t = match So.func_of_t t with
| Some (_, [], t) ->
print_sort_base ppf t
| Some (_, ts, t) ->
Format.fprintf ppf "%a %a"
(Misc.pprint_many false " " print_sort) ts
print_sort t
| None ->
print_sort_base ppf t
(*
let print_vdef ppf (x, t) = match So.func_of_t t with
| Some (_, ts, t') when So.is_bool t' ->
Format.fprintf ppf ":extrapreds ((%a %a))"
Sy.print x
(Misc.pprint_many false " " print_sort) ts
| _ ->
Format.fprintf ppf ":extrafuns ((%a %a))"
Sy.print x
print_sort t
*)
let rec print_fun_sorts ppf = function
| [t] -> print_sort ppf t
| t::ts -> Format.fprintf ppf "(Array %a %a)"
print_sort t
print_fun_sorts ts
let print_vdef ppf (x, t) = match So.func_of_t t with
| Some (_, ts, t') ->
Format.fprintf ppf "(%a %a)"
Sy.print x
print_fun_sorts (ts @ [t'])
| _ ->
Format.fprintf ppf "(%a %a)"
Sy.print x
print_sort t
let print_const ppf c =
Format.fprintf ppf "; constant \n%a\n" print_vdef c
let groupConsts cs =
cs |> Misc.kgroupby snd
|> Misc.map (Misc.app_snd (List.map fst))
|> List.filter (snd <+> List.length <+> (>) 0)
let print_distinct ppf (t, cs) =
Format.fprintf ppf
"; distinct constants of sort: %a\n(distinct %a)\n"
print_sort t
(Misc.pprint_many false " " Sy.print) cs
(*
let print_kdef ppf (kf, xts) =
Format.fprintf ppf ":extrapreds ((%a %a))"
Sy.print kf
(Misc.pprint_many false " " print_sort) (List.map snd xts)
*)
let print_kdef ppf (kf, xts) =
Format.fprintf ppf "(declare-fun %a (%a) Bool)"
Sy.print kf
(Misc.pprint_many false " " print_sort) (List.map snd xts)
let print_rhs ppf = function
| None -> Format.fprintf ppf "false"
| Some p -> Format.fprintf ppf "%a" print_pred p
(*
let print_cstr ppf c =
Format.fprintf ppf "\n; cid = %d\n:assumption\n(implies (%a) %a)\n"
c.id print_pred c.lhs print_rhs c.rhs
*)
let binds_of_cstr env c =
let lsyms = P.support c.lhs in
let rsyms = match c.rhs with None -> [] | Some p -> P.support p in
Misc.sort_and_compact (lsyms ++ rsyms)
|> List.filter (fun x -> SM.mem x env)
|> List.map (fun x -> (x, SM.safeFind x env "binds_of_cstr"))
let print_cstr env ppf c =
Format.fprintf ppf "\n; cid = %d\n(assert (forall (%a) \n (=> %a %a))\n)"
c.id
(Misc.pprint_many true " " print_vdef) (binds_of_cstr env c)
print_pred c.lhs
print_rhs c.rhs
(*
let print ppf smt =
Format.fprintf ppf
"(benchmark unknown\n:status unsat\n:logic AUFLIA\n%a\n%a\n%a\n%a\n%a\n)"
(Misc.pprint_many true "\n" print_vdef) smt.vars
(Misc.pprint_many true "\n" print_const) smt.consts
(Misc.pprint_many true "\n" print_kdef) smt.kvars
(Misc.pprint_many true "\n" print_distinct) (groupConsts smt.consts)
(Misc.pprint_many true "\n" print_cstr) smt.cstrs
*)
let print ppf smt =
let env = SM.of_list (smt.vars ++ smt.consts) in
Format.fprintf ppf
"(set-logic HORN)\n; KVARS\n\n%a\n\n; CONSTRAINTS\n%a\n"
(Misc.pprint_many true "\n" print_kdef) smt.kvars
(Misc.pprint_many true "\n" (print_cstr env)) smt.cstrs
(*************************************************************************)
(************* Helpers for extracting var-sort bindings ******************)
(*************************************************************************)
let sort_compat x t t' =
Ast.Sort.compat t t'
>> (fun b -> if not b then
Printf.printf "WARNING: k-sort incompatible for %s"
(Sy.to_string x))
(* HACKY sort_compat because in the end everything is an Int *)
let sort_compat x t1 t2 =
not (So.is_bool t1)
&& not (So.is_bool t2)
&& (not (So.is_func t1) || (t1 = t2))
&& (not (So.is_func t2) || (t1 = t2))
let vdefs_of_env env r =
env |> C.bindings_of_env
|> (++) [(C.vv_of_reft r, r)]
|> List.map (Misc.app_snd C.sort_of_reft)
(* |> List.filter (not <.> So.is_func <.> snd) *)
|> Misc.fsort fst
(*************************************************************************)
(************* Build VMap : gather all vars/sorts for regular vars *******)
(*************************************************************************)
let update_vmap vm (x, t) =
Misc.maybe_iter begin fun t' ->
asserts (sort_compat x t t') "ERROR: v-sort incompatible %s" (Sy.to_string x)
end (SM.maybe_find x vm);
SM.add x t vm
let update_vmap_int vm (x, t) =
SM.add x So.t_int vm
let add_c_var_to_vmap vm c =
let vvl = C.vv_of_reft (C.lhs_of_t c) in
let vvr = C.vv_of_reft (C.rhs_of_t c) in
let _ = asserts (vvl = vvr) "Different VVs in Constr: %d" (C.id_of_t c) in
vdefs_of_env (C.env_of_t c) (C.lhs_of_t c)
|> List.fold_left update_vmap_int vm
let add_wf_var_to_vmap vm w =
vdefs_of_env (C.env_of_wf w) (C.reft_of_wf w)
|> List.fold_left update_vmap_int vm
(*************************************************************************)
(************ Build KMap: gather scopes for each kvar from wfs** *********)
(*************************************************************************)
let check_no_subs wid suks =
asserts
(List.for_all (fst <+> A.Subst.is_empty) suks)
"NonTriv Subst wid=%d" wid
let join vds vds' =
let xm = SM.of_list vds in
List.filter begin fun (x, t) ->
match SM.maybe_find x xm with
| None -> false
| Some t' -> sort_compat x t t'
end vds'
let update_kmap vdefs km k : kmap =
match SM.maybe_find k km with
| None -> SM.add k (k, vdefs) km
| Some (_,vdefs') -> SM.add k (k, join vdefs vdefs') km
let add_wf_to_kmap km wf =
let vdefs = vdefs_of_env (C.env_of_wf wf) (C.reft_of_wf wf)
|> List.filter (snd <+> So.is_func <+> not)
in
C.reft_of_wf wf
|> C.kvars_of_reft
>> check_no_subs (C.id_of_wf wf)
|> List.map snd
|> List.fold_left (update_kmap vdefs) km
let make_kmap defs : kmap =
defs
|> Misc.map_partial (function Cg.Wfc x -> Some x | _ -> None)
|> List.fold_left add_wf_to_kmap SM.empty
let mkFreshI, _ = Misc.mk_int_factory ()
let mkFresh cid x =
Sy.of_string (Format.sprintf "%s_smt_%d_%d" (Sy.to_string x) cid (mkFreshI ()))
(* >> (fun x' -> Format.printf "fresh_var: %a \n" Sy.print x')
*)
let fresh_vars env cid es =
let t = Hashtbl.create 17 in
let msg = "fresh_vars: cid = "^(string_of_int cid) in
let es' = List.map begin fun e -> match e with
| (A.Var x, _) ->
if Hashtbl.mem t x then
x |> mkFresh cid >> Hashtbl.add t x |> A.eVar
else let _ = Hashtbl.add t x x in e
| _ -> failwith ("ERROR: " ^ msg)
end es in
let l' = Misc.hashtbl_keys t
|> Misc.flap begin fun x ->
let so = SM.safeFind x env msg in
foreach (Hashtbl.find_all t x) begin fun x' ->
(x', so), A.pEqual ((A.eVar x), (A.eVar x'))
end
end
|> List.split
|> Misc.app_snd A.pAnd in
l', es'
(*************************************************************************)
(************* Translating using the KMap ********************************)
(*************************************************************************)
let pred_of_kdef (kf, xts) =
A.pBexp <| A.eApp (kf, List.map (fst <+> A.eVar) xts)
let soln_of_kmap km k =
[pred_of_kdef <| SM.safeFind k km "soln_of_kmap"]
(* >> (Format.printf "soln_of_kmap: k = %a ps = %a \n" Sy.print k (Misc.pprint_many false " " P.print))
*)
let tx_constraint s c =
let cid = C.id_of_t c in
let lps = C.preds_of_lhs_nofilter s c in
let v,t,ras = C.rhs_of_t c in
ras |>: begin function
| C.Conc p -> { lhs = A.pAnd ((A.pNot p) :: lps)
; rhs = None
; id = cid }
| ra -> { lhs = A.pAnd lps
; rhs = (match C.preds_of_refa s ra with
| [p] -> Some p
| _ -> failwith "tx_constraint")
; id = cid}
end
|>: begin function
(* Ken needed this tx but it messes up with typeclasses... maybe not
* needed any more?
| { rhs = Some (A.Bexp (A.App (f, es),_), _) } as c' ->
let (xts, eqp), es' = fresh_vars (C.senv_of_t c) cid es in
let r' = A.pBexp (A.eApp (f, es')) in
(xts, {c' with lhs = A.pAnd [eqp; c'.lhs]; rhs = Some r' })
*)
| c' -> ([], c')
end
let tx_defs cfg =
let defs = List.map (fun c -> Cg.Cst c) cfg.Cg.cs ++
List.map (fun c -> Cg.Wfc c) cfg.Cg.ws in
let km = defs |> make_kmap in
let s = soln_of_kmap km in
let cs = cfg.Cg.cs in
let ws = cfg.Cg.ws in
let xts,cs' = List.split <| Misc.flap (tx_constraint s) cs in
{ vars = Misc.flatten xts
++ (SM.to_list <| List.fold_left add_c_var_to_vmap SM.empty cs)
++ (SM.to_list <| List.fold_left add_wf_var_to_vmap SM.empty ws)
; kvars = SM.range km
; cstrs = cs'
; consts = SM.to_list cfg.Cg.uops
}
(*************************************************************************)
(************* Slicing into Single Assertions ****************************)
(*************************************************************************)
let split_by_assertion cfg =
let ccs, kcs = Misc.tr_partition C.is_conc_rhs cfg.Cg.cs in
Misc.map (fun c -> { cfg with Cg.cs = c :: kcs }) ccs
let slice_by_assertion cfg =
let cs' = cfg.Cg.cs
|> Cindex.create cfg.Cg.kuts cfg.Cg.ds
|> Cindex.slice
|> Cindex.to_list
in {cfg with Cg.cs = cs' }
(*************************************************************************)
(************* API *******************************************************)
(*************************************************************************)
let dump_smtlib_indexed (no, cfg) =
let su = Misc.maybe_apply (fun x _ -> "." ^ (string_of_int x)) no "" in
let fo = Constants.get_out_file () in
let fn = fo ^ su ^ ".smt2" in
let _ = Co.bprintflush mydebug ("BEGIN: Dump SMTLIB \n") in
let me = tx_defs cfg in
let _ = Misc.with_out_formatter fn (fun ppf -> F.fprintf ppf "%a" print me) in
let _ = Co.bprintflush mydebug ("DONE: Dump SMTLIB to " ^ fo ^"\n") in
()
let dump_smtlib_mono cfg =
dump_smtlib_indexed (None, cfg);
exit 1
let dump_smtlib_sliced cfg =
cfg |> split_by_assertion
|>: slice_by_assertion
|> Misc.index_from 0
|>: (Misc.app_fst some)
|> List.iter dump_smtlib_indexed
|> (fun _ -> exit 1)
let dump_smtlib = dump_smtlib_sliced