diff --git a/project/Build.scala b/project/Build.scala index 284491fff92a1fb8348dd81874099ff4be7019fe..002494c53806c763a01bbf79fb20525622608b8c 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -80,6 +80,6 @@ object Leon extends Build { def project(repo: String, version: String) = RootProject(uri(s"${repo}#${version}")) lazy val bonsai = project("git://github.com/colder/bonsai.git", "0fec9f97f4220fa94b1f3f305f2e8b76a3cd1539") - lazy val scalaSmtLib = project("git://github.com/regb/scala-smtlib.git", "9f88509b13e7e64214c0b9b6a9124fc6c29e2084") + lazy val scalaSmtLib = project("git://github.com/regb/scala-smtlib.git", "24a8b85949c69189f37c5fdfab3265801a084ee8") } } diff --git a/src/main/scala/leon/LeonOption.scala b/src/main/scala/leon/LeonOption.scala index bec459b59568dff077061628db5a15f84b15b308..f481904558a7eba94e349814e6edd36de69770f9 100644 --- a/src/main/scala/leon/LeonOption.scala +++ b/src/main/scala/leon/LeonOption.scala @@ -45,6 +45,8 @@ sealed abstract class LeonOptionDef { val name: String val usageOption: String val usageDesc: String + + def usageDescs = usageDesc.split("\n").toList } case class LeonFlagOptionDef(name: String, diff --git a/src/main/scala/leon/Main.scala b/src/main/scala/leon/Main.scala index 2a6c90dcb3873c87e2cd3026d04ed5874f7ea85d..daff9774815c2718a4d8c51256705f3ae69616c9 100644 --- a/src/main/scala/leon/Main.scala +++ b/src/main/scala/leon/Main.scala @@ -28,7 +28,8 @@ object Main { // Add whatever you need here. lazy val allComponents : Set[LeonComponent] = allPhases.toSet ++ Set( - new solvers.z3.FairZ3Component{} + new solvers.z3.FairZ3Component{}, + solvers.smtlib.SMTLIBCVC4Component ) lazy val topLevelOptions : Set[LeonOptionDef] = Set( @@ -37,7 +38,10 @@ object Main { LeonFlagOptionDef ("synthesis", "--synthesis", "Partial synthesis of choose() constructs"), LeonFlagOptionDef ("xlang", "--xlang", "Support for extra program constructs (imperative,...)"), LeonFlagOptionDef ("watch", "--watch", "Rerun pipeline when file changes"), - LeonValueOptionDef("solvers", "--solvers=s1,s2", "Use solvers s1 and s2 (fairz3,enum,smt-z3,smt-cvc4)"), + LeonValueOptionDef("solvers", "--solvers=s1,s2", "Use solvers s1 and s2 \nAvailable:"+ + SolverFactory.definedSolvers.toSeq.sortBy(_._1).map { + case (name, desc) => f"\n $name%-14s : $desc" + }.mkString("")), LeonValueOptionDef("debug", "--debug=<sections..>", "Enables specific messages"), LeonFlagOptionDef ("noop", "--noop", "No operation performed, just output program"), LeonFlagOptionDef ("help", "--help", "Show help") @@ -49,9 +53,13 @@ object Main { reporter.info("usage: leon [--xlang] [--termination] [--synthesis] [--help] [--debug=<N>] [..] <files>") reporter.info("") for (opt <- topLevelOptions.toSeq.sortBy(_.name)) { - reporter.info(f"${opt.usageOption}%-20s ${opt.usageDesc}") + val (uhead :: utail) = opt.usageDescs + reporter.info(f"${opt.usageOption}%-20s ${uhead}") + for(u <- utail) { + reporter.info(f"${""}%-20s ${u}") + } + } - reporter.info("(By default, Leon verifies PureScala programs.)") reporter.info("") reporter.info("Additional options, by component:") @@ -151,7 +159,7 @@ object Main { case LeonFlagOption("xlang", value) => settings = settings.copy(xlang = value) case LeonValueOption("solvers", ListValue(ss)) => - val available = SolverFactory.definedSolvers + val available = SolverFactory.definedSolvers.keySet val unknown = ss.toSet -- available if (unknown.nonEmpty) { initReporter.error("Unknown solver(s): "+unknown.mkString(", ")+" (Available: "+available.mkString(", ")+")") diff --git a/src/main/scala/leon/solvers/SolverFactory.scala b/src/main/scala/leon/solvers/SolverFactory.scala index ec417d847b1a25d2d2a6835d4b679f4f83ecb0e5..19e2f20ea74552c5be641898e07bd26e7f672662 100644 --- a/src/main/scala/leon/solvers/SolverFactory.scala +++ b/src/main/scala/leon/solvers/SolverFactory.scala @@ -19,7 +19,16 @@ object SolverFactory { } } - val definedSolvers = Set("fairz3", "unrollz3", "enum", "smt", "smt-z3", "smt-z3-quantified", "smt-cvc4", "smt-2.5-cvc4") + val definedSolvers = Map( + "fairz3" -> "Native Z3 with z3-templates for unfolding (default)", + "smt-cvc4" -> "CVC4 through SMT-LIB", + "smt-z3" -> "Z3 through SMT-LIB", + "smt-z3-q" -> "Z3 through SMT-LIB, with quantified encoding", + "smt-cvc4-proof" -> "CVC4 through SMT-LIB, in-solver inductive reasonning, for proofs only", + "smt-cvc4-cex" -> "CVC4 through SMT-LIB, in-solver finite-model-finding, for counter-examples only", + "unrollz3" -> "Native Z3 with leon-templates for unfolding", + "enum" -> "Enumeration-based counter-example-finder" + ) def getFromSettings[S](ctx: LeonContext, program: Program): SolverFactory[TimeoutSolver] = { import combinators._ @@ -36,17 +45,20 @@ object SolverFactory { case "enum" => SolverFactory(() => new EnumerationSolver(ctx, program) with TimeoutSolver) - case "smt" | "smt-z3" => + case "smt-z3" => SolverFactory(() => new UnrollingSolver(ctx, program, new SMTLIBSolver(ctx, program) with SMTLIBZ3Target) with TimeoutSolver) - case "smt-z3-quantified" => + case "smt-z3-q" => SolverFactory(() => new SMTLIBSolver(ctx, program) with SMTLIBZ3QuantifiedTarget with TimeoutSolver) case "smt-cvc4" => SolverFactory(() => new UnrollingSolver(ctx, program, new SMTLIBSolver(ctx, program) with SMTLIBCVC4Target) with TimeoutSolver) - case "smt-2.5-cvc4" => - SolverFactory(() => new SMTLIBSolver(ctx, program) with SMTLIBUnrollingCVC4Target with TimeoutSolver) + case "smt-cvc4-proof" => + SolverFactory(() => new SMTLIBSolver(ctx, program) with SMTLIBCVC4ProofTarget with TimeoutSolver) + + case "smt-cvc4-cex" => + SolverFactory(() => new SMTLIBSolver(ctx, program) with SMTLIBCVC4CounterExampleTarget with TimeoutSolver) case _ => ctx.reporter.fatalError("Unknown solver "+name) diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4CounterExampleTarget.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4CounterExampleTarget.scala new file mode 100644 index 0000000000000000000000000000000000000000..62bd9307881ef29d22e0a3578eb3f8fe84b6aa67 --- /dev/null +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4CounterExampleTarget.scala @@ -0,0 +1,27 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package solvers.smtlib + +import purescala.Common.FreshIdentifier +import purescala.Expressions.{FunctionInvocation, BooleanLiteral, Expr, Implies} +import purescala.Definitions.TypedFunDef +import purescala.Constructors.application +import purescala.DefOps.typedTransitiveCallees +import leon.purescala.ExprOps.matchToIfThenElse +import smtlib.parser.Commands._ +import smtlib.parser.Terms._ + +trait SMTLIBCVC4CounterExampleTarget extends SMTLIBCVC4QuantifiedTarget { + this: SMTLIBSolver => + + override def interpreterOps(ctx: LeonContext) = { + Seq( + "-q", + "--print-success", + "--lang", "smt", + "--fmf-fun" + ) ++ userDefinedOps(ctx) + } + +} diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4ProofTarget.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4ProofTarget.scala new file mode 100644 index 0000000000000000000000000000000000000000..1df96a80d44be45e565f22e8bc8fd59b9fb0cd29 --- /dev/null +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4ProofTarget.scala @@ -0,0 +1,31 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package solvers.smtlib + +import purescala.Common.FreshIdentifier +import purescala.Expressions.{FunctionInvocation, BooleanLiteral, Expr, Implies} +import purescala.Definitions.TypedFunDef +import purescala.Constructors.application +import purescala.DefOps.typedTransitiveCallees +import leon.purescala.ExprOps.matchToIfThenElse +import smtlib.parser.Commands._ +import smtlib.parser.Terms._ + +trait SMTLIBCVC4ProofTarget extends SMTLIBCVC4QuantifiedTarget { + this: SMTLIBSolver => + + override def interpreterOps(ctx: LeonContext) = { + Seq( + "-q", + "--print-success", + "--lang", "smt", + "--quant-ind", + "--conjecture-gen", + "--conjecture-gen-per-round=3", + "--conjecture-gen-gt-enum=40", + "--full-saturate-quant" + ) ++ userDefinedOps(ctx) + } + +} diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBUnrollingCVC4Target.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala similarity index 96% rename from src/main/scala/leon/solvers/smtlib/SMTLIBUnrollingCVC4Target.scala rename to src/main/scala/leon/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala index 4a72c468d49e30b992549e483e4ceb3d5aa11ef5..c792aed5542a98e25195e47503371cb6e159eb61 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBUnrollingCVC4Target.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala @@ -12,12 +12,11 @@ import leon.purescala.ExprOps.matchToIfThenElse import smtlib.parser.Commands._ import smtlib.parser.Terms._ -trait SMTLIBUnrollingCVC4Target extends SMTLIBCVC4Target { +trait SMTLIBCVC4QuantifiedTarget extends SMTLIBCVC4Target { this: SMTLIBSolver => private val typedFunDefExplorationLimit = 10000 - override def targetName = "2.5-cvc4" override def declareFunction(tfd: TypedFunDef): SSymbol = { if (tfd.params.isEmpty) { super[SMTLIBCVC4Target].declareFunction(tfd) diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala index fcca0aad1de214367a01117e25a09e718cbe4244..e1c8c1d2662d098cd1bedc1a017267b5edc92aaa 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Target.scala @@ -21,7 +21,29 @@ trait SMTLIBCVC4Target extends SMTLIBTarget { def targetName = "cvc4" - def getNewInterpreter() = CVC4Interpreter.buildDefault + def userDefinedOps(ctx: LeonContext) = { + ctx.options.collect { + case LeonValueOption("solver:cvc4", value) => value + } + } + + def interpreterOps(ctx: LeonContext) = { + Seq( + "-q", + "--produce-models", + "--no-incremental", + "--tear-down-incremental", + "--dt-rewrite-error-sel", + "--print-success", + "--lang", "smt" + ) ++ userDefinedOps(ctx) + } + + def getNewInterpreter(ctx: LeonContext) = { + val opts = interpreterOps(ctx) + reporter.debug("Invoking solver with "+opts.mkString(" ")) + new CVC4Interpreter("cvc4", opts.toArray) + } override def declareSort(t: TypeTree): Sort = { val tpe = normalizeType(t) @@ -160,3 +182,13 @@ trait SMTLIBCVC4Target extends SMTLIBTarget { super[SMTLIBTarget].toSMT(e) } } + +object SMTLIBCVC4Component extends LeonComponent { + val name = "cvc4 solver" + + val description = "Solver invoked when --solvers=smt-cvc4" + + override val definedOptions : Set[LeonOptionDef] = Set( + LeonValueOptionDef("solver:cvc4", "--solver:cvc4=<cvc4-opt>", "Pass extra arguments to CVC4") + ) +} diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala index 8a38fd562998ef414ce1ec7743f98eeb61e3d4e4..fd687b77808ac57cc1decb6a889d6eb1b4393be1 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala @@ -34,9 +34,12 @@ trait SMTLIBTarget { val reporter = context.reporter def targetName: String - def getNewInterpreter(): SMTInterpreter - val interpreter = getNewInterpreter() + def interpreterOps(ctx: LeonContext): Seq[String] + + def getNewInterpreter(ctx: LeonContext): SMTInterpreter + + val interpreter = getNewInterpreter(context) var out: java.io.FileWriter = _ @@ -694,7 +697,7 @@ trait SMTLIBTarget { case CheckSatStatus(SatStatus) => Some(true) case CheckSatStatus(UnsatStatus) => Some(false) case CheckSatStatus(UnknownStatus) => None - case _ => None + case e => None } override def getModel: Map[Identifier, Expr] = { diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala index 44caf7c3b25d14afcc2aa57c4c64d2bce008e4ba..47088d817dfda6dfc9fe5cb4ef9e99118fd222b7 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala @@ -23,7 +23,14 @@ trait SMTLIBZ3Target extends SMTLIBTarget { def targetName = "z3" - def getNewInterpreter() = Z3Interpreter.buildDefault + def interpreterOps(ctx: LeonContext) = { + Seq( + "-in", + "-smt2" + ) + } + + def getNewInterpreter(ctx: LeonContext) = new Z3Interpreter("z3", interpreterOps(ctx).toArray) val extSym = SSymbol("_")