123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636(* 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. *)openUtilsopenDcalcopenAstopenZ3moduleStringMap:Map.Swithtypekey=String.t=Map.Make(String)typecontext={ctx_z3:Z3.context;(* The Z3 context, used to create symbols and expressions *)ctx_decl:decl_ctx;(* The declaration context from the Catala program, containing information to precisely pretty
print Catala expressions *)ctx_var:typPos.markedVarMap.t;(* A map from Catala variables to their types, needed to create Z3 expressions of the right
sort *)ctx_funcdecl:FuncDecl.func_declVarMap.t;(* 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.tStringMap.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 *)ctx_z3datatypes:Sort.sortEnumMap.t;(* A map from Catala enumeration names to the corresponding Z3 sort, from which we can retrieve
constructors and accessors *)ctx_z3matchsubsts:Expr.exprVarMap.t;(* A map from Catala temporary variables, generated when translating a match, to the corresponding
enum accessor call as a Z3 expression *)ctx_z3structs:Sort.sortStructMap.t;(* A map from Catala struct names to the corresponding Z3 sort, from which we can retrieve the
constructor and the accessors *)ctx_z3unit:Sort.sort*Expr.expr;(* A pair containing the Z3 encodings of the unit type, encoded as a tuple of 0 elements, and
the unit value *)}(** 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], [ctx_z3vars], and [ctx_z3datatypes] are computed
dynamically during the translation requiring us to pass the context around in a functional way **)(** [add_funcdecl] adds the mapping between the Catala variable [v] and the Z3 function declaration
[fd] to the context **)letadd_funcdecl(v:Var.t)(fd:FuncDecl.func_decl)(ctx:context):context={ctxwithctx_funcdecl=VarMap.addvfdctx.ctx_funcdecl}(** [add_z3var] adds the mapping between [name] and the Catala variable [v] to the context **)letadd_z3var(name:string)(v:Var.t)(ctx:context):context={ctxwithctx_z3vars=StringMap.addnamevctx.ctx_z3vars}(** [add_z3enum] adds the mapping between the Catala enumeration [enum] and the corresponding Z3
datatype [sort] to the context **)letadd_z3enum(enum:EnumName.t)(sort:Sort.sort)(ctx:context):context={ctxwithctx_z3datatypes=EnumMap.addenumsortctx.ctx_z3datatypes}(** [add_z3var] adds the mapping between temporary variable [v] and the Z3 expression [e]
representing an accessor application to the context **)letadd_z3matchsubst(v:Var.t)(e:Expr.expr)(ctx:context):context={ctxwithctx_z3matchsubsts=VarMap.addvectx.ctx_z3matchsubsts}(** [add_z3struct] adds the mapping between the Catala struct [s] and the corresponding Z3 datatype
[sort] to the context **)letadd_z3struct(s:StructName.t)(sort:Sort.sort)(ctx:context):context={ctxwithctx_z3structs=StructMap.addssortctx.ctx_z3structs}(** For the Z3 encoding of Catala programs, we define the "day 0" as Jan 1, 1900 **)letbase_day=CalendarLib.Date.make190011(** [unique_name] returns the full, unique name corresponding to variable [v], as given by Bindlib **)letunique_name(v:Var.t):string=Format.asprintf"%s_%d"(Bindlib.name_ofv)(Bindlib.uid_ofv)(** [date_to_int] translates [date] to an integer corresponding to the number of days since Jan 1,
1900 **)letdate_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 *)letdate:CalendarLib.Date.t=CalendarLib.Printer.Date.from_string(Runtime.date_to_stringd)inletperiod=CalendarLib.Date.subdatebase_dayinCalendarLib.Date.Period.nb_daysperiod(** [date_of_year] translates a [year], represented as an integer into an OCaml date corresponding
to Jan 1st of the same year *)letdate_of_year(year:int)=Runtime.date_of_numbersyear11(** Returns the date (as a string) corresponding to nb days after the base day, defined here as Jan
1, 1900 **)letnb_days_to_date(nb:int):string=CalendarLib.Printer.Date.to_string(CalendarLib.Date.addbase_day(CalendarLib.Date.Period.daynb))(** [print_z3model_expr] pretty-prints the value [e] given by a Z3 model according to the Catala
type [ty], corresponding to [e] **)letrecprint_z3model_expr(ctx:context)(ty:typPos.marked)(e:Expr.expr):string=letprint_lit(ty:typ_lit)=matchtywith(* TODO: Print boolean according to current language *)|TBool->Expr.to_stringe(* 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->""|TInt->Expr.to_stringe|TRat->Arithmetic.Real.to_decimal_stringe!Cli.max_prec_digits(* TODO: Print the right money symbol according to language *)|TMoney->letz3_str=Expr.to_stringein(* The Z3 model returns an integer corresponding to the amount of cents. We reformat it as
dollars *)letto_dollarss=Runtime.money_to_string(Runtime.money_of_cents_strings)inifString.containsz3_str'-'thenFormat.asprintf"-%s $"(to_dollars(String.subz3_str3(String.lengthz3_str-4)))elseFormat.asprintf"%s $"(to_dollarsz3_str)(* 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_stringe))|TDuration->failwith"[Z3 model]: Pretty-printing of duration literals not supported"inmatchPos.unmarktywith|TLitty->print_litty|TTuple(_,Somename)->lets=StructMap.findnamectx.ctx_decl.ctx_structsinletget_fieldname(fn:StructFieldName.t):string=Pos.unmark(StructFieldName.get_infofn)inletfields=List.map2(fun(fn,ty)e->Format.asprintf"-- %s : %s"(get_fieldnamefn)(print_z3model_exprctxtye))s(Expr.get_argse)inletfields_str=String.concat" "fieldsinFormat.asprintf"%s { %s }"(Pos.unmark(StructName.get_infoname))fields_str|TTuple(_,None)->failwith"[Z3 model]: Pretty-printing of unnamed structs not supported"|TEnum(_tys,name)->(* The value associated to the enum is a single argument *)lete'=List.hd(Expr.get_argse)inletfd=Expr.get_func_decleinletfd_name=Symbol.to_string(FuncDecl.get_namefd)inletenum_ctrs=EnumMap.findnamectx.ctx_decl.ctx_enumsinletcase=List.find(fun(ctr,_)->String.equalfd_name(Pos.unmark(EnumConstructor.get_infoctr)))enum_ctrsinFormat.asprintf"%s (%s)"fd_name(print_z3model_exprctx(sndcase)e')|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"(** [print_model] pretty prints a Z3 model, used to exhibit counter examples 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) **)letprint_model(ctx:context)(model:Model.model):string=letdecls=Model.get_declsmodelinFormat.asprintf"%a"(Format.pp_print_list~pp_sep:(funfmt()->Format.fprintffmt"\n")(funfmtd->ifFuncDecl.get_arityd=0then(* Constant case *)matchModel.get_const_interpmodeldwith(* TODO: Better handling of this case *)|None->failwith"[Z3 model]: A variable does not have an associated Z3 solution"(* Print "name : value\n" *)|Somee->letsymbol_name=Symbol.to_string(FuncDecl.get_named)inletv=StringMap.findsymbol_namectx.ctx_z3varsinFormat.fprintffmt"%s %s : %s"(Cli.print_with_style[ANSITerminal.blue]"%s""-->")(Cli.print_with_style[ANSITerminal.yellow]"%s"(Bindlib.name_ofv))(print_z3model_exprctx(VarMap.findvctx.ctx_var)e)else(* Declaration d is a function *)matchModel.get_func_interpmodeldwith(* TODO: Better handling of this case *)|None->failwith"[Z3 model]: A variable does not have an associated Z3 solution"(* Print "name : value\n" *)|Somef->letsymbol_name=Symbol.to_string(FuncDecl.get_named)inletv=StringMap.findsymbol_namectx.ctx_z3varsinFormat.fprintffmt"%s %s : %s"(Cli.print_with_style[ANSITerminal.blue]"%s""-->")(Cli.print_with_style[ANSITerminal.yellow]"%s"(Bindlib.name_ofv))(* TODO: Model of a Z3 function should be pretty-printed *)(Model.FuncInterp.to_stringf)))decls(** [translate_typ_lit] returns the Z3 sort corresponding to the Catala literal type [t] **)lettranslate_typ_lit(ctx:context)(t:typ_lit):Sort.sort=matchtwith|TBool->Boolean.mk_sortctx.ctx_z3|TUnit->fstctx.ctx_z3unit|TInt->Arithmetic.Integer.mk_sortctx.ctx_z3|TRat->Arithmetic.Real.mk_sortctx.ctx_z3|TMoney->Arithmetic.Integer.mk_sortctx.ctx_z3(* Dates are encoded as integers, corresponding to the number of days since Jan 1, 1900 *)|TDate->Arithmetic.Integer.mk_sortctx.ctx_z3|TDuration->failwith"[Z3 encoding] TDuration type not supported"(** [translate_typ] returns the Z3 sort correponding to the Catala type [t] **)letrectranslate_typ(ctx:context)(t:typ):context*Sort.sort=matchtwith|TLitt->(ctx,translate_typ_litctxt)|TTuple(_,Somename)->find_or_create_structctxname|TTuple(_,None)->failwith"[Z3 encoding] TTuple type of unnamed struct not supported"|TEnum(_,e)->find_or_create_enumctxe|TArrow_->failwith"[Z3 encoding] TArrow type not supported"|TArray_->failwith"[Z3 encoding] TArray type not supported"|TAny->failwith"[Z3 encoding] TAny type not supported"(** [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 *)andfind_or_create_enum(ctx:context)(enum:EnumName.t):context*Sort.sort=(* Creates a Z3 constructor corresponding to the Catala constructor [c] *)letcreate_constructor(ctx:context)(c:EnumConstructor.t*typPos.marked):context*Datatype.Constructor.constructor=letname,ty=cinletname=Pos.unmark(EnumConstructor.get_infoname)inletctx,arg_z3_ty=translate_typctx(Pos.unmarkty)in(* 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 *)(ctx,Datatype.mk_constructor_sctx.ctx_z3name(Symbol.mk_stringctx.ctx_z3name)(* 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_stringctx.ctx_z3(name^"!0")](* The type of the argument, translated to a Z3 sort *)[Somearg_z3_ty][Sort.get_idarg_z3_ty])inmatchEnumMap.find_optenumctx.ctx_z3datatypeswith|Somee->(ctx,e)|None->letctrs=EnumMap.findenumctx.ctx_decl.ctx_enumsinletctx,z3_ctrs=List.fold_left_mapcreate_constructorctxctrsinletz3_enum=Datatype.mk_sort_sctx.ctx_z3(Pos.unmark(EnumName.get_infoenum))z3_ctrsin(add_z3enumenumz3_enumctx,z3_enum)(** [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 context *)andfind_or_create_struct(ctx:context)(s:StructName.t):context*Sort.sort=matchStructMap.find_optsctx.ctx_z3structswith|Somes->(ctx,s)|None->lets_name=Pos.unmark(StructName.get_infos)inletfields=StructMap.findsctx.ctx_decl.ctx_structsinletz3_fieldnames=List.map(funf->Pos.unmark(StructFieldName.get_info(fstf))|>Symbol.mk_stringctx.ctx_z3)fieldsinletctx,z3_fieldtypes=List.fold_left_map(functxf->Pos.unmark(sndf)|>translate_typctx)ctxfieldsinletz3_sortrefs=List.mapSort.get_idz3_fieldtypesinletmk_struct_s="mk!"^s_nameinletz3_mk_struct=Datatype.mk_constructor_sctx.ctx_z3mk_struct_s(Symbol.mk_stringctx.ctx_z3mk_struct_s)z3_fieldnames(List.map(funx->Somex)z3_fieldtypes)z3_sortrefsinletz3_struct=Datatype.mk_sort_sctx.ctx_z3s_name[z3_mk_struct]in(add_z3structsz3_structctx,z3_struct)(** [translate_lit] returns the Z3 expression as a literal corresponding to [lit] **)lettranslate_lit(ctx:context)(l:lit):Expr.expr=matchlwith|LBoolb->ifbthenBoolean.mk_truectx.ctx_z3elseBoolean.mk_falsectx.ctx_z3|LEmptyError->failwith"[Z3 encoding] LEmptyError literals not supported"|LIntn->Arithmetic.Integer.mk_numeral_ictx.ctx_z3(Runtime.integer_to_intn)|LRatr->Arithmetic.Real.mk_numeral_sctx.ctx_z3(string_of_float(Runtime.decimal_to_floatr))|LMoneym->letz3_m=Runtime.integer_to_int(Runtime.money_to_centsm)inArithmetic.Integer.mk_numeral_ictx.ctx_z3z3_m|LUnit->failwith"[Z3 encoding] LUnit literals not supported"(* Encoding a date as an integer corresponding to the number of days since Jan 1, 1900 *)|LDated->Arithmetic.Integer.mk_numeral_ictx.ctx_z3(date_to_intd)|LDuration_->failwith"[Z3 encoding] LDuration literals not supported"(** [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 *)letfind_or_create_funcdecl(ctx:context)(v:Var.t):context*FuncDecl.func_decl=matchVarMap.find_optvctx.ctx_funcdeclwith|Somefd->(ctx,fd)|None->((* Retrieves the Catala type of the function [v] *)letf_ty=VarMap.findvctx.ctx_varinmatchPos.unmarkf_tywith|TArrow(t1,t2)->letctx,z3_t1=translate_typctx(Pos.unmarkt1)inletctx,z3_t2=translate_typctx(Pos.unmarkt2)inletname=unique_namevinletfd=FuncDecl.mk_func_decl_sctx.ctx_z3name[z3_t1]z3_t2inletctx=add_funcdeclvfdctxinletctx=add_z3varnamevctxin(ctx,fd)|TAny->failwith"[Z3 Encoding] A function being applied has type TAny, the type was not fully inferred"|_->failwith"[Z3 Encoding] Ill-formed VC, a function application does not have a function type")(** [translate_op] returns the Z3 expression corresponding to the application of [op] to the
arguments [args] **)letrectranslate_op(ctx:context)(op:operator)(args:exprPos.markedlist):context*Expr.expr=matchopwith|Ternop_top->let_e1,_e2,_e3=matchargswith|[e1;e2;e3]->(e1,e2,e3)|_->failwith(Format.asprintf"[Z3 encoding] Ill-formed ternary operator application: %a"(Print.format_exprctx.ctx_decl)(EApp((EOpop,Pos.no_pos),args),Pos.no_pos))infailwith"[Z3 encoding] ternary operator application not supported"|Binopbop->((* Special case for GetYear comparisons *)match(bop,args)with|LtKInt,[(EApp((EOp(UnopGetYear),_),[e1]),_);(ELit(LIntn),_)]->letn=Runtime.integer_to_intninletctx,e1=translate_exprctxe1inlete2=Arithmetic.Integer.mk_numeral_ictx.ctx_z3(date_to_int(date_of_yearn))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_ltctx.ctx_z3e1e2)|LteKInt,[(EApp((EOp(UnopGetYear),_),[e1]),_);(ELit(LIntn),_)]->letn=Runtime.integer_to_intninletctx,e1=translate_exprctxe1inletnb_days=ifCalendarLib.Date.is_leap_yearnthen365else364in(* 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 *)lete2=Arithmetic.Integer.mk_numeral_ictx.ctx_z3(date_to_int(date_of_yearn)+nb_days)in(ctx,Arithmetic.mk_lectx.ctx_z3e1e2)|GtKInt,[(EApp((EOp(UnopGetYear),_),[e1]),_);(ELit(LIntn),_)]->letn=Runtime.integer_to_intninletctx,e1=translate_exprctxe1inletnb_days=ifCalendarLib.Date.is_leap_yearnthen365else364in(* 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 year [n], which is Jan 1st +
365 days if [n] is a leap year, Jan 1st + 364 else *)lete2=Arithmetic.Integer.mk_numeral_ictx.ctx_z3(date_to_int(date_of_yearn)+nb_days)in(ctx,Arithmetic.mk_gtctx.ctx_z3e1e2)|GteKInt,[(EApp((EOp(UnopGetYear),_),[e1]),_);(ELit(LIntn),_)]->letn=Runtime.integer_to_intninletctx,e1=translate_exprctxe1inlete2=Arithmetic.Integer.mk_numeral_ictx.ctx_z3(date_to_int(date_of_yearn))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_gectx.ctx_z3e1e2)|_->(letctx,e1,e2=matchargswith|[e1;e2]->letctx,e1=translate_exprctxe1inletctx,e2=translate_exprctxe2in(ctx,e1,e2)|_->failwith(Format.asprintf"[Z3 encoding] Ill-formed binary operator application: %a"(Print.format_exprctx.ctx_decl)(EApp((EOpop,Pos.no_pos),args),Pos.no_pos))inmatchbopwith|And->(ctx,Boolean.mk_andctx.ctx_z3[e1;e2])|Or->(ctx,Boolean.mk_orctx.ctx_z3[e1;e2])|Xor->(ctx,Boolean.mk_xorctx.ctx_z3e1e2)|AddKInt|AddKRat|AddKMoney->(ctx,Arithmetic.mk_addctx.ctx_z3[e1;e2])|Add_->failwith"[Z3 encoding] application of non-integer binary operator Add not supported"|SubKInt|SubKRat|SubKMoney->(ctx,Arithmetic.mk_subctx.ctx_z3[e1;e2])|Sub_->failwith"[Z3 encoding] application of non-integer binary operator Sub not supported"|MultKInt|MultKRat|MultKMoney->(ctx,Arithmetic.mk_mulctx.ctx_z3[e1;e2])|Mult_->failwith"[Z3 encoding] application of non-integer binary operator Mult not supported"|DivKInt|DivKRat|DivKMoney->(ctx,Arithmetic.mk_divctx.ctx_z3e1e2)|Div_->failwith"[Z3 encoding] application of non-integer binary operator Div not supported"|LtKInt|LtKRat|LtKMoney|LtKDate->(ctx,Arithmetic.mk_ltctx.ctx_z3e1e2)|Lt_->failwith"[Z3 encoding] application of non-integer or money binary operator Lt not supported"|LteKInt|LteKRat|LteKMoney|LteKDate->(ctx,Arithmetic.mk_lectx.ctx_z3e1e2)|Lte_->failwith"[Z3 encoding] application of non-integer or money binary operator Lte not \
supported"|GtKInt|GtKRat|GtKMoney|GtKDate->(ctx,Arithmetic.mk_gtctx.ctx_z3e1e2)|Gt_->failwith"[Z3 encoding] application of non-integer or money binary operator Gt not supported"|GteKInt|GteKRat|GteKMoney|GteKDate->(ctx,Arithmetic.mk_gectx.ctx_z3e1e2)|Gte_->failwith"[Z3 encoding] application of non-integer or money binary operator Gte not \
supported"|Eq->(ctx,Boolean.mk_eqctx.ctx_z3e1e2)|Neq->(ctx,Boolean.mk_notctx.ctx_z3(Boolean.mk_eqctx.ctx_z3e1e2))|Map->failwith"[Z3 encoding] application of binary operator Map not supported"|Concat->failwith"[Z3 encoding] application of binary operator Concat not supported"|Filter->failwith"[Z3 encoding] application of binary operator Filter not supported"))|Unopuop->(letctx,e1=matchargswith|[e1]->translate_exprctxe1|_->failwith(Format.asprintf"[Z3 encoding] Ill-formed unary operator application: %a"(Print.format_exprctx.ctx_decl)(EApp((EOpop,Pos.no_pos),args),Pos.no_pos))inmatchuopwith|Not->(ctx,Boolean.mk_notctx.ctx_z3e1)|Minus_->failwith"[Z3 encoding] application of unary operator Minus not supported"(* Omitting the log from the VC *)|Log_->(ctx,e1)|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"|GetYear->failwith"[Z3 encoding] GetYear operator only supported in comparisons with literal")(** [translate_expr] translate the expression [vc] to its corresponding Z3 expression **)andtranslate_expr(ctx:context)(vc:exprPos.marked):context*Expr.expr=lettranslate_match_arm(head:Expr.expr)(ctx:context)(e:exprPos.marked*FuncDecl.func_decllist):context*Expr.expr=lete,accessors=einmatchPos.unmarkewith|EAbs(e,_)->(* Create a fresh Catala variable to substitue and obtain the body *)letfresh_v=Var.make("arm!tmp",Pos.no_pos)inletfresh_e=EVar(fresh_v,Pos.no_pos)in(* Invariant: Catala enums always have exactly one argument *)letaccessor=List.hdaccessorsinletproj=Expr.mk_appctx.ctx_z3accessor[head]in(* The fresh variable should be substituted by a projection into the enum in the body, we
add this to the context *)letctx=add_z3matchsubstfresh_vprojctxinletbody=Bindlib.msubst(Pos.unmarke)[|fresh_e|]intranslate_exprctxbody(* Invariant: Catala match arms are always lambda*)|_->failwith"[Z3 encoding] : Arms branches inside VCs should be lambdas"inmatchPos.unmarkvcwith|EVarv->(matchVarMap.find_opt(Pos.unmarkv)ctx.ctx_z3matchsubstswith|None->(* We are in the standard case, where this is a true Catala variable *)letv=Pos.unmarkvinlett=VarMap.findvctx.ctx_varinletname=unique_namevinletctx=add_z3varnamevctxinletctx,ty=translate_typctx(Pos.unmarkt)in(ctx,Expr.mk_const_sctx.ctx_z3namety)|Somee->(* 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))|ETuple_->failwith"[Z3 encoding] ETuple unsupported"|ETupleAccess(s,idx,oname,_tys)->letname=matchonamewith|None->failwith"[Z3 encoding]: ETupleAccess of unnamed struct unsupported"|Somen->ninletctx,z3_struct=find_or_create_structctxnamein(* This datatype should have only one constructor, corresponding to mk_struct. The accessors
of this constructor correspond to the field accesses *)letaccessors=List.hd(Datatype.get_accessorsz3_struct)inletaccessor=List.nthaccessorsidxinletctx,s=translate_exprctxsin(ctx,Expr.mk_appctx.ctx_z3accessor[s])|EInj_->failwith"[Z3 encoding] EInj unsupported"|EMatch(arg,arms,enum)->letctx,z3_enum=find_or_create_enumctxenuminletctx,z3_arg=translate_exprctxarginlet_ctx,z3_arms=List.fold_left_map(translate_match_armz3_arg)ctx(List.combinearms(Datatype.get_accessorsz3_enum))inletz3_arms=List.map2(funrarm->(* Encodes A? arg ==> body *)letis_r=Expr.mk_appctx.ctx_z3r[z3_arg]inBoolean.mk_impliesctx.ctx_z3is_rarm)(Datatype.get_recognizersz3_enum)z3_armsin(ctx,Boolean.mk_andctx.ctx_z3z3_arms)|EArray_->failwith"[Z3 encoding] EArray unsupported"|ELitl->(ctx,translate_litctxl)|EAbs_->failwith"[Z3 encoding] EAbs unsupported"|EApp(head,args)->(matchPos.unmarkheadwith|EOpop->translate_opctxopargs|EVarv->letctx,fd=find_or_create_funcdeclctx(Pos.unmarkv)in(* Fold_right to preserve the order of the arguments: The head argument is appended at the
head *)letctx,z3_args=List.fold_right(funarg(ctx,acc)->letctx,z3_arg=translate_exprctxargin(ctx,z3_arg::acc))args(ctx,[])in(ctx,Expr.mk_appctx.ctx_z3fdz3_args)|_->failwith"[Z3 encoding] EApp node: Catala function calls should only include operators or \
function names")|EAssert_->failwith"[Z3 encoding] EAssert unsupported"|EOp_->failwith"[Z3 encoding] EOp unsupported"|EDefault_->failwith"[Z3 encoding] EDefault unsupported"|EIfThenElse(e_if,e_then,e_else)->(* Encode this as (e_if ==> e_then) /\ (not e_if ==> e_else) *)letctx,z3_if=translate_exprctxe_ifinletctx,z3_then=translate_exprctxe_theninletctx,z3_else=translate_exprctxe_elsein(ctx,Boolean.mk_andctx.ctx_z3[Boolean.mk_impliesctx.ctx_z3z3_ifz3_then;Boolean.mk_impliesctx.ctx_z3(Boolean.mk_notctx.ctx_z3z3_if)z3_else;])|ErrorOnEmpty_->failwith"[Z3 encoding] ErrorOnEmpty unsupported"(** [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 **)letcreate_z3unit(ctx:Z3.context):Z3.context*(Sort.sort*Expr.expr)=letunit_sort=Tuple.mk_sortctx(Symbol.mk_stringctx"unit")[][]inletmk_unit=Tuple.get_mk_declunit_sortinletunit_val=Expr.mk_appctxmk_unit[]in(ctx,(unit_sort,unit_val))moduleBackend=structtypebackend_context=contexttypevc_encoding=Z3.Expr.exprletprint_encoding(vc:vc_encoding):string=Expr.to_stringvctypemodel=Z3.Model.modeltypesolver_result=ProvenTrue|ProvenFalseofmodeloption|Unknownletsolve_vc_encoding(ctx:backend_context)(encoding:vc_encoding):solver_result=letsolver=Z3.Solver.mk_solverctx.ctx_z3NoneinZ3.Solver.addsolver[Boolean.mk_notctx.ctx_z3encoding];matchZ3.Solver.checksolver[]with|UNSATISFIABLE->ProvenTrue|SATISFIABLE->ProvenFalse(Z3.Solver.get_modelsolver)|UNKNOWN->Unknownletprint_model(ctx:backend_context)(m:model):string=print_modelctxmletis_model_empty(m:model):bool=List.length(Z3.Model.get_declsm)=0lettranslate_expr(ctx:backend_context)(e:Dcalc.Ast.exprPos.marked)=translate_exprctxeletinit_backend()=Cli.debug_print(Format.asprintf"Running Z3 version %s"Version.to_string)letmake_context(decl_ctx:decl_ctx)(free_vars_typ:typPos.markedVarMap.t):backend_context=letcfg=(if!Cli.disable_counterexamplesthen[]else[("model","true")])@[("proof","false")]inletz3_ctx=mk_contextcfginletz3_ctx,z3unit=create_z3unitz3_ctxin{ctx_z3=z3_ctx;ctx_decl=decl_ctx;ctx_var=free_vars_typ;ctx_funcdecl=VarMap.empty;ctx_z3vars=StringMap.empty;ctx_z3datatypes=EnumMap.empty;ctx_z3matchsubsts=VarMap.empty;ctx_z3structs=StructMap.empty;ctx_z3unit=z3unit;}endmoduleIo=Io.MakeBackendIO(Backend)