123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703(**************************************************************************)(* This file is part of the Codex semantics library. *)(* *)(* Copyright (C) 2013-2025 *)(* CEA (Commissariat à l'énergie atomique et aux énergies *)(* alternatives) *)(* *)(* you can redistribute it and/or modify it under the terms of the GNU *)(* Lesser General Public License as published by the Free Software *)(* Foundation, version 2.1. *)(* *)(* It is distributed in the hope that it will be useful, *)(* but WITHOUT ANY WARRANTY; without even the implied warranty of *)(* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *)(* GNU Lesser General Public License for more details. *)(* *)(* See the GNU Lesser General Public License version 2.1 *)(* for more details (enclosed in the file LICENSE). *)(* *)(**************************************************************************)moduleLog=Tracelog.Make(structletcategory="Terms.SMT"end);;(* This is sound only if we check that binaries cannot overflow. *)letoption_translate_binary_to_integer=Codex_config.translation_to_smt_use_integer();;openOperator.Function_symbolmoduleIn_bits=Units.In_bits(* Generic: translation of operators to SMTLIB. It can create new
definitions; then the result of the translation is the freshly
created variable. *)moduleMake(T:Sig.TERMS)(S:Smtbackend.Smtlib_sig.UNTYPED_S)=struct(* Group pairs of Terms. *)type'at=|Unit:ar0t|Single:'aT.t->'aar1t|Pair:'aT.t*'bT.t->('a,'b)ar2tletvarname=function|T.AnyT.Bool{id}->"b"^(string_of_int@@T.Id.to_intid)^"__"|T.AnyT.Integer{id}->"i"^(string_of_int@@T.Id.to_intid)^"__"|T.AnyT.Binary{id}->"B"^(string_of_int@@T.Id.to_intid)^"__"|T.AnyT.Enum{id}->"e"^(string_of_int@@T.Id.to_intid)^"__";;moduleMake(M:sigvaltranslate:'aT.t->S.value(* Create definitions for constr. *)valdefine_var:constr:T.any->S.sort->S.value->S.valuevaldeclare_var:constr:T.any->S.sort->S.valueend)=structlettranslate:typeresarg.resT.t->(arg,res)function_symbol->argt->S.value=funconstrfunction_symbolarg->letar2opab=op(M.translatea)(M.translateb)inletar1oparg=op(M.translatearg)inletdefvar=M.define_var~constr:(T.Anyconstr)inletdefbool=defvarS.boolinletdefbin~(size:In_bits.t)=defvar(S.bitvec(size:>int))inletdefint=defvarS.intin(* Translate the arguments, but replace with unknown. *)letar2_unknownsortab=let_=M.translateainlet_=M.translatebinM.declare_var~constr:(T.Anyconstr)sortinletar1_unknownsorta=let_=M.translateainM.declare_var~constr:(T.Anyconstr)sortin(* Normal translation. *)let[@warning"-21"]normal:(arg,res)function_symbol*argt->S.value=function|True,Unit->defbool@@S.true_|False,Unit->defbool@@S.false_|And,Pair(a,b)->defbool@@ar2S.(&&)ab|Or,Pair(a,b)->defbool@@ar2S.(||)ab|Not,Singlea->defbool@@ar1S.nota|BoolUnion,Pair(a,b)->assertfalse|Biconst(size,k),Unit->defbin~size@@S.bvlit~size:(size:>int)k|Beq(size),Pair(a,b)->defbool@@ar2S.(=)ab|Bisle(size),Pair(a,b)->defbool@@ar2S.bvsleab|Biule(size),Pair(a,b)->defbool@@ar2S.bvuleab|Biadd{size},Pair(a,b)->defbin~size@@ar2S.bvaddab|Bisub{size},Pair(a,b)->defbin~size@@ar2(funab->S.bvadda(S.bvnegb))ab|Bimul{size},Pair(a,b)->defbin~size@@ar2S.bvmulab|Bismod(size),Pair(a,b)->defbin~size@@ar2S.bvsremab|Bisdiv(size),Pair(a,b)->defbin~size@@ar2S.bvsdivab|Biumod(size),Pair(a,b)->defbin~size@@ar2S.bvuremab|Biudiv(size),Pair(a,b)->defbin~size@@ar2S.bvudivab|Band(size),Pair(a,b)->defbin~size@@ar2S.bvandab|Bor(size),Pair(a,b)->defbin~size@@ar2S.bvorab|Bxor(size),Pair(a,b)->defbin~size@@ar2S.bvxorab|Bshl{size},Pair(a,b)->defbin~size@@ar2S.bvshlab|Blshr(size),Pair(a,b)->defbin~size@@ar2S.bvlshrab|Bashr(size),Pair(a,b)->defbin~size@@ar2S.bvashrab|Bconcat(size1,size2),Pair(a,b)->defbin~size:In_bits.(size1+size2)@@ar2S.concatab|Bextract{size;index;oldsize},Single(a)->letfirst=(index:>int)inletlast=(index:>int)+(size:>int)-1indefbin~size@@ar1(S.extract~first~last)a|Bofbool(size),Singlea->assertfalse|Bchoose(_,size),Singlea->assertfalse;defint@@M.translatea(* XXX: TODO. *)|Bunion(cond,size),Pair(a,b)->assertfalse;defint@@M.translatea(* XXX: TODO *)|Buext(size),Singlea->defbin~size@@ar1(S.zero_extend(In_bits.(size-T.size_ofa):>int))a|Bsext(size),Singlea->defbin~size@@ar1(S.sign_extend(In_bits.(size-T.size_ofa):>int))a|Iconstk,Unit->defint@@S.numeralzk|Idiv,Pair(a,b)->defint@@ar2S.divab|Imod,Pair(a,b)->defint@@ar2S.moduab|Iadd,Pair(a,b)->defint@@ar2S.(+)ab|Isub,Pair(a,b)->defint@@ar2S.(-)ab|Ieq,Pair(a,b)->defbool@@ar2S.(=)ab|Ile,Pair(a,b)->defbool@@ar2S.(<=)ab|Imul,Pair(a,b)->defint@@ar2S.(*)ab(* Note: the constraint should have been translated to * or /
when feasible. *)|Ishl,Pair(a,b)->ar2_unknownS.intab|Ishr,Pair(a,b)->ar2_unknownS.intab|Iand,Pair(a,b)->ar2_unknownS.intab|Ior,Pair(a,b)->ar2_unknownS.intab|Ixor,Pair(a,b)->ar2_unknownS.intab|Itimesk,Singlea->defint@@ar1(S.(*)(S.numeralzk))a(* TODO: Bitblasting translation to booleans might be more effective. *)|EnumConst(case),Unit->defint@@S.numeralz(Z.of_intcase)|CaseOf(case),Singlea->defbool@@ar1(S.(=)@@S.numeralz@@Z.of_intcase)a|_->.in(* Unsound translation; sound only if we check that no binary
overflow is possible. Overloads the normal translation. *)letbinary_to_integer:(arg,res)function_symbol*argt->S.value=function|Biconst(size,k),Unit->defint@@S.numeralzk|Beq(size),Pair(a,b)->defbool@@ar2S.(=)ab|Bisle(size),Pair(a,b)->defbool@@ar2S.(<=)ab|Biule(size),Pair(a,b)->defbool@@ar2S.(<=)ab|Biadd{size},Pair(a,b)->defint@@ar2S.(+)ab|Bisub{size},Pair(a,b)->defint@@ar2S.(-)ab|Bimul{size},Pair(a,b)->defint@@ar2S.(*)ab|Bismod(size),Pair(a,b)->defint@@ar2S.moduab|Bisdiv(size),Pair(a,b)->defint@@ar2S.divab|Biumod(size),Pair(a,b)->defint@@ar2S.moduab|Biudiv(size),Pair(a,b)->defint@@ar2S.divab|Band(size),Pair(a,b)->ar2_unknownS.intab|Bor(size),Pair(a,b)->ar2_unknownS.intab|Bxor(size),Pair(a,b)->ar2_unknownS.intab|Bshl{size},Pair(a,b)->ar2_unknownS.intab|Blshr(size),Pair(a,b)->ar2_unknownS.intab|Bashr(size),Pair(a,b)->ar2_unknownS.intab|Bconcat(size1,size2),Pair(a,b)->ar2_unknownS.intab|Bextract{size;index;oldsize},Single(a)whenindex==In_bits.zero->defint@@S.modu(M.translatea)(S.numeralz@@Z.pred@@Z.shift_leftZ.one(size:>int))|Bextract{size;index;oldsize},Single(a)->let_first=indexinlet_last=(index:>int)+(size:>int)-1inar1_unknownS.inta|Bofbool(size),Singlea->assertfalse|Buext(size),Singlea->defint@@M.translatea|Bsext(size),Singlea->defint@@M.translatea|x->normalxinifoption_translate_binary_to_integerthenbinary_to_integer(function_symbol,arg)elsenormal(function_symbol,arg)endend(* Translation to first-order, quantifier-free, SMT formula *)moduleMakeFirstOrder(T:Sig.TERMS)(S:Smtbackend.Smtlib_sig.UNTYPED_S)=structmoduleM=Make(T)(S)modulerecMakeArg:sigvaltranslate:'aT.t->S.valuevaldefine_var:constr:T.any->S.sort->S.value->S.valuevaldeclare_var:constr:T.any->S.sort->S.valueend=structopenTletar0~constrtag=MakeApply.translateconstrtagM.Unitletar1~constrtaga=MakeApply.translateconstrtag(M.Singlea)letar2~constrtagab=MakeApply.translateconstrtag(M.Pair(a,b))moduleAnyHash=Hashtbl.Make(T.Any);;lettr_memo=AnyHash.create17;;letrectranslate_:typea.aT.t->S.value=funconstr->matchconstrwith|Bool{term=(Mu_formal_)}->S.declare_varS.bool|Bool{term=Tuple_get_}->assertfalse|Bool{term=Unknown_level}->S.declare_varS.bool|Bool{term=Empty}->assertfalse|Bool{term=T2{tag;a;b}}->ar2~constrtagab|Bool{term=T1{tag;a}}->ar1~constrtaga|Bool{term=T0{tag}}->ar0~constrtag|Integer{term=(Mu_formal_)}->S.declare_varS.int|Integer{term=Tuple_get(i,Nondet{conda_bool;condb_bool;a;b})}->letAnyai=Immutable_array.getaiinletAnybi=Immutable_array.getbiinletv=S.declare_varS.intinlettrconda=(translateconda_bool)inlettrcondb=(translatecondb_bool)inS.assert_@@S.(=>)(* Temporary builds a constrain: often this creates a simplified constrain. *)(* (S.(||) trconda trcondb) *)(translate@@T.Build.Boolean.(||)conda_boolcondb_bool)(S.(||)(S.(&&)trconda(S.(=)v(translateai)))(S.(&&)trcondb(S.(=)v(translatebi))));v|Integer{term=Tuple_get(i,Mu_)}->S.declare_varS.int|Integer{term=Unknown_level}->S.declare_varS.int|Integer{term=Empty}->assertfalse|Integer{term=T2{tag;a;b}}->ar2~constrtagab|Integer{term=T1{tag;a}}->ar1~constrtaga|Integer{term=T0{tag}}->ar0~constrtag|Binary{term=_}->assertfalse|Bool{term=_}->assertfalse|Integer{term=_}->assertfalse|Enum{term=_}->assertfalseandtranslate:typea.aT.t->S.value=funx->letany=AnyxintryAnyHash.findtr_memoanywithNot_found->letres=translate_xinAnyHash.replacetr_memoanyres;res;;letdefine_var~constr=S.define_var~name:(M.varnameconstr)letdeclare_var~constr=S.declare_var~name:(M.varnameconstr)endandMakeApply:sigvaltranslate:'resT.t->('arg,'res)Operator.Function_symbol.function_symbol->'argM.t->S.valueend=M.Make(MakeArg)lettranslateassertion=letassertion=MakeArg.translateassertioninS.assert_assertion;matchS.check_sat()with|S.Sat->Smtbackend.Smtlib_sig.Sat()|S.Unsat->Smtbackend.Smtlib_sig.Unsat|S.Unknown->Smtbackend.Smtlib_sig.Unknownend(* Translation to Horn clauses, using z3 extensions for doing so
(declared variables are implicitly universally quantified). *)moduleMakeHorn(T:Sig.TERMS)(S:Smtbackend.Smtlib_sig.UNTYPED_MUZ)=structmoduleM=Make(T)(S)moduleCapturedSet=Set.Make(T.Any)moduleSlicing=Slicing.Make(T)(* The hashes etc. depend on the slicing, since we don't produce the
same thing depending on the assertion we want to prove. *)moduleMake(Slicing:sigvalslicing:T.cfg_node->intlistend)=structmodulerecMakeArg:sigvaltranslate:'aT.t->S.valuevalget_rels_and_captured_vars:T.level->T.anylist->CapturedSet.t*S.valuelistvaldefine_var:constr:T.any->S.sort->S.value->S.valuevaldeclare_var:constr:T.any->S.sort->S.valueend=structopenT(* We do not use Ephemeron here; especially, some Terms are
created temporarily during the conversion to SMT, but we still
need their translation. *)moduleAnyHash=Hashtbl.Make(T.Any);;moduleCFG_Node_Hash=Hashtbl.Make(T.CFG_Node);;(* Map from each constrain/tuple to the corresponding variable. *)lettr_memo=AnyHash.create17;;lettr_tuple_memo=CFG_Node_Hash.create17;;(* Map from each constrain/tuple to a relation that defines this constrain, if any. *)lettr_memo_rel=AnyHash.create17;;lettr_tuple_rel=CFG_Node_Hash.create17;;(* Map from each mu to the set of variables that it captures. *)letcaptured_hash=CFG_Node_Hash.create17;;(* let string_of_id id = string_of_int @@ T.Id.to_int id *)letar0~constrtag=MakeApply.translateconstrtagM.Unitletar1~constrtaga=MakeApply.translateconstrtag(M.Singlea)letar2~constrtagab=MakeApply.translateconstrtag(M.Pair(a,b))let[@warning"-11"]rectranslate_:typea.aT.t->S.value=funconstr->matchconstrwith|Bool{term=(Mu_formal_)|Unknown_|Inductive_var_}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.declare_muz_var~name:(M.varname@@Anyconstr)S.bool|Integer{term=(Mu_formal_)|Unknown_|Inductive_var_}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.declare_muz_var~name:(M.varname@@Anyconstr)S.int|Binary{size;term=(Mu_formal_)|Unknown_|Inductive_var_}->lettr_typ=ifoption_translate_binary_to_integerthenS.intelseS.bitvec(size:>int)inAnyHash.replacetr_memo_rel(Anyconstr)None;S.declare_muz_var~name:(M.varname@@Anyconstr)tr_typ|Enum{term=(Mu_formal_)|Unknown_|Inductive_var_}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.declare_muz_var~name:(M.varname@@Anyconstr)S.int(* Not handled by SMTlib; we over-approximate by choosing a fixed constant. *)|Bool{term=Empty}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.false_|Integer{term=Empty}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.numeral0|Binary{size;term=Empty}->AnyHash.replacetr_memo_rel(Anyconstr)None;ifoption_translate_binary_to_integerthenS.numeral0elseS.bvlit~size:(size:>int)Z.zero|Enum{term=Empty}->AnyHash.replacetr_memo_rel(Anyconstr)None;S.numeral0|Bool{term=T2{tag;a;b}}->ar2~constrtagab|Bool{term=T1{tag;a}}->ar1~constrtaga|Bool{term=T0{tag}}->ar0~constrtag|Integer{term=T2{tag;a;b}}->ar2~constrtagab|Integer{term=T1{tag;a}}->ar1~constrtaga|Integer{term=T0{tag}}->ar0~constrtag|Binary{term=T2{tag;a;b}}->ar2~constrtagab|Binary{term=T1{tag;a}}->ar1~constrtaga|Binary{term=T0{tag}}->ar0~constrtag|Enum{term=T2{tag;a;b}}->ar2~constrtagab|Enum{term=T1{tag;a}}->ar1~constrtaga|Enum{term=T0{tag}}->ar0~constrtag|Binary{term=Tuple_get(i,tup)}->AnyHash.replacetr_memo_rel(Anyconstr)None;Immutable_array.get(translate_tupletup)i|Integer{term=Tuple_get(i,tup)}->AnyHash.replacetr_memo_rel(Anyconstr)None;Immutable_array.get(translate_tupletup)i|Bool{term=Tuple_get(i,tup)}->AnyHash.replacetr_memo_rel(Anyconstr)None;Immutable_array.get(translate_tupletup)i|Enum{term=Tuple_get(i,tup)}->AnyHash.replacetr_memo_rel(Anyconstr)None;Immutable_array.get(translate_tupletup)i(* Alternate translation for nondet: define each element of a
nondet individually. MAYBE: compare if it makes a difference.
TODO: This version should eliminate the elements in the
nondet that are not needed. *)|Integer{term=Tuple_get(i,Nondet{conda_bool;condb_bool;a;b})}->letAnyai=Immutable_array.getaiinletAnybi=Immutable_array.getbiinletv=S.declare_muz_var~name:(M.varname@@Anyconstr)S.intinlettrconda=(translateconda_bool)inlettrcondb=(translatecondb_bool)inletrel=S.(=>)(* May build a constrain temporarily, which can be garbage-collected. *)(translate@@T.Build.Boolean.(||)conda_boolcondb_bool)(S.(||)(S.(&&)trconda(S.(=)v(translateai)))(S.(&&)trcondb(S.(=)v(translatebi))))inAnyHash.replacetr_memo_rel(Anyconstr)(Somerel);vandtranslate:typea.aT.t->S.value=funx->letany=AnyxintryAnyHash.findtr_memoanywithNot_found->letres=translate_xinAnyHash.replacetr_memoanyres;res(* Translate to prefix and sort *)andany_to_prefix_sort(Anyx)=matchxwith|(Bool_)->"b",S.bool|(Integer_)->"i",S.int|(Binary{size})->"B",ifoption_translate_binary_to_integerthenS.intelseS.bitvec(size:>int)|(Enum_)->assertfalseandany_to_sortx=snd@@any_to_prefix_sortxanddummy_var=S.declare_muz_var~name:"unused"S.boolandtranslate_tuple_tup=letused_indices=Slicing.slicingtupin(* When the indice is used, declare a variable, else fill with dummy. *)letdeclare_varslengthnamemodel=letrecloopindicesm=matchindices,mwith|[],mwhenm==length->[],[]|(i::rest,m)wheni>m->letx,y=loopindices(m+1)inx,dummy_var::y|[],m->letx,y=loopindices(m+1)inx,dummy_var::y|i::rest,mwheni==m->letprefix,sort=any_to_prefix_sort@@Immutable_array.getmodeliinletv=S.declare_muz_var~name:(prefix^name)sortinletx,y=looprest(m+1)inv::x,v::y|i::rest,m->assertfalse(* Impossible *)inletused,all=loopused_indices0inassert(List.lengthall==Immutable_array.lengthmodel);assert(List.lengthused==List.lengthused_indices);used,allinmatchtupwith|T.Inductive_vars_->assertfalse|T.Nondet{a;conda_bool;b;condb_bool}astup->lettrconda=(translateconda_bool)inlettrcondb=(translatecondb_bool)inletboth=(translate@@T.Build.Boolean.(||)conda_boolcondb_bool)inletv,ret=declare_vars(Immutable_array.lengtha)"nondet"ainletv_eq_a=S.and_list@@trconda::List.fold_left2(funaccvii->let(Anyai)=Immutable_array.getaiinS.(=)vi(translateai)::acc)[]vused_indicesinletv_eq_b=S.and_list@@trcondb::List.fold_left2(funaccvii->let(Anybi)=Immutable_array.getbiinS.(=)vi(translatebi)::acc)[]vused_indicesinletrel=S.(=>)both(S.(||)v_eq_av_eq_b)inCFG_Node_Hash.replacetr_tuple_reltuprel;ret|T.Mu{level;init;var;body;body_cond}asmu->(* First pass: output the declarations and compute translations. *)(* Note: we convert everything to a _reversed_ list. *)let(bodyt,initt,vart)=lettranslate_array_to_lista=List.fold_left(funacci->letAnyx=Immutable_array.getaiin(translatex)::acc)[]used_indicesintranslate_array_to_listbody,translate_array_to_listinit,translate_array_to_listvarinletmu_arg_sort=List.fold_left(funacci->letvi=Immutable_array.getinitiin(any_to_sortvi)::acc)[]used_indicesinletbody_cond_t=translatebody_condinletv,ret=declare_vars(Immutable_array.lengthinit)"mu"initinletv_list=List.revvin(* Second pass: compute the definition of the loop body, and
the set of captured variables, i.e. variables used inside
the mu, that have been defined before the mu, and which must be
explicitely passed as an (input) argument in the Horn formalism. *)let(captured,acc_rel)=letfiltered_body=used_indices|>List.map(funi->Immutable_array.getbodyi)inget_rels_and_captured_varslevel@@(Anybody_cond)::filtered_bodyin(* Captured is also used to compute the dependencies of a mu body. *)CFG_Node_Hash.replacecaptured_hashmucaptured;letcaptured_var_sort=CapturedSet.fold(funxacc->(any_to_sortx)::acc)captured[]inletcaptured_var_tr=CapturedSet.fold(funxacc->(x|>functionAnyx->x|>translate)::acc)captured[]in(* Output the definitions for mu. *)(* Order: mu(captured_vars,input,output). *)let(query,name)=S.declare_rel~name:"mu"(captured_var_sort@mu_arg_sort@mu_arg_sort)in(* mu(captured_var,init,init). Note: init may be replaced by a constant, this is OK. *)(* S.rule [] @@ query @@ captured_var_tr @ vart @ vart; *)S.rule[]@@query@@captured_var_tr@initt@initt;(* mu(captured,var,init) && rels => mu(captured,body,init). *)letbody_start=query@@captured_var_tr@vart@inittinS.rule(body_start::body_cond_t::acc_rel)(query@@captured_var_tr@bodyt@initt);(* Note that unknown() variables appear nowhere, because they
are implicitely universally quantified. *)letrel=query@@captured_var_tr@v_list@inittinCFG_Node_Hash.replacetr_tuple_relmurel;retandtranslate_tupletup=tryCFG_Node_Hash.findtr_tuple_memotupwithNot_found->letres=translate_tuple_tupinletres=Immutable_array.of_listresinCFG_Node_Hash.replacetr_tuple_memotupres;res(* DFS iteration, starting from root nodes, for all elements with
the same level. Computes the set of captured vars (i.e. found
elements whose level is < level), and fold on the relations for
every element traversed. *)andget_rels_and_captured_vars:level->T.anylist->(CapturedSet.t*S.valuelist)=funlevelterm_list->letvisited=AnyHash.create17inletvisited_tuple=CFG_Node_Hash.create17in(* DFS iteration, but only for constrains in the loop
(i.e. whose level correspond to the one in the loop). *)letrecloopaccnode=ifAnyHash.memvisitednodethenaccelsebeginAnyHash.addvisitednode();letT.Anyn=nodeinletnlevel=T.levelninifnlevel!=level+1then(* Capture a variable, which is not visited. *)ifnlevel==-1thenacc(* Do not capture constants. *)elseletacc_cset,acc_rel=accinCapturedSet.addnodeacc_cset,acc_relelseletacc=beginmatchT.Utils.get_termnwith|T.T2{a;b}->letacc=loopacc(T.Anya)inletacc=loopacc(T.Anyb)inacc|T.T1{a}->loopacc(T.Anya)|T.T0_|T.Mu_formal_|T.Unknown_|T.Empty|T.Inductive_var_->acc|T.Tuple_get(i,tup)->loop_tupleacctupendinmatchAnyHash.findtr_memo_relnodewith|None->acc|Somerel->letacc_cset,acc_rel=accinacc_cset,rel::acc_relendandloop_tupleacctup=ifCFG_Node_Hash.memvisited_tupletupthenaccelsebeginCFG_Node_Hash.addvisited_tupletup();(* Get the dependencies of the tuple. *)letacc=matchtupwith|Inductive_vars_->assertfalse|Nondet{conda_bool;condb_bool;a;b}->letacc=loopacc@@T.Anyconda_boolinletacc=loopacc@@T.Anycondb_boolinletused_indices=Slicing.slicingtupinletacc=List.fold_left(funacci->loopacc@@Immutable_array.getai)accused_indicesinletacc=List.fold_left(funacci->loopacc@@Immutable_array.getbi)accused_indicesin(* let acc = Immutable_array.fold_left loop acc a in
* let acc = Immutable_array.fold_left loop acc b in *)acc|Mu{init}asmu->letcaptured=CFG_Node_Hash.findcaptured_hashmuinletacc=CapturedSet.fold(funxacc->loopaccx)capturedaccinletused_indices=Slicing.slicingtupinletacc=List.fold_left(funacci->loopacc@@Immutable_array.getiniti)accused_indicesinaccin(* Add the tuple relation itself. *)letrel=CFG_Node_Hash.findtr_tuple_reltupinletacc_cset,acc_rel=accinacc_cset,rel::acc_relendinList.fold_leftloop(CapturedSet.empty,[])term_list;;(* Define the variables, and fill [tr_memo_rel]. *)letdefine_var~constrsortexpr=letAny(q)=constrin(* Do not define relations for constants. *)matchconstrwith|Any(Bool{term=T0_})->AnyHash.replacetr_memo_relconstrNone;expr|Any(Integer{term=T0_})->AnyHash.replacetr_memo_relconstrNone;expr|Any(Binary{term=T0_})->AnyHash.replacetr_memo_relconstrNone;expr|_->letvar=S.declare_muz_var~name:(M.varnameconstr)sortinletrel=S.(=)varexprinAnyHash.replacetr_memo_relconstr(Somerel);var;;letdeclare_var~constrsort=letvar=S.declare_muz_var~name:(M.varnameconstr)sortinAnyHash.replacetr_memo_relconstrNone;var;;endandMakeApply:sigvaltranslate:'resT.t->('arg,'res)Operator.Function_symbol.function_symbol->'argM.t->S.valueend=M.Make(MakeArg)endlettranslateassertion=letmoduleReal_Slicing()=struct(* Codex_log.feedback "Slicing on %a" T.pretty assertion;; *)letslicing=Slicing.depsassertion;;endinletmoduleDummy_Slicing()=struct[@@@ocaml.warning"-32"]letslicingt=letlength=matchtwith|T.Inductive_vars_->assertfalse|T.Mu{init}->Immutable_array.lengthinit|T.Nondet{a}->Immutable_array.lengthainletrecloopacc=function|xwhenx<0->acc|n->loop(n::acc)(n-1)inloop[](length-1)[@@@ocaml.warning"+32"]endinletmoduleM=Make(Real_Slicing())in(* let module M = Make(Dummy_Slicing()) in *)(* First pass: generation all the variable declarations; compute
the definitions. *)letassertiont=(M.MakeArg.translateassertion)in(* Second pass: compute the query. *)let(_,acc)=M.MakeArg.get_rels_and_captured_vars(-1)[T.Anyassertion]in(* We use an implicit quantification over free variables, and we
do not need any parameter for the query. *)let(query,name)=S.declare_rel~name:"qu"[]inS.rule(assertiont::acc)(query[]);(* We had to set this to false in older z3 versions. Works fine at
least since 4.4.1 *)letis_master=trueinletres=matchis_masterwith|true->S.query2name|false->S.query(query[](* used_horn_variables *))in(matchreswith|S.Sat->Log.debug(funp->p"Result is sat");Smtbackend.Smtlib_sig.Sat()|S.Unsat->Log.debug(funp->p"Result is unsat");Smtbackend.Smtlib_sig.Unsat|S.Unknown->Smtbackend.Smtlib_sig.Unknown);;end