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-08-12 23:42:39 +03:00
|
|
|
open Shared_ast
|
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-07-28 11:36:36 +03:00
|
|
|
ctx_var : (typed expr, typ Marked.pos) Var.Map.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-07-28 11:36:36 +03:00
|
|
|
ctx_funcdecl : (typed expr, FuncDecl.func_decl) Var.Map.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 *)
|
2022-07-28 11:36:36 +03:00
|
|
|
ctx_z3vars : typed var StringMap.t;
|
2022-01-14 17:13:35 +03:00
|
|
|
(* 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 *)
|
|
|
|
ctx_z3datatypes : Sort.sort EnumMap.t;
|
2022-01-14 21:01:54 +03:00
|
|
|
(* A map from Catala enumeration names to the corresponding Z3 sort, from
|
|
|
|
which we can retrieve constructors and accessors *)
|
2022-07-28 11:36:36 +03:00
|
|
|
ctx_z3matchsubsts : (typed expr, Expr.expr) Var.Map.t;
|
2022-01-14 22:08:42 +03:00
|
|
|
(* A map from Catala temporary variables, generated when translating a match,
|
|
|
|
to the corresponding enum accessor call as a Z3 expression *)
|
|
|
|
ctx_z3structs : Sort.sort StructMap.t;
|
|
|
|
(* A map from Catala struct names to the corresponding Z3 sort, from which we
|
|
|
|
can retrieve the constructor and the accessors *)
|
2022-02-17 20:34:30 +03:00
|
|
|
ctx_z3unit : Sort.sort * Expr.expr;
|
2022-03-15 20:52:02 +03:00
|
|
|
(* A pair containing the Z3 encodings of the unit type, encoded as a tuple of
|
|
|
|
0 elements, and the unit value *)
|
|
|
|
ctx_z3constraints : Expr.expr list;
|
|
|
|
(* A list of constraints about the created Z3 expressions accumulated
|
|
|
|
during their initialization, for instance, that the length of an array
|
|
|
|
is an integer which always is greater than 0 *)
|
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.
|
2022-01-14 17:13:35 +03:00
|
|
|
Unfortunately, the maps [ctx_funcdecl], [ctx_z3vars], and [ctx_z3datatypes]
|
|
|
|
are computed dynamically during the translation requiring us to pass the
|
|
|
|
context around in a functional way **)
|
2022-03-08 17:03:14 +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 **)
|
2022-07-28 11:36:36 +03:00
|
|
|
let add_funcdecl (v : typed var) (fd : FuncDecl.func_decl) (ctx : context) :
|
|
|
|
context =
|
|
|
|
{ ctx with ctx_funcdecl = Var.Map.add v fd ctx.ctx_funcdecl }
|
2022-01-12 20:59:01 +03:00
|
|
|
|
|
|
|
(** [add_z3var] adds the mapping between [name] and the Catala variable [v] to
|
|
|
|
the context **)
|
2022-07-28 11:36:36 +03:00
|
|
|
let add_z3var (name : string) (v : typed var) (ctx : context) : context =
|
2022-01-12 20:59:01 +03:00
|
|
|
{ ctx with ctx_z3vars = StringMap.add name v ctx.ctx_z3vars }
|
|
|
|
|
2022-01-14 19:37:00 +03:00
|
|
|
(** [add_z3enum] adds the mapping between the Catala enumeration [enum] and the
|
|
|
|
corresponding Z3 datatype [sort] to the context **)
|
|
|
|
let add_z3enum (enum : EnumName.t) (sort : Sort.sort) (ctx : context) : context
|
|
|
|
=
|
|
|
|
{ ctx with ctx_z3datatypes = EnumMap.add enum sort ctx.ctx_z3datatypes }
|
|
|
|
|
2022-01-14 21:01:54 +03:00
|
|
|
(** [add_z3var] adds the mapping between temporary variable [v] and the Z3
|
|
|
|
expression [e] representing an accessor application to the context **)
|
2022-07-28 11:36:36 +03:00
|
|
|
let add_z3matchsubst (v : typed var) (e : Expr.expr) (ctx : context) : context =
|
|
|
|
{ ctx with ctx_z3matchsubsts = Var.Map.add v e ctx.ctx_z3matchsubsts }
|
2022-01-14 21:01:54 +03:00
|
|
|
|
2022-01-14 22:08:42 +03:00
|
|
|
(** [add_z3struct] adds the mapping between the Catala struct [s] and the
|
|
|
|
corresponding Z3 datatype [sort] to the context **)
|
|
|
|
let add_z3struct (s : StructName.t) (sort : Sort.sort) (ctx : context) : context
|
|
|
|
=
|
|
|
|
{ ctx with ctx_z3structs = StructMap.add s sort ctx.ctx_z3structs }
|
|
|
|
|
2022-03-15 20:52:02 +03:00
|
|
|
let add_z3constraint (e : Expr.expr) (ctx : context) : context =
|
|
|
|
{ ctx with ctx_z3constraints = e :: ctx.ctx_z3constraints }
|
|
|
|
|
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 **)
|
2022-07-06 11:08:27 +03:00
|
|
|
let unique_name (v : 'm var) : string =
|
2022-01-12 19:38:12 +03:00
|
|
|
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
|
|
|
|
|
2022-01-17 17:02:56 +03:00
|
|
|
(** [date_of_year] translates a [year], represented as an integer into an OCaml
|
|
|
|
date corresponding to Jan 1st of the same year *)
|
|
|
|
let date_of_year (year : int) = Runtime.date_of_numbers year 1 1
|
|
|
|
|
2022-01-14 04:04:55 +03:00
|
|
|
(** 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-14 23:02:34 +03:00
|
|
|
(** [print_z3model_expr] pretty-prints the value [e] given by a Z3 model
|
|
|
|
according to the Catala type [ty], corresponding to [e] **)
|
2022-05-30 12:20:48 +03:00
|
|
|
let rec print_z3model_expr (ctx : context) (ty : typ Marked.pos) (e : Expr.expr)
|
2022-01-14 23:02:34 +03:00
|
|
|
: string =
|
2022-01-13 21:52:58 +03:00
|
|
|
let print_lit (ty : typ_lit) =
|
|
|
|
match ty with
|
|
|
|
(* TODO: Print boolean according to current language *)
|
|
|
|
| TBool -> Expr.to_string e
|
2022-02-17 20:34:30 +03:00
|
|
|
(* TUnit is only used for the absence of an enum constructor argument.
|
|
|
|
Hence, when pretty-printing, we print nothing to remain closer from
|
|
|
|
Catala sources *)
|
|
|
|
| TUnit -> ""
|
2022-01-13 21:52:58 +03:00
|
|
|
| TInt -> Expr.to_string e
|
2022-02-21 16:54:40 +03:00
|
|
|
| TRat -> Arithmetic.Real.to_decimal_string e !Cli.max_prec_digits
|
2022-01-13 21:52:58 +03:00
|
|
|
(* 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-03-24 19:15:22 +03:00
|
|
|
| TDuration -> Format.asprintf "%s days" (Expr.to_string e)
|
2022-01-13 21:52:58 +03:00
|
|
|
in
|
|
|
|
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark ty with
|
2022-01-13 21:52:58 +03:00
|
|
|
| TLit ty -> print_lit ty
|
2022-01-14 23:02:34 +03:00
|
|
|
| TTuple (_, Some name) ->
|
|
|
|
let s = StructMap.find name ctx.ctx_decl.ctx_structs in
|
|
|
|
let get_fieldname (fn : StructFieldName.t) : string =
|
2022-05-30 12:20:48 +03:00
|
|
|
Marked.unmark (StructFieldName.get_info fn)
|
2022-01-14 23:02:34 +03:00
|
|
|
in
|
|
|
|
let fields =
|
|
|
|
List.map2
|
|
|
|
(fun (fn, ty) e ->
|
|
|
|
Format.asprintf "-- %s : %s" (get_fieldname fn)
|
|
|
|
(print_z3model_expr ctx ty e))
|
|
|
|
s (Expr.get_args e)
|
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 23:02:34 +03:00
|
|
|
let fields_str = String.concat " " fields in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 23:02:34 +03:00
|
|
|
Format.asprintf "%s { %s }"
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.unmark (StructName.get_info name))
|
2022-01-14 23:02:34 +03:00
|
|
|
fields_str
|
|
|
|
| TTuple (_, None) ->
|
|
|
|
failwith "[Z3 model]: Pretty-printing of unnamed structs not supported"
|
2022-01-14 23:10:34 +03:00
|
|
|
| TEnum (_tys, name) ->
|
|
|
|
(* The value associated to the enum is a single argument *)
|
|
|
|
let e' = List.hd (Expr.get_args e) in
|
|
|
|
let fd = Expr.get_func_decl e in
|
|
|
|
let fd_name = Symbol.to_string (FuncDecl.get_name fd) in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 23:10:34 +03:00
|
|
|
let enum_ctrs = EnumMap.find name ctx.ctx_decl.ctx_enums in
|
|
|
|
let case =
|
|
|
|
List.find
|
|
|
|
(fun (ctr, _) ->
|
2022-05-30 12:20:48 +03:00
|
|
|
String.equal fd_name (Marked.unmark (EnumConstructor.get_info ctr)))
|
2022-01-14 23:10:34 +03:00
|
|
|
enum_ctrs
|
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 23:10:34 +03:00
|
|
|
Format.asprintf "%s (%s)" fd_name (print_z3model_expr ctx (snd case) e')
|
2022-01-13 21:52:58 +03:00
|
|
|
| TArrow _ -> failwith "[Z3 model]: Pretty-printing of arrows not supported"
|
2022-03-16 13:28:03 +03:00
|
|
|
| TArray _ ->
|
2022-03-16 13:35:13 +03:00
|
|
|
(* For now, only the length of arrays is modeled *)
|
2022-03-16 13:28:03 +03:00
|
|
|
Format.asprintf "(length = %s)" (Expr.to_string e)
|
2022-01-13 21:52:58 +03:00
|
|
|
| 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
|
2022-01-12 21:09:20 +03:00
|
|
|
where verification 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-02-10 18:49:01 +03:00
|
|
|
let decls = Model.get_decls model in
|
|
|
|
Format.asprintf "%a"
|
|
|
|
(Format.pp_print_list
|
|
|
|
~pp_sep:(fun fmt () -> Format.fprintf fmt "\n")
|
|
|
|
(fun fmt d ->
|
2022-02-18 02:34:42 +03:00
|
|
|
if FuncDecl.get_arity d = 0 then
|
2022-02-18 02:28:19 +03:00
|
|
|
(* Constant case *)
|
|
|
|
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"
|
|
|
|
(* Print "name : value\n" *)
|
|
|
|
| Some e ->
|
2022-02-10 18:49:01 +03:00
|
|
|
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"
|
2022-03-08 15:04:27 +03:00
|
|
|
(Cli.with_style [ANSITerminal.blue] "%s" "-->")
|
2022-07-28 11:36:36 +03:00
|
|
|
(Cli.with_style [ANSITerminal.yellow] "%s" (Bindlib.name_of v))
|
|
|
|
(print_z3model_expr ctx (Var.Map.find v ctx.ctx_var) e)
|
2022-02-18 02:34:42 +03:00
|
|
|
else
|
|
|
|
(* Declaration d is a function *)
|
|
|
|
match Model.get_func_interp model d with
|
|
|
|
(* TODO: Better handling of this case *)
|
|
|
|
| None ->
|
|
|
|
failwith
|
|
|
|
"[Z3 model]: A variable does not have an associated Z3 solution"
|
|
|
|
(* Print "name : value\n" *)
|
|
|
|
| Some f ->
|
|
|
|
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"
|
2022-03-08 15:04:27 +03:00
|
|
|
(Cli.with_style [ANSITerminal.blue] "%s" "-->")
|
2022-07-28 11:36:36 +03:00
|
|
|
(Cli.with_style [ANSITerminal.yellow] "%s" (Bindlib.name_of v))
|
2022-02-18 02:34:42 +03:00
|
|
|
(* TODO: Model of a Z3 function should be pretty-printed *)
|
|
|
|
(Model.FuncInterp.to_string f)))
|
2022-02-10 18:49:01 +03:00
|
|
|
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-02-17 20:34:30 +03:00
|
|
|
| TUnit -> fst ctx.ctx_z3unit
|
2022-01-10 16:51:36 +03:00
|
|
|
| TInt -> Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-02-19 03:59:34 +03:00
|
|
|
| TRat -> Arithmetic.Real.mk_sort ctx.ctx_z3
|
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-03-24 19:15:22 +03:00
|
|
|
| TDuration -> Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-01-10 16:49:04 +03:00
|
|
|
|
2022-01-10 16:46:41 +03:00
|
|
|
(** [translate_typ] returns the Z3 sort correponding to the Catala type [t] **)
|
2022-01-14 19:01:09 +03:00
|
|
|
let rec translate_typ (ctx : context) (t : typ) : context * Sort.sort =
|
2022-01-11 17:39:20 +03:00
|
|
|
match t with
|
2022-01-14 19:01:09 +03:00
|
|
|
| TLit t -> ctx, translate_typ_lit ctx t
|
2022-01-14 22:37:41 +03:00
|
|
|
| TTuple (_, Some name) -> find_or_create_struct ctx name
|
|
|
|
| TTuple (_, None) ->
|
|
|
|
failwith "[Z3 encoding] TTuple type of unnamed struct not supported"
|
2022-01-14 19:01:09 +03:00
|
|
|
| TEnum (_, e) -> find_or_create_enum ctx e
|
2022-01-10 16:46:41 +03:00
|
|
|
| TArrow _ -> failwith "[Z3 encoding] TArrow type not supported"
|
2022-03-15 20:43:11 +03:00
|
|
|
| TArray _ ->
|
|
|
|
(* For now, we are only encoding the (symbolic) length of an array.
|
|
|
|
Ultimately, the type of an array should also contain its elements *)
|
|
|
|
ctx, Arithmetic.Integer.mk_sort ctx.ctx_z3
|
2022-01-10 16:46:41 +03:00
|
|
|
| TAny -> failwith "[Z3 encoding] TAny type not supported"
|
|
|
|
|
2022-01-14 18:53:37 +03:00
|
|
|
(** [find_or_create_enum] attempts to retrieve the Z3 sort corresponding to the
|
|
|
|
Catala enumeration [enum]. If no such sort exists yet, it constructs it by
|
|
|
|
creating a Z3 constructor for each Catala constructor of [enum], and adds it
|
|
|
|
to the context *)
|
2022-01-14 19:01:09 +03:00
|
|
|
and find_or_create_enum (ctx : context) (enum : EnumName.t) :
|
|
|
|
context * Sort.sort =
|
2022-01-14 18:53:37 +03:00
|
|
|
(* Creates a Z3 constructor corresponding to the Catala constructor [c] *)
|
|
|
|
let create_constructor
|
|
|
|
(ctx : context)
|
2022-05-30 12:20:48 +03:00
|
|
|
(c : EnumConstructor.t * typ Marked.pos) :
|
2022-01-14 19:37:00 +03:00
|
|
|
context * Datatype.Constructor.constructor =
|
2022-01-14 18:53:37 +03:00
|
|
|
let name, ty = c in
|
2022-05-30 12:20:48 +03:00
|
|
|
let name = Marked.unmark (EnumConstructor.get_info name) in
|
|
|
|
let ctx, arg_z3_ty = translate_typ ctx (Marked.unmark ty) in
|
2022-01-14 18:53:37 +03:00
|
|
|
|
|
|
|
(* The mk_constructor_s Z3 function is not so well documented. From my
|
|
|
|
understanding, its argument are: - a string corresponding to the name of
|
|
|
|
the constructor - a recognizer as a symbol corresponding to the name
|
|
|
|
(unsure why) - a list of symbols corresponding to the arguments of the
|
|
|
|
constructor - a list of types, that must be of the same length as the
|
|
|
|
list of arguments - a list of sort_refs, of the same length as the list
|
|
|
|
of arguments. I'm unsure what this corresponds to *)
|
2022-01-14 19:37:00 +03:00
|
|
|
( ctx,
|
|
|
|
Datatype.mk_constructor_s ctx.ctx_z3 name
|
|
|
|
(Symbol.mk_string ctx.ctx_z3 name)
|
|
|
|
(* We need a name for the argument of the constructor, we arbitrary pick
|
|
|
|
the name of the constructor to which we append the special character
|
|
|
|
"!" and the integer 0 *)
|
|
|
|
[Symbol.mk_string ctx.ctx_z3 (name ^ "!0")]
|
|
|
|
(* The type of the argument, translated to a Z3 sort *)
|
|
|
|
[Some arg_z3_ty]
|
|
|
|
[Sort.get_id arg_z3_ty] )
|
2022-01-14 18:53:37 +03:00
|
|
|
in
|
|
|
|
|
|
|
|
match EnumMap.find_opt enum ctx.ctx_z3datatypes with
|
|
|
|
| Some e -> ctx, e
|
|
|
|
| None ->
|
|
|
|
let ctrs = EnumMap.find enum ctx.ctx_decl.ctx_enums in
|
2022-01-14 19:37:00 +03:00
|
|
|
let ctx, z3_ctrs = List.fold_left_map create_constructor ctx ctrs in
|
|
|
|
let z3_enum =
|
|
|
|
Datatype.mk_sort_s ctx.ctx_z3
|
2022-05-30 12:20:48 +03:00
|
|
|
(Marked.unmark (EnumName.get_info enum))
|
2022-01-14 19:37:00 +03:00
|
|
|
z3_ctrs
|
|
|
|
in
|
|
|
|
add_z3enum enum z3_enum ctx, z3_enum
|
2022-01-14 18:53:37 +03:00
|
|
|
|
2022-01-14 22:35:22 +03:00
|
|
|
(** [find_or_create_struct] attemps to retrieve the Z3 sort corresponding to the
|
|
|
|
struct [s]. If no such sort exists yet, we construct it as a datatype with
|
|
|
|
one constructor taking all the fields as arguments, and add it to the
|
2022-01-14 22:37:41 +03:00
|
|
|
context *)
|
|
|
|
and find_or_create_struct (ctx : context) (s : StructName.t) :
|
|
|
|
context * Sort.sort =
|
2022-01-14 22:35:22 +03:00
|
|
|
match StructMap.find_opt s ctx.ctx_z3structs with
|
|
|
|
| Some s -> ctx, s
|
|
|
|
| None ->
|
2022-05-30 12:20:48 +03:00
|
|
|
let s_name = Marked.unmark (StructName.get_info s) in
|
2022-01-14 22:35:22 +03:00
|
|
|
let fields = StructMap.find s ctx.ctx_decl.ctx_structs in
|
|
|
|
let z3_fieldnames =
|
|
|
|
List.map
|
|
|
|
(fun f ->
|
2022-05-30 12:20:48 +03:00
|
|
|
Marked.unmark (StructFieldName.get_info (fst f))
|
2022-01-14 22:35:22 +03:00
|
|
|
|> Symbol.mk_string ctx.ctx_z3)
|
|
|
|
fields
|
|
|
|
in
|
|
|
|
let ctx, z3_fieldtypes =
|
|
|
|
List.fold_left_map
|
2022-05-30 12:20:48 +03:00
|
|
|
(fun ctx f -> Marked.unmark (snd f) |> translate_typ ctx)
|
2022-01-14 22:35:22 +03:00
|
|
|
ctx fields
|
|
|
|
in
|
|
|
|
let z3_sortrefs = List.map Sort.get_id z3_fieldtypes in
|
|
|
|
let mk_struct_s = "mk!" ^ s_name in
|
|
|
|
let z3_mk_struct =
|
|
|
|
Datatype.mk_constructor_s ctx.ctx_z3 mk_struct_s
|
|
|
|
(Symbol.mk_string ctx.ctx_z3 mk_struct_s)
|
|
|
|
z3_fieldnames
|
|
|
|
(List.map (fun x -> Some x) z3_fieldtypes)
|
|
|
|
z3_sortrefs
|
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 22:35:22 +03:00
|
|
|
let z3_struct = Datatype.mk_sort_s ctx.ctx_z3 s_name [z3_mk_struct] in
|
|
|
|
add_z3struct s z3_struct ctx, z3_struct
|
|
|
|
|
2022-01-14 22:37:41 +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
|
|
|
|
| LBool b ->
|
|
|
|
if b then Boolean.mk_true ctx.ctx_z3 else Boolean.mk_false ctx.ctx_z3
|
|
|
|
| LEmptyError -> failwith "[Z3 encoding] LEmptyError literals not supported"
|
|
|
|
| LInt n ->
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 (Runtime.integer_to_int n)
|
2022-02-19 03:59:34 +03:00
|
|
|
| LRat r ->
|
|
|
|
Arithmetic.Real.mk_numeral_s ctx.ctx_z3
|
|
|
|
(string_of_float (Runtime.decimal_to_float r))
|
2022-01-14 22:37:41 +03:00
|
|
|
| LMoney m ->
|
|
|
|
let z3_m = Runtime.integer_to_int (Runtime.money_to_cents m) in
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 z3_m
|
2022-03-16 14:01:21 +03:00
|
|
|
| LUnit -> snd ctx.ctx_z3unit
|
2022-01-14 22:37:41 +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-03-24 19:22:31 +03:00
|
|
|
| LDuration d ->
|
2022-03-28 19:59:53 +03:00
|
|
|
let y, m, d = Runtime.duration_to_years_months_days d in
|
2022-03-28 19:47:13 +03:00
|
|
|
if y <> 0 || m <> 0 then
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding]: Duration literals containing years or months not \
|
|
|
|
supported";
|
2022-03-28 19:59:53 +03:00
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 d
|
2022-01-14 22:37:41 +03:00
|
|
|
|
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 *)
|
2022-07-28 11:36:36 +03:00
|
|
|
let find_or_create_funcdecl (ctx : context) (v : typed var) :
|
2022-01-12 19:38:12 +03:00
|
|
|
context * FuncDecl.func_decl =
|
2022-07-28 11:36:36 +03:00
|
|
|
match Var.Map.find_opt v ctx.ctx_funcdecl with
|
2022-01-12 19:38:12 +03:00
|
|
|
| Some fd -> ctx, fd
|
|
|
|
| None -> (
|
|
|
|
(* Retrieves the Catala type of the function [v] *)
|
2022-07-28 11:36:36 +03:00
|
|
|
let f_ty = Var.Map.find v ctx.ctx_var in
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark f_ty with
|
2022-01-12 19:38:12 +03:00
|
|
|
| TArrow (t1, t2) ->
|
2022-05-30 12:20:48 +03:00
|
|
|
let ctx, z3_t1 = translate_typ ctx (Marked.unmark t1) in
|
|
|
|
let ctx, z3_t2 = translate_typ ctx (Marked.unmark t2) in
|
2022-07-28 11:36:36 +03:00
|
|
|
let name = unique_name v in
|
2022-01-12 20:59:01 +03:00
|
|
|
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
|
2022-02-20 01:28:04 +03:00
|
|
|
| TAny ->
|
|
|
|
failwith
|
|
|
|
"[Z3 Encoding] A function being applied has type TAny, the type was \
|
|
|
|
not fully inferred"
|
2022-01-12 19:38:12 +03:00
|
|
|
| _ ->
|
|
|
|
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)
|
2022-07-06 11:08:27 +03:00
|
|
|
(args : 'm marked_expr 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)
|
2022-07-06 11:08:27 +03:00
|
|
|
( EApp
|
|
|
|
( (EOp op, Untyped { pos = Pos.no_pos }),
|
2022-08-16 11:04:01 +03:00
|
|
|
List.map
|
|
|
|
(fun arg -> Bindlib.unbox (Shared_ast.Expr.untype arg))
|
|
|
|
args ),
|
2022-07-06 11:08:27 +03:00
|
|
|
Untyped { pos = 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-17 17:02:56 +03:00
|
|
|
(* Special case for GetYear comparisons *)
|
|
|
|
match bop, args with
|
|
|
|
| Lt KInt, [(EApp ((EOp (Unop GetYear), _), [e1]), _); (ELit (LInt n), _)]
|
|
|
|
->
|
|
|
|
let n = Runtime.integer_to_int n in
|
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let e2 =
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
|
|
|
(date_to_int (date_of_year n))
|
|
|
|
in
|
|
|
|
(* e2 corresponds to the first day of the year n. GetYear e1 < e2 can thus
|
|
|
|
be directly translated as < in the Z3 encoding using the number of
|
|
|
|
days *)
|
|
|
|
ctx, Arithmetic.mk_lt ctx.ctx_z3 e1 e2
|
|
|
|
| Lte KInt, [(EApp ((EOp (Unop GetYear), _), [e1]), _); (ELit (LInt n), _)]
|
|
|
|
->
|
|
|
|
let n = Runtime.integer_to_int n in
|
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let nb_days = if CalendarLib.Date.is_leap_year n then 365 else 364 in
|
|
|
|
(* We want that the year corresponding to e1 is smaller or equal to n. We
|
|
|
|
encode this as the day corresponding to e1 is smaller or equal than the
|
|
|
|
last day of the year [n], which is Jan 1st + 365 days if [n] is a leap
|
|
|
|
year, Jan 1st + 364 else *)
|
|
|
|
let e2 =
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
|
|
|
(date_to_int (date_of_year n) + nb_days)
|
|
|
|
in
|
|
|
|
ctx, Arithmetic.mk_le ctx.ctx_z3 e1 e2
|
|
|
|
| Gt KInt, [(EApp ((EOp (Unop GetYear), _), [e1]), _); (ELit (LInt n), _)]
|
|
|
|
->
|
|
|
|
let n = Runtime.integer_to_int n in
|
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let nb_days = if CalendarLib.Date.is_leap_year n then 365 else 364 in
|
|
|
|
(* We want that the year corresponding to e1 is greater to n. We encode
|
|
|
|
this as the day corresponding to e1 is greater than the last day of the
|
2022-03-15 20:07:27 +03:00
|
|
|
year [n], which is Jan 1st + 365 days if [n] is a leap year, Jan 1st +
|
|
|
|
364 else *)
|
|
|
|
let e2 =
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
|
|
|
(date_to_int (date_of_year n) + nb_days)
|
|
|
|
in
|
|
|
|
ctx, Arithmetic.mk_gt ctx.ctx_z3 e1 e2
|
2022-01-17 17:02:56 +03:00
|
|
|
| Gte KInt, [(EApp ((EOp (Unop GetYear), _), [e1]), _); (ELit (LInt n), _)]
|
|
|
|
->
|
2022-03-15 20:07:27 +03:00
|
|
|
let n = Runtime.integer_to_int n in
|
2022-01-17 17:02:56 +03:00
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let e2 =
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
|
|
|
(date_to_int (date_of_year n))
|
|
|
|
in
|
|
|
|
(* e2 corresponds to the first day of the year n. GetYear e1 >= e2 can
|
|
|
|
thus be directly translated as >= in the Z3 encoding using the number
|
|
|
|
of days *)
|
2022-02-20 01:18:23 +03:00
|
|
|
ctx, Arithmetic.mk_ge ctx.ctx_z3 e1 e2
|
2022-01-17 17:02:56 +03:00
|
|
|
| Eq, [(EApp ((EOp (Unop GetYear), _), [e1]), _); (ELit (LInt n), _)] ->
|
|
|
|
let n = Runtime.integer_to_int n in
|
|
|
|
let ctx, e1 = translate_expr ctx e1 in
|
|
|
|
let min_date =
|
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
2022-03-24 19:15:22 +03:00
|
|
|
(date_to_int (date_of_year n))
|
|
|
|
in
|
|
|
|
let max_date =
|
2022-02-19 03:59:34 +03:00
|
|
|
Arithmetic.Integer.mk_numeral_i ctx.ctx_z3
|
2022-03-24 19:15:22 +03:00
|
|
|
(date_to_int (date_of_year (n + 1)))
|
2022-05-04 18:40:55 +03:00
|
|
|
in
|
2022-02-19 03:59:34 +03:00
|
|
|
( ctx,
|
|
|
|
Boolean.mk_and ctx.ctx_z3
|
2022-05-04 18:40:55 +03:00
|
|
|
[
|
2022-02-19 03:59:34 +03:00
|
|
|
Arithmetic.mk_ge ctx.ctx_z3 e1 min_date;
|
|
|
|
Arithmetic.mk_lt ctx.ctx_z3 e1 max_date;
|
2022-01-17 17:02:56 +03:00
|
|
|
] )
|
2022-01-08 20:37:04 +03:00
|
|
|
| _ -> (
|
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
|
2022-01-17 17:02:56 +03:00
|
|
|
let ctx, e2 = translate_expr ctx e2 in
|
|
|
|
ctx, e1, e2
|
2022-01-12 21:15:51 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
|
|
|
(Format.asprintf
|
|
|
|
"[Z3 encoding] Ill-formed binary operator application: %a"
|
|
|
|
(Print.format_expr ctx.ctx_decl)
|
2022-07-06 11:08:27 +03:00
|
|
|
( EApp
|
|
|
|
( (EOp op, Untyped { pos = Pos.no_pos }),
|
2022-07-22 12:22:54 +03:00
|
|
|
List.map
|
2022-08-16 11:04:01 +03:00
|
|
|
(fun arg ->
|
|
|
|
arg |> Shared_ast.Expr.untype |> Bindlib.unbox)
|
2022-07-22 12:22:54 +03:00
|
|
|
args ),
|
2022-07-06 11:08:27 +03:00
|
|
|
Untyped { pos = 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-17 17:02:56 +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
|
2022-03-24 19:15:22 +03:00
|
|
|
| Add KInt | Add KRat | Add KMoney | Add KDate | Add KDuration ->
|
2022-02-20 01:18:23 +03:00
|
|
|
ctx, Arithmetic.mk_add ctx.ctx_z3 [e1; e2]
|
2022-03-24 19:15:22 +03:00
|
|
|
| Sub KInt | Sub KRat | Sub KMoney | Sub KDate | Sub KDuration ->
|
2022-02-20 01:18:23 +03:00
|
|
|
ctx, Arithmetic.mk_sub ctx.ctx_z3 [e1; e2]
|
2022-03-24 19:15:22 +03:00
|
|
|
| Mult KInt | Mult KRat | Mult KMoney | Mult KDate | Mult KDuration ->
|
2022-02-20 01:18:23 +03:00
|
|
|
ctx, Arithmetic.mk_mul ctx.ctx_z3 [e1; e2]
|
|
|
|
| Div KInt | Div KRat | Div KMoney ->
|
2022-01-17 17:02:56 +03:00
|
|
|
ctx, Arithmetic.mk_div ctx.ctx_z3 e1 e2
|
|
|
|
| Div _ ->
|
2022-05-04 18:40:55 +03:00
|
|
|
failwith
|
2022-01-12 20:59:01 +03:00
|
|
|
"[Z3 encoding] application of non-integer binary operator Div not \
|
2022-01-17 17:02:56 +03:00
|
|
|
supported"
|
2022-03-24 19:15:22 +03:00
|
|
|
| Lt KInt | Lt KRat | Lt KMoney | Lt KDate | Lt KDuration ->
|
2022-01-17 17:02:56 +03:00
|
|
|
ctx, Arithmetic.mk_lt ctx.ctx_z3 e1 e2
|
2022-03-24 19:15:22 +03:00
|
|
|
| Lte KInt | Lte KRat | Lte KMoney | Lte KDate | Lte KDuration ->
|
2022-01-17 17:02:56 +03:00
|
|
|
ctx, Arithmetic.mk_le ctx.ctx_z3 e1 e2
|
2022-03-24 19:15:22 +03:00
|
|
|
| Gt KInt | Gt KRat | Gt KMoney | Gt KDate | Gt KDuration ->
|
2022-01-17 17:02:56 +03:00
|
|
|
ctx, Arithmetic.mk_gt ctx.ctx_z3 e1 e2
|
2022-03-24 19:15:22 +03:00
|
|
|
| Gte KInt | Gte KRat | Gte KMoney | Gte KDate | Gte KDuration ->
|
2022-01-17 17:02:56 +03:00
|
|
|
ctx, Arithmetic.mk_ge ctx.ctx_z3 e1 e2
|
|
|
|
| Eq -> ctx, Boolean.mk_eq ctx.ctx_z3 e1 e2
|
2022-01-12 20:59:01 +03:00
|
|
|
| Neq -> ctx, Boolean.mk_not ctx.ctx_z3 (Boolean.mk_eq ctx.ctx_z3 e1 e2)
|
2022-01-07 20:36:25 +03:00
|
|
|
| Map ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of binary operator Map not supported"
|
|
|
|
| Concat ->
|
2022-03-15 20:43:11 +03:00
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of binary operator Concat not supported"
|
2022-01-17 17:02:56 +03:00
|
|
|
| Filter ->
|
2022-05-04 18:40:55 +03:00
|
|
|
failwith
|
2022-03-15 20:43:11 +03:00
|
|
|
"[Z3 encoding] application of binary operator Filter not supported"))
|
2022-01-08 20:37:04 +03:00
|
|
|
| Unop uop -> (
|
2022-03-15 20:43:11 +03:00
|
|
|
let ctx, e1 =
|
|
|
|
match args with
|
2022-01-17 17:02:56 +03:00
|
|
|
| [e1] -> translate_expr ctx e1
|
2022-05-04 18:40:55 +03:00
|
|
|
| _ ->
|
|
|
|
failwith
|
2022-01-17 17:02:56 +03:00
|
|
|
(Format.asprintf
|
2022-03-15 20:43:11 +03:00
|
|
|
"[Z3 encoding] Ill-formed unary operator application: %a"
|
2022-01-17 17:02:56 +03:00
|
|
|
(Print.format_expr ctx.ctx_decl)
|
2022-07-06 11:08:27 +03:00
|
|
|
( EApp
|
|
|
|
( (EOp op, Untyped { pos = Pos.no_pos }),
|
2022-07-22 12:22:54 +03:00
|
|
|
List.map
|
2022-08-12 23:42:39 +03:00
|
|
|
(fun arg -> arg |> Shared_ast.Expr.untype |> Bindlib.unbox)
|
2022-07-22 12:22:54 +03:00
|
|
|
args ),
|
2022-07-06 11:08:27 +03:00
|
|
|
Untyped { pos = Pos.no_pos } ))
|
2022-03-15 20:43:11 +03:00
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-17 17:02:56 +03:00
|
|
|
match uop with
|
2022-03-15 20:43:11 +03:00
|
|
|
| Not -> ctx, Boolean.mk_not ctx.ctx_z3 e1
|
|
|
|
| Minus _ ->
|
|
|
|
failwith "[Z3 encoding] application of unary operator Minus not supported"
|
|
|
|
(* Omitting the log from the VC *)
|
|
|
|
| Log _ -> ctx, e1
|
2022-01-14 22:37:41 +03:00
|
|
|
| Length ->
|
2022-03-15 20:52:02 +03:00
|
|
|
(* For now, an array is only its symbolic length. We simply return it *)
|
2022-01-12 20:59:01 +03:00
|
|
|
ctx, e1
|
2022-01-07 20:36:25 +03:00
|
|
|
| IntToRat ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of unary operator IntToRat not supported"
|
2022-07-19 14:52:36 +03:00
|
|
|
| MoneyToRat ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of unary operator MoneyToRat not supported"
|
|
|
|
| RatToMoney ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of unary operator RatToMoney not supported"
|
2022-01-07 20:36:25 +03:00
|
|
|
| GetDay ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of unary operator GetDay not supported"
|
|
|
|
| GetMonth ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] application of unary operator GetMonth not supported"
|
2022-01-17 17:02:56 +03:00
|
|
|
| GetYear ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] GetYear operator only supported in comparisons with \
|
2022-03-17 14:30:14 +03:00
|
|
|
literal"
|
2022-07-21 15:11:56 +03:00
|
|
|
| FirstDayOfMonth ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] FirstDayOfMonth operator only supported in comparisons \
|
|
|
|
with literal"
|
|
|
|
| LastDayOfMonth ->
|
|
|
|
failwith
|
|
|
|
"[Z3 encoding] LastDayOfMonth operator only supported in comparisons \
|
|
|
|
with literal"
|
2022-04-29 22:18:15 +03:00
|
|
|
| RoundDecimal ->
|
|
|
|
failwith "[Z3 encoding] RoundDecimal operator not implemented yet"
|
2022-03-17 14:30:14 +03:00
|
|
|
| RoundMoney ->
|
|
|
|
failwith "[Z3 encoding] RoundMoney operator not implemented yet")
|
2022-03-08 17:03:14 +03:00
|
|
|
|
2022-01-07 20:14:01 +03:00
|
|
|
(** [translate_expr] translate the expression [vc] to its corresponding Z3
|
|
|
|
expression **)
|
2022-07-06 11:08:27 +03:00
|
|
|
and translate_expr (ctx : context) (vc : 'm marked_expr) : context * Expr.expr =
|
2022-01-14 21:01:54 +03:00
|
|
|
let translate_match_arm
|
|
|
|
(head : Expr.expr)
|
|
|
|
(ctx : context)
|
2022-07-06 11:08:27 +03:00
|
|
|
(e : 'm marked_expr * FuncDecl.func_decl list) : context * Expr.expr =
|
2022-01-14 21:01:54 +03:00
|
|
|
let e, accessors = e in
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark e with
|
2022-01-14 20:24:32 +03:00
|
|
|
| EAbs (e, _) ->
|
|
|
|
(* Create a fresh Catala variable to substitue and obtain the body *)
|
2022-07-28 11:36:36 +03:00
|
|
|
let fresh_v = Var.make "arm!tmp" in
|
2022-06-03 17:40:03 +03:00
|
|
|
let fresh_e = EVar fresh_v in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-01-14 21:01:54 +03:00
|
|
|
(* Invariant: Catala enums always have exactly one argument *)
|
|
|
|
let accessor = List.hd accessors in
|
|
|
|
let proj = Expr.mk_app ctx.ctx_z3 accessor [head] in
|
|
|
|
(* The fresh variable should be substituted by a projection into the enum
|
|
|
|
in the body, we add this to the context *)
|
2022-07-28 11:36:36 +03:00
|
|
|
let ctx = add_z3matchsubst fresh_v proj ctx in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-06-03 17:40:03 +03:00
|
|
|
let body = Bindlib.msubst e [| fresh_e |] in
|
2022-01-14 20:24:32 +03:00
|
|
|
translate_expr ctx body
|
2022-01-18 20:51:02 +03:00
|
|
|
(* Invariant: Catala match arms are always lambda*)
|
2022-01-14 20:24:32 +03:00
|
|
|
| _ -> failwith "[Z3 encoding] : Arms branches inside VCs should be lambdas"
|
|
|
|
in
|
|
|
|
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark vc with
|
2022-01-14 21:01:54 +03:00
|
|
|
| EVar v -> (
|
2022-07-28 11:36:36 +03:00
|
|
|
match Var.Map.find_opt v ctx.ctx_z3matchsubsts with
|
2022-01-14 21:01:54 +03:00
|
|
|
| None ->
|
|
|
|
(* We are in the standard case, where this is a true Catala variable *)
|
2022-07-28 11:36:36 +03:00
|
|
|
let t = Var.Map.find v ctx.ctx_var in
|
2022-01-14 21:01:54 +03:00
|
|
|
let name = unique_name v in
|
2022-07-28 11:36:36 +03:00
|
|
|
let ctx = add_z3var name v ctx in
|
2022-05-30 12:20:48 +03:00
|
|
|
let ctx, ty = translate_typ ctx (Marked.unmark t) in
|
2022-03-15 20:52:02 +03:00
|
|
|
let z3_var = Expr.mk_const_s ctx.ctx_z3 name ty in
|
|
|
|
let ctx =
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.unmark t with
|
2022-03-15 20:52:02 +03:00
|
|
|
(* If we are creating a new array, we need to log that its length is
|
|
|
|
greater than 0 *)
|
|
|
|
| TArray _ ->
|
|
|
|
add_z3constraint
|
|
|
|
(Arithmetic.mk_ge ctx.ctx_z3 z3_var
|
|
|
|
(Arithmetic.Integer.mk_numeral_i ctx.ctx_z3 0))
|
|
|
|
ctx
|
|
|
|
| _ -> ctx
|
|
|
|
in
|
2022-05-04 18:40:55 +03:00
|
|
|
|
2022-03-15 20:52:02 +03:00
|
|
|
ctx, z3_var
|
2022-01-14 21:01:54 +03:00
|
|
|
| Some e ->
|
|
|
|
(* This variable is a temporary variable generated during VC translation
|
|
|
|
of a match. It actually corresponds to applying an accessor to an enum,
|
|
|
|
the corresponding Z3 expression was previously stored in the context *)
|
|
|
|
ctx, e)
|
2022-01-07 19:50:45 +03:00
|
|
|
| ETuple _ -> failwith "[Z3 encoding] ETuple unsupported"
|
2022-01-14 22:17:27 +03:00
|
|
|
| ETupleAccess (s, idx, oname, _tys) ->
|
2022-01-14 22:35:22 +03:00
|
|
|
let name =
|
|
|
|
match oname with
|
|
|
|
| None ->
|
|
|
|
failwith "[Z3 encoding]: ETupleAccess of unnamed struct unsupported"
|
|
|
|
| Some n -> n
|
2022-01-14 22:17:27 +03:00
|
|
|
in
|
2022-01-14 22:35:22 +03:00
|
|
|
let ctx, z3_struct = find_or_create_struct ctx name in
|
|
|
|
(* This datatype should have only one constructor, corresponding to
|
|
|
|
mk_struct. The accessors of this constructor correspond to the field
|
|
|
|
accesses *)
|
2022-01-14 22:17:27 +03:00
|
|
|
let accessors = List.hd (Datatype.get_accessors z3_struct) in
|
|
|
|
let accessor = List.nth accessors idx in
|
|
|
|
let ctx, s = translate_expr ctx s in
|
2022-01-14 22:35:22 +03:00
|
|
|
ctx, Expr.mk_app ctx.ctx_z3 accessor [s]
|
2022-03-16 14:00:19 +03:00
|
|
|
| EInj (e, idx, en, _tys) ->
|
|
|
|
(* This node corresponds to creating a value for the enumeration [en], by
|
|
|
|
calling the [idx]-th constructor of enum [en], with argument [e] *)
|
|
|
|
let ctx, z3_enum = find_or_create_enum ctx en in
|
|
|
|
let ctx, z3_arg = translate_expr ctx e in
|
|
|
|
let ctrs = Datatype.get_constructors z3_enum in
|
|
|
|
(* This should always succeed if the expression is well-typed in dcalc *)
|
|
|
|
let ctr = List.nth ctrs idx in
|
|
|
|
ctx, Expr.mk_app ctx.ctx_z3 ctr [z3_arg]
|
2022-01-14 20:24:32 +03:00
|
|
|
| EMatch (arg, arms, enum) ->
|
2022-01-14 20:33:56 +03:00
|
|
|
let ctx, z3_enum = find_or_create_enum ctx enum in
|
2022-01-14 20:24:32 +03:00
|
|
|
let ctx, z3_arg = translate_expr ctx arg in
|
2022-01-14 21:01:54 +03:00
|
|
|
let _ctx, z3_arms =
|
|
|
|
List.fold_left_map
|
|
|
|
(translate_match_arm z3_arg)
|
|
|
|
ctx
|
|
|
|
(List.combine arms (Datatype.get_accessors z3_enum))
|
|
|
|
in
|
2022-01-14 20:33:56 +03:00
|
|
|
let z3_arms =
|
|
|
|
List.map2
|
|
|
|
(fun r arm ->
|
|
|
|
(* Encodes A? arg ==> body *)
|
|
|
|
let is_r = Expr.mk_app ctx.ctx_z3 r [z3_arg] in
|
|
|
|
Boolean.mk_implies ctx.ctx_z3 is_r arm)
|
|
|
|
(Datatype.get_recognizers z3_enum)
|
|
|
|
z3_arms
|
|
|
|
in
|
|
|
|
ctx, Boolean.mk_and ctx.ctx_z3 z3_arms
|
2022-01-07 19:50:45 +03:00
|
|
|
| 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) -> (
|
2022-05-30 12:20:48 +03:00
|
|
|
match Marked.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 ->
|
2022-07-28 11:36:36 +03:00
|
|
|
let ctx, fd = find_or_create_funcdecl ctx 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-02-17 20:34:30 +03:00
|
|
|
(** [create_z3unit] creates a Z3 sort and expression corresponding to the unit
|
|
|
|
type and value respectively. Concretely, we represent unit as a tuple with 0
|
|
|
|
elements **)
|
|
|
|
let create_z3unit (ctx : Z3.context) : Z3.context * (Sort.sort * Expr.expr) =
|
|
|
|
let unit_sort = Tuple.mk_sort ctx (Symbol.mk_string ctx "unit") [] [] in
|
|
|
|
let mk_unit = Tuple.get_mk_decl unit_sort in
|
|
|
|
let unit_val = Expr.mk_app ctx mk_unit [] in
|
|
|
|
ctx, (unit_sort, unit_val)
|
|
|
|
|
2022-01-18 20:51:02 +03:00
|
|
|
module Backend = struct
|
|
|
|
type backend_context = context
|
|
|
|
type vc_encoding = Z3.Expr.expr
|
|
|
|
|
|
|
|
let print_encoding (vc : vc_encoding) : string = Expr.to_string vc
|
|
|
|
|
|
|
|
type model = Z3.Model.model
|
|
|
|
type solver_result = ProvenTrue | ProvenFalse of model option | Unknown
|
|
|
|
|
|
|
|
let solve_vc_encoding (ctx : backend_context) (encoding : vc_encoding) :
|
|
|
|
solver_result =
|
|
|
|
let solver = Z3.Solver.mk_solver ctx.ctx_z3 None in
|
2022-03-16 13:20:20 +03:00
|
|
|
(* We take the negation of the query to check for possible
|
|
|
|
counterexamples *)
|
|
|
|
let query = Boolean.mk_not ctx.ctx_z3 encoding in
|
|
|
|
(* Add all the hypotheses stored in the context *)
|
|
|
|
let query_and_hyps = query :: ctx.ctx_z3constraints in
|
|
|
|
Z3.Solver.add solver query_and_hyps;
|
2022-01-18 20:51:02 +03:00
|
|
|
match Z3.Solver.check solver [] with
|
|
|
|
| UNSATISFIABLE -> ProvenTrue
|
|
|
|
| SATISFIABLE -> ProvenFalse (Z3.Solver.get_model solver)
|
|
|
|
| UNKNOWN -> Unknown
|
|
|
|
|
|
|
|
let print_model (ctx : backend_context) (m : model) : string =
|
|
|
|
print_model ctx m
|
|
|
|
|
|
|
|
let is_model_empty (m : model) : bool = List.length (Z3.Model.get_decls m) = 0
|
2022-01-19 11:47:08 +03:00
|
|
|
|
2022-07-06 11:08:27 +03:00
|
|
|
let translate_expr (ctx : backend_context) (e : 'm marked_expr) =
|
2022-01-19 11:47:08 +03:00
|
|
|
translate_expr ctx e
|
2022-01-19 12:12:20 +03:00
|
|
|
|
2022-03-08 15:04:27 +03:00
|
|
|
let init_backend () =
|
|
|
|
Cli.debug_print "Running Z3 version %s" Version.to_string
|
2022-01-19 12:12:20 +03:00
|
|
|
|
|
|
|
let make_context
|
|
|
|
(decl_ctx : decl_ctx)
|
2022-07-28 11:36:36 +03:00
|
|
|
(free_vars_typ : (typed expr, typ Marked.pos) Var.Map.t) : backend_context
|
|
|
|
=
|
2022-01-26 18:24:09 +03:00
|
|
|
let cfg =
|
|
|
|
(if !Cli.disable_counterexamples then [] else ["model", "true"])
|
|
|
|
@ ["proof", "false"]
|
|
|
|
in
|
2022-01-19 12:12:20 +03:00
|
|
|
let z3_ctx = mk_context cfg in
|
2022-02-17 20:34:30 +03:00
|
|
|
let z3_ctx, z3unit = create_z3unit z3_ctx in
|
2022-01-19 12:12:20 +03:00
|
|
|
{
|
|
|
|
ctx_z3 = z3_ctx;
|
|
|
|
ctx_decl = decl_ctx;
|
|
|
|
ctx_var = free_vars_typ;
|
2022-07-28 11:36:36 +03:00
|
|
|
ctx_funcdecl = Var.Map.empty;
|
2022-01-19 12:12:20 +03:00
|
|
|
ctx_z3vars = StringMap.empty;
|
|
|
|
ctx_z3datatypes = EnumMap.empty;
|
2022-07-28 11:36:36 +03:00
|
|
|
ctx_z3matchsubsts = Var.Map.empty;
|
2022-01-19 12:12:20 +03:00
|
|
|
ctx_z3structs = StructMap.empty;
|
2022-02-17 20:34:30 +03:00
|
|
|
ctx_z3unit = z3unit;
|
2022-03-15 20:52:02 +03:00
|
|
|
ctx_z3constraints = [];
|
2022-01-19 12:12:20 +03:00
|
|
|
}
|
2022-01-18 20:51:02 +03:00
|
|
|
end
|
2022-01-10 12:59:30 +03:00
|
|
|
|
2022-01-19 12:17:19 +03:00
|
|
|
module Io = Io.MakeBackendIO (Backend)
|