@@ -9,17 +9,19 @@ package viper.silicon.decider
99import com .typesafe .scalalogging .LazyLogging
1010import viper .silicon .common .config .Version
1111import viper .silicon .interfaces .decider .{Prover , Result , Sat , Unknown , Unsat }
12- import viper .silicon .state .IdentifierFactory
13- import viper .silicon .state .terms .{App , Decl , Fun , FunctionDecl , Implies , MacroDecl , Sort , SortDecl , SortWrapperDecl , Term , sorts }
12+ import viper .silicon .state .{ IdentifierFactory , State }
13+ import viper .silicon .state .terms .{App , Decl , Fun , FunctionDecl , Implies , Ite , MacroDecl , Quantification , Sort , SortDecl , SortWrapperDecl , Term , Trigger , TriggerGenerator , sorts }
1414import viper .silicon .{Config , Map }
1515import viper .silicon .verifier .Verifier
1616import viper .silver .reporter .{InternalWarningMessage , Reporter }
1717import viper .silver .verifier .{MapEntry , ModelEntry , ModelParser , ValueEntry , DefaultDependency => SilDefaultDependency , Model => ViperModel }
18+
1819import java .io .PrintWriter
1920import java .nio .file .Path
20-
2121import scala .collection .mutable
2222import com .microsoft .z3 ._
23+ import com .microsoft .z3 .enumerations .Z3_param_kind
24+ import viper .silicon .decider .Z3ProverAPI .oldVersionOnlyParams
2325import viper .silicon .reporting .ExternalToolError
2426
2527import scala .jdk .CollectionConverters .MapHasAsJava
@@ -28,8 +30,8 @@ import scala.util.Random
2830
2931object Z3ProverAPI {
3032 val name = " Z3-API"
31- val minVersion = Version (" 4.8.6 .0" )
32- val maxVersion = Some (Version (" 4.8.7 .0" )) /* X.Y.Z if that is the *last supported* version */
33+ val minVersion = Version (" 4.8.7 .0" )
34+ val maxVersion = Some (Version (" 4.12.1 .0" )) /* X.Y.Z if that is the *last supported* version */
3335
3436 // these are not actually used, but since there is a lot of code that expects command line parameters and a
3537 // config file, we just supply this information here (whose contents will then be ignored)
@@ -46,34 +48,31 @@ object Z3ProverAPI {
4648 val initialOptions = Map (" auto_config" -> " false" , " type_check" -> " true" )
4749 val boolParams = Map (
4850 " smt.delay_units" -> true ,
49- " delay_units" -> true ,
5051 " smt.mbqi" -> false ,
51- " mbqi" -> false ,
5252 // "pp.bv_literals" -> false, // This is part of z3config.smt2 but Z3 won't accept it via API.
5353 " model.v2" -> true
5454 )
5555 val intParams = Map (
5656 " smt.case_split" -> 3 ,
57- " case_split" -> 3 ,
5857 " smt.qi.max_multi_patterns" -> 1000 ,
59- " qi.max_multi_patterns" -> 1000 ,
6058 " smt.arith.solver" -> 2 ,
61- " arith.solver" -> 2
6259 )
6360 val stringParams : Map [String , String ] = Map (
6461 // currently none
6562 )
6663 val doubleParams = Map (
6764 " smt.qi.eager_threshold" -> 100.0 ,
68- " qi.eager_threshold" -> 100.0 ,
6965 )
66+ val allParams = boolParams ++ intParams ++ stringParams ++ doubleParams
67+ val oldVersionOnlyParams = Set (" smt.arith.solver" )
7068}
7169
7270
7371class Z3ProverAPI (uniqueId : String ,
7472 termConverter : TermToZ3APIConverter ,
7573 identifierFactory : IdentifierFactory ,
76- reporter : Reporter )
74+ reporter : Reporter ,
75+ triggerGenerator : TriggerGenerator )
7776 extends Prover
7877 with LazyLogging
7978{
@@ -114,19 +113,59 @@ class Z3ProverAPI(uniqueId: String,
114113 lastTimeout = - 1
115114 ctx = new Context (Z3ProverAPI .initialOptions.asJava)
116115 val params = ctx.mkParams()
116+
117+ // When setting parameters via API, we have to remove the smt. prefix
118+ def removeSmtPrefix (s : String ) = {
119+ if (s.startsWith(" smt." ))
120+ s.substring(4 )
121+ else
122+ s
123+ }
124+
125+ val useOldVersionParams = version() <= Version (" 4.8.7.0" )
117126 Z3ProverAPI .boolParams.foreach{
118- case (k, v) => params.add(k, v)
127+ case (k, v) =>
128+ if (useOldVersionParams || ! oldVersionOnlyParams.contains(k))
129+ params.add(removeSmtPrefix(k), v)
119130 }
120131 Z3ProverAPI .intParams.foreach{
121- case (k, v) => params.add(k, v)
132+ case (k, v) =>
133+ if (useOldVersionParams || ! oldVersionOnlyParams.contains(k))
134+ params.add(removeSmtPrefix(k), v)
122135 }
123136 Z3ProverAPI .doubleParams.foreach{
124- case (k, v) => params.add(k, v)
137+ case (k, v) =>
138+ if (useOldVersionParams || ! oldVersionOnlyParams.contains(k))
139+ params.add(removeSmtPrefix(k), v)
125140 }
126141 Z3ProverAPI .stringParams.foreach{
127- case (k, v) => params.add(k, v)
142+ case (k, v) =>
143+ if (useOldVersionParams || ! oldVersionOnlyParams.contains(k))
144+ params.add(removeSmtPrefix(k), v)
128145 }
146+ val userProvidedArgs = Verifier .config.proverConfigArgs
129147 prover = ctx.mkSolver()
148+ val descrs = prover.getParameterDescriptions
149+ for ((origKey, vl) <- userProvidedArgs) {
150+ val key = if (origKey.startsWith(" smt." ))
151+ origKey.substring(4 )
152+ else
153+ origKey
154+ val keySymbol = ctx.mkSymbol(key)
155+ val param_kind = descrs.getKind(keySymbol)
156+ param_kind match {
157+ case Z3_param_kind .Z3_PK_BOOL =>
158+ params.add(key, vl.toBoolean)
159+ case Z3_param_kind .Z3_PK_UINT =>
160+ params.add(key, vl.toInt)
161+ case Z3_param_kind .Z3_PK_DOUBLE =>
162+ params.add(key, vl.toDouble)
163+ case Z3_param_kind .Z3_PK_STRING =>
164+ params.add(key, vl)
165+ case _ =>
166+ reporter.report(InternalWarningMessage (" Z3 error: unknown parameter" + key))
167+ }
168+ }
130169 prover.setParameters(params)
131170 termConverter.start()
132171 termConverter.ctx = ctx
@@ -212,7 +251,21 @@ class Z3ProverAPI(uniqueId: String,
212251 else
213252 preambleAssumes.add(termConverter.convert(term).asInstanceOf [BoolExpr ])
214253 } catch {
215- case e : Z3Exception => reporter.report(InternalWarningMessage (" Z3 error: " + e.getMessage))
254+ case e : Z3Exception =>
255+ // The only reason we get an exception here is that we've tried to assume a quantifier with an invalid trigger.
256+ // When used via API, Z3 completely discards assumptions that contain invalid triggers (whereas it just ignores
257+ // the invalid trigger when used via stdio). Thus, to make sure our assumption is not discarded, we manually
258+ // walk through all quantifiers and remove invalid terms inside the trigger.
259+ triggerGenerator.setCustomIsForbiddenInTrigger(triggerGenerator.advancedIsForbiddenInTrigger)
260+ val cleanTerm = term.transform{
261+ case q@ Quantification (_, _, _, triggers, _, _, _) if triggers.nonEmpty =>
262+ val goodTriggers = triggers.filterNot(trig => trig.p.exists(ptrn => ptrn.shallowCollect{
263+ case t => triggerGenerator.isForbiddenInTrigger(t)
264+ }.nonEmpty))
265+ q.copy(triggers = goodTriggers)
266+ }()
267+ prover.add(termConverter.convert(cleanTerm).asInstanceOf [BoolExpr ])
268+ reporter.report(InternalWarningMessage (" Z3 error: " + e.getMessage))
216269 }
217270 }
218271
@@ -316,7 +369,13 @@ class Z3ProverAPI(uniqueId: String,
316369 if (! preamblePhaseOver) {
317370 preamblePhaseOver = true
318371
319- val merged = emittedPreambleString.mkString(" \n " )
372+ // Setting all options again , since otherwise some of them seem to get lost.
373+ val standardOptionPrefix = Seq (" (set-option :auto_config false)" , " (set-option :type_check true)" ) ++
374+ Z3ProverAPI .allParams.map(bp => s " (set-option : ${bp._1} ${bp._2}) " )
375+
376+ val customOptionPrefix = Verifier .config.proverConfigArgs.map(a => s " (set-option : ${a._1} ${a._2}) " )
377+
378+ val merged = (standardOptionPrefix ++ customOptionPrefix ++ emittedPreambleString).mkString(" \n " )
320379 val parsed = ctx.parseSMTLIB2String(merged, emittedSortSymbols.toArray, emittedSorts.toArray, emittedFuncSymbols.toArray, emittedFuncs.toArray)
321380 prover.add(parsed : _* )
322381 prover.add(preambleAssumes.toSeq : _* )
0 commit comments