Skip to content

Commit 4cdc6b9

Browse files
authored
Merge pull request #752 from viperproject/meilers_fix_751
Fix issue #751
2 parents 2e306af + 8527848 commit 4cdc6b9

2 files changed

Lines changed: 36 additions & 15 deletions

File tree

src/main/scala/decider/TermToZ3APIConverter.scala

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class TermToZ3APIConverter
3030

3131
val sortCache = mutable.HashMap[Sort, Z3Sort]()
3232
val funcDeclCache = mutable.HashMap[(String, Seq[Sort], Sort), Z3FuncDecl]()
33+
val smtFuncDeclCache = mutable.HashMap[(String, Seq[Sort]), (Z3FuncDecl, Seq[Z3Expr])]()
3334
val termCache = mutable.HashMap[Term, Z3Expr]()
3435

3536
def convert(s: Sort): Z3Sort = convertSort(s)
@@ -471,19 +472,37 @@ class TermToZ3APIConverter
471472
// workaround: since we cannot create a function application with just the name, we let Z3 parse
472473
// a string that uses the function, take the AST, and get the func decl from there, so that we can
473474
// programmatically create a func app.
474-
val decls = args.zipWithIndex.map{case (a, i) => s"(declare-const workaround${i} ${smtlibConverter.convert(a.sort)})"}.mkString(" ")
475-
val funcAppString = s"(${functionName} ${(0 until args.length).map(i => "workaround" + i).mkString(" ")})"
476-
val assertion = decls + s" (assert (= ${funcAppString} ${funcAppString}))"
477-
val workaround = ctx.parseSMTLIB2String(assertion, null, null, null, null)
478-
val app = workaround(0).getArgs()(0)
479-
val decl = app.getFuncDecl
480-
val actualArgs = if (decl.getArity > args.length){
481-
// the function name we got wasn't just a function name but also contained a first argument.
482-
// this happens with float operations where functionName contains a rounding mode.
483-
app.getArgs.toSeq.slice(0, decl.getArity - args.length) ++ args.map(convertTerm(_))
484-
}else {
485-
args.map(convertTerm(_))
475+
476+
val cacheKey = (functionName, args.map(_.sort))
477+
val (decl, additionalArgs: Seq[Z3Expr]) = if (smtFuncDeclCache.contains(cacheKey)) {
478+
smtFuncDeclCache(cacheKey)
479+
} else {
480+
val declPreamble = "(define-sort $Perm () Real) " // ME: Re-declare the Perm sort.
481+
// ME: The parsing happens in a fresh context that doesn't know any of our current declarations.
482+
// In principle, it might be necessary to re-declare all sorts we're using anywhere. However, I don't see how there
483+
// could be any Z3 internal functions that exist for those custom sorts. For the Real (i.e., Perm) sort, however,
484+
// such functions exist. So we re-declare *only* this sort.
485+
val decls = declPreamble + args.zipWithIndex.map { case (a, i) => s"(declare-const workaround${i} ${smtlibConverter.convert(a.sort)})" }.mkString(" ")
486+
val funcAppString = if (args.nonEmpty)
487+
s"(${functionName} ${(0 until args.length).map(i => "workaround" + i).mkString(" ")})"
488+
else
489+
functionName
490+
val assertion = decls + s" (assert (= ${funcAppString} ${funcAppString}))"
491+
val workaround = ctx.parseSMTLIB2String(assertion, null, null, null, null)
492+
val app = workaround(0).getArgs()(0)
493+
val decl = app.getFuncDecl
494+
val additionalArgs = if (decl.getArity > args.length) {
495+
// the function name we got wasn't just a function name but also contained a first argument.
496+
// this happens with float operations where functionName contains a rounding mode.
497+
app.getArgs.toSeq.slice(0, decl.getArity - args.length)
498+
} else {
499+
Seq()
500+
}
501+
smtFuncDeclCache.put(cacheKey, (decl, additionalArgs))
502+
(decl, additionalArgs)
486503
}
504+
505+
val actualArgs = additionalArgs ++ args.map(convertTerm(_))
487506
ctx.mkApp(decl, actualArgs.toArray : _*)
488507
}
489508

@@ -522,6 +541,7 @@ class TermToZ3APIConverter
522541
sanitizedNamesCache.clear()
523542
macros.clear()
524543
funcDeclCache.clear()
544+
smtFuncDeclCache.clear()
525545
sortCache.clear()
526546
termCache.clear()
527547
unitConstructor = null

src/main/scala/decider/Z3ProverAPI.scala

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import com.typesafe.scalalogging.LazyLogging
1010
import viper.silicon.common.config.Version
1111
import viper.silicon.interfaces.decider.{Prover, Result, Sat, Unknown, Unsat}
1212
import viper.silicon.state.IdentifierFactory
13-
import viper.silicon.state.terms.{App, Decl, Fun, FunctionDecl, Implies, MacroDecl, Not, Quantification, Sort, SortDecl, SortWrapperDecl, Term, TriggerGenerator, sorts}
13+
import viper.silicon.state.terms.{App, Decl, Fun, FunctionDecl, Implies, MacroDecl, Not, Quantification, Sort, SortDecl, SortWrapperDecl, Term, TriggerGenerator, Var, sorts}
1414
import viper.silicon.{Config, Map}
1515
import viper.silicon.verifier.Verifier
1616
import viper.silver.reporter.{InternalWarningMessage, Reporter}
@@ -258,11 +258,12 @@ class Z3ProverAPI(uniqueId: String,
258258
triggerGenerator.setCustomIsForbiddenInTrigger(triggerGenerator.advancedIsForbiddenInTrigger)
259259
val cleanTerm = term.transform {
260260
case q@Quantification(_, _, _, triggers, _, _, _) if triggers.nonEmpty =>
261-
val goodTriggers = triggers.filterNot(trig => trig.p.exists(ptrn => ptrn.shallowCollect {
261+
val goodTriggers = triggers.filterNot(trig => trig.p.exists(ptrn =>
262+
ptrn.isInstanceOf[Var] || ptrn.shallowCollect {
262263
case t => triggerGenerator.isForbiddenInTrigger(t)
263264
}.nonEmpty))
264265
q.copy(triggers = goodTriggers)
265-
}()
266+
}(_ => true)
266267
cleanTerm
267268
}
268269

0 commit comments

Comments
 (0)