diff --git a/src/main/scala/inox/solvers/cvc4/CVC4Solver.scala b/src/main/scala/inox/solvers/cvc4/CVC4Solver.scala deleted file mode 100644 index ff112d7921fdf9f1c74bf7c7187f1e312c58d713..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/cvc4/CVC4Solver.scala +++ /dev/null @@ -1,7 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package cvc4 - -trait CVC4Solver extends Solver diff --git a/src/main/scala/inox/solvers/cvc4/CVC4UnrollingSolver.scala b/src/main/scala/inox/solvers/cvc4/CVC4UnrollingSolver.scala deleted file mode 100644 index b293abbe209269ef1373ed4937be52a43850bc1c..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/cvc4/CVC4UnrollingSolver.scala +++ /dev/null @@ -1,15 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package solvers -package cvc4 - -import purescala.Definitions._ - -import unrolling._ -import theories._ - -class CVC4UnrollingSolver(context: SolverContext, program: Program, underlying: CVC4Solver) - extends UnrollingSolver(context, program, underlying, - theories = new BagEncoder(context.context, program)) - with CVC4Solver diff --git a/src/main/scala/inox/solvers/isabelle/AdaptationPhase.scala b/src/main/scala/inox/solvers/isabelle/AdaptationPhase.scala deleted file mode 100644 index d3743903d176b6ebe47220afbf5daed6753e5b41..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/AdaptationPhase.scala +++ /dev/null @@ -1,70 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import leon._ -import leon.purescala.Common._ -import leon.purescala.Definitions._ -import leon.purescala.DefOps._ -import leon.purescala.Expressions._ -import leon.purescala.TypeOps -import leon.purescala.Types._ -import leon.utils._ - -object AdaptationPhase extends TransformationPhase { - - val name = "adaptation" - val description = "Function parameter adaptation" - - implicit val debugSection = DebugSectionIsabelle - - def apply(context: LeonContext, program: Program): Program = { - val dummy = program.library.Dummy match { - case Some(dummy) => dummy - case None => context.reporter.internalError("Definition of dummy type not found") - } - - def mkDummyTyp(tp: TypeParameter) = - CaseClassType(dummy, List(tp)) - - def mkDummyParameter(tp: TypeParameter) = - ValDef(FreshIdentifier("dummy", mkDummyTyp(tp))) - - def mkDummyArgument(tree: TypeTree) = - CaseClass(CaseClassType(dummy, List(tree)), Nil) - - val enabled = - context.findOptionOrDefault(GlobalOptions.optSelectedSolvers).contains("isabelle") || - context.findOptionOrDefault(Main.MainComponent.optIsabelle) - - if (!enabled) program - else { - context.reporter.debug("Running adaptation phase, because Isabelle is selected ...") - - val map = program.definedFunctions.flatMap { fd => - val expected = fd.tparams.map(_.tp).toSet - val actual = fd.params.map(_.getType).flatMap(TypeOps.typeParamsOf).toSet ++ TypeOps.typeParamsOf(fd.returnType) - if (expected == actual) - None - else { - val diff: List[TypeParameter] = (expected -- actual).toList - context.reporter.debug(s"Rewriting function definition ${fd.qualifiedName(program)}: adding dummies for hidden type parameter(s) ${diff.map(_.id).mkString(" and ")}") - val nid = FreshIdentifier(fd.id.name, FunctionType(fd.params.map(_.getType) ++ diff.map(mkDummyTyp), fd.returnType)) - val nfd = fd.duplicate(id = nid, params = fd.params ++ diff.map(mkDummyParameter)) - Some(fd -> ((nfd, diff))) - } - }.toMap - - def mapFunDef(fd: FunDef): Option[FunDef] = map.get(fd).map(_._1) - - def mapFunInvocation(fi: FunctionInvocation, nfd: FunDef): Option[Expr] = map.get(fi.tfd.fd).map { case (_, diff) => - val inst = (nfd.tparams.map(_.tp) zip fi.tfd.tps).toMap - val args = diff.map(t => mkDummyArgument(inst(t))) - FunctionInvocation(nfd.typed(fi.tfd.tps), fi.args ++ args) - } - - transformProgram(funDefReplacer(mapFunDef, mapFunInvocation), program) - } - } - -} diff --git a/src/main/scala/inox/solvers/isabelle/Component.scala b/src/main/scala/inox/solvers/isabelle/Component.scala deleted file mode 100644 index 463ed8478b62fb3753cf5522e14b485e7baccc58..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/Component.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import java.nio.file.Paths - -import scala.concurrent._ -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -import leon._ - -import edu.tum.cs.isabelle.setup._ - -object Component extends LeonComponent { - - val name = "Isabelle" - val description = "Isabelle solver" - - def platform = - Platform.guess.getOrElse(sys.error("Unknown platform; can't run Isabelle here")) - - val optMapping = LeonFlagOptionDef( - name = "isabelle:mapping", - description = "Enable processing of type and function mapping annotations", - default = true - ) - - val optDump = LeonStringOptionDef( - name = "isabelle:dump", - description = "Dump theory source files into the specified directory", - default = "", - usageRhs = "path" - ) - - val optStrict = LeonFlagOptionDef( - name = "isabelle:strict", - description = "Require proofs for indirectly referenced functions", - default = true - ) - - override val definedOptions: Set[LeonOptionDef[Any]] = - Set(optMapping, optDump, optStrict) - -} diff --git a/src/main/scala/inox/solvers/isabelle/Functions.scala b/src/main/scala/inox/solvers/isabelle/Functions.scala deleted file mode 100644 index a258fd1cd83813a05c0303af2d067f1e1f181f21..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/Functions.scala +++ /dev/null @@ -1,155 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.math.BigInt - -import leon.LeonContext -import leon.purescala.Common._ -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.purescala.ExprOps -import leon.purescala.Types._ -import leon.utils._ - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.pure.{Expr => _, _} - -final class Functions(context: LeonContext, program: Program, types: Types, funs: List[FunDef], system: System)(implicit ec: ExecutionContext) { - - private implicit val debugSection = DebugSectionIsabelle - private val strict = context.findOptionOrDefault(Component.optStrict) - - private val translator = new Translator(context, program, types, system) - - private val allLemmas = program.definedFunctions.flatMap(_.checkLemma(program, context)).toMap - - private def closure(funs: Set[FunDef]): Set[FunDef] = { - val referenced = funs.flatMap(program.callGraph.transitiveCallees) - val lemmas = allLemmas.filter(_._2.exists(referenced.contains)).keySet - if (lemmas.subsetOf(funs) && referenced.subsetOf(funs)) - funs - else { - context.reporter.debug(s"Discovered relevant lemma(s): ${lemmas.map(_.qualifiedName(program)).mkString(", ")}") - closure(funs ++ referenced ++ lemmas) - } - } - - private val relevant = closure(funs.toSet).toList - - private val preGroups = program.callGraph.stronglyConnectedComponents.map(_.filter(relevant.contains)).filterNot(_.isEmpty).toSet - private val callGraph = preGroups.map { group => group -> - preGroups.filter(cand => cand.exists(to => group.exists(from => program.callGraph.calls(from, to))) && cand != group) - }.toMap - - private val groups = GraphOps.topologicalSorting(callGraph) match { - case Right(seq) => seq.toList - case Left(_) => context.reporter.internalError("unexpected cycle in call graph") - } - - context.reporter.debug(s"Functions to be defined: ${groups.map(_.map(_.qualifiedName(program)).mkString("{", ", ", "}")).mkString(", ")}") - - private def globalLookup(data: List[FunDef])(fun: FunDef, typ: Typ) = - if (data.contains(fun)) - Const(s"${theory}.${fun.id.mangledName}", typ) - else - context.reporter.internalError(s"Unknown function ${fun.qualifiedName(program)}") - - private def processGroup(group: Set[FunDef], data: List[FunDef]): Future[List[FunDef]] = { - def lookup(fun: FunDef, typ: Typ): Term = - if (group.contains(fun)) - Free(fun.id.mangledName, typ) - else if (data.contains(fun)) - Const(s"${theory}.${fun.id.mangledName}", typ) - else - context.reporter.internalError(s"Unknown function ${fun.qualifiedName(program)} while defining functions") - - val defs = group.toList - - context.reporter.debug(s"Defining function(s) ${defs.map(_.qualifiedName(program)).mkString(", ")} ...") - - val pairs = defs.flatMap { fun => - val name = fun.qualifiedName(program) - fun.extAnnotations.get("isabelle.function") match { - case Some(List(Some(name: String))) => Some(fun -> (fun.id.mangledName -> name)) - case Some(List(_)) => - context.reporter.fatalError(s"In function mapping for function definition $name: expected a string literal as name") - case Some(_) => - context.reporter.internalError(s"Function mapping for function definition $name") - case None => - None - } - } - - system.invoke(Declare)(defs.map(_.id.mangledName)).assertSuccess(context).flatMap { case () => Future.traverse(defs) { fun => - val name = fun.id.mangledName - val params = Future.traverse(fun.params.toList) { param => - types.typ(param.getType, strict = true).map(param.id.mangledName -> _) - } - - val full = fun.annotations.contains("isabelle.fullBody") - val none = fun.annotations.contains("isabelle.noBody") - - val body = (full, none) match { - case (true, false) => translator.term(fun.fullBody, Nil, lookup) - case (false, true) => translator.mkFreshError(None) - case (false, false) => translator.term(fun.body.get, Nil, lookup) - case (true, true) => context.reporter.fatalError(s"Conflicting body annotations for function definition ${fun.qualifiedName(program)}") - } - - for { - params <- params - body <- body - typ <- types.typ(fun.returnType, strict = true) - } - yield - (name, params, (body, typ)) - }}.flatMap(system.invoke(Functions)).assertSuccess(context).flatMap { - case Some(msg) => context.reporter.fatalError(s"In function definition: $msg") - case None => - def requireProof(fun: FunDef) = funs.contains(fun) || strict - - val (checked, unchecked) = pairs.partition { case (fun, _) => requireProof(fun) } - - val equivalences = - for { - failed <- system.invoke(Equivalences)(checked.map(_._2)).assertSuccess(context) - () <- system.invoke(AssumeEquivalences)(unchecked.map(_._2)).assertSuccess(context) - } - yield failed - - equivalences.foreach { - case Nil => () - case failed => context.reporter.warning(s"Equivalence check(s) for ${failed.mkString(" and ")} failed") - } - equivalences.map(_ => data ++ defs) - }.flatMap { data => - def generateStatement(fun: FunDef): Option[Future[(String, Term)]] = - fun.statement.map(expr => translator.term(expr, Nil, globalLookup(data)).map(fun.id.mangledName -> _)) - - val lemmas = - Future.sequence(defs.filterNot(funs.contains).flatMap(generateStatement)).flatMap { statements => - if (strict) - system.invoke(Lemmas)(statements).assertSuccess(context) - else - system.invoke(AssumeLemmas)(statements).assertSuccess(context).map(_ => Nil) - } - - lemmas.foreach { - case Nil => () - case failed => context.reporter.warning(s"Proof(s) of lemma(s) for ${failed.mkString(" and ")} failed") - } - - lemmas.map(_ => data) - } - } - - val data: Future[List[FunDef]] = - groups.foldLeft(Future.successful(List.empty[FunDef])) { (acc, group) => - acc.flatMap(processGroup(group, _)) - } - - def term(expr: Expr): Future[Term] = data.flatMap(data => translator.term(expr, Nil, globalLookup(data))) - -} diff --git a/src/main/scala/inox/solvers/isabelle/IsabelleEnvironment.scala b/src/main/scala/inox/solvers/isabelle/IsabelleEnvironment.scala deleted file mode 100644 index 6b3346152ef0bfc0e4e16dd20291190154b5387d..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/IsabelleEnvironment.scala +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import java.io.FileWriter -import java.nio.file.{Files, Paths} - -import scala.concurrent._ -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -import leon.LeonContext -import leon.OptionsHelpers._ -import leon.GlobalOptions -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.purescala.Common._ -import leon.solvers._ -import leon.utils._ - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.api._ -import edu.tum.cs.isabelle.setup._ - -object IsabelleEnvironment { - - private implicit val debugSection = DebugSectionIsabelle - - def apply(context: LeonContext, program: Program): Future[IsabelleEnvironment] = { - LeonLoggerFactory.reporter = context.reporter - - val version = Version(isabelleVersion) - val dump = context.findOptionOrDefault(Component.optDump) - val strict = context.findOptionOrDefault(Component.optStrict) - - val funFilter = - // FIXME duplicated from AnalysisPhase - filterInclusive[FunDef](context.findOption(GlobalOptions.optFunctions).map(fdMatcher(program)), Some(_.annotations contains "library")) - - val funs = program.definedFunctions.filter(funFilter) - - val scripts = funs.flatMap { fun => - val name = fun.qualifiedName(program) - fun.extAnnotations.get("isabelle.script") match { - case Some(List(Some(name: String), Some(source: String))) => Some(name -> source) - case Some(List(_, _)) => - context.reporter.fatalError(s"In script for function definition $name: expected a string literal as name and a string literal as source") - case Some(_) => - context.reporter.internalError(s"Script for function definition $name") - case None => - None - } - }.toList - - context.reporter.info(s"Preparing Isabelle setup (this might take a while) ...") - val setup = Setup.defaultSetup(version) - - val system = setup.flatMap { setup => - val resources = Resources.dumpIsabelleResources() - val config = resources.makeConfiguration(Nil, "Leon") - - setup.makeEnvironment.flatMap { env => - context.reporter.info(s"Building session ...") - if (!System.build(env, config)) - context.reporter.internalError("Build failed") - - context.reporter.info(s"Starting $version instance ...") - System.create(env, config) - } - } - - val thy = system.flatMap { system => - context.reporter.debug(s"Loading theory as $theory ...") - - system.invoke(Load)(("Leon", theory, scripts)).assertSuccess(context).map { - case Nil => - () - case bad => - context.reporter.fatalError(s"Could not process the following scripts: ${bad.mkString(", ")}") - } - } - - val oracle = - if (strict) { - context.reporter.debug("Strict mode enabled; background proofs will be replayed in Isabelle") - Future.successful { () } - } - else { - context.reporter.warning("Strict mode disabled; proofs may be unsound") - thy.flatMap(_ => system.flatMap(_.invoke(Oracle)(()))).assertSuccess(context) - } - - val types = - for { - s <- system - () <- thy - _ = context.reporter.debug(s"Defining types ...") - } - yield - new Types(context, program, s) - - val functions = - for { - s <- system - t <- types - () <- oracle - _ <- t.data - } - yield - new Functions(context, program, t, funs, s) - - functions.flatMap(_.data).foreach { _ => - if (dump.isEmpty) - system.foreach { sys => - sys.invoke(Report)(()).assertSuccess(context).foreach { report => - context.reporter.debug(s"Report for $theory ...") - report.foreach { case (key, value) => - context.reporter.debug(s"$key: ${canonicalizeOutput(sys, value)}") - } - } - } - else - system.flatMap(_.invoke(Dump)(())).assertSuccess(context).foreach { output => - context.reporter.debug(s"Dumping theory sources to $dump ...") - val path = Files.createDirectories(Paths.get(dump)) - output.foreach { case (name, content) => - val writer = new FileWriter(path.resolve(s"$name.thy").toFile()) - writer.write(content) - writer.close() - } - } - } - - for { - s <- system - t <- types - f <- functions - _ <- t.data - _ <- f.data - } - yield new IsabelleEnvironment(context, program, t, f, s, funs) - } - -} - -final class IsabelleEnvironment private( - val context: LeonContext, - val program: Program, - val types: Types, - val functions: Functions, - val system: System, - val selectedFunDefs: List[FunDef] -) { - def solver: IsabelleSolver with TimeoutSolver = - new IsabelleSolver(context.toSctx, program, types, functions, system) with TimeoutSolver -} diff --git a/src/main/scala/inox/solvers/isabelle/IsabellePhase.scala b/src/main/scala/inox/solvers/isabelle/IsabellePhase.scala deleted file mode 100644 index 3cfe02990a70c14abf3a0df83536b21e6c18cd22..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/IsabellePhase.scala +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -import leon._ -import leon.purescala.Definitions._ -import leon.utils._ -import leon.verification._ - -object IsabellePhase extends SimpleLeonPhase[Program, VerificationReport] { - - object IsabelleVC extends VCKind("Isabelle", "isa") - - val name = "isabelle" - val description = "Isabelle verification" - - implicit val debugSection = DebugSectionIsabelle - - def apply(context: LeonContext, program: Program): VerificationReport = { - val env = IsabelleEnvironment(context, program) - - val report = env.map { env => - def prove(fd: FunDef) = fd.statement.map { expr => - context.reporter.debug(s"Proving postcondition of ${fd.qualifiedName(program)} ...") - val vc = VC(expr, fd, IsabelleVC).setPos(fd.getPos) - val term = env.functions.term(expr) - val input = term.map(t => (List(t), fd.proofMethod(vc, context))) - val result = Await.result(input.flatMap(env.system.invoke(Prove)).assertSuccess(context), Duration.Inf) match { - case Some(thm) => VCResult(VCStatus.Valid, None, None) - case None => VCResult(VCStatus.Unknown, None, None) - } - vc -> Some(result) - } - - VerificationReport(program, env.selectedFunDefs.flatMap(prove).toMap) - } - - Await.result(report, Duration.Inf) - } - -} diff --git a/src/main/scala/inox/solvers/isabelle/IsabelleSolver.scala b/src/main/scala/inox/solvers/isabelle/IsabelleSolver.scala deleted file mode 100644 index b1895380c94483c4646950d68b676aacf3cdb986..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/IsabelleSolver.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.concurrent.duration._ - -import scala.math.BigInt - -import leon.LeonContext -import leon.purescala.Common._ -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.solvers._ -import leon.utils.Interruptible -import leon.verification.VC - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.pure.{Expr => _, _} - -class IsabelleSolver(val sctx: SolverContext, program: Program, types: Types, functions: Functions, system: System) extends Solver with Interruptible { self: TimeoutSolver => - - context.interruptManager.registerForInterrupts(this) - - import system.executionContext - - private def timeout = optTimeout.map(_.millis).getOrElse(Duration.Inf) - - val name = "isabelle" - - private var pending: List[Future[Term]] = Nil - private var method: Option[String] = None - private var running: Option[CancellableFuture[_]] = None - - def reset() = { pending = Nil; method = None; running = None } - private def addPending(future: Future[Term]) = { pending ::= future } - private def sequencePending() = { Future.sequence(pending) } - - override def assertVC(vc: VC): Unit = { - val name = vc.fd.qualifiedName(program) - addPending(functions.term(vc.condition)) - (vc.fd.proofMethod(vc, context), method) match { - case (None, _) => - case (Some(m1), Some(m2)) if m1 == m2 => - case (Some(m1), Some(m2)) => context.reporter.error(s"In proof hint for function definition $name: can't override existing method $m2 with method $m1") - case (Some(m1), None) => method = Some(m1) - } - } - - def check: Option[Boolean] = { - val verdict = sequencePending().flatMapC { ts => - Future.traverse(ts)(system.invoke(Pretty)(_).assertSuccess(context)).foreach { strs => - context.reporter.debug(s"Attempting to prove disjunction of ${canonicalizeOutput(system, strs.mkString(", "))}") - } - - system.cancellableInvoke(Prove)((ts, method)) - } - running = Some(verdict) - - try { - Await.result(verdict.future.assertSuccess(context), timeout) match { - case Some(thm) => - context.reporter.debug(s"Proved theorem: ${canonicalizeOutput(system, thm)}") - Some(false) - case None => - None - } - } - catch { - case _: TimeoutException => - context.reporter.info("Isabelle timed out") - verdict.cancel() - None - case _: CancellationException => - None - } - } - - def free(): Unit = {} - def interrupt(): Unit = { running.foreach(_.cancel()) } - def recoverInterrupt(): Unit = {} - - def checkAssumptions(assumptions: Set[Expr]): Option[Boolean] = None - - // 'check' never returns 'Some(true)' - def getModel = sys.error("impossible") - - // 'checkAssumptions' never returns 'Some' - def getUnsatCore = sys.error("impossible") - - // custom 'assertVC' - def assertCnstr(expression: Expr): Unit = sys.error("impossible") - - // unimplemented - def pop(): Unit = ??? - def push(): Unit = ??? - -} diff --git a/src/main/scala/inox/solvers/isabelle/IsabelleSolverFactory.scala b/src/main/scala/inox/solvers/isabelle/IsabelleSolverFactory.scala deleted file mode 100644 index 11dfe40d392d1b0bb88fee7e1d7f9f1a2166450b..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/IsabelleSolverFactory.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.concurrent.duration._ -import scala.concurrent.ExecutionContext.Implicits.global - -import leon.LeonContext -import leon.purescala.Definitions._ -import leon.solvers._ - -final class IsabelleSolverFactory(context: LeonContext, program: Program) extends SolverFactory[TimeoutSolver] { - - val name = "isabelle" - - private val env = IsabelleEnvironment(context, program) - - override def shutdown() = - Await.result(env.flatMap(_.system.dispose), Duration.Inf) - - def getNewSolver() = - Await.result(env.map(_.solver), Duration.Inf) - -} diff --git a/src/main/scala/inox/solvers/isabelle/LeonLoggerFactory.scala b/src/main/scala/inox/solvers/isabelle/LeonLoggerFactory.scala deleted file mode 100644 index 078659c9c138fce536196c7642d5496dd85d9311..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/LeonLoggerFactory.scala +++ /dev/null @@ -1,48 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import org.slf4j.{Logger, ILoggerFactory} -import org.slf4j.helpers.NOPLogger - -import info.hupel.slf4j.DefaultLogger - -import leon.Reporter -import leon.utils._ - -object LeonLoggerFactory { - - private implicit val debugSection = DebugSectionIsabelle - - private var _reporter: Option[Reporter] = None - - private[isabelle] def reporter_=(r: Reporter): Unit = _reporter = Some(r) - - private[isabelle] def reporter: Reporter = _reporter.getOrElse(sys.error("No reporter set")) - - private class LeonLogger extends DefaultLogger { - import DefaultLogger._ - protected def writeLogMessage(level: LogLevel, message: Option[String], t: Option[Throwable] = None) { - message.foreach { m => - level match { - case TRACE => reporter.debug(m) - case DEBUG => reporter.debug(m) - case INFO => reporter.info(m) - case WARN => reporter.warning(m) - case ERROR => reporter.error(m) - } - } - t.foreach(t => reporter.debug(t)) - } - - } - -} - -class LeonLoggerFactory extends ILoggerFactory { - override def getLogger(name: String): Logger = - if (name startsWith "edu.tum.cs.isabelle") - new LeonLoggerFactory.LeonLogger - else - NOPLogger.NOP_LOGGER -} diff --git a/src/main/scala/inox/solvers/isabelle/Translator.scala b/src/main/scala/inox/solvers/isabelle/Translator.scala deleted file mode 100644 index 3a0734726f0a2118bf3621fd1b9da35aa31b8f2e..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/Translator.scala +++ /dev/null @@ -1,329 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.math.BigInt - -import leon.LeonContext -import leon.purescala.Common._ -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.purescala.ExprOps -import leon.purescala.Types._ -import leon.utils._ - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.pure.{Expr => _, _} - -final class Translator(context: LeonContext, program: Program, types: Types, system: System)(implicit ec: ExecutionContext) { - - private implicit val debugSection = DebugSectionIsabelle - - private def mkApp(f: Term, args: Term*): Term = - args.foldLeft(f) { (acc, arg) => App(acc, arg) } - - private def lookupConstructor(typ: CaseClassType): Future[Constructor] = - types.data.map(_.get(Types.findRoot(typ).classDef).flatMap(_.constructors.get(typ.classDef)) match { - case Some(constr) => constr - case None => context.reporter.internalError(s"$typ not found in program") - }) - - private def mkFresh(typ: Option[TypeTree]) = - system.invoke(Fresh)("fresh''").assertSuccess(context).flatMap { name => - types.optTyp(typ).map(Free(name, _)) - } - - def mkFreshError(typ: Option[TypeTree]): Future[Term] = - system.invoke(SerialNat)(()).assertSuccess(context).flatMap { nat => - types.optTyp(typ).map { typ => - App(Const("Leon_Library.error", Type("fun", List(Typ.dummyT, typ))), nat) - } - } - - private def arity(typ: TypeTree): Int = typ match { - case t: TupleType => t.dimension - case _ => context.reporter.internalError(s"Expected tuple type, got $typ") - } - - def term(expr: Expr, bounds: List[Identifier], consts: (FunDef, Typ) => Term): Future[Term] = { - def mkAbs(params: List[Identifier], body: Expr, bounds: List[Identifier], wrap: Term => Term = identity): Future[Term] = params match { - case Nil => term(body, bounds, consts) - case p :: ps => - for { - rec <- mkAbs(ps, body, p :: bounds, wrap) - typ <- types.typ(p.getType) - } - yield - wrap(Abs(p.mangledName /* name hint, no logical content */, typ, rec)) - } - - def nary(const: Term, xs: Expr*) = - Future.traverse(xs.toList)(term(_, bounds, consts)).map(ts => mkApp(const, ts: _*)) - - def flexary1(const: Term, xs: Seq[Expr]) = - Future.traverse(xs.toList)(term(_, bounds, consts)).map(_.reduceRight { (t, acc) => mkApp(const, t, acc) }) - - def flexary(const: Term, init: Term, xs: Seq[Expr]) = - Future.traverse(xs.toList)(term(_, bounds, consts)).map(_.foldRight(init) { (t, acc) => mkApp(const, t, acc) }) - - def inSet(set: Term, elem: Expr) = - term(elem, bounds, consts).map(mkApp(Const("Set.member", Typ.dummyT), _, set)) - - def lets(bindings: List[(Identifier, Term)], body: Expr) = { - val referenced = ExprOps.variablesOf(body) - val filtered = bindings.filter { case (id, _) => referenced contains id } - - val (names, rhss) = filtered.unzip - rhss.foldLeft(mkAbs(names, body, bounds)) { (term, rhs) => - term.map(mkApp(Const("HOL.Let", Typ.dummyT), rhs, _)) - } - } - - def let(name: Identifier, rhs: Term, body: Expr) = - lets(List(name -> rhs), body) - - def precondition(pred: Expr, body: Expr) = - for { - p <- term(pred, bounds, consts) - b <- term(body, bounds, consts) - e <- mkFreshError(Some(body.getType)) - } - yield - mkApp(Const("HOL.If", Typ.dummyT), p, b, e) - - def postcondition(pred: Expr, body: Expr) = - for { - p <- term(pred, bounds, consts) - b <- term(body, bounds, consts) - e <- mkFreshError(Some(body.getType)) - } - yield - mkApp(Const("HOL.If", Typ.dummyT), App(p, b), b, e) - - def pattern(pat: Pattern): Future[Option[(Term, List[(Identifier, Term)])]] = { - val res = pat match { - case CaseClassPattern(_, typ, pats) => - Future.traverse(pats.toList)(pattern).flatMap { _.sequence match { - case Some(args) => - val (pats, bindings) = args.unzip - - lookupConstructor(typ).map(_.term).map { head => - Some((mkApp(head, pats: _*), bindings.flatten)) - } - case None => - Future.successful(None) - }} - - case InstanceOfPattern(_, typ: CaseClassType) => - lookupConstructor(typ).flatMap { case Constructor(term, _, sels) => - val length = sels.size - Future.traverse(1 to length) { _ => mkFresh(None) }.map { vars => - Some((mkApp(term, vars: _*), Nil)) - } - } - - case TuplePattern(_, pats) => - Future.traverse(pats.toList)(pattern).map { _.sequence match { - case Some(args) => - val (pats, bindings) = args.unzip - val pat = pats.reduceRight { (p, acc) => mkApp(Const("Product_Type.Pair", Typ.dummyT), p, acc) } - Some(pat, bindings.flatten) - case None => - None - }} - - case WildcardPattern(None) => - mkFresh(pat.binder.map(_.getType)).map { term => Some((term, Nil)) } - - case WildcardPattern(Some(id)) => - types.typ(id.getType).map { typ => Some((Free(id.mangledName, typ), Nil)) } - - case _ => - Future.successful(None) - } - - val bind = pat match { - case WildcardPattern(_) => false - case _ => true - } - - pat.binder match { - case Some(id) if bind => res.map(_.map { case (term, bindings) => - (term, (id -> term) :: bindings) - }) - case _ => res - } - } - - def clause(clause: MatchCase) = clause.optGuard match { - case Some(_) => - Future.successful(None) - case None => - pattern(clause.pattern).flatMap { - case Some((pat, bindings)) => - lets(bindings, clause.rhs).map { term => Some(pat -> term) } - case None => - Future.successful(None) - } - } - - expr match { - case Variable(id) => - types.typ(id.getType) map { typ => - bounds.indexOf(id) match { - case -1 => Free(id.mangledName, typ) - case n => Bound(n) - } - } - - case Let(x, t, y) => - term(t, bounds, consts).flatMap { let(x, _, y) } - - case Lambda(args, expr) => - mkAbs(args.map(_.id).toList, expr, bounds) - - case Forall(args, expr) => - mkAbs(args.map(_.id).toList, expr, bounds, wrap = mkApp(Const("HOL.All", Typ.dummyT), _)) - - case CaseClass(typ, exprs) => - lookupConstructor(typ).map(_.term).flatMap { nary(_, exprs: _*) } - - case CaseClassSelector(typ, expr, sel) => - lookupConstructor(typ).map(_.sels.collectFirst { - case (field, term) if field.id == sel => term - }).flatMap { - case Some(term) => nary(term, expr) - case None => context.reporter.internalError(s"$typ or $sel not found in program") - } - - case IsInstanceOf(expr, typ: CaseClassType) => - lookupConstructor(typ).map(_.disc).flatMap { nary(_, expr) } - - case AsInstanceOf(expr, _) => - term(expr, bounds, consts) - - case Tuple(exprs) => - flexary1(Const("Product_Type.Pair", Typ.dummyT), exprs) - - case TupleSelect(expr, i) => - def sel(i: Int, len: Int, term: Term): Term = (i, len) match { - case (1, _) => App(Const("Product_Type.prod.fst", Typ.dummyT), term) - case (2, 2) => App(Const("Product_Type.prod.snd", Typ.dummyT), term) - case _ => App(Const("Product_Type.prod.snd", Typ.dummyT), sel(i - 1, len - 1, term)) - } - - term(expr, bounds, consts).map(sel(i, arity(expr.getType), _)) - - case MatchExpr(scrutinee, clauses) => - term(scrutinee, bounds, consts).flatMap { scrutinee => - Future.traverse(clauses.toList)(clause).flatMap { _.sequence match { - case Some(clauses) => - val catchall = - for { - pat <- mkFresh(None) - rhs <- mkFreshError(Some(expr.getType)) - } - yield (pat, rhs) - - types.typ(expr.getType).flatMap { typ => - catchall.flatMap { catchall => - Future.traverse(bounds) { id => - types.typ(id.getType).map(id.mangledName -> _) - }.flatMap { bounds => - system.invoke(Cases)(((scrutinee, bounds), (typ, clauses :+ catchall))).assertSuccess(context) - } - } - } - case None => - context.reporter.warning("Match uses unsupported features; translating to if-then-else") - term(ExprOps.matchToIfThenElse(expr), bounds, consts) - }} - } - - case FunctionInvocation(fun, args) => - types.functionTyp(args.map(_.getType), expr.getType).flatMap { typ => - nary(consts(fun.fd, typ), args: _*) - } - - case Application(fun, args) => - term(fun, bounds, consts).flatMap(nary(_, args: _*)) - - case Error(tpe, _) => - mkFreshError(Some(tpe)) - - case UnitLiteral() => Future.successful { Const("Product_Type.Unity", Typ.dummyT) } - case BooleanLiteral(true) => Future.successful { Const("HOL.True", Typ.dummyT) } - case BooleanLiteral(false) => Future.successful { Const("HOL.False", Typ.dummyT) } - - case InfiniteIntegerLiteral(n) => - system.invoke(NumeralLiteral)((n, Type("Int.int", Nil))).assertSuccess(context) - - case CharLiteral(c) => - types.char.flatMap(typ => system.invoke(NumeralLiteral)((c.toLong, typ))).assertSuccess(context) - - case IntLiteral(n) => - types.typ(expr.getType).flatMap(typ => system.invoke(NumeralLiteral)((n, typ))).assertSuccess(context) - - case Assert(pred, _, body) => precondition(pred, body) - case Require(pred, body) => precondition(pred, body) - case Ensuring(body, pred) => postcondition(pred, body) - - case IfExpr(x, y, z) => nary(Const("HOL.If", Typ.dummyT), x, y, z) - case Not(x) => nary(Const("HOL.Not", Typ.dummyT), x) - case Implies(x, y) => nary(Const("HOL.implies", Typ.dummyT), x, y) - case And(xs) => flexary1(Const("HOL.conj", Typ.dummyT), xs) - case Or(xs) => flexary1(Const("HOL.disj", Typ.dummyT), xs) - - // Isabelle doesn't have > or >=, need to swap operands in these cases - case Equals(x, y) => nary(Const("HOL.eq", Typ.dummyT), x, y) - case LessThan(x, y) => nary(Const("Orderings.ord_class.less", Typ.dummyT), x, y) - case LessEquals(x, y) => nary(Const("Orderings.ord_class.less_eq", Typ.dummyT), x, y) - case GreaterThan(x, y) => nary(Const("Orderings.ord_class.less", Typ.dummyT), y, x) - case GreaterEquals(x, y) => nary(Const("Orderings.ord_class.less_eq", Typ.dummyT), y, x) - - case Plus(x, y) => nary(Const("Groups.plus_class.plus", Typ.dummyT), x, y) - case Minus(x, y) => nary(Const("Groups.minus_class.minus", Typ.dummyT), x, y) - case UMinus(x) => nary(Const("Groups.uminus_class.uminus", Typ.dummyT), x) - case Times(x, y) => nary(Const("Groups.times_class.times", Typ.dummyT), x, y) - - case RealPlus(x, y) => nary(Const("Groups.plus_class.plus", Typ.dummyT), x, y) - case RealMinus(x, y) => nary(Const("Groups.minus_class.minus", Typ.dummyT), x, y) - case RealUMinus(x) => nary(Const("Groups.uminus_class.uminus", Typ.dummyT), x) - case RealTimes(x, y) => nary(Const("Groups.times_class.times", Typ.dummyT), x, y) - - case FiniteSet(elems, _) => flexary(Const("Set.insert", Typ.dummyT), Const("Set.empty", Typ.dummyT), elems.toList) - case SetUnion(x, y) => nary(Const("Lattices.sup_class.sup", Typ.dummyT), x, y) - case SetIntersection(x, y) => nary(Const("Lattices.inf_class.inf", Typ.dummyT), x, y) - case SetDifference(x, y) => nary(Const("Groups.minus_class.minus", Typ.dummyT), x, y) - case SubsetOf(x, y) => nary(Const("Orderings.ord_class.less_eq", Typ.dummyT), x, y) - case ElementOfSet(elem, set) => term(set, bounds, consts).flatMap(inSet(_, elem)) - - case FiniteMap(elems, from, to) => - val es = Future.traverse(elems.toList) { case (k, v) => term(k, bounds, consts) zip term(v, bounds, consts) } - for { - f <- types.typ(from) - t <- types.typ(to) - es <- es - res <- system.invoke(MapLiteral)((es, (f, t))).assertSuccess(context) - } - yield res - - case MapApply(map, key) => - term(map, bounds, consts).flatMap(nary(_, key)).map(App(Const("Option.option.the", Typ.dummyT), _)) - case MapIsDefinedAt(map, key) => - term(map, bounds, consts).flatMap { map => - inSet(App(Const("Map.dom", Typ.dummyT), map), key) - } - case MapUnion(x, y) => nary(Const("Map.map_add", Typ.dummyT), x, y) - - case Choose(pred) => - nary(Const("Hilbert_Choice.Eps", Typ.dummyT), pred) - - case _ => - context.reporter.warning(s"Unknown expression $expr, translating to unspecified term") - mkFreshError(Some(expr.getType)) - } - } - -} diff --git a/src/main/scala/inox/solvers/isabelle/Types.scala b/src/main/scala/inox/solvers/isabelle/Types.scala deleted file mode 100644 index 3847d80c5831fd84c1fc342c8fe8e9d61429c6bd..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/Types.scala +++ /dev/null @@ -1,196 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.isabelle - -import scala.concurrent._ -import scala.math.BigInt - -import leon.LeonContext -import leon.purescala.Common._ -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.purescala.Types._ - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.pure._ - -case class Constructor(term: Term, disc: Term, sels: Map[ValDef, Term]) -case class Datatype(typ: String, constructors: Map[CaseClassDef, Constructor]) - -object Types { - - def findRoot(typ: ClassType): ClassType = - typ.parent.map(findRoot).getOrElse(typ) - -} - -final class Types(context: LeonContext, program: Program, system: System)(implicit ec: ExecutionContext) { - - lazy val char = system.invoke(WordLiteral)(32).assertSuccess(context) - - private val enableMapping = context.findOptionOrDefault(Component.optMapping) - - private val (mapped, unmapped) = - program.classHierarchyRoots.map { root => - val descendants = root match { - case ccd: CaseClassDef => List(ccd) - case _ => root.knownDescendants.collect { case ccd: CaseClassDef => ccd } - } - - root -> descendants - }.toMap.partition { case (root, descendants) => - root.annotations.contains("isabelle.typ") && { - if (!enableMapping) - context.reporter.warning(s"Class definition ${root.id} is mapped, but mapping is disabled") - enableMapping - } - } - - private val mappedData: Future[Map[ClassDef, Datatype]] = Future.traverse(mapped) { case (root, descendants) => - val name = root.extAnnotations("isabelle.typ") match { - case List(Some(name: String)) => name - case List(_) => context.reporter.fatalError(s"In type mapping for class definition ${root.id}: expected a string literal as name") - case _ => context.reporter.internalError(s"Type mapping for class definition ${root.id}") - } - - val constructors = descendants.map { desc => - val name = desc.extAnnotations.get("isabelle.constructor") match { - case Some(List(Some(name: String))) => name - case Some(List(_)) => - context.reporter.fatalError(s"In constructor mapping for class definition ${desc.id}: expected a string literal as name") - case None => - context.reporter.fatalError(s"Expected constructor mapping for class definition ${desc.id}, because it inherits from ${root.id} which has a type mapping") - case Some(_) => - context.reporter.internalError(s"Constructor mapping for class definition ${desc.id}") - } - - (desc.id.mangledName, name) - }.toList - - system.invoke(LookupDatatype)(name -> constructors).assertSuccess(context).map { - case Left(err) => - context.reporter.fatalError(s"In mapping for class definition ${root.id}: $err") - case Right(constructors) => root -> - Datatype( - typ = name, - constructors = - constructors.map { case (name, (term, disc, sels)) => - val desc = descendants.find(_.id.mangledName == name).get - if (desc.fields.length != sels.length) - context.reporter.fatalError(s"Invalid constructor mapping for ${desc.id}: Isabelle constructor $name has different number of arguments") - desc -> Constructor(term, disc, (desc.fields, sels).zipped.toMap) - }.toMap - ) - } - }.map(_.toMap) - - def optTyp(tree: Option[TypeTree]): Future[Typ] = tree match { - case Some(tree) => typ(tree) - case None => Future.successful(Typ.dummyT) - } - - def typ(tree: TypeTree, subst: Map[Identifier, Identifier] = Map.empty, strict: Boolean = false): Future[Typ] = { - def flexary1(constructor: String, ts: Seq[TypeTree]): Future[Typ] = - Future.traverse(ts.toList)(typ(_, subst, strict)).map(_.reduceRight { (t, acc) => Type(constructor, List(t, acc)) }) - - tree match { - case BooleanType => Future.successful { Type("HOL.bool", Nil) } - case IntegerType => Future.successful { Type("Int.int", Nil) } - case UnitType => Future.successful { Type("Product_Type.unit", Nil) } - case RealType => Future.successful { Type("Real.real", Nil) } - case CharType => char - case TypeParameter(id) => - val id0 = subst.getOrElse(id, id) - Future.successful { TFree(s"'${id0.mangledName}", List("HOL.type")) } - case SetType(base) => - typ(base, subst, strict).map { typ => Type("Set.set", List(typ)) } - case TupleType(bases) => - flexary1("Product_Type.prod", bases) - case FunctionType(args, res) => - for { - r <- typ(res, subst, strict) - as <- Future.traverse(args)(typ(_, subst, strict)) - } - yield - as.foldRight(r) { (t, acc) => Type("fun", List(t, acc)) } - case bvt: BitVectorType => - system.invoke(WordLiteral)(bvt.size).assertSuccess(context) - case MapType(from, to) => - for { - f <- typ(from, subst, strict) - t <- typ(to, subst, strict) - } - yield - Type("fun", List(f, Type("Option.option", List(t)))) - case ct: ClassType => - val root = Types.findRoot(ct) - Future.traverse(root.tps.toList) { typ(_, subst, strict) }.flatMap { args => - mappedData.map { _.get(root.classDef) match { - case None => s"$theory.${root.id.mangledName}" - case Some(datatype) => datatype.typ - }}.map { Type(_, args) } - } - case StringType => - context.reporter.warning("Strings are not yet supported, translating to unspecified type") - Future.successful { Type("Leon_Library.string", Nil) } - case _ if strict => - context.reporter.fatalError(s"Unsupported type $tree, can't be inferred") - case _ => - context.reporter.warning(s"Unknown type $tree, translating to dummy (to be inferred)") - Future.successful { Typ.dummyT } - } - } - - def functionTyp(args: Seq[TypeTree], res: TypeTree): Future[Typ] = - typ(res).flatMap { res => - Future.traverse(args.toList)(typ(_)).map(_.foldRight(res) { (t, acc) => Type("fun", List(t, acc)) }) - } - - private val unmappedData: Future[Map[ClassDef, Datatype]] = { - val entries = unmapped.map { case (root, descendants) => - val name = s"$theory.${root.id.mangledName}" - - if (!enableMapping) - descendants.foreach { desc => - if (desc.annotations.contains("isabelle.constructor")) - context.reporter.warning(s"Ignoring constructor mapping of class definition ${desc.id}, because its root ${root.id} has no type mapping") - } - - val constructors = descendants.map { desc => desc -> - Constructor( - term = Const(s"$name.c${desc.id.mangledName}", Typ.dummyT), - disc = Const(s"$name.d${desc.id.mangledName}", Typ.dummyT), - sels = desc.fields.map { field => field -> - Const(s"$name.s${field.id.mangledName}", Typ.dummyT) - }.toMap - ) - }.toMap - - root -> Datatype(name, constructors) - } - - val translation = Future.traverse(entries.toList) { case (cls, Datatype(_, constructors)) => - val tparams = cls.tparams.map(_.id) - Future.traverse(constructors.toList) { case (subcls, Constructor(_, _, sels)) => - val subst = (subcls.tparams.map(_.id), tparams).zipped.toMap - Future.traverse(subcls.fields.toList) { field => - typ(field.getType, subst, true).map { typ => - field.id.mangledName -> typ - } - }.map { - subcls.id.mangledName -> _ - } - }.map { - (cls.id.mangledName, tparams.toList.map(_.mangledName), _) - } - } - - translation.flatMap(system.invoke(Datatypes)).assertSuccess(context).map { - case None => entries - case Some(msg) => context.reporter.fatalError(s"In datatype definition: $msg") - } - } - - val data = for { d1 <- mappedData; d2 <- unmappedData } yield d1 ++ d2 - -} diff --git a/src/main/scala/inox/solvers/isabelle/package.scala b/src/main/scala/inox/solvers/isabelle/package.scala deleted file mode 100644 index 2e5341013311cd469c4fb9f1a8d3dabc10ac738d..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/isabelle/package.scala +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers.isabelle - -import scala.concurrent._ -import scala.math.BigInt -import scala.reflect.NameTransformer - -import leon.LeonContext -import leon.purescala.Common.Identifier -import leon.purescala.Definitions._ -import leon.purescala.Expressions._ -import leon.verification.VC - -import edu.tum.cs.isabelle._ -import edu.tum.cs.isabelle.api.Environment -import edu.tum.cs.isabelle.pure.{Expr => _, _} - -import shapeless.tag - -object `package` { - - val theory = "Leon_Runtime" - val isabelleVersion = "2016" - - - implicit class FutureResultOps[A](val future: Future[ProverResult[A]]) extends AnyVal { - def assertSuccess(context: LeonContext)(implicit ec: ExecutionContext): Future[A] = future map { - case ProverResult.Success(a) => a - case ProverResult.Failure(err: Operation.ProverException) => context.reporter.internalError(err.fullMessage) - case ProverResult.Failure(err) => context.reporter.internalError(err.getMessage) - } - } - - implicit class ListOptionOps[A](val list: List[Option[A]]) extends AnyVal { - def sequence: Option[List[A]] = list match { - case Nil => Some(Nil) - case None :: _ => None - case Some(x) :: xs => xs.sequence.map(x :: _) - } - } - - implicit class IdentifierOps(val id: Identifier) extends AnyVal { - def mangledName: String = - NameTransformer.encode(id.name) - .replace("_", "'u'") - .replace("$", "'d'") - .replaceAll("^_*", "") - .replaceAll("_*$", "") - .replaceAll("^'*", "") + "'" + id.globalId - } - - implicit class FunDefOps(val fd: FunDef) extends AnyVal { - private def name = fd.id.name - - def statement: Option[Expr] = fd.postcondition match { - case Some(post) => - val args = fd.params.map(_.id.toVariable) - val appliedPost = Application(post, List(FunctionInvocation(fd.typed, args))) - Some(fd.precondition match { - case None => appliedPost - case Some(pre) => Implies(pre, appliedPost) - }) - case None => None - } - - def proofMethod(vc: VC, context: LeonContext): Option[String] = - fd.extAnnotations.get("isabelle.proof") match { - case Some(List(Some(method: String), Some(kind: String))) => - val kinds = kind.split(',') - if (kinds.contains(vc.kind.name) || kind == "") - Some(method) - else { - context.reporter.warning(s"Ignoring proof hint for function definition $name: only applicable for VC kinds ${kinds.mkString(", ")}") - None - } - case Some(List(Some(method: String), None)) => - Some(method) - case Some(List(_, _)) => - context.reporter.fatalError(s"In proof hint for function definition $name: expected a string literal as method and (optionally) a string literal as kind") - case Some(_) => - context.reporter.internalError(s"Proof hint for function definition $name") - case None => - None - } - - def isLemma: Boolean = - fd.annotations.contains("isabelle.lemma") - - def checkLemma(program: Program, context: LeonContext): Option[(FunDef, List[FunDef])] = { - val name = fd.qualifiedName(program) - - def error(msg: String) = - context.reporter.fatalError(s"In lemma declaration for function definition $name: $msg") - - fd.extAnnotations.get("isabelle.lemma") match { - case Some(List(Some(abouts: String))) => - val refs = abouts.split(',').toList.map { about => - program.lookupAll(about) match { - case List(fd: FunDef) if !fd.isLemma => fd - case List(_: FunDef) => error(s"lemmas can only be about plain functions, but $about is itself a lemma") - case Nil | List(_) => error(s"$about is not a function definition") - case _ => error(s"$about is ambiguous") - } - } - Some(fd -> refs) - case Some(List(_)) => error("expected a string literal for about") - case Some(_) => context.reporter.internalError(s"Lemma declaration for function definition $name") - case None => None - } - } - } - - def canonicalizeOutput(system: System, str: String) = { - // FIXME use this everywhere - val raw = """\s+""".r.replaceAllIn(str, " ") - system.env.decode(tag[Environment.Raw].apply(raw)) - } - - val Prove = Operation.implicitly[(List[Term], Option[String]), Option[String]]("prove") - val Pretty = Operation.implicitly[Term, String]("pretty") - val Load = Operation.implicitly[(String, String, List[(String, String)]), List[String]]("load") - val Report = Operation.implicitly[Unit, List[(String, String)]]("report") - val Dump = Operation.implicitly[Unit, List[(String, String)]]("dump") - val Oracle = Operation.implicitly[Unit, Unit]("oracle") - val Fresh = Operation.implicitly[String, String]("fresh") - val NumeralLiteral = Operation.implicitly[(BigInt, Typ), Term]("numeral_literal") - val Cases = Operation.implicitly[((Term, List[(String, Typ)]), (Typ, List[(Term, Term)])), Term]("cases") - val SerialNat = Operation.implicitly[Unit, Term]("serial_nat") - val MapLiteral = Operation.implicitly[(List[(Term, Term)], (Typ, Typ)), Term]("map_literal") - val Functions = Operation.implicitly[(List[(String, List[(String, Typ)], (Term, Typ))]), Option[String]]("functions") - val Declare = Operation.implicitly[List[String], Unit]("declare") - val Equivalences = Operation.implicitly[List[(String, String)], List[String]]("equivalences") - val AssumeEquivalences = Operation.implicitly[List[(String, String)], Unit]("assume_equivalences") - val Lemmas = Operation.implicitly[List[(String, Term)], List[String]]("lemmas") - val AssumeLemmas = Operation.implicitly[List[(String, Term)], Unit]("assume_lemmas") - val WordLiteral = Operation.implicitly[BigInt, Typ]("word_literal") - val Datatypes = Operation.implicitly[List[(String, List[String], List[(String, List[(String, Typ)])])], Option[String]]("datatypes") - val LookupDatatype = Operation.implicitly[(String, List[(String, String)]), Either[String, List[(String, (Term, Term, List[Term]))]]]("lookup_datatype") - -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4CounterExampleSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4CounterExampleSolver.scala deleted file mode 100644 index ff27a312f858992f5fdd890181f9d86ac5f8aac3..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4CounterExampleSolver.scala +++ /dev/null @@ -1,25 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Definitions.Program - -class SMTLIBCVC4CounterExampleSolver(context: SolverContext, program: Program) extends SMTLIBCVC4QuantifiedSolver(context, program) { - - override def targetName = "cvc4-cex" - - override def interpreterOps(ctx: LeonContext) = { - Seq( - "-q", - "--rewrite-divk", - "--produce-models", - "--print-success", - "--lang", "smt2.5", - "--fmf-fun" - ) ++ userDefinedOps(ctx) - } - - protected val allowQuantifiedAssertions: Boolean = false -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4ProofSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4ProofSolver.scala deleted file mode 100644 index ce6b963ba88fe7e5280f6e30291d56a17d431b4b..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4ProofSolver.scala +++ /dev/null @@ -1,47 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Definitions.Program -import purescala.Expressions.Expr - -import _root_.smtlib.parser.Commands.{Assert => SMTAssert} -import _root_.smtlib.parser.Terms.{Exists => SMTExists} - -class SMTLIBCVC4ProofSolver(context: SolverContext, program: Program) - extends SMTLIBCVC4QuantifiedSolver(context, program) { - - override def targetName = "cvc4-proof" - - override def interpreterOps(ctx: LeonContext) = { - Seq( - "-q", - "--print-success", - "--lang", "smt2.5", - "--quant-ind", - "--rewrite-divk", - "--conjecture-gen", - "--conjecture-gen-per-round=3", - "--conjecture-gen-gt-enum=40", - "--full-saturate-quant" - ) ++ userDefinedOps(ctx) - } - - // For this solver, we prefer the variables of assert() commands to be exist. quantified instead of free - override def assertCnstr(e: Expr) = try { - emit(SMTAssert(quantifiedTerm(SMTExists, e)(Map()))) - } catch { - case _ : SMTLIBUnsupportedError => - addError() - } - - // This solver does not support model extraction - override def getModel: solvers.Model = { - // We don't send the error through reporter because it may be caught by PortfolioSolver - throw LeonFatalError(Some(s"Solver $name does not support model extraction.")) - } - - protected val allowQuantifiedAssertions: Boolean = true -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedSolver.scala deleted file mode 100644 index 86768670465a89ea498e5bf30d29fc19d578c48a..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedSolver.scala +++ /dev/null @@ -1,14 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Definitions.Program - -// This solver utilizes the define-funs-rec command of SMTLIB-2.5 to define mutually recursive functions. -// It is not meant as an underlying solver to UnrollingSolver, and does not handle HOFs. -abstract class SMTLIBCVC4QuantifiedSolver(context: SolverContext, program: Program) - extends SMTLIBCVC4Solver(context, program) - with SMTLIBQuantifiedSolver - with SMTLIBCVC4QuantifiedTarget diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala deleted file mode 100644 index 8b5fd705b6aa6fe37ff55a0c902599f957f2011b..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBCVC4QuantifiedTarget.scala +++ /dev/null @@ -1,114 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Expressions._ -import purescala.Constructors._ -import purescala.Definitions._ -import purescala.DefOps.typedTransitiveCallees - -import _root_.smtlib.parser.Commands.{Assert => SMTAssert, FunDef => _, _} -import _root_.smtlib.parser.Terms.{Exists => SMTExists, Forall => SMTForall, _ } -import _root_.smtlib.theories.Core.Equals -import _root_.smtlib.parser.Commands._ - -trait SMTLIBCVC4QuantifiedTarget extends SMTLIBCVC4Target with SMTLIBQuantifiedTarget { - - override def targetName = "cvc4-quantified" - - override def declareFunction(tfd: TypedFunDef): SSymbol = { - val (funs, exploredAll) = typedTransitiveCallees(Set(tfd), Some(typedFunDefExplorationLimit)) - - if (!exploredAll) { - context.reporter.warning( - "Did not manage to explore the space of typed functions " + - s"transitively called from ${tfd.id}. The solver may fail" - ) - } - - // define-funs-rec does not accept parameterless functions, so we have to treat them differently: - // we declare-fun each one and assert it is equal to its body - val (withParams, withoutParams) = funs.toSeq filterNot functions.containsA partition(_.params.nonEmpty) - - // FIXME this may introduce dependency errors - val parameterlessAssertions = withoutParams flatMap { tfd => - val s = super.declareFunction(tfd) - - try { - val bodyAssert = tfd.body.map { body => - SMTAssert(Equals(s: Term, toSMT(body)(Map()))) - } - - val specAssert = tfd.postcondition map { post => - val term = implies( - tfd.precOrTrue, - application(post, Seq(FunctionInvocation(tfd, Seq()))) - ) - SMTAssert(toSMT(term)(Map())) - } - - bodyAssert ++ specAssert - } catch { - case _ : SMTLIBUnsupportedError => - addError() - Seq() - } - } - - val smtFunDecls = withParams map { tfd => - val id = if (tfd.tps.isEmpty) { - tfd.id - } else { - tfd.id.freshen - } - val sym = id2sym(id) - functions +=(tfd, sym) - FunDec( - sym, - tfd.params map { p => SortedVar(id2sym(p.id), declareSort(p.getType)) }, - declareSort(tfd.returnType) - ) - } - - val smtBodies = smtFunDecls map { case f => - val tfd = functions.toA(f.name) - try { - toSMT(tfd.body.get)(tfd.params.map { p => - (p.id, id2sym(p.id): Term) - }.toMap) - } catch { - case _: SMTLIBUnsupportedError => - addError() - toSMT(Error(tfd.body.get.getType, ""))(Map()) - } - } - - if (smtFunDecls.nonEmpty) { - emit(DefineFunsRec(smtFunDecls, smtBodies)) - // Assert contracts for defined functions - if (allowQuantifiedAssertions) for { - // If we encounter a function that does not refer to the current function, - // it is sound to assume its contracts for all inputs - tfd <- withParams if !refersToCurrent(tfd.fd) - post <- tfd.postcondition - } { - val term = implies( - tfd.precOrTrue, - application(post, Seq(tfd.applied)) - ) - try { - emit(SMTAssert(quantifiedTerm(SMTForall, term)(Map()))) - } catch { - case _ : SMTLIBUnsupportedError => - addError() - } - } - } - - parameterlessAssertions.foreach(a => emit(a)) - - functions.toB(tfd) - } -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedSolver.scala deleted file mode 100644 index d4819cfbae997d729e064926b98cc879f0effeb0..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedSolver.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Common.Identifier -import verification.VC - -trait SMTLIBQuantifiedSolver { - this: SMTLIBSolver with SMTLIBQuantifiedTarget => - - // We need to know the function context. - // The reason is we do not want to assume postconditions of functions referring to - // the current function, as this may make the proof unsound - override def assertVC(vc: VC) = { - dbg(s"; Solving $vc") - currentFunDef = Some(vc.fd) - assertCnstr(withInductiveHyp(vc.condition)) - } - - // Normally, UnrollingSolver tracks the input variable, but this one - // is invoked alone so we have to filter them here - override def getModel: Model = { - val filter = currentFunDef.map{ _.paramIds.toSet }.getOrElse( (_:Identifier) => true ) - getModel(filter) - } - -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedTarget.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedTarget.scala deleted file mode 100644 index 76037a9451a27011a7ff47609073fcc07e99a92b..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBQuantifiedTarget.scala +++ /dev/null @@ -1,42 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Expressions._ -import purescala.Definitions._ -import purescala.Constructors._ -import purescala.ExprOps._ - -trait SMTLIBQuantifiedTarget extends SMTLIBTarget { - - protected var currentFunDef: Option[FunDef] = None - - protected def refersToCurrent(fd: FunDef) = { - (currentFunDef contains fd) || (currentFunDef exists { - program.callGraph.transitivelyCalls(fd, _) - }) - } - - protected val allowQuantifiedAssertions: Boolean - - protected val typedFunDefExplorationLimit = 10000 - - protected def withInductiveHyp(cond: Expr): Expr = { - - val inductiveHyps = for { - fi @ FunctionInvocation(tfd, args) <- functionCallsOf(cond).toSeq - } yield { - val post = application( - tfd.withParamSubst(args, tfd.postOrTrue), - Seq(fi) - ) - and(tfd.precOrTrue, post) - } - - // We want to check if the negation of the vc is sat under inductive hyp. - // So we need to see if (indHyp /\ !vc) is satisfiable - liftLets(matchToIfThenElse(andJoin(inductiveHyps :+ not(cond)))) - } -} diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedSolver.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedSolver.scala deleted file mode 100644 index 7dd3e4be718ce0e2d39de37086ccc637cc90d202..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedSolver.scala +++ /dev/null @@ -1,18 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Definitions.Program - -import theories._ - -/** - * This solver models function definitions as universally quantified formulas. - * It is not meant as an underlying solver to UnrollingSolver, and does not handle HOFs. - */ -class SMTLIBZ3QuantifiedSolver(context: SolverContext, program: Program) - extends SMTLIBZ3Solver(context, program) - with SMTLIBQuantifiedSolver - with SMTLIBZ3QuantifiedTarget diff --git a/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedTarget.scala b/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedTarget.scala deleted file mode 100644 index 9b696cea0b496014d402ac4b9f9491d4b9494272..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/smtlib/SMTLIBZ3QuantifiedTarget.scala +++ /dev/null @@ -1,69 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package smtlib - -import purescala.Expressions._ -import purescala.Constructors._ -import purescala.Definitions._ -import purescala.DefOps.typedTransitiveCallees - -import _root_.smtlib.parser.Commands.{Assert => SMTAssert, FunDef => _, _} -import _root_.smtlib.parser.Terms.{Exists => SMTExists, Forall => SMTForall, _ } - -trait SMTLIBZ3QuantifiedTarget extends SMTLIBZ3Target with SMTLIBQuantifiedTarget { - - protected val allowQuantifiedAssertions: Boolean = true - - override def targetName = "z3-q" - - override def declareFunction(tfd: TypedFunDef): SSymbol = { - - val (funs, exploredAll) = typedTransitiveCallees(Set(tfd), Some(typedFunDefExplorationLimit)) - - if (!exploredAll) { - context.reporter.warning( - "Did not manage to explore the space of typed functions " + - s"transitively called from ${tfd.id}. The solver may fail" - ) - } - - val notSeen = funs.toSeq filterNot functions.containsA - - val smtFunDecls = notSeen map super.declareFunction - - smtFunDecls foreach { sym => - val tfd = functions.toA(sym) - val term = quantifiedTerm( - SMTForall, - tfd.paramIds, - Equals( - FunctionInvocation(tfd, tfd.params.map {_.toVariable}), - tfd.body.get - ) - )(Map()) - emit(SMTAssert(term)) - } - - // If we encounter a function that does not refer to the current function, - // it is sound to assume its contracts for all inputs - if (allowQuantifiedAssertions) for { - tfd <- notSeen if !refersToCurrent(tfd.fd) - post <- tfd.postcondition - } { - val term = implies( - tfd.precOrTrue, - application(post, Seq(tfd.applied)) - ) - try { - emit(SMTAssert(quantifiedTerm(SMTForall, term)(Map()))) - } catch { - case _ : SMTLIBUnsupportedError => - addError() - } - } - - functions.toB(tfd) - } -} diff --git a/src/main/scala/inox/solvers/string/StringSolver.scala b/src/main/scala/inox/solvers/string/StringSolver.scala deleted file mode 100644 index c39dc9043500498f1a0e4fbde866a0a2259aa77f..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/string/StringSolver.scala +++ /dev/null @@ -1,705 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon.solvers.string - -import leon.purescala.Common._ -import scala.collection.mutable.ListBuffer -import scala.annotation.tailrec - -/** - * @author Mikael - */ -object StringSolver { - type Assignment = Map[Identifier, String] - - type StringFormToken = Either[String, Identifier] - - type StringForm = List[StringFormToken] - - type Equation = (StringForm, String) - - /** Sequences of equalities such as xyz"1"uv"2" = "1, 2" */ - type Problem = List[Equation] - - def renderStringForm(sf: StringForm): String = sf match { - case Left(const)::Nil => "\""+const+"\"" - case Right(id)::Nil => id.toString - case Left(const)::q => "\""+const+"\"+" + renderStringForm(q) - case Right(id)::q => id.toString + "+" + renderStringForm(q) - case Nil => "" - } - - def renderProblem(p: Problem): String = { - def renderEquation(e: Equation): String = { - renderStringForm(e._1) + "==\""+e._2+"\"" - } - p match {case Nil => "" - case e::q => renderEquation(e) + ", " + renderProblem(q)} - } - - /** Evaluates a String form. Requires the solution to have an assignment to all identifiers. */ - @tailrec def evaluate(s: Assignment, acc: StringBuffer = new StringBuffer(""))(sf: StringForm): String = sf match { - case Nil => acc.toString - case Left(constant)::q => evaluate(s, acc append constant)(q) - case Right(identifier)::q => evaluate(s, acc append s(identifier))(q) - } - - /** Assigns the new values to the equations and simplify them at the same time. */ - @tailrec def reduceStringForm(s: Assignment, acc: ListBuffer[StringFormToken] = ListBuffer())(sf: StringForm): StringForm = sf match { - case Nil => acc.toList - case (l@Left(constant))::(l2@Left(constant2))::q => reduceStringForm(s, acc)(Left(constant + constant2)::q) - case (l@Left(constant))::(r2@Right(id))::q => - s.get(id) match { - case Some(sid) => - reduceStringForm(s, acc)(Left(constant + sid)::q) - case None => - reduceStringForm(s, (acc += l) += r2)(q) - } - case (l@Left(constant))::q => reduceStringForm(s, acc += l)(q) - case (r@Right(id))::q => - s.get(id) match { - case Some(sid) => - reduceStringForm(s, acc)(Left(sid)::q) - case None => - reduceStringForm(s, acc += r)(q) - } - } - - /** Assignes the variable to their values in all equations and simplifies them. */ - def reduceProblem(s: Assignment, acc: ListBuffer[Equation] = ListBuffer())(p: Problem): Problem = p match { - case Nil => acc.toList - case ((sf, rhs)::q) => reduceProblem(s, acc += ((reduceStringForm(s)(sf): StringForm, rhs)))(q) - } - - /** Computes a foldable property on the problem */ - def fold[T](init: T, s: StringFormToken => T, f: (T, T) => T)(p: Problem) = { - p.view.foldLeft(init) { - case (t, (lhs, rhs)) => - lhs.view.foldLeft(t) { - case (t, sft) => f(t, s(sft)) - } - } - } - - /** Returns true if there is a StringFormToken which satisfies the given property */ - def exists(s: StringFormToken => Boolean)(p: Problem) = fold[Boolean](false, s, _ || _)(p) - /** Counts the number of StringFormToken which satisfy the given property */ - def count(s: StringFormToken => Boolean)(p: Problem) = fold[Int](0, s andThen (if(_) 1 else 0), _ + _)(p) - - /** Maps every StringFormToken of the problem to create a new one */ - def map(s: StringFormToken => StringFormToken)(p: Problem): Problem = { - p.map { case (lhs, rhs) => (lhs map s, rhs) } - } - - /** Returns true if the assignment is a solution to the problem */ - def errorcheck(p: Problem, s: Assignment): Option[String] = { - val res = p flatMap {eq => - val resultNotCorrect = reduceStringForm(s)(eq._1) match { - case Left(a)::Nil if a == eq._2 => None - case Nil if eq._2 == "" => None - case res => Some(res) - } - if(resultNotCorrect.nonEmpty) Some(renderStringForm(eq._1) + " == "+ renderStringForm(resultNotCorrect.get) + ", but expected " + eq._2) else None - } - if(res == Nil) None else Some(res.mkString("\n") + "\nAssignment: " + s) - } - - /** Concatenates constants together */ - def reduceStringForm(s: StringForm): StringForm = { - @tailrec def rec(s: StringForm, acc: ListBuffer[StringFormToken] = ListBuffer()): StringForm = s match { - case Nil => acc.toList - case Left("")::q => rec(q, acc) - case Left(c)::Left(d)::q => rec(Left(c+d)::q, acc) - case a::q => rec(q, acc += a) - } - rec(s) - } - - /** Split the stringFrom at the last constant. - * @param s The concatenation of constants and variables - * @return (a, b, c) such that a ++ Seq(b) ++ c == s and b is the last constant of s */ - def splitAtLastConstant(s: StringForm): (StringForm, StringFormToken, StringForm) = { - val i = s.lastIndexWhere(x => x.isInstanceOf[Left[_, _]]) - (s.take(i), s(i), s.drop(i+1)) - } - - /** Use `val ConsReverse(init, last) = list` to retrieve the n-1 elements and the last element directly. */ - object ConsReverse { - def unapply[A](l: List[A]): Option[(List[A], A)] = { - if(l.nonEmpty) Some((l.init, l.last)) else None - } - def apply[A](q: List[A], a: A): List[A] = q :+ a - } - - - /** Returns all start positions of the string s in text.*/ - def occurrences(s: String, text: String): List[Int] = { - ("(?="+java.util.regex.Pattern.quote(s)+")").r.findAllMatchIn(text).map(m => m.start).toList - } - - /** Returns a list of all possible positions of the constants inside the string */ - def repartitions(constants: List[String], text: String): List[List[Int]] = constants match { - case Nil => List(List()) - case c::q => - occurrences(c, text).flatMap(startPos => - repartitions(q, text.substring(startPos + c.length)) - .map(startPos :: _.map(_ + (startPos + c.length)))) - } - - /** Computes the Combinatorial coefficient c(n, k)*/ - def cnk(n: BigInt, k: BigInt): BigInt = { - var res = BigInt(1) - var i = 0 - while(i < k) { - res *= n - i - i+=1 - } - i = 2 - while(i <= k) { - res /= i - i+=1 - } - res - } - - /** Returns a stream of prioritary positions and another of remaining positions */ - def prioritizedPositions(s: String): (Stream[Int], Stream[Int])= { - val separations = "\\b".r.findAllMatchIn(s).map(m => m.start).toStream - val done = separations.toSet - val remaining = for( i <- (0 to s.length).toStream if !done(i)) yield i - (separations, remaining) - } - - - /** A single pass simplification process. Converts as much as equations to assignments if possible. */ - trait ProblemSimplicationPhase { self => - def apply(p: Problem, s: Assignment) = run(p, s) - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] - def andThen(other: ProblemSimplicationPhase): ProblemSimplicationPhase = new ProblemSimplicationPhase { - def run(p: Problem, s: Assignment) = { - ProblemSimplicationPhase.this.run(p, s) match { - case Some((p, s)) => - //println("Problem before " + other.getClass.getName.substring(33) + ":" + (renderProblem(p), s)) - other.run(p, s) - case None => - None - } - } - } - /** Applies a phase until the output does not change anymore */ - def loopUntilConvergence = new ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - leon.utils.fixpoint[Option[(Problem, Assignment)]]{ - case None => None - case Some((problem: Problem, assignment: Assignment)) => self.run(problem, assignment) - }(Option((p, s))) - } - } - } - def loopUntilConvergence(psp: ProblemSimplicationPhase) = psp.loopUntilConvergence - - /** Removes duplicate equations from the problem */ - object DistinctEquation extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - Some((p.distinct, s)) - } - } - - /** Merge constant in string forms */ - object MergeConstants extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def mergeConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match { - case Nil => Some(acc.toList) - case (sf, rhs)::q => mergeConstants(q, acc += ((reduceStringForm(sf), rhs))) - } - mergeConstants(p).map((_, s)) - } - } - - /** Unsat if Const1 = Const2 but constants are different. - * if Const1 = Const2 and constants are same, remove equality. */ - object SimplifyConstants extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def simplifyConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match { - case Nil => Some(acc.toList) - case (Nil, rhs)::q => if("" != rhs) None else simplifyConstants(q, acc) - case (List(Left(c)), rhs)::q => if(c != rhs) None else simplifyConstants(q, acc) - case sentence::q => simplifyConstants(q, acc += sentence) - } - simplifyConstants(p).map((_, s)) - } - } - - /** . Get new assignments from equations such as id = Const, and propagate them */ - object PropagateAssignments extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def obtainAssignments(p: Problem, assignments: Assignment = Map()): Option[Assignment] = p match { - case Nil => Some(assignments) - case (List(Right(id)), rhs)::q => - assignments.get(id) match { // It was assigned already. - case Some(v) => - if(rhs != v) None else obtainAssignments(q, assignments) - case None => - obtainAssignments(q, assignments + (id -> rhs)) - } - case sentence::q => obtainAssignments(q, assignments) - } - obtainAssignments(p, s).map(newAssignments => { - //println("Obtained new assignments: " + newAssignments) - val newProblem = if(newAssignments.nonEmpty) reduceProblem(newAssignments)(p) else p - (newProblem, s ++ newAssignments) - }) - } - } - - /** Removes all constants from the left of the equations (i.e. "ab" x ... = "abcd" ==> x ... = "cd" ) */ - object RemoveLeftConstants extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def removeLeftconstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match { - case Nil => Some(acc.toList) - case ((Left(constant)::q, rhs))::ps => - if(rhs.startsWith(constant)) { - removeLeftconstants(ps, acc += ((q, rhs.substring(constant.length)))) - } else None - case (t@(q, rhs))::ps => - removeLeftconstants(ps, acc += t) - } - removeLeftconstants(p).map((_, s)) - } - } - - /** Removes all constants from the right of the equations (i.e. ... x "cd" = "abcd" ==> ... x = "ab" ) */ - object RemoveRightConstants extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def removeRightConstants(p: Problem, acc: ListBuffer[Equation] = ListBuffer()): Option[Problem] = p match { - case Nil => Some(acc.toList) - case (ConsReverse(q, Left(constant)), rhs)::ps => - if(rhs.endsWith(constant)) { - removeRightConstants(ps, acc += ((q, rhs.substring(0, rhs.length - constant.length)))) - } else None - case (t@(q, rhs))::ps => - removeRightConstants(ps, acc += t) - } - removeRightConstants(p).map((_, s)) - } - } - - /** If constants can have only one position in an equation, splits the equation. - * If an equation is empty, treats all left-hand-side identifiers as empty. - * Requires MergeConstants so that empty constants have been removed. */ - object PropagateMiddleConstants extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - @tailrec def constantPropagate(p: Problem, assignments: Assignment = Map(), newProblem: ListBuffer[Equation] = ListBuffer()): Option[(Problem, Assignment)] = { - p match { - case Nil => - Some((newProblem.toList, assignments)) - case (ids, "")::q => // All identifiers should be empty. - val constants = ids.find { - case Left(s) if s != "" => true - case Right(_) => false - } - (constants match { - case Some(_) => None - case None => // Ok now let's assign all variables to empty string. - val newMap = ids.collect { case Right(id) => id -> "" } - val newAssignments = (Option(assignments) /: newMap) { - case (None, (id, rhs)) => None - case (Some(a), (id, rhs)) => - a.get(id) match { // It was assigned already. - case Some(v) => - if(rhs != v) None else Some(a) - case None => - Some(a + (id -> rhs)) - } - } - newAssignments - }) match { - case None => None - case Some(a) => - constantPropagate(q, a, newProblem) - } - case (sentence@(ids, rhs))::q => // If the constants have an unique position, we should split the equation. - val constants = ids.collect { - case l@Left(s) => s - } - // Check if constants form a partition in the string, i.e. getting the .indexOf() the first time is the only way to split the string. - - if(constants.nonEmpty) { - - var pos = -2 - var lastPos = -2 - var lastConst = "" - var invalid = false - - for(c <- constants) { - val i = rhs.indexOfSlice(c, pos) - lastPos = i - lastConst = c - if(i == -1) invalid = true - pos = i + c.length - } - if(invalid) None else { - val i = rhs.indexOfSlice(lastConst, lastPos + 1) - if(i == -1) { // OK it's the smallest position possible, hence we can split at the last position. - val (before, _, after) = splitAtLastConstant(ids) - val firstConst = rhs.substring(0, lastPos) - val secondConst = rhs.substring(lastPos + lastConst.length) - constantPropagate((before, firstConst)::(after, secondConst)::q, assignments, newProblem) - } else { - // Other splitting strategy: independent constants ? - - val constantsSplit = constants.flatMap{ c => { - val i = rhs.indexOfSlice(c, -1) - val j = rhs.indexOfSlice(c, i + 1) - if(j == -1 && i != -1) { - List((c, i)) - } else Nil - }} - - constantsSplit match { - case Nil => - constantPropagate(q, assignments, newProblem += sentence) - case ((c, i)::_) => - val (before, after) = ids.splitAt(ids.indexOf(Left(c))) - val firstconst = rhs.substring(0, i) - val secondConst = rhs.substring(i+c.length) - constantPropagate((before, firstconst)::(after.tail, secondConst)::q, assignments, newProblem) - } - - } - } - } else { - constantPropagate(q, assignments, newProblem += sentence) - } - case sentence::q => constantPropagate(q, assignments, newProblem += sentence) - } - } - constantPropagate(p).map(ps => { - val newP = if(ps._2.nonEmpty) reduceProblem(ps._2)(p) else p - (newP, s ++ ps._2) - }) - } - } - - /** If a left-hand side of the equation appears somewhere else, replace it by the right-hand-side of this equation */ - object PropagateEquations extends ProblemSimplicationPhase { - def run(p: Problem, s: Assignment): Option[(Problem, Assignment)] = { - var newP = p.distinct - for((lhs, rhs) <- newP if lhs.length >= 2) { // Original distinct p. - var indexInP = 0 - for(eq@(lhs2, rhs2) <- newP) { - if((!(lhs2 eq lhs) || !(rhs2 eq rhs))) { - val i = lhs2.indexOfSlice(lhs) - if(i != -1) { - val res = (lhs2.take(i) ++ Seq(Left(rhs)) ++ lhs2.drop(i + lhs.size), rhs2) - newP = newP.updated(indexInP, res) - } - } - indexInP += 1 - } - } - Some((newP, s)) - } - } - - /** returns a simplified version of the problem. If it is not satisfiable, returns None. */ - val simplifyProblem: ProblemSimplicationPhase = { - loopUntilConvergence(MergeConstants andThen - SimplifyConstants andThen - PropagateAssignments) - } - - /** Removes all constants from the left and right of the equations */ - val noLeftRightConstants: ProblemSimplicationPhase = { - RemoveLeftConstants andThen RemoveRightConstants - } - - /** Composition of simplifyProblem and noLeftRightConstants */ - val forwardStrategy = - loopUntilConvergence( - simplifyProblem andThen - noLeftRightConstants andThen - PropagateMiddleConstants andThen - DistinctEquation andThen - PropagateEquations - ) - - - /** Solves the equation x1x2x3...xn = CONSTANT - * Prioritizes split positions in the CONSTANT that are word boundaries - * Prioritizes variables which have a higher frequency */ - def simpleSplit(ids: List[Identifier], rhs: String)(implicit statistics: Map[Identifier, Int]): Stream[Assignment] = { - ids match { - case Nil => if(rhs == "") Stream(Map()) else Stream.empty - case List(x) => - Stream(Map(x -> rhs)) - case x :: ys => - val (bestVar, bestScore, worstScore) = ((None: Option[(Identifier, Int, Int)]) /: ids) { - case (None, x) => val sx = statistics(x) - Some((x, sx, sx)) - case (s@Some((x, i, ws)), y) => val yi = statistics(y) - if (i >= yi) Some((x, i, Math.min(yi, ws))) else Some((y, yi, ws)) - }.get - val (prioritaryPos, remainingPos) = prioritizedPositions(rhs) - val numBestVars = ids.count { x => x == bestVar } - - if(worstScore == bestScore) { - for{ - i <- (prioritaryPos append remainingPos) // Prioritization on positions which are separators. - xvalue = rhs.substring(0, i) - rvalue = rhs.substring(i) - remaining_split <- simpleSplit(ys, rvalue) - if !remaining_split.contains(x) || remaining_split(x) == xvalue - } yield (remaining_split + (x -> xvalue)) - } else { // A variable appears more than others in the same equation, so its changes are going to propagate more. - val indexBestVar = ids.indexOf(bestVar) - val strings = if(indexBestVar == 0) { // Test only string prefixes or empty string - ((for{j <- (rhs.length to 1 by -1).toStream - if prioritaryPos contains j} yield rhs.substring(0, j)) append - Stream("") append - (for{j <- (rhs.length to 1 by -1).toStream - if remainingPos contains j} yield rhs.substring(0, j))) - } else { - val lastIndexBestVar = ids.lastIndexOf(bestVar) - if(lastIndexBestVar == ids.length - 1) { - val pos = prioritaryPos append remainingPos - (for{ i <- pos.toStream // Try to maximize the size of the string from the start - if i != rhs.length - } yield rhs.substring(i)) #::: - Stream("") - } else { // Inside, can be anything. - def substrings(startPos: Stream[Int], endPos: Stream[Int]): Stream[String] = { - for{ i <- startPos.toStream - if i != rhs.length - j <- rhs.length to (i+1) by -1 - if endPos contains j} yield rhs.substring(i, j) - } - (substrings(prioritaryPos, prioritaryPos) append - Stream("") append - substrings(prioritaryPos, remainingPos) append - substrings(remainingPos, prioritaryPos) append - substrings(remainingPos, remainingPos)) - } - } - for {str <- strings.distinct - if java.util.regex.Pattern.quote(str).r.findAllMatchIn(rhs).length >= numBestVars - } yield { - Map(bestVar -> str) - } - } - } - } - - @tailrec def statsStringForm(e: StringForm, acc: Map[Identifier, Int] = Map()): Map[Identifier, Int] = e match { - case Nil => acc - case Right(id)::q => statsStringForm(q, acc + (id -> (acc.getOrElse(id, 0) + 1))) - case (_::q) => statsStringForm(q, acc) - } - - @tailrec def stats(p: Problem, acc: Map[Identifier, Int] = Map()): Map[Identifier, Int] = p match { - case Nil => acc - case (sf, rhs)::q => - stats(q, (statsStringForm(sf) /: acc) { case (m, (k, v)) => m + (k -> (m.getOrElse(k, 0) + v)) }) - } - - /** Deduces possible new assignments from the problem. */ - def splitStrategy(p: Problem): Stream[Assignment] = { - // Look for the equation with the least degree of freedom. - if(p.isEmpty) return Stream(Map()) - - var minStatement = p.head - var minweight = BigInt(-1) - var minConstants = List[String]() - var minIdentifiersGrouped = List[List[Identifier]]() - // Counts the number of possible enumerations, take the least. - for((lhs, rhs) <- p) { - val constants = lhs.collect{ case Left(constant) => constant } - val identifiers_grouped = ListBuffer[List[Identifier]]() - val current_buffer = ListBuffer[Identifier]() - for(e <- lhs) e match { - case Left(constant) => // At this point, there is only one constant here. - identifiers_grouped.append(current_buffer.toList) - current_buffer.clear() - case Right(identifier) => - current_buffer.append(identifier) - } - identifiers_grouped.append(current_buffer.toList) - var weight = BigInt(9) - for(positions <- repartitions(constants, rhs)) { - var tmp_weight = BigInt(1) - var prev_position = 0 - for(((p, c), ids) <- positions.zip(constants).zip(identifiers_grouped.init)) { - tmp_weight *= cnk(p - prev_position, ids.length - 1) // number of positions - prev_position = p + c.length - } - tmp_weight *= cnk(rhs.length - prev_position, identifiers_grouped.last.length - 1) - weight += tmp_weight - } - if(minweight == -1 || weight < minweight) { - minweight = weight - minStatement = (lhs, rhs) - minConstants = constants - minIdentifiersGrouped = identifiers_grouped.toList - } - } - val (_, rhs) = minStatement - val constants = minConstants - val identifiers_grouped = minIdentifiersGrouped - val statistics = stats(p) - if(identifiers_grouped.length > 1) { - // There might be different repartitions of the first boundary constant. We need to investigate all of them. - repartitions(constants, rhs).map(_.head).distinct.toStream.flatMap(p => { - val firstString = rhs.substring(0, p) // We only split on the first string. - simpleSplit(identifiers_grouped.head, firstString)(statistics) - }) - } else if(identifiers_grouped.length == 1) { - simpleSplit(identifiers_grouped.head, rhs)(statistics) // All new assignments - } else { - if(rhs == "") Stream(Map()) else Stream.Empty - } - } - - /** Solves the problem and returns all possible satisfying assignment */ - def solve(p: Problem): Stream[Assignment] = { - val realProblem = forwardStrategy.run(p, Map()) - /*println("Problem:\n"+renderProblem(p)) - if(realProblem.nonEmpty && realProblem.get._1.nonEmpty) { - println("Solutions:\n"+realProblem.get._2) - println("Real problem:\n"+renderProblem(realProblem.get._1)) - }*/ - - realProblem match { - case None => - Stream.Empty - case Some((Nil, solution)) => - Stream(solution) - case Some((problem, partialSolution)) => - // 1) No double constants ("a" + "b" have been replaced by "ab") - // 2) No just an identifier on the LHS (x = "a" has been replaced by an assignment - // 3) No leading or trailing constants in equation ("a" + ... + "b" = "axyzb" has been replaced by ... = "xyz") - - /* Equation of the type - variables "constant" variables "constant".... variables = "constant". - For each constant we check its possible positions in the output, which determines possible assignments. - - Then since variables = constant, we can just iterate on them. - Heuristic: We need to resolve smaller equations first. - */ - for{assignment <- splitStrategy(problem) - newProblem = reduceProblem(assignment)(problem) - remainingSolution <- solve(newProblem) - } yield { - partialSolution ++ assignment ++ remainingSolution - } - } - } - - //////////////////////////////////////////////// - //// Transitively Bounded problem extension //// - //////////////////////////////////////////////// - - /** More general types of equations */ - type GeneralEquation = (StringForm, StringForm) - - /** Supposes that all variables are transitively bounded by length*/ - type GeneralProblem = List[GeneralEquation] - - def variablesStringForm(sf: StringForm): Set[Identifier] = sf.collect { case Right(id) => id }.toSet - def variables(gf: GeneralEquation): Set[Identifier] = variablesStringForm(gf._1) ++ variablesStringForm(gf._2) - - /** Returns true if the problem is transitively bounded */ - def isTransitivelyBounded(b: GeneralProblem, transitivelyBounded: Set[Identifier] = Set()): Boolean = { - def isBounded(sf: GeneralEquation) = { - variablesStringForm(sf._1).forall(transitivelyBounded) || variablesStringForm(sf._2).forall(transitivelyBounded) - } - val (bounded, notbounded) = b.partition(isBounded) - - if(notbounded == Nil) true - else if(notbounded == b) false - else { - isTransitivelyBounded(notbounded, transitivelyBounded ++ bounded.flatMap { x => variables(x) }) - } - } - - /** Propagates an assignment into a general equation */ - def reduceGeneralEquation(a: Assignment)(g: GeneralEquation): GeneralEquation = g match { - case (sf1, sf2) => (reduceStringForm(a)(sf1), reduceStringForm(a)(sf2)) - } - - /** Solves the bounded problem version. - * Requires all the variables to be transitively bounded by size. */ - def solveGeneralProblem(b: GeneralProblem): Stream[Assignment] = { - val realProblem = b map { case (lhs, rhs) => (reduceStringForm(lhs), reduceStringForm(rhs)) } - - def partition(b: GeneralProblem, bounded: ListBuffer[Equation] = ListBuffer(), unbounded: ListBuffer[GeneralEquation] = ListBuffer()): (Problem, GeneralProblem) = b match { - case Nil => (bounded.toList, unbounded.toList) - case (sf1, List(Left(a)))::q => partition(q, bounded += ((sf1, a)), unbounded) - case (sf1, Nil)::q => partition(q, bounded += ((sf1, "")), unbounded) - case (List(Left(a)), sf1)::q => partition(q, bounded += ((sf1, a)), unbounded) - case (Nil, sf1)::q => partition(q, bounded += ((sf1, "")), unbounded) - case a::q => partition(q, bounded, unbounded += a) - } - - val (bounded, unbounded) = partition(realProblem) - - if(unbounded == Nil) solve(bounded) else - solve(bounded).flatMap(assignment => { - solveGeneralProblem(unbounded.map(reduceGeneralEquation(assignment)(_))).map(assignment ++ _) - }) - } - - - //////////////////////////////////////////////// - //// Incremental problem extension //// - //////////////////////////////////////////////// - - /** Returns all subsets of i elements of a sequence. */ - def take[A](i: Int, of: Seq[A]): Stream[Seq[A]] = { - if(i > of.size || i < 0) Stream.empty - else if(i == of.size) Stream(of) - else if(i == 0) Stream(Seq.empty) - else { - take(i - 1, of.tail).map(of.head +: _) #::: take(i, of.tail) - } - } - - /** Keeps only partial assignments which completely differ from the initial mapping. */ - def removeRedundancies(s: Stream[Assignment], initialMapping: Assignment): Stream[Assignment] = { - s.filter(m => - m.keys forall (key => initialMapping(key) != m(key))) - } - - /** If the stream is not empty and there are more than two variables, - * it will try to assign values to variables which minimize changes. - * It will try deletions from the end and from the start of one variable. - * */ - def minimizeChanges(s: Stream[Assignment], p: Problem, ifVariable: Set[Identifier], initialMapping: Assignment): Stream[Assignment] = { - if(s.isEmpty || ifVariable.size <= 1) s else { - ((for{v <- ifVariable.toStream - originalValue = initialMapping(v) - i <- originalValue.length to 0 by -1 - prefix <- (if(i == 0 || i == originalValue.length) List(true) else List(true, false)) - possibleValue = (if(prefix) originalValue.substring(0, i) else originalValue.substring(originalValue.length - i)) - s <- solve(reduceProblem(Map(v -> possibleValue))(p)) - } yield s + (v -> possibleValue)) ++ s).distinct - } - } - - /** Solves the problem while supposing that a minimal number of variables have been changed. - * Will try to replace variables from the left first. - * If a variable occurs multiple times, will try to replace each of its occurrence first. - * */ - def solveMinChange(p: Problem, initialMapping: Assignment): Stream[Assignment] = { - // First try to see if the problem is solved. If yes, returns the initial mapping - val initKeys = initialMapping.keys.toSeq - for{ - i <- (0 to initialMapping.size).toStream - toReplace <- take(i, initKeys) - ifVariable = toReplace.toSet - newProblem = reduceProblem(initialMapping filterKeys (x => !ifVariable(x)))(p) - newSolutions = solve(newProblem) - newSolutions_nonredundant = removeRedundancies(newSolutions, initialMapping) - solution <- minimizeChanges(newSolutions_nonredundant, newProblem, ifVariable, initialMapping: Assignment) - } yield solution - } -} diff --git a/src/main/scala/inox/solvers/sygus/CVC4SygusSolver.scala b/src/main/scala/inox/solvers/sygus/CVC4SygusSolver.scala deleted file mode 100644 index 564dc67fe75fe016bcbba38a62dfbb6ba0544efc..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/sygus/CVC4SygusSolver.scala +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package sygus - -import purescala._ -import Definitions.Program - -import synthesis.Problem - -import leon.solvers.smtlib._ - -import _root_.smtlib.interpreters.CVC4Interpreter - -class CVC4SygusSolver(ctx: LeonContext, pgm: Program, p: Problem) extends SygusSolver(ctx, pgm, p) with SMTLIBCVC4QuantifiedTarget { - override def targetName = "cvc4-sygus"; - - def interpreterOps(ctx: LeonContext) = { - Seq( - "-q", - "--cegqi-si", - "--cbqi-sym-lia", - "--macros-quant", - "--lang", "sygus", - "--print-success" - ) - } - - protected val allowQuantifiedAssertions: Boolean = true -} diff --git a/src/main/scala/inox/solvers/sygus/SygusSolver.scala b/src/main/scala/inox/solvers/sygus/SygusSolver.scala deleted file mode 100644 index 9cfefb228f95ac9adaa39023aa1671fb98fa4509..0000000000000000000000000000000000000000 --- a/src/main/scala/inox/solvers/sygus/SygusSolver.scala +++ /dev/null @@ -1,140 +0,0 @@ -/* Copyright 2009-2016 EPFL, Lausanne */ - -package leon -package solvers -package sygus - -import purescala.Common._ -import purescala.Definitions._ -import purescala.ExprOps._ -import purescala.Constructors._ -import purescala.Expressions._ - -import synthesis.Problem - -import leon.solvers.smtlib._ - -import _root_.smtlib.parser.Terms.{Identifier => SMTIdentifier, _} -import _root_.smtlib.parser.Commands.{FunDef => SMTFunDef, _} -import _root_.smtlib.parser.CommandsResponses.{Error => _, _} -import _root_.smtlib.parser.Parser.UnexpectedEOFException - -abstract class SygusSolver(val context: LeonContext, val program: Program, val p: Problem) extends SMTLIBTarget { - implicit val ctx = context - implicit val debugSection = leon.utils.DebugSectionSynthesis - - protected def unsupported(t: Tree, str: String): Nothing = { - throw new Unsupported(t, str) - } - - def checkSynth(): Option[Expr] = { - - emit(SList(SSymbol("set-logic"), SSymbol("ALL_SUPPORTED"))) - - val constraintId = QualifiedIdentifier(SMTIdentifier(SSymbol("constraint"))) - - val bindings = p.as.map(a => a -> (symbolToQualifiedId(id2sym(a)): Term)).toMap - - // declare inputs - for (a <- p.as) { - emit(SList(SSymbol("declare-var"), id2sym(a), toSMT(a.getType))) - variables += a -> id2sym(a) - } - - // declare outputs - val xToFd = for (x <- p.xs) yield { - val fd = new FunDef(x.freshen, Seq(), p.as.map(a => ValDef(a)), x.getType) - - val fsym = id2sym(fd.id) - - functions += fd.typed -> fsym - - // declare function to synthesize - emit(SList(SSymbol("synth-fun"), id2sym(fd.id), SList(fd.params.map(vd => SList(id2sym(vd.id), toSMT(vd.getType))) :_*), toSMT(fd.returnType))) - - x -> fd - } - - val xToFdCall = xToFd.toMap.mapValues(fd => FunctionInvocation(fd.typed, p.as.map(_.toVariable))) - - val synthPhi = replaceFromIDs(xToFdCall, p.phi) - - val constraint = p.pc implies synthPhi - - emit(FunctionApplication(constraintId, Seq(toSMT(constraint)(bindings)))) - - emit(SList(SSymbol("check-synth"))) // check-synth emits: success; unsat; fdef* - - // We currently cannot predict the amount of success we will get, so we read as many as possible - try { - var lastRes = interpreter.parser.parseSExpr - while(lastRes == SSymbol("success")) { - lastRes = interpreter.parser.parseSExpr - } - - lastRes match { - case SSymbol("unsat") => - - val solutions = (for (x <- p.xs) yield { - interpreter.parser.parseCommand match { - case DefineFun(SMTFunDef(name, params, retSort, body)) => - val res = fromSMT(body, sorts.toA(retSort))(Map(), Map()) - Some(res) - case r => - context.reporter.warning("Unnexpected result from cvc4-sygus: "+r) - None - } - }).flatten - - if (solutions.size == p.xs.size) { - Some(tupleWrap(solutions)) - } else { - None - } - - case SSymbol("unknown") => - None - - case r => - context.reporter.warning("Unnexpected result from cvc4-sygus: "+r+" expected unsat") - None - } - } catch { - case _: UnexpectedEOFException => - None - } - } -} - - //val g = BaseGrammar || OneOf(p.as.map(_.toVariable)) - - //type Label = TypeTree - - //var defined = Map[Label, Identifier]() - //var definitions = new ArrayBuffer[(Identifier, Seq[Expr])]() - - //def getLabel(l: Label): Identifier = defined.getOrElse(l, { - // val id = FreshIdentifier(l.getType.toString, l.getType) - // defined += l -> id - - // val defs = g.getProductions(l).map { g => - // g.builder(g.subTrees.map(getLabel(_).toVariable)) - // } - - // definitions += (id -> defs) - - // id - //}) - - //// discover grammar - //getLabel(out.getType) - - //val grammarBindings: Map[Identifier, Term] = defined.map{ case (_, id) => - // id -> QualifiedIdentifier(SMTIdentifier(idToSymbol(id))) - //} - - //// define grammar - //val grammar = SList((for ((id, ds) <- definitions) yield { - // SList(idToSymbol(id), typeToSort(id.getType), SList(ds.map(d => smt.toSMT(d)(bindings++grammarBindings)): _*)) - //}).toList) -