diff --git a/src/main/scala/inox/InoxContext.scala b/src/main/scala/inox/InoxContext.scala index 1c3554235ab2dc9a8e721a7c4010850b9f822a8a..42086b486c3ec865aa838878a5d338c98a1a0dff 100644 --- a/src/main/scala/inox/InoxContext.scala +++ b/src/main/scala/inox/InoxContext.scala @@ -11,16 +11,8 @@ import inox.utils._ case class InoxContext( reporter: Reporter, interruptManager: InterruptManager, - options: Seq[InoxOption[Any]] = Seq(), - timers: TimerStorage = new TimerStorage) extends InoxOptions[InoxContext] { - - def build(opts: Seq[InoxOption[Any]]) = InoxContext(reporter, interruptManager, opts, timers) - - def toSolver: solvers.SolverOptions = solvers.SolverOptions( - options.filter(opt => solvers.SolverOptions.options.exists(_ == opt.optionDef))) - def toEvaluator: evaluators.EvaluatorOptions = evaluators.EvaluatorOptions( - options.filter(opt => evaluators.EvaluatorOptions.options.exists(_ == opt.optionDef))) -} + options: InoxOptions = InoxOptions(Seq()), + timers: TimerStorage = new TimerStorage) object InoxContext { def empty = { @@ -33,7 +25,7 @@ object InoxContext { InoxContext( reporter, new InterruptManager(reporter), - options = Seq(InoxOption[Set[DebugSection]](InoxOptions.optDebug)(Set(ast.DebugSectionTrees))) + options = InoxOptions.empty + InoxOption[Set[DebugSection]](InoxOptions.optDebug)(Set(ast.DebugSectionTrees)) ) } } diff --git a/src/main/scala/inox/InoxOptions.scala b/src/main/scala/inox/InoxOptions.scala index 151a60ba9d57856c060fb2684d1f421084808b04..8ccdfec4f418208f2e5962d708dd3511256572d4 100644 --- a/src/main/scala/inox/InoxOptions.scala +++ b/src/main/scala/inox/InoxOptions.scala @@ -154,10 +154,7 @@ object OptionsHelpers { } } -trait InoxOptions[Self <: InoxOptions[Self]] { - val options: Seq[InoxOption[Any]] - - protected def build(newOpts: Seq[InoxOption[Any]]): Self +case class InoxOptions(options: Seq[InoxOption[Any]]) { def findOption[A: ClassTag](optDef: InoxOptionDef[A]): Option[A] = options.collectFirst { case InoxOption(`optDef`, value: A) => value @@ -165,30 +162,31 @@ trait InoxOptions[Self <: InoxOptions[Self]] { def findOptionOrDefault[A: ClassTag](optDef: InoxOptionDef[A]): A = findOption(optDef).getOrElse(optDef.default) - def +(newOpt: InoxOption[Any]): Self = build { + def +(newOpt: InoxOption[Any]): InoxOptions = InoxOptions( options.filter(_.optionDef != newOpt.optionDef) :+ newOpt - } + ) - def ++(newOpts: Seq[InoxOption[Any]]): Self = build { + def ++(newOpts: Seq[InoxOption[Any]]): InoxOptions = InoxOptions { val defs = newOpts.map(_.optionDef).toSet options.filter(opt => !defs(opt.optionDef)) ++ newOpts } @inline - def ++(that: Self): Self = this ++ that.options + def ++(that: InoxOptions): InoxOptions = this ++ that.options } object InoxOptions { - /* + def empty: InoxOptions = InoxOptions(Seq()) + val optSelectedSolvers = new InoxOptionDef[Set[String]] { val name = "solvers" - val description = "Use solvers s1, s2,...\n" + solvers.SolverFactory.availableSolversPretty - val default = Set("fairz3") + val description = "Use solvers s1, s2,...\n" + + solvers.SolverFactory.solversPretty + val default = Set("nativez3") val parser = setParser(stringParser) val usageRhs = "s1,s2,..." } - */ val optDebug = new InoxOptionDef[Set[DebugSection]] { import OptionParsers._ diff --git a/src/main/scala/inox/evaluators/Evaluator.scala b/src/main/scala/inox/evaluators/Evaluator.scala index af3fc771fcfd6e516ea4177fec393e2cad57e4cb..eea69bb554aa1ffc1e13f5ca370ed43328257e2c 100644 --- a/src/main/scala/inox/evaluators/Evaluator.scala +++ b/src/main/scala/inox/evaluators/Evaluator.scala @@ -3,23 +3,18 @@ package inox package evaluators -case class EvaluatorOptions(options: Seq[InoxOption[Any]]) extends InoxOptions[EvaluatorOptions] { - protected def build(opts: Seq[InoxOption[Any]]): EvaluatorOptions = EvaluatorOptions(opts) -} - object EvaluatorOptions { - def empty = EvaluatorOptions(Seq()) - val options = Seq( optIgnoreContracts ) } -object optIgnoreContracts extends InoxFlagOptionDef("ignorecontracts", "Don't fail on invalid contracts during evaluation", false) +object optIgnoreContracts extends InoxFlagOptionDef( + "ignorecontracts", "Don't fail on invalid contracts during evaluation", false) trait Evaluator { val program: Program - val options: EvaluatorOptions + val options: InoxOptions import program.trees._ /** The type of value that this [[Evaluator]] calculates diff --git a/src/main/scala/inox/evaluators/RecursiveEvaluator.scala b/src/main/scala/inox/evaluators/RecursiveEvaluator.scala index 745b7e1e7a0a949979add45d19b88bc972282527..715a8d8dd25bc6d9ca239a6f1149f7aabd5e2974 100644 --- a/src/main/scala/inox/evaluators/RecursiveEvaluator.scala +++ b/src/main/scala/inox/evaluators/RecursiveEvaluator.scala @@ -533,16 +533,15 @@ trait RecursiveEvaluator } object RecursiveEvaluator { - def apply(p: InoxProgram)(evOpts: EvaluatorOptions, solverOpts: solvers.SolverOptions): - RecursiveEvaluator { val program: p.type } = { + def apply(p: InoxProgram, opts: InoxOptions): RecursiveEvaluator { val program: p.type } = { new { val program: p.type = p } with RecursiveEvaluator with HasDefaultGlobalContext with HasDefaultRecContext { - val options = p.ctx.toEvaluator ++ evOpts + val options = opts val maxSteps = 50000 - def getSolver(opts: InoxOption[Any]*) = solvers.SolverFactory(p)(p.ctx.toSolver ++ opts, options) + def getSolver(moreOpts: InoxOption[Any]*) = solvers.SolverFactory(p, opts ++ moreOpts) } } - def default(p: InoxProgram) = apply(p)(EvaluatorOptions.empty, solvers.SolverOptions.empty) + def default(p: InoxProgram) = apply(p, p.ctx.options) } diff --git a/src/main/scala/inox/evaluators/SolvingEvaluator.scala b/src/main/scala/inox/evaluators/SolvingEvaluator.scala index cbe03bda120b01fbf3ec4d0d6d5641c19c2f7cf8..9b17eae6bc69d5df2e30e416efba8e33e9ca5604 100644 --- a/src/main/scala/inox/evaluators/SolvingEvaluator.scala +++ b/src/main/scala/inox/evaluators/SolvingEvaluator.scala @@ -29,7 +29,7 @@ trait SolvingEvaluator extends Evaluator { val timer = ctx.timers.evaluators.specs.start() val sf = getSolver(options.options.collect { - case o@InoxOption(opt, _) if opt == optForallCache => o + case o @ InoxOption(opt, _) if opt == optForallCache => o } : _*) import SolverResponses._ diff --git a/src/main/scala/inox/solvers/Solver.scala b/src/main/scala/inox/solvers/Solver.scala index b6874bea60ef94735202f6963474757b8ee2b6b5..03dffbcac13bd6620ec3108ce833768899682530 100644 --- a/src/main/scala/inox/solvers/Solver.scala +++ b/src/main/scala/inox/solvers/Solver.scala @@ -5,13 +5,7 @@ package solvers import utils._ -case class SolverOptions(options: Seq[InoxOption[Any]]) extends InoxOptions[SolverOptions] { - def build(opts: Seq[InoxOption[Any]]): SolverOptions = SolverOptions(opts) -} - object SolverOptions { - def empty = SolverOptions(Seq()) - val options = Seq( optCheckModels, optSilentErrors, @@ -22,15 +16,18 @@ object SolverOptions { ) } -case object DebugSectionSolver extends DebugSection("solver") +object optCheckModels extends InoxFlagOptionDef( + "checkmodels", "Double-check counter-examples with evaluator", false) + +object optSilentErrors extends InoxFlagOptionDef( + "silenterrors", "Fail silently into UNKNOWN when encountering an error", false) -object optCheckModels extends InoxFlagOptionDef("checkmodels", "Double-check counter-examples with evaluator", false) -object optSilentErrors extends InoxFlagOptionDef("silenterrors", "Fail silently into UNKNOWN when encountering an error", false) +case object DebugSectionSolver extends DebugSection("solver") trait AbstractSolver extends Interruptible { def name: String val program: Program - val options: SolverOptions + val options: InoxOptions import program._ import program.trees._ diff --git a/src/main/scala/inox/solvers/SolverFactory.scala b/src/main/scala/inox/solvers/SolverFactory.scala index d1521868348716c30f6943575902a95e666b308a..392898a0590402b65e8df1badf413b25c6312314 100644 --- a/src/main/scala/inox/solvers/SolverFactory.scala +++ b/src/main/scala/inox/solvers/SolverFactory.scala @@ -3,8 +3,6 @@ package inox package solvers -import scala.reflect.runtime.universe._ - trait SolverFactory { val program: Program @@ -22,7 +20,7 @@ trait SolverFactory { } object SolverFactory { - def create[S1 <: Solver : TypeTag](p: Program)(nme: String, builder: () => S1 { val program: p.type }): + def create[S1 <: Solver](p: Program)(nme: String, builder: () => S1 { val program: p.type }): SolverFactory { val program: p.type; type S = S1 { val program: p.type } } = { new SolverFactory { val program: p.type = p @@ -33,10 +31,79 @@ object SolverFactory { } } - def apply(p: InoxProgram) - (solverOpts: SolverOptions, evOpts: evaluators.EvaluatorOptions): - SolverFactory { val program: p.type } = ??? + import evaluators._ + import combinators._ + + private val solverNames = Map( + "nativez3" -> "Native Z3 with z3-templates for unrolling (default)", + "unrollz3" -> "Native Z3 with leon-templates for unrolling", + "smt-cvc4" -> "CVC4 through SMT-LIB", + "smt-z3" -> "Z3 through SMT-LIB" + ) + + def getFromName(name: String) + (p: InoxProgram, opts: InoxOptions) + (ev: DeterministicEvaluator with SolvingEvaluator { val program: p.type }): + SolverFactory { val program: p.type; type S <: TimeoutSolver } = name match { + case "nativez3" => create(p)(name, () => new { + val program: p.type = p + val options = opts + } with z3.NativeZ3Solver with TimeoutSolver { + val evaluator = ev + }) + + case "unrollz3" => create(p)(name, () => new { + val program: p.type = p + val options = opts + } with unrolling.UnrollingSolver with theories.Z3Theories with TimeoutSolver { + val evaluator = ev + + object underlying extends { + val program: theories.targetProgram.type = theories.targetProgram + val options = opts + } with z3.UninterpretedZ3Solver + }) + + case "smt-cvc4" => create(p)(name, () => new { + val program: p.type = p + val options = opts + } with unrolling.UnrollingSolver with theories.CVC4Theories with TimeoutSolver { + val evaluator = ev + + object underlying extends { + val program: theories.targetProgram.type = theories.targetProgram + val options = opts + } with smtlib.CVC4Solver + }) + + case "smt-z3" => create(p)(name, () => new { + val program: p.type = p + val options = opts + } with unrolling.UnrollingSolver with theories.Z3Theories with TimeoutSolver { + val evaluator = ev + + object underlying extends { + val program: theories.targetProgram.type = theories.targetProgram + val options = opts + } with smtlib.Z3Solver + }) + + case _ => throw FatalError("Unknown solver: " + name) + } + + val solversPretty = "Available: " + solverNames.toSeq.sortBy(_._1).map { + case (name, desc) => f"\n $name%-14s : $desc" + } + + def apply(p: InoxProgram, opts: InoxOptions): SolverFactory { val program: p.type; type S <: TimeoutSolver } = + p.ctx.options.findOptionOrDefault(InoxOptions.optSelectedSolvers).toSeq match { + case Seq() => throw FatalError("No selected solver") + case Seq(single) => getFromName(single)(p, opts)(RecursiveEvaluator(p, opts)) + case multiple => PortfolioSolverFactory(p) { + multiple.map(name => getFromName(name)(p, opts)(RecursiveEvaluator(p, opts))) + } + } def default(p: InoxProgram): SolverFactory { val program: p.type } = - apply(p)(SolverOptions.empty, evaluators.EvaluatorOptions.empty) + apply(p, p.ctx.options) } diff --git a/src/main/scala/inox/solvers/combinators/PortfolioSolverFactory.scala b/src/main/scala/inox/solvers/combinators/PortfolioSolverFactory.scala index f1f682975522f8f3c844ca40b59c4ca369425206..dd62ddd6b6ef87b369ee5d69940afe9e79907705 100644 --- a/src/main/scala/inox/solvers/combinators/PortfolioSolverFactory.scala +++ b/src/main/scala/inox/solvers/combinators/PortfolioSolverFactory.scala @@ -6,19 +6,16 @@ package combinators trait PortfolioSolverFactory extends SolverFactory { self => - final type PT = program.type - override type S = PortfolioSolver { val program: PT } - final type SF = SolverFactory { val program: PT } + type S = PortfolioSolver with TimeoutSolver { val program: self.program.type } + type SF <: SolverFactory { val program: self.program.type } val sfs: Seq[SF] - def getNewSolver(): S = { - new PortfolioSolver { - val program: PT = self.program - val solvers = sfs map (_.getNewSolver()) - val options: SolverOptions = solvers.head.options - } - } + def getNewSolver(): S = new { + val program: self.program.type = self.program + val solvers = sfs map (_.getNewSolver()) + val options = solvers.head.options + } with PortfolioSolver with TimeoutSolver // Assumes s is a P/Solver with the correct subsolver types override def reclaim(s: S) = sfs.zip(s.solvers).foreach { case (sf, s) => @@ -28,3 +25,13 @@ trait PortfolioSolverFactory extends SolverFactory { self => val name = sfs.map(_.name).mkString("Pfolio(", ",", ")") } +object PortfolioSolverFactory { + def apply(p: InoxProgram) + (factories: Seq[SolverFactory { val program: p.type; type S <: TimeoutSolver }]): + PortfolioSolverFactory { val program: p.type; type S <: TimeoutSolver } = new { + val program: p.type = p + } with PortfolioSolverFactory { + type SF = SolverFactory { val program: p.type; type S <: TimeoutSolver } + val sfs: Seq[SF] = factories + } +} diff --git a/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala b/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala index 02306d21936bbeb34adb428ef13bec8903c8d6cd..e19fc66c81935f09bfc05e995eb65ed312e814a8 100644 --- a/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala +++ b/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala @@ -6,13 +6,7 @@ package smtlib import inox.OptionParsers._ -class CVC4Solver(val program: Program, val options: SolverOptions) - extends SMTLIBSolver - with CVC4Target { - - protected val userDefinedOps = { - options.findOptionOrDefault(optCVC4Options) - } +trait CVC4Solver extends SMTLIBSolver with CVC4Target { def interpreterOps(ctx: InoxContext) = { Seq( @@ -23,7 +17,7 @@ class CVC4Solver(val program: Program, val options: SolverOptions) "--rewrite-divk", "--print-success", "--lang", "smt2.5" - ) ++ userDefinedOps.toSeq + ) ++ options.findOptionOrDefault(optCVC4Options) } } diff --git a/src/main/scala/inox/solvers/smtlib/Z3Solver.scala b/src/main/scala/inox/solvers/smtlib/Z3Solver.scala index 64b5b127f0486728df72cf7a34a8a9ec4c30f2c2..b8d50712b027379ade7893b3cb1bff63ed434e17 100644 --- a/src/main/scala/inox/solvers/smtlib/Z3Solver.scala +++ b/src/main/scala/inox/solvers/smtlib/Z3Solver.scala @@ -4,6 +4,4 @@ package inox package solvers package smtlib -class Z3Solver(val program: Program, val options: SolverOptions) - extends SMTLIBSolver - with Z3Target +trait Z3Solver extends SMTLIBSolver with Z3Target diff --git a/src/main/scala/inox/solvers/theories/package.scala b/src/main/scala/inox/solvers/theories/package.scala new file mode 100644 index 0000000000000000000000000000000000000000..b2bd02c4bdbb460d6ec5b85bd49bc9a153d3f925 --- /dev/null +++ b/src/main/scala/inox/solvers/theories/package.scala @@ -0,0 +1,20 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package solvers + +package object theories { + + trait Z3Theories { self: unrolling.AbstractUnrollingSolver => + object theories extends { + val sourceProgram: self.program.type = self.program + } with StringEncoder + } + + trait CVC4Theories { self: unrolling.AbstractUnrollingSolver => + object theories extends { + val sourceProgram: self.program.type = self.program + } with BagEncoder + } +} + diff --git a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala index 9650c3903bcace2ad71b15955d1374cce682bbd9..1f89dc0cd2da738edab7e9450e290263a871c3aa 100644 --- a/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala +++ b/src/main/scala/inox/solvers/unrolling/UnrollingSolver.scala @@ -9,9 +9,14 @@ import utils._ import theories._ import evaluators._ -object optUnrollFactor extends InoxLongOptionDef("unrollfactor", "Number of unfoldings to perform in each unfold step", default = 1, "<PosInt>") -object optFeelingLucky extends InoxFlagOptionDef("feelinglucky", "Use evaluator to find counter-examples early", false) -object optUnrollCores extends InoxFlagOptionDef("unrollcores", "Use unsat-cores to drive unfolding while remaining fair", false) +object optUnrollFactor extends InoxLongOptionDef( + "unrollfactor", "Number of unfoldings to perform in each unfold step", default = 1, "<PosInt>") + +object optFeelingLucky extends InoxFlagOptionDef( + "feelinglucky", "Use evaluator to find counter-examples early", false) + +object optUnrollCores extends InoxFlagOptionDef( + "unrollcores", "Use unsat-cores to drive unfolding while remaining fair", false) trait AbstractUnrollingSolver extends Solver { @@ -41,11 +46,11 @@ trait AbstractUnrollingSolver type Cores = Set[Encoded] } - lazy val unfoldFactor = options.findOptionOrDefault(optUnrollFactor) - lazy val feelingLucky = options.findOptionOrDefault(optFeelingLucky) - lazy val checkModels = options.findOptionOrDefault(optCheckModels) - lazy val unrollUnsatCores = options.findOptionOrDefault(optUnrollCores) - lazy val silentErrors = options.findOptionOrDefault(optSilentErrors) + lazy val checkModels = options.findOptionOrDefault(optCheckModels) + lazy val silentErrors = options.findOptionOrDefault(optSilentErrors) + lazy val unrollFactor = options.findOptionOrDefault(optUnrollFactor) + lazy val feelingLucky = options.findOptionOrDefault(optFeelingLucky) + lazy val unrollCores = options.findOptionOrDefault(optUnrollCores) def check(config: Configuration): config.Response[Model, Cores] = checkAssumptions(config)(Set.empty) @@ -302,7 +307,7 @@ trait AbstractUnrollingSolver val checkConfig = config .min(Configuration(model = !templates.requiresFiniteRangeCheck, cores = true)) - .max(Configuration(model = false, cores = unrollUnsatCores)) + .max(Configuration(model = false, cores = unrollCores)) val timer = ctx.timers.solvers.check.start() val res: SolverResponse[underlying.Model, underlying.Cores] = @@ -329,7 +334,7 @@ trait AbstractUnrollingSolver case _: Unsatisfiable if !templates.canUnroll => CheckResult.cast(res) - case UnsatWithCores(cores) if unrollUnsatCores => + case UnsatWithCores(cores) if unrollCores => for (c <- cores) templates.extractNot(c) match { case Some(b) => templates.promoteBlocker(b) case None => reporter.fatalError("Unexpected blocker polarity for unsat core unrolling: " + c) @@ -371,7 +376,9 @@ trait AbstractUnrollingSolver } case Validate(model) => - val valid = !checkModels || validateModel(model, assumptionsSeq, silenceErrors = silentErrors) + val valid: Boolean = !checkModels || + validateModel(model, assumptionsSeq, silenceErrors = silentErrors) + if (valid) { CheckResult(config cast SatWithModel(model)) } else { @@ -436,8 +443,8 @@ trait AbstractUnrollingSolver reporter.debug("- We need to keep going") val timer = ctx.timers.solvers.unroll.start() - // unfolling `unfoldFactor` times - for (i <- 1 to unfoldFactor.toInt) { + // unfolling `unrollFactor` times + for (i <- 1 to unrollFactor.toInt) { val newClauses = templates.unroll for (ncl <- newClauses) { underlying.assertCnstr(ncl) diff --git a/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala b/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala index ae3e08eed5fadedc5af52fc854a81b4607717a45..a7ce7e3fbe8fd8b65e9448da595e24a30da6991a 100644 --- a/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala +++ b/src/main/scala/inox/solvers/z3/NativeZ3Solver.scala @@ -72,6 +72,8 @@ trait NativeZ3Solver underlying.reset() } + def free(): Unit = underlying.free() + protected def declareVariable(v: Variable): Z3AST = underlying.declareVariable(v) protected def wrapModel(model: Z3Model): super.ModelWrapper = ModelWrapper(model) diff --git a/src/main/scala/inox/solvers/z3/UninterpretedZ3Solver.scala b/src/main/scala/inox/solvers/z3/UninterpretedZ3Solver.scala index 249f08ad7e54b4f7fce70dae315821634aa2c5ae..8f10f1fb25e03f0269bf9392621b9ae8c8796822 100644 --- a/src/main/scala/inox/solvers/z3/UninterpretedZ3Solver.scala +++ b/src/main/scala/inox/solvers/z3/UninterpretedZ3Solver.scala @@ -49,6 +49,11 @@ trait UninterpretedZ3Solver freeVars.reset() } + def free(): Unit = underlying.free() + + def interrupt(): Unit = underlying.interrupt() + def recoverInterrupt(): Unit = underlying.recoverInterrupt() + def assertCnstr(expression: Expr) { freeVars ++= exprOps.variablesOf(expression) underlying.assertCnstr(underlying.toZ3Formula(expression))