2022-01-07 19:50:45 +03:00
|
|
|
(* This file is part of the Catala compiler, a specification language for tax and social benefits
|
|
|
|
computation rules. Copyright (C) 2022 Inria, contributor: Aymeric Fromherz
|
|
|
|
<aymeric.fromherz@inria.fr>
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
|
|
|
in compliance with the License. You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software distributed under the License
|
|
|
|
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
|
|
|
or implied. See the License for the specific language governing permissions and limitations under
|
|
|
|
the License. *)
|
|
|
|
|
|
|
|
open Utils
|
2022-01-08 20:37:04 +03:00
|
|
|
open Dcalc
|
2022-01-07 20:36:25 +03:00
|
|
|
open Ast
|
2022-01-07 19:50:45 +03:00
|
|
|
open Z3
|
|
|
|
|
2022-01-12 20:35:36 +03:00
|
|
|
module StringMap : Map.S with type key = String.t = Map.Make (String)
|
|
|
|
|
2022-01-12 19:38:12 +03:00
|
|
|
type context = {
|
|
|
|
ctx_z3 : Z3.context;
|
2022-01-12 20:23:50 +03:00
|
|
|
(* The Z3 context, used to create symbols and expressions *)
|
2022-01-12 19:38:12 +03:00
|
|
|
ctx_decl : decl_ctx;
|
2022-01-12 20:23:50 +03:00
|
|
|
(* The declaration context from the Catala program, containing information to precisely pretty
|
|
|
|
print Catala expressions *)
|
2022-01-12 19:38:12 +03:00
|
|
|
ctx_var : typ Pos.marked VarMap.t;
|
2022-01-12 20:23:50 +03:00
|
|
|
(* A map from Catala variables to their types, needed to create Z3 expressions of the right
|
|
|
|
sort *)
|
2022-01-12 19:38:12 +03:00
|
|
|
ctx_funcdecl : FuncDecl.func_decl VarMap.t;
|
2022-01-12 20:35:36 +03:00
|
|
|
(* A map from Catala function names (represented as variables) to Z3 function declarations, used
|
|
|
|
to only define once functions in Z3 queries *)
|
|
|
|
ctx_z3vars : Var.t StringMap.t;
|
|
|
|
(* A map from strings, corresponding to Z3 symbol names, to the Catala variable they
|
|
|
|
represent. Used when to pretty-print Z3 models when a counterexample is generated *)
|
2022-01-12 19:38:12 +03:00
|
|
|
}
|
2022-01-12 21:02:56 +03:00
|
|
|
(** The context contains all the required information to encode a VC represented as a Catala term to
|
|
|
|
Z3. The fields [ctx_decl] and [ctx_var] are computed before starting the translation to Z3, and
|
|
|
|
are thus unmodified throughout the translation. The [ctx_z3] context is an OCaml abstraction on
|
|
|
|
top of an underlying C++ imperative implementation, it is therefore only created once.
|
|
|
|
Unfortunately, the maps [ctx_funcdecl] and [ctx_z3vars] are computed dynamically during the
|
|
|
|
translation requiring us to pass the context around in a functional way **)
|
2022-01-12 19:38:12 +03:00
|
|
|
|
2022-01-12 20:59:01 +03:00
|
|
|
(** [add_funcdecl] adds the mapping between the Catala variable [v] and the Z3 function declaration
|
|
|
|
[fd] to the context **)
|
|
|
|
let add_funcdecl (v : Var.t) (fd : FuncDecl.func_decl) (ctx : context) : context =
|
|
|
|
{ ctx with ctx_funcdecl = VarMap.add v fd ctx.ctx_funcdecl }
|
|
|
|
|
|
|
|
(** [add_z3var] adds the mapping between [name] and the Catala variable [v] to the context **)
|
|
|
|
let add_z3var (name : string) (v : Var.t) (ctx : context) : context =
|
|
|
|
{ ctx with ctx_z3vars = StringMap.add name v ctx.ctx_z3vars }
|
|
|
|
|
2022-01-14 04:04:55 +03:00
|
|
|
(** For the Z3 encoding of Catala programs, we define the "day 0" as Jan 1, 1900 **)
|
|
|
|
let base_day = CalendarLib.Date.make 1900 1 1
|
|
|
|
|
2022-01-12 19:38:12 +03:00
|
|
|
(** [unique_name] returns the full, unique name corresponding to variable [v], as given by Bindlib **)
|
|
|
|
let unique_name (v : Var.t) : string =
|
|
|
|
Format.asprintf "%s_%d" (Bindlib.name_of v) (Bindlib.uid_of v)
|
2022-01-10 13:52:48 +03:00
|
|
|
|
2022-01-14 04:04:55 +03:00
|
|
|
(** [date_to_int] translates [date] to an integer corresponding to the number of days since Jan 1,
|
|
|
|
1900 **)
|
|
|
|
let date_to_int (d : Runtime.date) : int =
|
|
|
|
(* Alternatively, could expose this from Runtime as a (noop) coercion, but would allow to break
|
|
|
|
abstraction more easily elsewhere *)
|
|
|
|
let date : CalendarLib.Date.t = CalendarLib.Printer.Date.from_string (Runtime.date_to_string d) in
|
|
|
|
let period = CalendarLib.Date.sub date base_day in
|
|
|
|
CalendarLib.Date.Period.nb_days period
|
|
|
|
|
|
|
|
(** Returns the date (as a string) corresponding to nb days after the base day, defined here as Jan
|
|
|
|
1, 1900 **)
|
|
|
|
let nb_days_to_date (nb : int) : string =
|
|
|
|
CalendarLib.Printer.Date.to_string
|
|
|
|
(CalendarLib.Date.add base_day (CalendarLib.Date.Period.day nb))
|
|
|
|
|
2022-01-13 21:52:58 +03:00
|
|
|
(** [print_z3model_expr] pretty-prints the value [e] given by a Z3 model according to the type of
|
|
|
|
the Catala variable [v], corresponding to [e] **)
|
|
|
|
let print_z3model_expr (ctx : context) (v : Var.t) (e : Expr.expr) : string =
|
|
|
|
let print_lit (ty : typ_lit) =
|
|
|
|
match ty with
|
|
|
|
(* TODO: Print boolean according to current language *)
|
|
|
|
| TBool -> Expr.to_string e
|
|
|
|
| TUnit -> failwith "[Z3 model]: Pretty-printing of unit literals not supported"
|
|
|
|
| TInt -> Expr.to_string e
|
|
|
|
| TRat -> failwith "[Z3 model]: Pretty-printing of rational literals not supported"
|
|
|
|
(* TODO: Print the right money symbol according to language *)
|
2022-01-13 22:02:14 +03:00
|
|
|
| TMoney ->
|
2022-01-14 13:45:47 +03:00
|
|
|
let z3_str = Expr.to_string e in
|
|
|
|
(* The Z3 model returns an integer corresponding to the amount of cents. We reformat it as
|
|
|
|
dollars *)
|
|
|
|
let to_dollars s = Runtime.money_to_string (Runtime.money_of_cents_string s) in
|
|
|
|
if String.contains z3_str '-' then
|
|
|
|
Format.asprintf "-%s $" (to_dollars (String.sub z3_str 3 (String.length z3_str - 4)))
|
|
|
|
else Format.asprintf "%s $" (to_dollars z3_str)
|
2022-01-14 04:08:00 +03:00
|
|
|
(* The Z3 date representation corresponds to the number of days since Jan 1, 1900. We
|
|
|
|
pretty-print it as the actual date *)
|
|
|
|
(* TODO: Use differnt dates conventions depending on the language ? *)
|
|
|
|
| TDate -> nb_days_to_date (int_of_string (Expr.to_string e))
|
2022-01-13 21:52:58 +03:00
|
|
|
| TDuration -> failwith "[Z3 model]: Pretty-printing of duration literals not supported"
|
|
|
|
in
|
|
|
|
|
|
|
|
match Pos.unmark (VarMap.find v ctx.ctx_var) with
|
|
|
|
| TLit ty -> print_lit ty
|
|
|
|
| TTuple _ -> failwith "[Z3 model]: Pretty-printing of tuples not supported"
|
|
|
|
| TEnum _ -> failwith "[Z3 model]: Pretty-printing of enums not supported"
|
|
|
|
| TArrow _ -> failwith "[Z3 model]: Pretty-printing of arrows not supported"
|
|
|
|
| TArray _ -> failwith "[Z3 model]: Pretty-printing of arrays not supported"
|
|
|
|
| TAny -> failwith "[Z3 model]: Pretty-printing of Any not supported"
|
|
|
|
|
2022-01-12 19:55:32 +03:00
|
|
|
(** [print_model] pretty prints a Z3 model, used to exhibit counter examples where verification
|
2022-01-12 21:09:20 +03:00
|
|
|
conditions are not satisfied. The context [ctx] is useful to retrieve the mapping between Z3
|
|
|
|
variables and Catala variables, and to retrieve type information about the variables that was
|
|
|
|
lost during the translation (e.g., by translating a date to an integer) **)
|
|
|
|
let print_model (ctx : context) (model : Model.model) : string =
|
2022-01-12 20:15:09 +03:00
|
|
|
let decls = Model.get_decls model in
|
2022-01-13 12:46:23 +03:00
|
|
|
Format.asprintf "%a"
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "\n")
|
|
|
|
(fun fmt d ->
|
|
|
|
match Model.get_const_interp model d with
|
|
|
|
(* TODO: Better handling of this case *)
|
|
|
|
| None -> failwith "[Z3 model]: A variable does not have an associated Z3 solution"
|
|
|
|
(* Prints "name : value\n" *)
|
|
|
|
| Some e ->
|
|
|
|
if FuncDecl.get_arity d = 0 then
|
|
|
|
(* Constant case *)
|
|
|
|
let symbol_name = Symbol.to_string (FuncDecl.get_name d) in
|
|
|
|
let v = StringMap.find symbol_name ctx.ctx_z3vars in
|
|
|
|
Format.fprintf fmt "%s %s : %s"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.blue ] "%s" "-->")
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "%s" (Bindlib.name_of v))
|
2022-01-13 21:52:58 +03:00
|
|
|
(print_z3model_expr ctx v e)
|
2022-01-13 12:46:23 +03:00
|
|
|
else failwith "[Z3 model]: Printing of functions is not yet supported"))
|
|
|
|
decls
|
2022-01-12 19:55:32 +03:00
|
|
|
|
2022-01-10 16:49:04 +03:00
|
|
|
(** [translate_typ_lit] returns the Z3 sort corresponding to the Catala literal type [t] **)
|
2022-01-11 17:39:20 +03:00
|
|
|
let translate_typ_lit (ctx : context) (t : typ_lit) : Sort.sort =
|
|
|
|
match t with
|
2022-01-10 16:51:36 +03:00
|
|
|
| TBool -> Boolean.mk_sort ctx.ctx_z3
|
2022-01-10 16:49:04 +03:00
|
|
|
| TUnit -> failwith "[Z3 encoding] TUnit type not supported"
|
2022-01-10 16:51:36 +03:00
|
|
|
| TInt -> Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-01-10 16:49:04 +03:00
|
|
|
| TRat -> failwith "[Z3 encoding] TRat type not supported"
|
2022-01-13 22:02:14 +03:00
|
|
|
| TMoney -> Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-01-14 03:54:33 +03:00
|
|
|
(* Dates are encoded as integers, corresponding to the number of days since Jan 1, 1900 *)
|
|
|
|
| TDate -> Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-01-10 16:49:04 +03:00
|
|
|
| TDuration -> failwith "[Z3 encoding] TDuration type not supported"
|
|
|
|
|
2022-01-10 16:46:41 +03:00
|
|
|
(** [translate_typ] returns the Z3 sort correponding to the Catala type [t] **)
|
2022-01-11 17:39:20 +03:00
|
|
|
let translate_typ (ctx : context) (t : typ) : Sort.sort =
|
|
|
|
match t with
|
2022-01-10 16:51:36 +03:00
|
|
|
| TLit t -> translate_typ_lit ctx t
|
2022-01-10 16:46:41 +03:00
|
|
|
| TTuple _ -> failwith "[Z3 encoding] TTuple type not supported"
|
|
|
|
| TEnum _ -> failwith "[Z3 encoding] TEnum type not supported"
|
|
|
|
| TArrow _ -> failwith "[Z3 encoding] TArrow type not supported"
|
|
|
|
| TArray _ -> failwith "[Z3 encoding] TArray type not supported"
|
|
|
|
| TAny -> failwith "[Z3 encoding] TAny type not supported"
|
|
|
|
|
2022-01-07 20:25:35 +03:00
|
|
|
(** [translate_lit] returns the Z3 expression as a literal corresponding to [lit] **)
|
|
|
|
let translate_lit (ctx : context) (l : lit) : Expr.expr =
|
|
|
|
match l with
|
2022-01-10 13:52:48 +03:00
|
|
|
| LBool b -> if b then Boolean.mk_true ctx.ctx_z3 else Boolean.mk_false ctx.ctx_z3
|
2022-01-07 20:25:35 +03:00
|
|
|
| LEmptyError -> failwith "[Z3 encoding] LEmptyError literals not supported"
|
2022-01-10 13:52:48 +03:00
|
|
|
| LInt n -> Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 (Runtime.integer_to_int n)
|
2022-01-07 20:36:25 +03:00
|
|
|
| LRat _ -> failwith "[Z3 encoding] LRat literals not supported"
|
2022-01-13 22:02:14 +03:00
|
|
|
| LMoney m ->
|
2022-01-13 22:11:28 +03:00
|
|
|
let z3_m = Runtime.integer_to_int (Runtime.money_to_cents m) in
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 z3_m
|
2022-01-07 20:25:35 +03:00
|
|
|
| LUnit -> failwith "[Z3 encoding] LUnit literals not supported"
|
2022-01-14 03:54:33 +03:00
|
|
|
(* Encoding a date as an integer corresponding to the number of days since Jan 1, 1900 *)
|
|
|
|
| LDate d -> Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 (date_to_int d)
|
2022-01-07 20:25:35 +03:00
|
|
|
| LDuration _ -> failwith "[Z3 encoding] LDuration literals not supported"
|
|
|
|
|
2022-01-12 19:38:12 +03:00
|
|
|
(** [find_or_create_funcdecl] attempts to retrieve the Z3 function declaration corresponding to the
|
|
|
|
variable [v]. If no such function declaration exists yet, we construct it and add it to the
|
|
|
|
context, thus requiring to return a new context *)
|
|
|
|
let find_or_create_funcdecl (ctx : context) (v : Var.t) : context * FuncDecl.func_decl =
|
|
|
|
match VarMap.find_opt v ctx.ctx_funcdecl with
|
|
|
|
| Some fd -> (ctx, fd)
|
|
|
|
| None -> (
|
|
|
|
(* Retrieves the Catala type of the function [v] *)
|
|
|
|
let f_ty = VarMap.find v ctx.ctx_var in
|
|
|
|
match Pos.unmark f_ty with
|
|
|
|
| TArrow (t1, t2) ->
|
|
|
|
let z3_t1 = translate_typ ctx (Pos.unmark t1) in
|
|
|
|
let z3_t2 = translate_typ ctx (Pos.unmark t2) in
|
2022-01-12 20:59:01 +03:00
|
|
|
let name = unique_name v in
|
|
|
|
let fd = FuncDecl.mk_func_decl_s ctx.ctx_z3 name [ z3_t1 ] z3_t2 in
|
|
|
|
let ctx = add_funcdecl v fd ctx in
|
|
|
|
let ctx = add_z3var name v ctx in
|
2022-01-12 19:38:12 +03:00
|
|
|
(ctx, fd)
|
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
"[Z3 Encoding] Ill-formed VC, a function application does not have a function type")
|
|
|
|
|
2022-01-08 20:37:04 +03:00
|
|
|
(** [translate_op] returns the Z3 expression corresponding to the application of [op] to the
|
|
|
|
arguments [args] **)
|
2022-01-12 20:59:01 +03:00
|
|
|
let rec translate_op (ctx : context) (op : operator) (args : expr Pos.marked list) :
|
|
|
|
context * Expr.expr =
|
2022-01-07 20:14:01 +03:00
|
|
|
match op with
|
|
|
|
| Ternop _top ->
|
2022-01-08 20:37:04 +03:00
|
|
|
let _e1, _e2, _e3 =
|
|
|
|
match args with
|
|
|
|
| [ e1; e2; e3 ] -> (e1, e2, e3)
|
2022-01-10 13:52:48 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
(Format.asprintf "[Z3 encoding] Ill-formed ternary operator application: %a"
|
|
|
|
(Print.format_expr ctx.ctx_decl)
|
|
|
|
(EApp ((EOp op, Pos.no_pos), args), Pos.no_pos))
|
2022-01-07 20:45:54 +03:00
|
|
|
in
|
2022-01-07 20:36:25 +03:00
|
|
|
|
2022-01-07 20:14:01 +03:00
|
|
|
failwith "[Z3 encoding] ternary operator application not supported"
|
2022-01-08 20:37:04 +03:00
|
|
|
| Binop bop -> (
|
2022-01-12 20:59:01 +03:00
|
|
|
let ctx, e1, e2 =
|
2022-01-08 20:37:04 +03:00
|
|
|
match args with
|
2022-01-12 20:59:01 +03:00
|
|
|
| [ e1; e2 ] ->
|
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let ctx, e2 = translate_expr ctx e2 in
|
|
|
|
(ctx, e1, e2)
|
2022-01-10 13:52:48 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
(Format.asprintf "[Z3 encoding] Ill-formed binary operator application: %a"
|
|
|
|
(Print.format_expr ctx.ctx_decl)
|
|
|
|
(EApp ((EOp op, Pos.no_pos), args), Pos.no_pos))
|
2022-01-07 20:45:54 +03:00
|
|
|
in
|
2022-01-07 20:36:25 +03:00
|
|
|
|
2022-01-08 20:37:04 +03:00
|
|
|
match bop with
|
2022-01-12 20:59:01 +03:00
|
|
|
| And -> (ctx, Boolean.mk_and ctx.ctx_z3 [ e1; e2 ])
|
|
|
|
| Or -> (ctx, Boolean.mk_or ctx.ctx_z3 [ e1; e2 ])
|
|
|
|
| Xor -> (ctx, Boolean.mk_xor ctx.ctx_z3 e1 e2)
|
|
|
|
| Add KInt -> (ctx, Arithmetic.mk_add ctx.ctx_z3 [ e1; e2 ])
|
2022-01-11 20:33:45 +03:00
|
|
|
| Add _ ->
|
|
|
|
failwith "[Z3 encoding] application of non-integer binary operator Add not supported"
|
2022-01-12 20:59:01 +03:00
|
|
|
| Sub KInt -> (ctx, Arithmetic.mk_sub ctx.ctx_z3 [ e1; e2 ])
|
2022-01-11 20:33:45 +03:00
|
|
|
| Sub _ ->
|
|
|
|
failwith "[Z3 encoding] application of non-integer binary operator Sub not supported"
|
2022-01-12 20:59:01 +03:00
|
|
|
| Mult KInt -> (ctx, Arithmetic.mk_mul ctx.ctx_z3 [ e1; e2 ])
|
2022-01-11 20:33:45 +03:00
|
|
|
| Mult _ ->
|
|
|
|
failwith "[Z3 encoding] application of non-integer binary operator Mult not supported"
|
2022-01-12 20:59:01 +03:00
|
|
|
| Div KInt -> (ctx, Arithmetic.mk_div ctx.ctx_z3 e1 e2)
|
2022-01-11 20:33:45 +03:00
|
|
|
| Div _ ->
|
|
|
|
failwith "[Z3 encoding] application of non-integer binary operator Div not supported"
|
2022-01-14 03:54:33 +03:00
|
|
|
| Lt KInt | Lt KMoney | Lt KDate -> (ctx, Arithmetic.mk_lt ctx.ctx_z3 e1 e2)
|
2022-01-13 22:11:28 +03:00
|
|
|
| Lt _ ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of non-integer or money binary operator Lt not supported"
|
2022-01-14 03:54:33 +03:00
|
|
|
| Lte KInt | Lte KMoney | Lte KDate -> (ctx, Arithmetic.mk_le ctx.ctx_z3 e1 e2)
|
2022-01-12 14:16:54 +03:00
|
|
|
| Lte _ ->
|
2022-01-13 22:11:28 +03:00
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of non-integer or money binary operator Lte not supported"
|
2022-01-14 03:54:33 +03:00
|
|
|
| Gt KInt | Gt KMoney | Gt KDate -> (ctx, Arithmetic.mk_gt ctx.ctx_z3 e1 e2)
|
2022-01-13 22:11:28 +03:00
|
|
|
| Gt _ ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of non-integer or money binary operator Gt not supported"
|
2022-01-14 03:54:33 +03:00
|
|
|
| Gte KInt | Gte KMoney | Gte KDate -> (ctx, Arithmetic.mk_ge ctx.ctx_z3 e1 e2)
|
2022-01-11 17:39:20 +03:00
|
|
|
| Gte _ ->
|
2022-01-13 22:11:28 +03:00
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of non-integer or money binary operator Gte not supported"
|
2022-01-12 20:59:01 +03:00
|
|
|
| Eq -> (ctx, Boolean.mk_eq ctx.ctx_z3 e1 e2)
|
|
|
|
| Neq -> (ctx, Boolean.mk_not ctx.ctx_z3 (Boolean.mk_eq ctx.ctx_z3 e1 e2))
|
2022-01-07 20:14:01 +03:00
|
|
|
| Map -> failwith "[Z3 encoding] application of binary operator Map not supported"
|
|
|
|
| Concat -> failwith "[Z3 encoding] application of binary operator Concat not supported"
|
2022-01-08 20:37:04 +03:00
|
|
|
| Filter -> failwith "[Z3 encoding] application of binary operator Filter not supported")
|
|
|
|
| Unop uop -> (
|
2022-01-12 20:59:01 +03:00
|
|
|
let ctx, e1 =
|
2022-01-08 20:37:04 +03:00
|
|
|
match args with
|
2022-01-12 20:59:01 +03:00
|
|
|
| [ e1 ] -> translate_expr ctx e1
|
2022-01-12 21:15:51 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
(Format.asprintf "[Z3 encoding] Ill-formed unary operator application: %a"
|
|
|
|
(Print.format_expr ctx.ctx_decl)
|
|
|
|
(EApp ((EOp op, Pos.no_pos), args), Pos.no_pos))
|
2022-01-07 20:45:54 +03:00
|
|
|
in
|
2022-01-07 20:36:25 +03:00
|
|
|
|
2022-01-08 20:37:04 +03:00
|
|
|
match uop with
|
2022-01-12 20:59:01 +03:00
|
|
|
| Not -> (ctx, Boolean.mk_not ctx.ctx_z3 e1)
|
2022-01-07 20:36:25 +03:00
|
|
|
| Minus _ -> failwith "[Z3 encoding] application of unary operator Minus not supported"
|
2022-01-07 20:45:54 +03:00
|
|
|
(* Omitting the log from the VC *)
|
2022-01-12 20:59:01 +03:00
|
|
|
| Log _ -> (ctx, e1)
|
2022-01-07 20:36:25 +03:00
|
|
|
| Length -> failwith "[Z3 encoding] application of unary operator Length not supported"
|
|
|
|
| IntToRat -> failwith "[Z3 encoding] application of unary operator IntToRat not supported"
|
|
|
|
| GetDay -> failwith "[Z3 encoding] application of unary operator GetDay not supported"
|
|
|
|
| GetMonth -> failwith "[Z3 encoding] application of unary operator GetMonth not supported"
|
2022-01-08 20:37:04 +03:00
|
|
|
| GetYear -> failwith "[Z3 encoding] application of unary operator GetYear not supported")
|
2022-01-07 20:14:01 +03:00
|
|
|
|
|
|
|
(** [translate_expr] translate the expression [vc] to its corresponding Z3 expression **)
|
2022-01-12 20:59:01 +03:00
|
|
|
and translate_expr (ctx : context) (vc : expr Pos.marked) : context * Expr.expr =
|
2022-01-07 19:50:45 +03:00
|
|
|
match Pos.unmark vc with
|
2022-01-10 16:46:41 +03:00
|
|
|
| EVar v ->
|
|
|
|
let v = Pos.unmark v in
|
2022-01-12 19:38:12 +03:00
|
|
|
let t = VarMap.find v ctx.ctx_var in
|
|
|
|
let name = unique_name v in
|
2022-01-12 20:59:01 +03:00
|
|
|
let ctx = add_z3var name v ctx in
|
|
|
|
(ctx, Expr.mk_const_s ctx.ctx_z3 name (translate_typ ctx (Pos.unmark t)))
|
2022-01-07 19:50:45 +03:00
|
|
|
| ETuple _ -> failwith "[Z3 encoding] ETuple unsupported"
|
|
|
|
| ETupleAccess _ -> failwith "[Z3 encoding] ETupleAccess unsupported"
|
|
|
|
| EInj _ -> failwith "[Z3 encoding] EInj unsupported"
|
|
|
|
| EMatch _ -> failwith "[Z3 encoding] EMatch unsupported"
|
|
|
|
| EArray _ -> failwith "[Z3 encoding] EArray unsupported"
|
2022-01-12 20:59:01 +03:00
|
|
|
| ELit l -> (ctx, translate_lit ctx l)
|
2022-01-07 19:50:45 +03:00
|
|
|
| EAbs _ -> failwith "[Z3 encoding] EAbs unsupported"
|
2022-01-08 20:37:04 +03:00
|
|
|
| EApp (head, args) -> (
|
|
|
|
match Pos.unmark head with
|
2022-01-07 20:14:01 +03:00
|
|
|
| EOp op -> translate_op ctx op args
|
2022-01-12 19:38:12 +03:00
|
|
|
| EVar v ->
|
|
|
|
let ctx, fd = find_or_create_funcdecl ctx (Pos.unmark v) in
|
2022-01-12 20:59:01 +03:00
|
|
|
(* Fold_right to preserve the order of the arguments: The head argument is appended at the
|
|
|
|
head *)
|
|
|
|
let ctx, z3_args =
|
|
|
|
List.fold_right
|
|
|
|
(fun arg (ctx, acc) ->
|
|
|
|
let ctx, z3_arg = translate_expr ctx arg in
|
|
|
|
(ctx, z3_arg :: acc))
|
|
|
|
args (ctx, [])
|
|
|
|
in
|
|
|
|
(ctx, Expr.mk_app ctx.ctx_z3 fd z3_args)
|
2022-01-12 19:38:12 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] EApp node: Catala function calls should only include operators or \
|
|
|
|
function names")
|
2022-01-08 20:37:04 +03:00
|
|
|
| EAssert _ -> failwith "[Z3 encoding] EAssert unsupported"
|
2022-01-07 19:50:45 +03:00
|
|
|
| EOp _ -> failwith "[Z3 encoding] EOp unsupported"
|
|
|
|
| EDefault _ -> failwith "[Z3 encoding] EDefault unsupported"
|
2022-01-07 20:36:25 +03:00
|
|
|
| EIfThenElse (e_if, e_then, e_else) ->
|
2022-01-07 20:18:15 +03:00
|
|
|
(* Encode this as (e_if ==> e_then) /\ (not e_if ==> e_else) *)
|
2022-01-12 20:59:01 +03:00
|
|
|
let ctx, z3_if = translate_expr ctx e_if in
|
|
|
|
let ctx, z3_then = translate_expr ctx e_then in
|
|
|
|
let ctx, z3_else = translate_expr ctx e_else in
|
|
|
|
( ctx,
|
|
|
|
Boolean.mk_and ctx.ctx_z3
|
|
|
|
[
|
|
|
|
Boolean.mk_implies ctx.ctx_z3 z3_if z3_then;
|
|
|
|
Boolean.mk_implies ctx.ctx_z3 (Boolean.mk_not ctx.ctx_z3 z3_if) z3_else;
|
|
|
|
] )
|
2022-01-07 19:50:45 +03:00
|
|
|
| ErrorOnEmpty _ -> failwith "[Z3 encoding] ErrorOnEmpty unsupported"
|
|
|
|
|
2022-01-12 20:59:01 +03:00
|
|
|
type vc_encoding_result = Success of context * Expr.expr | Fail of string
|
2022-01-10 12:59:30 +03:00
|
|
|
|
2022-01-12 18:49:44 +03:00
|
|
|
let print_positive_result (vc : Conditions.verification_condition) : string =
|
|
|
|
match vc.Conditions.vc_kind with
|
|
|
|
| Conditions.NoEmptyError ->
|
2022-01-13 12:46:23 +03:00
|
|
|
Format.asprintf "%s This variable never returns an empty error"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "[%s.%s]"
|
2022-01-12 18:49:44 +03:00
|
|
|
(Format.asprintf "%a" ScopeName.format_t vc.vc_scope)
|
|
|
|
(Bindlib.name_of (Pos.unmark vc.vc_variable)))
|
|
|
|
| Conditions.NoOverlappingExceptions ->
|
2022-01-13 12:46:23 +03:00
|
|
|
Format.asprintf "%s No two exceptions to ever overlap for this variable"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "[%s.%s]"
|
2022-01-12 18:49:44 +03:00
|
|
|
(Format.asprintf "%a" ScopeName.format_t vc.vc_scope)
|
|
|
|
(Bindlib.name_of (Pos.unmark vc.vc_variable)))
|
|
|
|
|
2022-01-13 12:46:23 +03:00
|
|
|
let print_negative_result (vc : Conditions.verification_condition) (ctx : context)
|
|
|
|
(solver : Solver.solver) : string =
|
|
|
|
let var_and_pos =
|
|
|
|
match vc.Conditions.vc_kind with
|
|
|
|
| Conditions.NoEmptyError ->
|
|
|
|
Format.asprintf "%s This variable might return an empty error:\n%s"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "[%s.%s]"
|
|
|
|
(Format.asprintf "%a" ScopeName.format_t vc.vc_scope)
|
|
|
|
(Bindlib.name_of (Pos.unmark vc.vc_variable)))
|
|
|
|
(Pos.retrieve_loc_text (Pos.get_position vc.vc_variable))
|
|
|
|
| Conditions.NoOverlappingExceptions ->
|
|
|
|
Format.asprintf "%s At least two exceptions overlap for this variable:\n%s"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "[%s.%s]"
|
|
|
|
(Format.asprintf "%a" ScopeName.format_t vc.vc_scope)
|
|
|
|
(Bindlib.name_of (Pos.unmark vc.vc_variable)))
|
|
|
|
(Pos.retrieve_loc_text (Pos.get_position vc.vc_variable))
|
|
|
|
in
|
2022-01-13 18:58:07 +03:00
|
|
|
let counterexample : string option =
|
2022-01-13 12:46:23 +03:00
|
|
|
match Solver.get_model solver with
|
|
|
|
| None ->
|
2022-01-13 18:58:07 +03:00
|
|
|
Some
|
|
|
|
"The solver did not manage to generate a counterexample to explain the faulty behavior."
|
2022-01-13 12:46:23 +03:00
|
|
|
| Some model ->
|
2022-01-13 18:58:07 +03:00
|
|
|
if List.length (Model.get_decls model) = 0 then None
|
|
|
|
else
|
|
|
|
Some
|
|
|
|
(Format.asprintf
|
|
|
|
"The solver generated the following counterexample to explain the faulty behavior:\n\
|
|
|
|
%s"
|
|
|
|
(print_model ctx model))
|
2022-01-13 12:46:23 +03:00
|
|
|
in
|
2022-01-13 18:58:07 +03:00
|
|
|
var_and_pos
|
|
|
|
^ match counterexample with None -> "" | Some counterexample -> "\n" ^ counterexample
|
2022-01-12 16:43:10 +03:00
|
|
|
|
2022-01-12 16:08:35 +03:00
|
|
|
(** [encode_and_check_vc] spawns a new Z3 solver and tries to solve the expression [vc] **)
|
2022-01-12 16:43:10 +03:00
|
|
|
let encode_and_check_vc (decl_ctx : decl_ctx) (z3_ctx : Z3.context)
|
2022-01-12 17:21:13 +03:00
|
|
|
(vc : Conditions.verification_condition * vc_encoding_result) : unit =
|
2022-01-12 16:43:10 +03:00
|
|
|
let vc, z3_vc = vc in
|
|
|
|
|
2022-01-12 18:49:44 +03:00
|
|
|
Cli.debug_print
|
2022-01-12 16:43:10 +03:00
|
|
|
(Format.asprintf "For this variable:\n%s\n"
|
|
|
|
(Pos.retrieve_loc_text (Pos.get_position vc.Conditions.vc_guard)));
|
|
|
|
Cli.debug_print
|
|
|
|
(Format.asprintf "This verification condition was generated for %s:@\n%a"
|
|
|
|
(Cli.print_with_style [ ANSITerminal.yellow ] "%s"
|
|
|
|
(match vc.vc_kind with
|
|
|
|
| Conditions.NoEmptyError -> "the variable definition never to return an empty error"
|
|
|
|
| NoOverlappingExceptions -> "no two exceptions to ever overlap"))
|
|
|
|
(Dcalc.Print.format_expr decl_ctx)
|
|
|
|
vc.vc_guard);
|
|
|
|
|
2022-01-12 17:21:13 +03:00
|
|
|
match z3_vc with
|
2022-01-12 21:09:20 +03:00
|
|
|
| Success (ctx, z3_vc) ->
|
2022-01-12 17:21:13 +03:00
|
|
|
Cli.debug_print
|
|
|
|
(Format.asprintf "The translation to Z3 is the following:@\n%s" (Expr.to_string z3_vc));
|
2022-01-12 16:20:52 +03:00
|
|
|
|
2022-01-12 17:21:13 +03:00
|
|
|
let solver = Solver.mk_solver z3_ctx None in
|
2022-01-12 16:08:35 +03:00
|
|
|
|
2022-01-12 17:21:13 +03:00
|
|
|
Solver.add solver [ Boolean.mk_not z3_ctx z3_vc ];
|
2022-01-12 16:08:35 +03:00
|
|
|
|
2022-01-12 18:49:44 +03:00
|
|
|
if Solver.check solver [] = UNSATISFIABLE then Cli.result_print (print_positive_result vc)
|
2022-01-13 12:46:23 +03:00
|
|
|
else
|
2022-01-12 17:21:13 +03:00
|
|
|
(* TODO: Print model as error message for Catala debugging purposes *)
|
2022-01-13 12:46:23 +03:00
|
|
|
Cli.error_print (print_negative_result vc ctx solver)
|
2022-01-12 17:21:13 +03:00
|
|
|
| Fail msg -> Cli.error_print (Format.asprintf "The translation to Z3 failed:@\n%s" msg)
|
2022-01-12 16:08:35 +03:00
|
|
|
|
2022-01-08 20:37:04 +03:00
|
|
|
(** [solve_vc] is the main entry point of this module. It takes a list of expressions [vcs]
|
|
|
|
corresponding to verification conditions that must be discharged by Z3, and attempts to solve
|
|
|
|
them **)
|
2022-01-11 17:39:20 +03:00
|
|
|
let solve_vc (prgm : program) (decl_ctx : decl_ctx) (vcs : Conditions.verification_condition list) :
|
|
|
|
unit =
|
2022-01-11 18:13:34 +03:00
|
|
|
Cli.debug_print (Format.asprintf "Running Z3 version %s" Version.to_string);
|
2022-01-07 19:50:45 +03:00
|
|
|
|
2022-01-08 20:37:04 +03:00
|
|
|
let cfg = [ ("model", "true"); ("proof", "false") ] in
|
2022-01-10 13:52:48 +03:00
|
|
|
let z3_ctx = mk_context cfg in
|
2022-01-07 20:36:25 +03:00
|
|
|
|
2022-01-10 12:59:30 +03:00
|
|
|
let z3_vcs =
|
|
|
|
List.map
|
|
|
|
(fun vc ->
|
2022-01-10 13:52:48 +03:00
|
|
|
( vc,
|
|
|
|
try
|
2022-01-12 20:59:01 +03:00
|
|
|
let ctx, z3_vc =
|
|
|
|
translate_expr
|
|
|
|
{
|
|
|
|
ctx_z3 = z3_ctx;
|
|
|
|
ctx_decl = decl_ctx;
|
|
|
|
ctx_var =
|
|
|
|
VarMap.union
|
|
|
|
(fun _ _ _ ->
|
|
|
|
failwith "[Z3 encoding]: A Variable cannot be both free and bound")
|
|
|
|
(variable_types prgm) vc.Conditions.vc_free_vars_typ;
|
|
|
|
ctx_funcdecl = VarMap.empty;
|
|
|
|
ctx_z3vars = StringMap.empty;
|
|
|
|
}
|
|
|
|
(Bindlib.unbox (Dcalc.Optimizations.remove_all_logs vc.Conditions.vc_guard))
|
|
|
|
in
|
|
|
|
Success (ctx, z3_vc)
|
2022-01-10 13:52:48 +03:00
|
|
|
with Failure msg -> Fail msg ))
|
2022-01-10 12:59:30 +03:00
|
|
|
vcs
|
|
|
|
in
|
|
|
|
|
2022-01-12 16:43:10 +03:00
|
|
|
List.iter (encode_and_check_vc decl_ctx z3_ctx) z3_vcs
|