diff --git a/build.sbt b/build.sbt index 372c4cb07a8a429a2405ab61be47060b457fa63b..0c1f4400527fe9f724082d5c83f77bb84a38d141 100644 --- a/build.sbt +++ b/build.sbt @@ -92,6 +92,8 @@ concurrentRestrictions in Global += Tags.limit(Tags.Test, nParallel) testOptions in Test := Seq(Tests.Argument("-oDF")) +testOptions in IntegrationTest := Seq(Tests.Argument("-oDF")) + // Integration Tests //lazy val IntegrTest = config("integration") extend(Test) diff --git a/src/it/scala/inox/InoxTestSuite.scala b/src/it/scala/inox/InoxTestSuite.scala index 4ca956cf14fe275609357255dc7cc8053b8e3aa8..9abb984f9d91cd9a9c39fb8f44f49d72cf371143 100644 --- a/src/it/scala/inox/InoxTestSuite.scala +++ b/src/it/scala/inox/InoxTestSuite.scala @@ -12,12 +12,19 @@ trait InoxTestSuite extends FunSuite with Matchers with Timeouts { val configurations: Seq[Seq[InoxOption[Any]]] = Seq(Seq.empty) - protected def test(name: String, tags: Tag*)(test: InoxContext => Unit): Unit = { + private def optionsString(options: InoxOptions): String = { + "solver=" + options.findOptionOrDefault(InoxOptions.optSelectedSolvers).head + " " + + "feelinglucky=" + options.findOptionOrDefault(solvers.unrolling.optFeelingLucky) + " " + + "checkmodels=" + options.findOptionOrDefault(solvers.optCheckModels) + " " + + "unrollassumptions=" + options.findOptionOrDefault(solvers.unrolling.optUnrollAssumptions) + } + + protected def test(name: String, tags: Tag*)(body: InoxContext => Unit): Unit = { for (config <- configurations) { val reporter = new TestSilentReporter val ctx = InoxContext(reporter, new InterruptManager(reporter), InoxOptions(config)) try { - test(ctx) + super.test(name + " " + optionsString(ctx.options))(body(ctx)) } catch { case err: FatalError => reporter.lastErrors :+= err.msg @@ -25,4 +32,10 @@ trait InoxTestSuite extends FunSuite with Matchers with Timeouts { } } } + + protected def ignore(name: String, tags: Tag*)(body: InoxContext => Unit): Unit = { + for (config <- configurations) { + super.ignore(name + " " + optionsString(InoxOptions(config)))(()) + } + } } diff --git a/src/it/scala/inox/TestSilentReporter.scala b/src/it/scala/inox/TestSilentReporter.scala index 178262039cd630af9a5b13dfc7adfe01ffd5c413..96deee1c47d6b20025c24707fffc9c3e15c73b37 100644 --- a/src/it/scala/inox/TestSilentReporter.scala +++ b/src/it/scala/inox/TestSilentReporter.scala @@ -12,7 +12,7 @@ class TestSilentReporter extends DefaultReporter(Set()) { } override def debug(pos: utils.Position, msg: => Any)(implicit section: DebugSection): Unit = { - println(msg) + //println(msg) } } diff --git a/src/it/scala/inox/regression/SimpleSolversSuite.scala b/src/it/scala/inox/regression/SimpleSolversSuite.scala deleted file mode 100644 index 175cdab263759f6500ed2f0147e28dbbcdd58865..0000000000000000000000000000000000000000 --- a/src/it/scala/inox/regression/SimpleSolversSuite.scala +++ /dev/null @@ -1,9 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package inox -package regression - -class SimpleSolversSuite extends InoxTestSuite { - - -} diff --git a/src/it/scala/inox/SolvingTestSuite.scala b/src/it/scala/inox/solvers/SolvingTestSuite.scala similarity index 97% rename from src/it/scala/inox/SolvingTestSuite.scala rename to src/it/scala/inox/solvers/SolvingTestSuite.scala index 348906f82f787e3a6c0c11448c11b80691d802ae..63bc046642c7348fc838adf954a11b79427193b5 100644 --- a/src/it/scala/inox/SolvingTestSuite.scala +++ b/src/it/scala/inox/solvers/SolvingTestSuite.scala @@ -1,9 +1,9 @@ /* Copyright 2009-2016 EPFL, Lausanne */ package inox +package solvers trait SolvingTestSuite extends InoxTestSuite { - import solvers._ override val configurations = for { solverName <- Seq("nativez3", "unrollz3", "smt-z3", "smt-cvc4") diff --git a/src/it/scala/inox/regression/SimpleQuantifiersSuite.scala b/src/it/scala/inox/solvers/unrolling/AssociativeQuantifiersSuite.scala similarity index 70% rename from src/it/scala/inox/regression/SimpleQuantifiersSuite.scala rename to src/it/scala/inox/solvers/unrolling/AssociativeQuantifiersSuite.scala index a21b07ef51c3049f3164395c0c0b592bfcdddcee..df07e5468621937b44387befe2445f9878763af4 100644 --- a/src/it/scala/inox/regression/SimpleQuantifiersSuite.scala +++ b/src/it/scala/inox/solvers/unrolling/AssociativeQuantifiersSuite.scala @@ -1,11 +1,10 @@ /* Copyright 2009-2016 EPFL, Lausanne */ package inox -package regression +package solvers +package unrolling -import solvers._ - -class SimpleQuantifiersSuite extends SolvingTestSuite { +class AssociativeQuantifiersSuite extends SolvingTestSuite { import inox.trees._ import dsl._ @@ -36,7 +35,7 @@ class SimpleQuantifiersSuite extends SolvingTestSuite { isRotateID -> isRotate ), Map.empty) - test("Pair of associative ==> associative pair") { ctx => + ignore("Pair of associative ==> associative pair") { ctx => val program = InoxProgram(ctx, symbols) val (aT,bT) = (T("A"), T("B")) @@ -45,55 +44,45 @@ class SimpleQuantifiersSuite extends SolvingTestSuite { \("p1" :: T(aT,bT), "p2" :: T(aT, bT))((p1,p2) => E(f1(p1._1,p2._1), f2(p1._2,p2._2))) } - val sf = SolverFactory.default(program) - val api = SimpleSolverAPI(sf) - assert(api.solveSAT(clause).isUNSAT) + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isUNSAT) } - test("Commutative and rotate ==> associative") { ctx => + ignore("Commutative and rotate ==> associative") { ctx => val program = InoxProgram(ctx, symbols) val aT = T("A") val f = ("f" :: ((aT, aT) =>: aT)).toVariable val clause = isCommutative(aT)(f) && isRotate(aT)(f) && !isAssociative(aT)(f) - val sf = SolverFactory.default(program) - val api = SimpleSolverAPI(sf) - assert(api.solveSAT(clause).isUNSAT) + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isUNSAT) } - test("Commutative and rotate ==> associative (integer type)") { ctx => + ignore("Commutative and rotate ==> associative (integer type)") { ctx => val program = InoxProgram(ctx, symbols) val f = ("f" :: ((IntegerType, IntegerType) =>: IntegerType)).toVariable val clause = isCommutative(IntegerType)(f) && isRotate(IntegerType)(f) && !isAssociative(IntegerType)(f) - val sf = SolverFactory.default(program) - val api = SimpleSolverAPI(sf) - assert(api.solveSAT(clause).isUNSAT) + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isUNSAT) } - test("Associatve =!=> commutative") { ctx => + ignore("Associatve =!=> commutative") { ctx => val program = InoxProgram(ctx, symbols) val aT = T("A") val f = ("f" :: ((aT, aT) =>: aT)).toVariable val clause = isAssociative(aT)(f) && !isCommutative(aT)(f) - val sf = SolverFactory.default(program) - val api = SimpleSolverAPI(sf) - assert(api.solveSAT(clause).isSAT) + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isSAT) } - test("Commutative =!=> associative") { ctx => + ignore("Commutative =!=> associative") { ctx => val program = InoxProgram(ctx, symbols) val aT = T("A") val f = ("f" :: ((aT, aT) =>: aT)).toVariable val clause = isCommutative(aT)(f) && !isAssociative(aT)(f) - val sf = SolverFactory.default(program) - val api = SimpleSolverAPI(sf) - assert(api.solveSAT(clause).isSAT) + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isSAT) } } diff --git a/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala b/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..aabaeca997e6987049c42b20c8222b87b32a9376 --- /dev/null +++ b/src/it/scala/inox/solvers/unrolling/InductiveUnrollingSuite.scala @@ -0,0 +1,13 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package solvers +package unrolling + +class InductiveUnrollingSuite extends SolvingTestSuite { + import trees._ + import dsl._ + + + +} diff --git a/src/it/scala/inox/solvers/unrolling/SimpleUnrollingSuite.scala b/src/it/scala/inox/solvers/unrolling/SimpleUnrollingSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..24d46a798a0dacda2d523883bb6fb46813b9dd51 --- /dev/null +++ b/src/it/scala/inox/solvers/unrolling/SimpleUnrollingSuite.scala @@ -0,0 +1,102 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package inox +package solvers +package unrolling + +class SimpleUnrollingSuite extends SolvingTestSuite { + import inox.trees._ + import dsl._ + + import SolverResponses._ + + val listID = FreshIdentifier("List") + val consID = FreshIdentifier("Cons") + val nilID = FreshIdentifier("Nil") + + val head = FreshIdentifier("head") + val tail = FreshIdentifier("tail") + + val List = mkAbstractClass(listID)("A")(Seq(consID, nilID)) + val Nil = mkCaseClass(nilID)("A")(Some(listID))(_ => Seq.empty) + val Cons = mkCaseClass(consID)("A")(Some(listID)) { + case Seq(aT) => Seq(ValDef(head, aT), ValDef(tail, T(listID)(aT))) + } + + val sizeID = FreshIdentifier("size") + val sizeFd = mkFunDef(sizeID)("A") { case Seq(aT) => ( + Seq("l" :: T(listID)(aT)), IntegerType, { case Seq(l) => + if_ (l.isInstOf(T(consID)(aT))) { + E(BigInt(1)) + E(sizeID)(aT)(l.asInstOf(T(consID)(aT)).getField(tail)) + } else_ { + E(BigInt(0)) + } + }) + } + + val symbols = new Symbols( + Map(sizeID -> sizeFd), + Map(listID -> List, consID -> Cons, nilID -> Nil) + ) + + test("size(x) > 0 is satisfiable") { ctx => + val program = InoxProgram(ctx, symbols) + + val vd: ValDef = "x" :: T(listID)(IntegerType) + val clause = sizeFd(IntegerType)(vd.toVariable) > E(BigInt(0)) + + SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause) match { + case SatWithModel(model) => + symbols.valuateWithModel(model)(vd) match { + case CaseClass(ClassType(`consID`, Seq(IntegerType)), _) => + // success!! + case r => + fail("Unexpected valuation: " + r) + } + + case r => + fail("Unexpected response: " + r) + } + } + + test("size(x) == 0 is satisfiable") { ctx => + val program = InoxProgram(ctx, symbols) + + val tp = TypeParameter.fresh("A") + val vd: ValDef = "x" :: T(listID)(tp) + val clause = sizeFd(tp)(vd.toVariable) === E(BigInt(0)) + + SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause) match { + case SatWithModel(model) => + symbols.valuateWithModel(model)(vd) match { + case CaseClass(ClassType(`nilID`, Seq(tp)), Seq()) => + // success!! + case r => + fail("Unexpected valuation: " + r) + } + + case r => + fail("Unexpected response: " + r) + } + } + + test("size(x) < 0 is not satisfiable (unknown)") { ctx => + val program = InoxProgram(ctx, symbols) + + val vd: ValDef = "x" :: T(listID)(IntegerType) + val clause = sizeFd(IntegerType)(vd.toVariable) < E(BigInt(0)) + + assert(!SimpleSolverAPI(SolverFactory.default(program).withTimeout(100)).solveSAT(clause).isSAT) + } + + test("size(x) > size(y) is satisfiable") { ctx => + val program = InoxProgram(ctx, symbols) + + val x: ValDef = "x" :: T(listID)(IntegerType) + val y: ValDef = "y" :: T(listID)(IntegerType) + val clause = sizeFd(IntegerType)(x.toVariable) > sizeFd(IntegerType)(y.toVariable) + + assert(SimpleSolverAPI(SolverFactory.default(program)).solveSAT(clause).isSAT) + } + +} diff --git a/src/main/scala/inox/ast/Expressions.scala b/src/main/scala/inox/ast/Expressions.scala index bd4766373748d195660355797b85f924a0b0cb3f..b14cb7cac96901990bd7b81f91a1c0741fdb67e1 100644 --- a/src/main/scala/inox/ast/Expressions.scala +++ b/src/main/scala/inox/ast/Expressions.scala @@ -240,7 +240,7 @@ trait Expressions { self: Trees => /** $encodingof `.isInstanceOf[...]` */ case class IsInstanceOf(expr: Expr, classType: ClassType) extends Expr with CachingTyped { protected def computeType(implicit s: Symbols): Type = - if (s.isSubtypeOf(expr.getType, classType)) BooleanType else Untyped + if (s.typesCompatible(expr.getType, classType)) BooleanType else Untyped } /** $encodingof `expr.asInstanceOf[tpe]` diff --git a/src/main/scala/inox/ast/TypeOps.scala b/src/main/scala/inox/ast/TypeOps.scala index 79e0c4fdc9780df0db79e885255e4e14e30db2fa..c6d1a97217975563594f04712c18f852f6e3550b 100644 --- a/src/main/scala/inox/ast/TypeOps.scala +++ b/src/main/scala/inox/ast/TypeOps.scala @@ -93,6 +93,7 @@ trait TypeOps { } else { (cd1 == cd2).option(cd1) } + for { cd <- bound (subs, map) <- flatten((ct1.tps zip ct2.tps).map { case (tp1, tp2) => @@ -272,7 +273,7 @@ trait TypeOps { case accd: TypedAbstractClassDef => accd.descendants.map(_.toType) case tccd: TypedCaseClassDef => - tccd.fieldsTypes + tccd.fieldsTypes ++ tccd.parent.map(_.toType) } case NAryType(tps, _) => tps diff --git a/src/main/scala/inox/evaluators/SolvingEvaluator.scala b/src/main/scala/inox/evaluators/SolvingEvaluator.scala index 7e5ab5c7d0c3a4729278967b182707e38284bb1c..18a7e5c07f049281b26b5f07a56cf91ad8d33256 100644 --- a/src/main/scala/inox/evaluators/SolvingEvaluator.scala +++ b/src/main/scala/inox/evaluators/SolvingEvaluator.scala @@ -55,7 +55,8 @@ trait SolvingEvaluator extends Evaluator { val sf = getSolver( InoxOption(optSilentErrors)(true), - InoxOption(optCheckModels)(false), + InoxOption(optCheckModels)(false), // model is checked manually!! (see below) + InoxOption(unrolling.optFeelingLucky)(false), InoxOption(optForallCache)(cache) ) diff --git a/src/main/scala/inox/solvers/ADTManagers.scala b/src/main/scala/inox/solvers/ADTManagers.scala index 2dd2e707877f516836fe692fe52068eb5dcd093c..ca0f74363b83e9478defebb53c9fe5636ddac9c9 100644 --- a/src/main/scala/inox/solvers/ADTManagers.scala +++ b/src/main/scala/inox/solvers/ADTManagers.scala @@ -51,7 +51,7 @@ trait ADTManagers { case _ => } - for (scc <- sccs) { + for (scc <- sccs.map(scc => scc.map(bestRealType))) { val declarations = (for (tpe <- scc if !declared(tpe)) yield (tpe match { case ct: ClassType => diff --git a/src/main/scala/inox/solvers/SolverFactory.scala b/src/main/scala/inox/solvers/SolverFactory.scala index 1d6b48f41851aad983316dd0b14d5e9b174f61ce..c96cc9c351c1f3051d7f58b7cf78e1214851b028 100644 --- a/src/main/scala/inox/solvers/SolverFactory.scala +++ b/src/main/scala/inox/solvers/SolverFactory.scala @@ -37,7 +37,7 @@ object SolverFactory { import combinators._ private val solverNames = Map( - "nativez3" -> "Native Z3 with z3-templates for unrolling (default)", + "nativez3" -> "Native Z3 with z3-templates for unrolling", "unrollz3" -> "Native Z3 with leon-templates for unrolling", "smt-cvc4" -> "CVC4 through SMT-LIB", "smt-z3" -> "Z3 through SMT-LIB", @@ -117,6 +117,6 @@ object SolverFactory { } } - def default(p: InoxProgram): SolverFactory { val program: p.type } = + def default(p: InoxProgram): SolverFactory { val program: p.type; type S <: TimeoutSolver } = apply(p, p.ctx.options) } diff --git a/src/main/scala/inox/solvers/combinators/TimeoutSolverFactory.scala b/src/main/scala/inox/solvers/combinators/TimeoutSolverFactory.scala index 0c20fddc9d5ec4112588fb82e627e40ab97ba7fc..51c746358736ac9390a417f8a1e8397803a38f2d 100644 --- a/src/main/scala/inox/solvers/combinators/TimeoutSolverFactory.scala +++ b/src/main/scala/inox/solvers/combinators/TimeoutSolverFactory.scala @@ -18,7 +18,7 @@ trait TimeoutSolverFactory extends SolverFactory { override def getNewSolver() = factory.getNewSolver().setTimeout(to) - override val name = factory.name+" with t.o" + override lazy val name = factory.name+" with t.o" override def reclaim(s: S) = factory.reclaim(s) diff --git a/src/main/scala/inox/solvers/package.scala b/src/main/scala/inox/solvers/package.scala index 2fcab5abac35e61c4a9ddc1357cb4133b59f5121..1a34eae08c15a480763866466db9662937531497 100644 --- a/src/main/scala/inox/solvers/package.scala +++ b/src/main/scala/inox/solvers/package.scala @@ -2,31 +2,51 @@ package inox +import scala.language.implicitConversions import scala.concurrent.duration._ package object solvers { import combinators._ - implicit class TimeoutableSolverFactory[S1 <: TimeoutSolver](val sf: SolverFactory { type S = S1 }) { - def withTimeout(timeout: Long): TimeoutSolverFactory { type S <: S1 } = { - val innerFactory = (sf match { + /* XXX: We use an implicit conversion here instead of an implicit + * class in order for dependent types to work correctly. */ + implicit def factoryToTimeoutableFactory(sf: SolverFactory { + type S <: TimeoutSolver + }): TimeoutableFactory { + val factory: sf.type + type S = sf.S { val program: sf.program.type } + } = new TimeoutableFactory { + val factory: sf.type = sf + type S = sf.S { val program:sf.program.type } + } + + trait TimeoutableFactory { self => + val factory: SolverFactory { type S <: TimeoutSolver } + + def withTimeout(timeout: Long): TimeoutSolverFactory { + val program: self.factory.program.type + type S = self.factory.S { val program: self.factory.program.type } + } = { + val innerFactory = (factory match { case tsf: TimeoutSolverFactory => tsf.factory - case _ => sf + case _ => factory }).asInstanceOf[SolverFactory { - val program: sf.program.type - type S = S1 { val program: sf.program.type } + val program: self.factory.program.type + type S = self.factory.S { val program: self.factory.program.type } }] new TimeoutSolverFactory { - val program: sf.program.type = sf.program - type S = S1 { val program: sf.program.type } + val program: self.factory.program.type = innerFactory.program + type S = self.factory.S { val program: self.factory.program.type } val to = timeout val factory = innerFactory } } - def withTimeout(du: Duration): TimeoutSolverFactory { type S <: S1 } = { - withTimeout(du.toMillis) - } + def withTimeout(du: Duration): TimeoutSolverFactory { + val program: self.factory.program.type + type S = self.factory.S { val program: self.factory.program.type } + } = withTimeout(du.toMillis) } + } diff --git a/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala b/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala index e19fc66c81935f09bfc05e995eb65ed312e814a8..b76460324fbc3b6e6651eb5428c17a8edfa21d8c 100644 --- a/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala +++ b/src/main/scala/inox/solvers/smtlib/CVC4Solver.scala @@ -7,6 +7,8 @@ package smtlib import inox.OptionParsers._ trait CVC4Solver extends SMTLIBSolver with CVC4Target { + import program.trees._ + import SolverResponses._ def interpreterOps(ctx: InoxContext) = { Seq( @@ -19,6 +21,19 @@ trait CVC4Solver extends SMTLIBSolver with CVC4Target { "--lang", "smt2.5" ) ++ options.findOptionOrDefault(optCVC4Options) } + + override def checkAssumptions(config: Configuration)(assumptions: Set[Expr]) = { + push() + for (cl <- assumptions) assertCnstr(cl) + val res: SolverResponse[Model, Assumptions] = check(Model min config) + pop() + + config.cast(res match { + case Unsat if config.withUnsatAssumptions => + UnsatWithAssumptions(Set.empty) + case _ => res + }) + } } object optCVC4Options extends InoxOptionDef[Set[String]] { diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBSolver.scala index 0d4449f40eda45254bea0250221bed31e3a68696..cae4b915a78665bf7860f20a682e8ac340d2e578 100644 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBSolver.scala +++ b/src/main/scala/inox/solvers/smtlib/SMTLIBSolver.scala @@ -53,7 +53,7 @@ trait SMTLIBSolver extends Solver with SMTLIBTarget { } } - private def extractResponse(config: Configuration, res: SExpr): config.Response[Model, Assumptions] = + protected def extractResponse(config: Configuration, res: SExpr): config.Response[Model, Assumptions] = { config.cast(res match { case CheckSatStatus(SatStatus) => if (config.withModel) { @@ -101,6 +101,7 @@ trait SMTLIBSolver extends Solver with SMTLIBTarget { case CheckSatStatus(UnknownStatus) => Unknown case e => Unknown }) + } def check(config: CheckConfiguration): config.Response[Model, Assumptions] = extractResponse(config, emit(CheckSat())) diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBTarget.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBTarget.scala index a89a8ab6c66dffc730dcfbf03cef25643e0ced18..c072fda15b720353b417c83ae4194a731b0498c7 100644 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBTarget.scala +++ b/src/main/scala/inox/solvers/smtlib/SMTLIBTarget.scala @@ -152,7 +152,6 @@ trait SMTLIBTarget extends Interruptible with ADTManagers { body: Expr) (implicit bindings: Map[Identifier, Term]) : Term = { - if (vars.isEmpty) toSMT(body) if (vars.isEmpty) toSMT(body)(Map()) else { val sortedVars = vars map { vd => diff --git a/src/main/scala/inox/solvers/smtlib/Z3Solver.scala b/src/main/scala/inox/solvers/smtlib/Z3Solver.scala index b8d50712b027379ade7893b3cb1bff63ed434e17..d0b608091ca699e29d4c4371e0de0d83b0ce4c84 100644 --- a/src/main/scala/inox/solvers/smtlib/Z3Solver.scala +++ b/src/main/scala/inox/solvers/smtlib/Z3Solver.scala @@ -4,4 +4,27 @@ package inox package solvers package smtlib -trait Z3Solver extends SMTLIBSolver with Z3Target +import _root_.smtlib.parser.Terms.{Identifier => _, _} +import _root_.smtlib.parser.CommandsResponses._ + +trait Z3Solver extends SMTLIBSolver with Z3Target { + import program.trees._ + import SolverResponses._ + + /** Z3 uses a non-standard syntax for check-sat-assuming, namely + * {{{ + * (check-sat a1 ... an) + * }}} + * so we have to use a [[_root_.smtlib.parser.Terms.SList]] to actually + * send the command to the underlying smtlib solver. + */ + override def checkAssumptions(config: Configuration)(assumptions: Set[Expr]) = { + val cmd = SList(SSymbol("check-sat") +: assumptions.toSeq.map(as => toSMT(as)(Map.empty)) : _*) + val res = emit(cmd) match { + case SSymbol("sat") => CheckSatStatus(SatStatus) + case SSymbol("unsat") => CheckSatStatus(UnsatStatus) + case res => res + } + extractResponse(config, res) + } +} diff --git a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala index d50c5feff0e729011e162372e55ab1a02b358ef5..27f1edf537fd4e19cd7644db1f51e502096b3da9 100644 --- a/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala +++ b/src/main/scala/inox/solvers/unrolling/QuantificationTemplates.scala @@ -192,7 +192,9 @@ trait QuantificationTemplates { self: Templates => val quantifications = new IncrementalSeq[Quantification] val ignoredMatchers = new IncrementalSeq[(Int, Set[Encoded], Matcher)] - val handledMatchers = new IncrementalSeq[(Set[Encoded], Matcher)] + + // to avoid [[MatcherSet]] escaping defining context, we must keep this ~private + private[QuantificationTemplates] val handledMatchers = new MatcherSet val ignoredSubsts = new IncrementalMap[Quantification, Set[(Int, Set[Encoded], Map[Encoded,Arg])]] val handledSubsts = new IncrementalMap[Quantification, Set[(Set[Encoded], Map[Encoded,Arg])]] @@ -203,6 +205,11 @@ trait QuantificationTemplates { self: Templates => val incrementals: Seq[IncrementalState] = Seq( quantifications, ignoredMatchers, handledMatchers, ignoredSubsts, handledSubsts, lambdaAxioms, templates) + override def push(): Unit = { super.push(); for (q <- quantifications) q.push() } + override def pop(): Unit = { super.pop(); for (q <- quantifications) q.pop() } + override def clear(): Unit = { super.clear(); for (q <- quantifications) q.clear() } + override def reset(): Unit = { super.reset(); for (q <- quantifications) q.reset() } + private def assumptions: Seq[Encoded] = quantifications.collect { case q: GeneralQuantification => q.currentQ2Var } @@ -240,8 +247,13 @@ trait QuantificationTemplates { self: Templates => @inline private def instantiateMatcher(blockers: Set[Encoded], matcher: Matcher): Clauses = { - handledMatchers += blockers -> matcher - quantifications.flatMap(_.instantiate(blockers, matcher)) + if (handledMatchers(blockers -> matcher)) { + Seq.empty + } else { + //println(blockers -> matcher) + handledMatchers += blockers -> matcher + quantifications.flatMap(_.instantiate(blockers, matcher)) + } } def hasQuantifiers: Boolean = quantifications.nonEmpty @@ -278,13 +290,11 @@ trait QuantificationTemplates { self: Templates => private def correspond(m1: Matcher, m2: Matcher): Boolean = correspond(matcherKey(m1), matcherKey(m2)) - private class GroundSet private( - private val map: MutableMap[Arg, MutableSet[Set[Encoded]]] - ) extends Iterable[(Set[Encoded], Arg)] { + private trait BlockedSet[Element] extends Iterable[(Set[Encoded], Element)] with IncrementalState { + private var map: MutableMap[Element, MutableSet[Set[Encoded]]] = MutableMap.empty + private val stack = new MutableStack[MutableMap[Element, MutableSet[Set[Encoded]]]] - def this() = this(MutableMap.empty) - - def apply(p: (Set[Encoded], Arg)): Boolean = map.get(p._2) match { + def apply(p: (Set[Encoded], Element)): Boolean = map.get(p._2) match { case Some(blockerSets) => blockerSets(p._1) || // we assume here that iterating through the powerset of `p._1` // will be significantly faster then iterating through `blockerSets` @@ -292,18 +302,18 @@ trait QuantificationTemplates { self: Templates => case None => false } - def +=(p: (Set[Encoded], Arg)): Unit = if (!this(p)) map.get(p._2) match { + def +=(p: (Set[Encoded], Element)): Unit = if (!this(p)) map.get(p._2) match { case Some(blockerSets) => blockerSets += p._1 case None => map(p._2) = MutableSet.empty + p._1 } - def iterator: Iterator[(Set[Encoded], Arg)] = new collection.AbstractIterator[(Set[Encoded], Arg)] { - private val mapIt: Iterator[(Arg, MutableSet[Set[Encoded]])] = GroundSet.this.map.iterator + def iterator: Iterator[(Set[Encoded], Element)] = new collection.AbstractIterator[(Set[Encoded], Element)] { + private val mapIt: Iterator[(Element, MutableSet[Set[Encoded]])] = BlockedSet.this.map.iterator private var setIt: Iterator[Set[Encoded]] = Iterator.empty - private var current: Arg = _ + private var current: Element = _ def hasNext = mapIt.hasNext || setIt.hasNext - def next: (Set[Encoded], Arg) = if (setIt.hasNext) { + def next: (Set[Encoded], Element) = if (setIt.hasNext) { val bs = setIt.next bs -> current } else { @@ -314,15 +324,31 @@ trait QuantificationTemplates { self: Templates => } } - override def clone: GroundSet = { - val newMap: MutableMap[Arg, MutableSet[Set[Encoded]]] = MutableMap.empty + def push(): Unit = { + val newMap: MutableMap[Element, MutableSet[Set[Encoded]]] = MutableMap.empty for ((e, bss) <- map) { newMap += e -> bss.clone } - new GroundSet(newMap) + stack.push(map) + map = newMap + } + + def pop(): Unit = { + map = stack.pop() } + + def clear(): Unit = { + stack.clear() + map = MutableMap.empty + } + + def reset(): Unit = clear() } + private class GroundSet extends BlockedSet[Arg] + + private class MatcherSet extends BlockedSet[Matcher] + private def totalDepth(m: Matcher): Int = 1 + m.args.map { case Right(ma) => totalDepth(ma) case _ => 0 @@ -331,7 +357,7 @@ trait QuantificationTemplates { self: Templates => private def encodeEnablers(es: Set[Encoded]): Encoded = if (es.isEmpty) trueT else mkAnd(es.toSeq.sortBy(_.toString) : _*) - private[solvers] trait Quantification { + private[solvers] trait Quantification extends IncrementalState { val pathVar: (Variable, Encoded) val quantifiers: Seq[(Variable, Encoded)] val condVars: Map[Variable, Encoded] @@ -362,6 +388,11 @@ trait QuantificationTemplates { self: Templates => private val grounds: Map[Encoded, GroundSet] = quantified.map(q => q -> new GroundSet).toMap + def push(): Unit = for (gs <- grounds.values) gs.push() + def pop(): Unit = for (gs <- grounds.values) gs.pop() + def clear(): Unit = for (gs <- grounds.values) gs.clear() + def reset(): Unit = for (gs <- grounds.values) gs.reset() + def instantiate(bs: Set[Encoded], m: Matcher): Clauses = { /* Build mappings from quantifiers to all potential ground values previously encountered. */ @@ -814,7 +845,7 @@ trait QuantificationTemplates { self: Templates => def getGroundInstantiations(e: Encoded, tpe: Type): Seq[(Encoded, Seq[Encoded])] = { val bestTpe = bestRealType(tpe) - handledMatchers.flatMap { case (bs, m) => + handledMatchers.toSeq.flatMap { case (bs, m) => val enabler = encodeEnablers(bs) val optArgs = matcherKey(m) match { case TypeKey(tpe) if bestTpe == tpe => Some(m.args.map(_.encoded)) diff --git a/src/main/scala/inox/solvers/unrolling/Templates.scala b/src/main/scala/inox/solvers/unrolling/Templates.scala index 696b3596bb73eeeaeb30fbe3338abeb10ebd2cc0..3e0af30b5d11fb73d77e5fd5068bbe289049ebc9 100644 --- a/src/main/scala/inox/solvers/unrolling/Templates.scala +++ b/src/main/scala/inox/solvers/unrolling/Templates.scala @@ -201,7 +201,7 @@ trait Templates extends TemplateGenerator /** Represents an E-matching matcher that will be used to instantiate relevant quantified propositions */ case class Matcher(key: Either[(Encoded, Type), TypedFunDef], args: Seq[Arg], encoded: Encoded) { override def toString: String = (key match { - case Left((c, tpe)) => asString(c) + ":" + tpe.asString + case Left((c, tpe)) => asString(c) case Right(tfd) => tfd.signature }) + args.map { case Right(m) => m.toString