diff --git a/library/lang/synthesis/package.scala b/library/lang/synthesis/package.scala index 540054899b37dc540f0c359f51fe6e32dc553c82..fe87f4eb0bbe514e8d9a6be97b8b29f2278a686e 100644 --- a/library/lang/synthesis/package.scala +++ b/library/lang/synthesis/package.scala @@ -4,6 +4,9 @@ package leon.lang import leon.annotation._ +import scala.language.implicitConversions +import scala.annotation.implicitNotFound + package object synthesis { @ignore private def noImpl = throw new RuntimeException("Implementation not supported") @@ -19,15 +22,16 @@ package object synthesis { @ignore def choose[A, B, C, D, E](predicate: (A, B, C, D, E) => Boolean): (A, B, C, D, E) = noImpl - @library - def ???[T](implicit o: Oracle[T]): T = o.head + @ignore + def ???[T]: T = noImpl - @library - def ?[T](e1: T)(implicit o1: Oracle[Boolean], o2: Oracle[T]): T = if(???[Boolean](o1)) e1 else ???[T](o2) + @ignore + def ?[T](e1: T): T = if(???[Boolean]) e1 else ???[T] @ignore - def ?[T](e1: T, es: T*)(implicit o: Oracle[Boolean]): T = noImpl + def ?[T](e1: T, es: T*): T = noImpl @ignore def withOracle[A, R](body: Oracle[A] => R): R = noImpl + } diff --git a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala index f387182d8257e30a433db9aa3c314054b9498202..e444d225de8427ebbce9890d030274a2879a96ec 100644 --- a/src/main/scala/leon/frontends/scalac/ASTExtractors.scala +++ b/src/main/scala/leon/frontends/scalac/ASTExtractors.scala @@ -278,11 +278,11 @@ trait ASTExtractors { } object ExHoleExpression { - def unapply(tree: Tree) : Option[(Tree, List[Tree], List[Tree])] = tree match { - case a @ Apply(Apply(TypeApply(s @ ExSymbol("leon", "lang", "synthesis", "$qmark"), List(tpt)), args1), args2) => - Some((tpt, args1, args2)) - case a @ Apply(TypeApply(s @ ExSymbol("leon", "lang", "synthesis", "$qmark$qmark$qmark"), List(tpt)), List(o)) => - Some((tpt, Nil, List(o))) + def unapply(tree: Tree) : Option[(Tree, List[Tree])] = tree match { + case a @ Apply(TypeApply(s @ ExSymbol("leon", "lang", "synthesis", "$qmark"), List(tpt)), args1) => + Some((tpt, args1)) + case TypeApply(s @ ExSymbol("leon", "lang", "synthesis", "$qmark$qmark$qmark"), List(tpt)) => + Some((tpt, Nil)) case _ => None } } diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 9826e01a013efb4b7b495303f7df115b606da4fa..51338cb30628baaee0f5b9f03873deb818fd3b22 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -964,38 +964,10 @@ trait CodeExtraction extends ASTExtractors { outOfSubsetError(tr, "Unidentified variable "+sym+" "+sym.id+".") } - case hole @ ExHoleExpression(tpt, exprs, os) => + case hole @ ExHoleExpression(tpt, exprs) => val leonExprs = exprs.map(extractTree) - val leonOracles = os.map(extractTree) - def rightOf(o: LeonExpr): LeonExpr = { - val Some((cl, fd)) = libraryMethod("Oracle", "right") - MethodInvocation(o, cl, fd.typed(Nil), Nil) - } - - def valueOf(o: LeonExpr): LeonExpr = { - val Some((cl, fd)) = libraryMethod("Oracle", "head") - MethodInvocation(o, cl, fd.typed(Nil), Nil) - } - - - leonExprs match { - case Nil => - valueOf(leonOracles(0)) - - case List(e) => - IfExpr(valueOf(leonOracles(0)), e, valueOf(leonOracles(1))) - - case exs => - val l = exs.last - var o = leonOracles(0) - - exs.init.foldRight(l)({ (e: LeonExpr, r: LeonExpr) => - val res = IfExpr(valueOf(leonOracles(0)), e, r) - o = rightOf(o) - res - }) - } + Hole(extractType(tpt), exprs.map(extractTree)) case ops @ ExWithOracleExpression(oracles, body) => val newOracles = oracles map { case (tpt, sym) => diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala index 7873fbaa138fb09dafc4d4bd3ba27b1f056896a4..0973edddbcf4ccc981309c841f7c3f447c1b2001 100644 --- a/src/main/scala/leon/purescala/PrettyPrinter.scala +++ b/src/main/scala/leon/purescala/PrettyPrinter.scala @@ -174,6 +174,13 @@ class PrettyPrinter(opts: PrinterOptions, val sb: StringBuffer = new StringBuffe | $pred |}""" + case h @ Hole(tpe, es) => + if (es.isEmpty) { + p"""|???[$tpe]""" + } else { + p"""|?($es)""" + } + case CaseClass(cct, args) => if (cct.classDef.isCaseObject) { p"$cct" diff --git a/src/main/scala/leon/purescala/Trees.scala b/src/main/scala/leon/purescala/Trees.scala index fd0b1f4d0cf8ccec5dc2cace3c9ffa6ba66a3b84..387b085f18b4b8b0d66a58b450667214c6c51422 100644 --- a/src/main/scala/leon/purescala/Trees.scala +++ b/src/main/scala/leon/purescala/Trees.scala @@ -64,6 +64,13 @@ object Trees { } } + case class Hole(fixedType: TypeTree, alts: Seq[Expr]) extends Expr with FixedType with NAryExtractable { + + def extract = { + Some((alts, (es: Seq[Expr]) => Hole(fixedType, es).setPos(this))) + } + } + /* Like vals */ case class Let(binder: Identifier, value: Expr, body: Expr) extends Expr with FixedType { val fixedType = body.getType diff --git a/src/main/scala/leon/synthesis/ConvertHoles.scala b/src/main/scala/leon/synthesis/ConvertHoles.scala new file mode 100644 index 0000000000000000000000000000000000000000..60a6f8ea0bf1e4b2346c98085699fa7a8b9d228c --- /dev/null +++ b/src/main/scala/leon/synthesis/ConvertHoles.scala @@ -0,0 +1,120 @@ +/* Copyright 2009-2014 EPFL, Lausanne */ + +package leon +package synthesis + +import purescala.Common._ +import purescala.Trees._ +import purescala.TypeTrees._ +import purescala.TreeOps._ +import purescala.Definitions._ + +object ConvertHoles extends LeonPhase[Program, Program] { + val name = "Convert Holes to Choose" + val description = "Convert Holes found in bodies to equivalent Choose" + + /** + * This phase converts a body with "withOracle{ .. }" into a choose construct: + * + * def foo(a: T) = { + * require(..a..) + * expr(a, ???) + * } ensuring { x => post(x) } + * + * gets converted into: + * + * def foo(a: T) { + * require(..a..) + * val h = choose { (h) => { + * val res = expr(a, ???) + * post(res) + * } + * expr(a, h) + * } ensuring { res => + * post(res) + * } + * + */ + def run(ctx: LeonContext)(pgm: Program): Program = { + + pgm.definedFunctions.foreach(fd => { + if (fd.hasBody) { + + var holes = List[Identifier]() + + val newBody = preMap { + case h @ Hole(tpe, es) => + val (expr, ids) = toExpr(h) + + holes ++= ids + + Some(expr) + case _ => + None + }(fd.body.get) + + if (holes.nonEmpty) { + val cids = holes.map(_.freshen) + val pred = fd.postcondition match { + case Some((id, post)) => + replaceFromIDs((holes zip cids.map(_.toVariable)).toMap, Let(id, newBody, post)) + case None => + BooleanLiteral(true) + } + + val withChoose = if (holes.size > 1) { + LetTuple(holes, Choose(cids, pred), newBody) + } else { + Let(holes.head, Choose(cids, pred), newBody) + } + + fd.body = Some(withChoose) + } + + } + + // Ensure that holes are not found in pre and/or post conditions + fd.precondition.foreach { + preTraversal{ + case _: Hole => + ctx.reporter.error("Holes are not supported in preconditions. (function "+fd.id.asString(ctx)+")") + case _ => + } + } + + fd.postcondition.foreach { case (id, post) => + preTraversal{ + case _: Hole => + ctx.reporter.error("Holes are not supported in postconditions. (function "+fd.id.asString(ctx)+")") + case _ => + }(post) + } + + }) + + pgm + } + + def toExpr(h: Hole): (Expr, List[Identifier]) = { + h.alts match { + case Seq() => + val h1 = FreshIdentifier("hole", true).setType(h.getType) + (h1.toVariable, List(h1)) + + case Seq(v) => + val h1 = FreshIdentifier("hole", true).setType(BooleanType) + val h2 = FreshIdentifier("hole", true).setType(h.getType) + (IfExpr(h1.toVariable, h2.toVariable, v), List(h1, h2)) + + case exs => + var ids: List[Identifier] = Nil + val ex = exs.init.foldRight(exs.last)({ (e: Expr, r: Expr) => + val h = FreshIdentifier("hole", true).setType(BooleanType) + ids ::= h + IfExpr(h.toVariable, e, r) + }) + + (ex, ids.reverse) + } + } +} diff --git a/src/main/scala/leon/utils/PreprocessingPhase.scala b/src/main/scala/leon/utils/PreprocessingPhase.scala index d5719090ec749321821eef874e9b612aef525880..9d1c277e9a81c69dcb968275c03e0febcb201ed3 100644 --- a/src/main/scala/leon/utils/PreprocessingPhase.scala +++ b/src/main/scala/leon/utils/PreprocessingPhase.scala @@ -6,7 +6,7 @@ package utils import purescala.Definitions.Program import purescala.{MethodLifting, CompleteAbstractDefinitions} -import synthesis.{ConvertWithOracle} +import synthesis.{ConvertWithOracle, ConvertHoles} object PreprocessingPhase extends TransformationPhase { @@ -19,6 +19,7 @@ object PreprocessingPhase extends TransformationPhase { MethodLifting andThen TypingPhase andThen ConvertWithOracle andThen + ConvertHoles andThen CompleteAbstractDefinitions andThen InjectAsserts diff --git a/src/test/resources/regression/synthesis/Holes/Hole1.scala b/src/test/resources/regression/synthesis/Holes/Hole1.scala deleted file mode 100644 index 084185b0efcb96208163158049ebfac429a6a116..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/synthesis/Holes/Hole1.scala +++ /dev/null @@ -1,13 +0,0 @@ -/* Copyright 2009-2014 EPFL, Lausanne */ - -import leon.lang._ -import leon.annotation._ -import leon.lang.synthesis._ -import leon.collection._ -import leon._ - -object Hole1 { - def largestInt(a: Int, b: Int)(implicit o: Oracle[Boolean]) = { - ?(a, b) - } ensuring { r => r >= b && r >= a } -} diff --git a/src/test/resources/regression/synthesis/Holes/Hole2.scala b/src/test/resources/regression/synthesis/Holes/Hole2.scala deleted file mode 100644 index 3a10748eb70d48aab7a46cc8379dfb3bbaacbfca..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/synthesis/Holes/Hole2.scala +++ /dev/null @@ -1,17 +0,0 @@ -/* Copyright 2009-2014 EPFL, Lausanne */ - -import leon.lang._ -import leon.annotation._ -import leon.lang.synthesis._ -import leon.collection._ -import leon._ - -object Hole2 { - def genList()(implicit o: Oracle[Boolean]): List[Int] = ?(Nil[Int](), Cons[Int](0, genList()(o.right))) - - def listOfSize2(implicit o: Oracle[Boolean]) = { - genList() - } ensuring { - _.size == 2 - } -}