Skip to content

Commit b787367

Browse files
authored
Merge pull request #140 from jad-hamza/no-incremental
Add support for non-incremental mode
2 parents 0157b1b + 8066fd7 commit b787367

33 files changed

+205
-73
lines changed

build.sbt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,16 @@ resolvers ++= Seq(
3737
)
3838

3939
libraryDependencies ++= Seq(
40-
"org.scalatest" %% "scalatest" % "3.0.1" % "test;it",
40+
"org.scalatest" %% "scalatest" % "3.2.7" % "test;it",
4141
"org.apache.commons" % "commons-lang3" % "3.4",
4242
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
43-
"com.regblanc" %% "scala-smtlib" % "0.2.2-7-g00a9686",
4443
"uuverifiers" %% "princess" % "2018-02-26"
4544
)
4645

46+
def ghProject(repo: String, version: String) = RootProject(uri(s"${repo}#${version}"))
47+
// lazy val smtlib = RootProject(file("../scala-smtlib"))
48+
lazy val smtlib = ghProject("https://github.com/epfl-lara/scala-smtlib.git", "5b9503e13d69c7116039a243025b2ce657c32c77")
49+
4750
lazy val scriptName = settingKey[String]("Name of the generated 'inox' script")
4851

4952
scriptName := "inox"
@@ -127,6 +130,8 @@ lazy val root = (project in file("."))
127130
parallelExecution := false
128131
)) : _*)
129132
.settings(compile := ((compile in Compile) dependsOn script dependsOn genDoc).value)
133+
.dependsOn(smtlib)
134+
130135

131136
publishMavenStyle := true
132137

src/it/scala/inox/ResourceUtils.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
package inox
44

5-
import org.scalatest._
5+
import org.scalatest.funsuite.AnyFunSuite
66
import org.scalatest.concurrent._
77

88
import java.io.File

src/it/scala/inox/TestSuite.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
package inox
44

5-
import org.scalatest._
5+
import org.scalatest.funsuite.AnyFunSuite
66
import org.scalatest.concurrent._
7+
import org.scalatest.matchers.should.Matchers
8+
import org.scalatest.Tag
9+
import org.scalatest.exceptions
710

811
import utils._
912

10-
trait TestSuite extends FunSuite with Matchers with TimeLimits {
13+
trait TestSuite extends AnyFunSuite with Matchers with TimeLimits {
1114

1215
protected def configurations: Seq[Seq[OptionValue[_]]] = Seq(Seq.empty)
1316

src/it/scala/inox/tip/TipPrintingSuite.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
package inox
44
package tip
55

6-
import org.scalatest._
6+
import org.scalatest.funsuite.AnyFunSuite
77

8-
class TipPrintingSuite extends FunSuite with ResourceUtils {
8+
class TipPrintingSuite extends AnyFunSuite with ResourceUtils {
99
import inox.trees._
1010

1111
val ctx = TestContext.empty

src/it/scala/inox/tip/TipSerializationSuite.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
package inox
44
package tip
55

6-
import org.scalatest._
6+
import org.scalatest.funspec.AnyFunSpec
77

8-
class TipSerializationSuite extends FunSpec with ResourceUtils {
8+
class TipSerializationSuite extends AnyFunSpec with ResourceUtils {
99
import inox.trees._
1010

1111
val ctx = TestContext.empty

src/it/scala/inox/tip/TipTestSuite.scala

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import scala.language.existentials
1010
class TipTestSuite extends TestSuite with ResourceUtils {
1111

1212
override def configurations = Seq(
13-
Seq(optSelectedSolvers(Set("nativez3")), optCheckModels(true)),
14-
Seq(optSelectedSolvers(Set("smt-z3")), optCheckModels(true)),
15-
Seq(optSelectedSolvers(Set("smt-cvc4")), optCheckModels(true)),
16-
Seq(optSelectedSolvers(Set("smt-z3")), optCheckModels(true), optAssumeChecked(true))
13+
Seq(optSelectedSolvers(Set("nativez3")), optCheckModels(true)),
14+
Seq(optSelectedSolvers(Set("smt-z3")), optCheckModels(true)),
15+
Seq(optSelectedSolvers(Set("smt-cvc4")), optCheckModels(true)),
16+
Seq(optSelectedSolvers(Set("smt-z3")), optCheckModels(true), optAssumeChecked(true)),
17+
Seq(optSelectedSolvers(Set("no-inc:smt-z3")), optCheckModels(true))
1718
)
1819

1920
override protected def optionsString(options: Options): String = {

src/main/scala/inox/Main.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ trait MainHelpers {
1111
protected def getDebugSections: Set[DebugSection] = Set(
1212
utils.DebugSectionTimers,
1313
solvers.DebugSectionSolver,
14+
solvers.smtlib.DebugSectionSMT,
1415
tip.DebugSectionTip
1516
)
1617

@@ -50,7 +51,8 @@ trait MainHelpers {
5051
"Use solvers s1,s2,...\nAvailable: " +
5152
solvers.SolverFactory.solverNames.toSeq.sortBy(_._1).map {
5253
case (name, desc) => f"\n $name%-14s : $desc"
53-
}.mkString("")
54+
}.mkString("") +
55+
"\nYou can prefix the solvers unrollz3, smt-z3, smt-z3:<exec> and smt-cvc4, with 'no-inc:' to use them in non-incremental mode"
5456
}),
5557
optDebug -> Description(General, {
5658
val sects = debugSections.toSeq.map(_.name).sorted

src/main/scala/inox/solvers/SolverFactory.scala

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,17 +90,32 @@ object SolverFactory {
9090
// extract <exec> in "smt-z3:<exec>"
9191
private def getZ3Executable(name: String): String = name.drop(7)
9292

93+
// extract solver in "no-inc:solver"
94+
private def removeNoInc(name: String): String = name.drop(7)
95+
9396
def getFromName(name: String, force: Boolean = false)
9497
(p: Program, ctx: Context)
9598
(enc: ProgramTransformer {
9699
val sourceProgram: p.type
97100
val targetProgram: Program { val trees: inox.trees.type }
98101
})(implicit sem: p.Semantics): SolverFactory { val program: p.type; type S <: TimeoutSolver { val program: p.type } } = {
99102

103+
val nonIncremental = name.startsWith("no-inc:")
104+
val noPrefixName = if (nonIncremental) removeNoInc(name) else name
105+
106+
if (
107+
nonIncremental &&
108+
noPrefixName != "smt-cvc4" &&
109+
noPrefixName != "unrollz3" &&
110+
noPrefixName != "smt-z3" &&
111+
!noPrefixName.startsWith("smt-z3:")
112+
)
113+
throw FatalError(s"Non incremental mode is not available for solver $name")
114+
100115
val finalName = if (force) {
101-
name
116+
noPrefixName
102117
} else {
103-
fallbacks.get(name) match {
118+
fallbacks.get(noPrefixName) match {
104119
case Some((guard, names, requirement)) if !guard() =>
105120
val replacement = names.collectFirst {
106121
case name if fallbacks(name)._1() => name
@@ -112,23 +127,54 @@ object SolverFactory {
112127
}
113128
replacement
114129

115-
case Some(_) => name
116-
case None if name.startsWith("smt-z3:") =>
117-
val z3Exec = getZ3Executable(name)
130+
case Some(_) => noPrefixName
131+
case None if noPrefixName.startsWith("smt-z3:") =>
132+
val z3Exec = getZ3Executable(noPrefixName)
118133
val hasZ3Exec = try {
119134
new Z3Interpreter(z3Exec, Array("-in", "-smt2"))
120135
true
121136
} catch {
122137
case _: java.io.IOException => false
123138
}
124139

125-
if (hasZ3Exec) name
140+
if (hasZ3Exec) noPrefixName
126141
else throw FatalError("Unknown solver: " + z3Exec)
127142

128-
case _ => throw FatalError("Unknown solver: " + name)
143+
case _ => throw FatalError("Unknown solver: " + noPrefixName)
144+
}
145+
}
146+
147+
def nonIncrementalWrap[T, M](targetProgram: Program)(
148+
nme: String,
149+
targetSem: targetProgram.Semantics,
150+
underlyingSolver: () => AbstractSolver {
151+
val program: targetProgram.type
152+
type Trees = T
153+
type Model = M
154+
}): AbstractSolver {
155+
val program: targetProgram.type
156+
type Trees = T
157+
type Model = M
158+
} = {
159+
160+
if (nonIncremental) {
161+
new {
162+
val program: targetProgram.type = targetProgram
163+
val context = ctx
164+
} with NonIncrementalSolver {
165+
type Trees = T
166+
type Model = M
167+
val semantics: targetProgram.Semantics = targetSem
168+
def name = s"no-inc:$nme"
169+
170+
def underlying() = underlyingSolver()
171+
}
172+
} else {
173+
underlyingSolver()
129174
}
130175
}
131176

177+
132178
finalName match {
133179
case "nativez3" => create(p)(finalName, {
134180
val chooseEnc = ChooseEncoder(p)(enc)
@@ -195,12 +241,12 @@ object SolverFactory {
195241
override protected lazy val targetProgram: targetProg.type = targetProg
196242
override protected val targetSemantics = targetSem
197243

198-
protected val underlying = new {
244+
protected val underlying = nonIncrementalWrap(progEnc.targetProgram)(finalName, targetSem, () => new {
199245
val program: progEnc.targetProgram.type = progEnc.targetProgram
200246
val context = ctx
201247
} with z3.UninterpretedZ3Solver {
202248
val semantics: program.Semantics = targetSem
203-
}
249+
})
204250
}
205251
})
206252

@@ -256,13 +302,13 @@ object SolverFactory {
256302
override protected lazy val targetProgram: targetProg.type = targetProg
257303
override protected val targetSemantics = targetSem
258304

259-
protected val underlying = new {
305+
protected val underlying = nonIncrementalWrap(progEnc.targetProgram)(finalName, targetSem, () => new {
260306
val program: progEnc.targetProgram.type = progEnc.targetProgram
261307
val context = ctx
262308
} with smtlib.Z3Solver {
263309
val semantics: program.Semantics = targetSem
264310
override def targetName = executableName
265-
}
311+
})
266312
}
267313
})
268314

@@ -288,12 +334,12 @@ object SolverFactory {
288334
override protected lazy val targetProgram: targetProg.type = targetProg
289335
override protected val targetSemantics = targetSem
290336

291-
protected val underlying = new {
337+
protected val underlying = nonIncrementalWrap(progEnc.targetProgram)(finalName, targetSem, () => new {
292338
val program: progEnc.targetProgram.type = progEnc.targetProgram
293339
val context = ctx
294340
} with smtlib.CVC4Solver {
295341
val semantics: program.Semantics = targetSem
296-
}
342+
})
297343
}
298344
})
299345

@@ -324,7 +370,13 @@ object SolverFactory {
324370
}
325371
}
326372

327-
def supportedSolver(s: String) = solverNames.contains(s) || s.startsWith("smt-z3:")
373+
def supportedSolver(s: String) =
374+
solverNames.contains(s) ||
375+
s.startsWith("smt-z3:") ||
376+
s.startsWith("no-inc:smt-z3:") ||
377+
s == "no-inc:smt-z3" ||
378+
s == "no-inc:smt-cvc4" ||
379+
s == "no-inc:unrollz3"
328380

329381
def getFromSettings(p: Program, ctx: Context)
330382
(enc: ProgramTransformer {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/* Copyright 2009-2018 EPFL, Lausanne */
2+
3+
package inox
4+
package solvers
5+
package combinators
6+
7+
import utils._
8+
import scala.concurrent.duration._
9+
import scala.collection.mutable.ArrayBuffer
10+
11+
trait NonIncrementalSolver extends AbstractSolver { self =>
12+
import program.trees._
13+
import SolverResponses._
14+
15+
protected def underlying(): AbstractSolver {
16+
val program: self.program.type
17+
type Trees = self.Trees
18+
type Model = self.Model
19+
}
20+
21+
val assertions: IncrementalSeq[Trees] = new IncrementalSeq[Trees]
22+
23+
var currentSolver: Option[AbstractSolver] = None
24+
25+
def assertCnstr(expression: Trees): Unit = assertions += expression
26+
def reset(): Unit = assertions.clear()
27+
def free(): Unit = for (solver <- currentSolver) solver.free()
28+
def interrupt(): Unit = for (solver <- currentSolver) solver.interrupt()
29+
30+
override def check(config: CheckConfiguration): config.Response[Model, Assumptions] = {
31+
assert(currentSolver.isEmpty,
32+
"`currentSolver` should be empty when invoking `check` in NonIncrementalSolver")
33+
val newSolver = underlying()
34+
try {
35+
currentSolver = Some(newSolver)
36+
for (expression <- assertions)
37+
newSolver.assertCnstr(expression)
38+
val res = newSolver.check(config)
39+
currentSolver = None
40+
res
41+
} finally {
42+
newSolver.free()
43+
}
44+
}
45+
46+
override def checkAssumptions(config: Configuration)
47+
(assumptions: Set[Trees]): config.Response[Model, Assumptions] = {
48+
assert(currentSolver.isEmpty,
49+
"`currentSolver` should be empty when invoking `checkAssumptions` in NonIncrementalSolver")
50+
val newSolver = underlying()
51+
try {
52+
currentSolver = Some(newSolver)
53+
for (expression <- assertions)
54+
newSolver.assertCnstr(expression)
55+
val res = newSolver.checkAssumptions(config)(assumptions)
56+
currentSolver = None
57+
res
58+
} finally {
59+
newSolver.free()
60+
}
61+
}
62+
63+
def push(): Unit = assertions.push()
64+
def pop(): Unit = assertions.pop()
65+
}

src/main/scala/inox/solvers/smtlib/SMTLIBDebugger.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ package smtlib
77
import utils._
88
import _root_.smtlib.trees.Terms._
99

10+
case object DebugSectionSMT extends DebugSection("smt")
11+
1012
trait SMTLIBDebugger extends SMTLIBTarget {
1113
import context._
1214
import program._
@@ -22,6 +24,7 @@ trait SMTLIBDebugger extends SMTLIBTarget {
2224

2325
/* Printing VCs */
2426
protected lazy val debugOut: Option[java.io.FileWriter] = {
27+
implicit val debugSection = DebugSectionSMT
2528
if (reporter.isDebugEnabled) {
2629
val file = options.findOptionOrDefault(Main.optFiles).headOption.map(_.getName).getOrElse("NA")
2730
val n = DebugFileNumbers.next(targetName + file)

0 commit comments

Comments
 (0)