diff --git a/src/main/scala/leon/purescala/DefOps.scala b/src/main/scala/leon/purescala/DefOps.scala index ea4bff382d1d20b52ba27823cdf92dbaf62d851c..f713cab8f1a20d54a1fb47254f2c09e9e2655d2c 100644 --- a/src/main/scala/leon/purescala/DefOps.scala +++ b/src/main/scala/leon/purescala/DefOps.scala @@ -93,6 +93,29 @@ object DefOps { } } + private def stripPrefix(off: List[String], from: List[String]): List[String] = { + val commonPrefix = (off zip from).takeWhile(p => p._1 == p._2) + + val res = off.drop(commonPrefix.size) + + if (res.isEmpty) { + if (off.isEmpty) List() + else List(off.last) + } else { + res + } + } + + def simplifyPath(namesOf: List[String], from: Definition, useUniqueIds: Boolean)(implicit pgm: Program) = { + val pathFrom = pathFromRoot(from).dropWhile(_.isInstanceOf[Program]) + + val namesFrom = pathToNames(pathFrom, useUniqueIds) + + val names: Set[List[String]] = Set(namesOf, stripPrefix(namesOf, namesFrom)) ++ + getNameUnderImports(pathFrom, namesOf) + + names.toSeq.minBy(_.size).mkString(".") + } def fullNameFrom(of: Definition, from: Definition, useUniqueIds: Boolean)(implicit pgm: Program): String = { val pathFrom = pathFromRoot(from).dropWhile(_.isInstanceOf[Program]) @@ -100,30 +123,22 @@ object DefOps { val namesFrom = pathToNames(pathFrom, useUniqueIds) val namesOf = pathToNames(pathFromRoot(of), useUniqueIds) - def stripPrefix(off: List[String], from: List[String]) = { - val commonPrefix = (off zip from).takeWhile(p => p._1 == p._2) - - val res = off.drop(commonPrefix.size) - - if (res.isEmpty) { - if (off.isEmpty) List() - else List(off.last) - } else { - res - } - } - val sp = stripPrefix(namesOf, namesFrom) if (sp.isEmpty) return "**** " + of.id.uniqueName - var names: Set[List[String]] = Set(namesOf, stripPrefix(namesOf, namesFrom)) + val names: Set[List[String]] = + Set(namesOf, stripPrefix(namesOf, namesFrom)) ++ getNameUnderImports(pathFrom, namesOf) - pathFrom match { + names.toSeq.minBy(_.size).mkString(".") + } + + private def getNameUnderImports(pathFrom: List[Definition], namesOf: List[String]): Seq[List[String]] = { + (pathFrom match { case (u: UnitDef) :: _ => val imports = u.imports.map { case Import(path, true) => path case Import(path, false) => path.init }.toList - + def stripImport(of: List[String], imp: List[String]): Option[List[String]] = { if (of.startsWith(imp)) { Some(stripPrefix(of, imp)) @@ -131,15 +146,13 @@ object DefOps { None } } - - for (imp <- imports) { - names ++= stripImport(namesOf, imp) - } - + + for {imp <- imports + strippedImport <- stripImport(namesOf, imp) + } yield strippedImport case _ => - } - - names.toSeq.minBy(_.size).mkString(".") + Nil + }) } def pathToNames(path: List[Definition], useUniqueIds: Boolean): List[String] = { diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index ca52d4592842da60629423e9d0ba3ff52ea2a73c..058e3a0ead6262f367ef5c4441dd075dd9e79be7 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -11,6 +11,7 @@ import Extractors._ import Constructors._ import utils._ import solvers._ +import scala.language.implicitConversions /** Provides functions to manipulate [[purescala.Expressions]]. * @@ -1285,20 +1286,18 @@ object ExprOps extends { val Deconstructor = Operator } with SubTreeOps[Expr] { false } + type Apriori = Map[Identifier, Identifier] + /** Checks whether two expressions can be homomorphic and returns the corresponding mapping */ def canBeHomomorphic(t1: Expr, t2: Expr): Option[Map[Identifier, Identifier]] = { val freeT1Variables = ExprOps.variablesOf(t1) val freeT2Variables = ExprOps.variablesOf(t2) - def mergeContexts(a: Option[Map[Identifier, Identifier]], b: =>Option[Map[Identifier, Identifier]]) = a match { - case Some(m) => - b match { - case Some(n) if (m.keySet & n.keySet) forall (key => m(key) == n(key)) => - Some(m ++ n) - case _ =>None - } - case _ => None - } + def mergeContexts( + a: Option[Apriori], + b: Apriori => Option[Apriori]): + Option[Apriori] = a.flatMap(b) + object Same { def unapply(tt: (Expr, Expr)): Option[(Expr, Expr)] = { if (tt._1.getClass == tt._2.getClass) { @@ -1308,27 +1307,38 @@ object ExprOps extends { val Deconstructor = Operator } with SubTreeOps[Expr] { } } } - implicit class AugmentedContext(c: Option[Map[Identifier, Identifier]]) { - def &&(other: => Option[Map[Identifier, Identifier]]) = mergeContexts(c, other) + implicit class AugmentedContext(c: Option[Apriori]) { + def &&(other: Apriori => Option[Apriori]): Option[Apriori] = mergeContexts(c, other) def --(other: Seq[Identifier]) = c.map(_ -- other) } - implicit class AugmentedBooleant(c: Boolean) { - def &&(other: => Option[Map[Identifier, Identifier]]) = if(c) other else None + implicit class AugmentedBoolean(c: Boolean) { + def &&(other: => Option[Apriori]) = if(c) other else None + } + implicit class AugmentedFilter(c: Apriori => Option[Apriori]) { + def &&(other: Apriori => Option[Apriori]): + Apriori => Option[Apriori] + = (m: Apriori) => c(m).flatMap(mp => other(mp)) } implicit class AugmentedSeq[T](c: Seq[T]) { - def mergeall(p: T => Option[Map[Identifier, Identifier]]) = - (Option(Map[Identifier, Identifier]()) /: c) { - case (s, c) => s && p(c) + def mergeall(p: T => Apriori => Option[Apriori])(apriori: Apriori) = + (Option(apriori) /: c) { + case (s, c) => s.flatMap(apriori => p(c)(apriori)) } } + implicit def noneToContextTaker(c: None.type) = { + (m: Apriori) => None + } - def idHomo(i1: Identifier, i2: Identifier): Option[Map[Identifier, Identifier]] = { - if(!(freeT1Variables(i1) || freeT2Variables(i2)) || i1 == i2) Some(Map(i1 -> i2)) else None + def idHomo(i1: Identifier, i2: Identifier)(apriori: Apriori): Option[Apriori] = { + if(!(freeT1Variables(i1) || freeT2Variables(i2)) || i1 == i2 || apriori.get(i1) == Some(i2)) Some(Map(i1 -> i2)) else None + } + def idOptionHomo(i1: Option[Identifier], i2: Option[Identifier])(apriori: Apriori): Option[Apriori] = { + (i1.size == i2.size) && (i1 zip i2).headOption.flatMap(i => idHomo(i._1, i._2)(apriori)) } - def fdHomo(fd1: FunDef, fd2: FunDef): Option[Map[Identifier, Identifier]] = { + def fdHomo(fd1: FunDef, fd2: FunDef)(apriori: Apriori): Option[Apriori] = { if(fd1.params.size == fd2.params.size) { val newMap = Map(( (fd1.id -> fd2.id) +: @@ -1337,114 +1347,113 @@ object ExprOps extends { val Deconstructor = Operator } with SubTreeOps[Expr] { } else None } - def isHomo(t1: Expr, t2: Expr): Option[Map[Identifier, Identifier]] = { - def casesMatch(cs1 : Seq[MatchCase], cs2 : Seq[MatchCase]) : Option[Map[Identifier, Identifier]] = { - def patternHomo(p1: Pattern, p2: Pattern): (Boolean, Map[Identifier, Identifier]) = (p1, p2) match { + def isHomo(t1: Expr, t2: Expr)(apriori: Apriori): Option[Apriori] = { + def casesMatch(cs1 : Seq[MatchCase], cs2 : Seq[MatchCase])(apriori: Apriori) : Option[Apriori] = { + def patternHomo(p1: Pattern, p2: Pattern)(apriori: Apriori): Option[Apriori] = (p1, p2) match { case (InstanceOfPattern(ob1, cd1), InstanceOfPattern(ob2, cd2)) => - (ob1.size == ob2.size && cd1 == cd2, Map((ob1 zip ob2).toSeq : _*)) + cd1 == cd2 && idOptionHomo(ob1, ob2)(apriori) case (WildcardPattern(ob1), WildcardPattern(ob2)) => - (ob1.size == ob2.size, Map((ob1 zip ob2).toSeq : _*)) + idOptionHomo(ob1, ob2)(apriori) case (CaseClassPattern(ob1, ccd1, subs1), CaseClassPattern(ob2, ccd2, subs2)) => - val m = Map[Identifier, Identifier]() ++ (ob1 zip ob2) + val m = idOptionHomo(ob1, ob2)(apriori) - if (ob1.size == ob2.size && ccd1 == ccd2 && subs1.size == subs2.size) { - (subs1 zip subs2).map { case (p1, p2) => patternHomo(p1, p2) }.foldLeft((true, m)) { - case ((b1, m1), (b2,m2)) => (b1 && b2, m1 ++ m2) - } - } else { - (false, Map()) - } + (ccd1 == ccd2 && subs1.size == subs2.size) && m && + ((subs1 zip subs2) mergeall { case (p1, p2) => patternHomo(p1, p2) }) - case (UnapplyPattern(ob1, fd1, subs1), UnapplyPattern(ob2, fd2, subs2)) => - val m = Map[Identifier, Identifier]() ++ (ob1 zip ob2) + case (UnapplyPattern(ob1, TypedFunDef(fd1, ts1), subs1), UnapplyPattern(ob2, TypedFunDef(fd2, ts2), subs2)) => + val m = idOptionHomo(ob1, ob2)(apriori) - if (ob1.size == ob2.size && fd1 == fd2 && subs1.size == subs2.size) { - (subs1 zip subs2).map { case (p1, p2) => patternHomo(p1, p2) }.foldLeft((true, m)) { - case ((b1, m1), (b2,m2)) => (b1 && b2, m1 ++ m2) - } - } else { - (false, Map()) - } + (subs1.size == subs2.size && ts1 == ts2) && m && fdHomo(fd1, fd2) && ( + (subs1 zip subs2) mergeall { case (p1, p2) => patternHomo(p1, p2) }) case (TuplePattern(ob1, subs1), TuplePattern(ob2, subs2)) => - val m = Map[Identifier, Identifier]() ++ (ob1 zip ob2) + val m = idOptionHomo(ob1, ob2)(apriori) - if (ob1.size == ob2.size && subs1.size == subs2.size) { - (subs1 zip subs2).map { case (p1, p2) => patternHomo(p1, p2) }.foldLeft((true, m)) { - case ((b1, m1), (b2,m2)) => (b1 && b2, m1 ++ m2) - } - } else { - (false, Map()) - } + (ob1.size == ob2.size && subs1.size == subs2.size) && m && ( + (subs1 zip subs2) mergeall { case (p1, p2) => patternHomo(p1, p2) }) case (LiteralPattern(ob1, lit1), LiteralPattern(ob2,lit2)) => - (ob1.size == ob2.size && lit1 == lit2, (ob1 zip ob2).toMap) + lit1 == lit2 && idOptionHomo(ob1, ob2)(apriori) case _ => - (false, Map()) + None } (cs1 zip cs2).mergeall { case (MatchCase(p1, g1, e1), MatchCase(p2, g2, e2)) => - val (h, nm) = patternHomo(p1, p2) - val g: Option[Map[Identifier, Identifier]] = (g1, g2) match { - case (Some(g1), Some(g2)) => Some(nm) && isHomo(g1,g2) - case (None, None) => Some(Map()) + val h = patternHomo(p1, p2) _ + val g: Apriori => Option[Apriori] = (g1, g2) match { + case (Some(g1), Some(g2)) => isHomo(g1, g2)(_) + case (None, None) => (m: Apriori) => Some(m) case _ => None } - val e = Some(nm) && isHomo(e1, e2) + val e = isHomo(e1, e2) _ h && g && e - } - + }(apriori) } import synthesis.Witnesses.Terminating - val res: Option[Map[Identifier, Identifier]] = (t1, t2) match { + val res: Option[Apriori] = (t1, t2) match { case (Variable(i1), Variable(i2)) => - idHomo(i1, i2) + idHomo(i1, i2)(apriori) case (Let(id1, v1, e1), Let(id2, v2, e2)) => - isHomo(v1, v2) && - isHomo(e1, e2) && Some(Map(id1 -> id2)) + + isHomo(v1, v2)(apriori + (id1 -> id2)) && + isHomo(e1, e2) + + case (Hole(_, _), Hole(_, _)) => + None case (LetDef(fds1, e1), LetDef(fds2, e2)) => fds1.size == fds2.size && { val zipped = fds1.zip(fds2) - (zipped mergeall (fds => fdHomo(fds._1, fds._2))) && Some(zipped.map(fds => fds._1.id -> fds._2.id).toMap) && + (zipped mergeall (fds => fdHomo(fds._1, fds._2)))(apriori) && isHomo(e1, e2) } case (MatchExpr(s1, cs1), MatchExpr(s2, cs2)) => - cs1.size == cs2.size && casesMatch(cs1,cs2) && isHomo(s1, s2) + cs1.size == cs2.size && casesMatch(cs1,cs2)(apriori) && isHomo(s1, s2) case (Passes(in1, out1, cs1), Passes(in2, out2, cs2)) => - (cs1.size == cs2.size && casesMatch(cs1,cs2)) && isHomo(in1,in2) && isHomo(out1,out2) + (cs1.size == cs2.size && casesMatch(cs1,cs2)(apriori)) && isHomo(in1,in2) && isHomo(out1,out2) case (FunctionInvocation(tfd1, args1), FunctionInvocation(tfd2, args2)) => - idHomo(tfd1.fd.id, tfd2.fd.id) && tfd1.tps.zip(tfd2.tps).mergeall{ case (t1, t2) => if(t1 == t2) Option(Map()) else None} && + (if(tfd1 == tfd2) Some(apriori) else (apriori.get(tfd1.fd.id) match { + case None => + isHomo(tfd1.fd.fullBody, tfd2.fd.fullBody)(apriori + (tfd1.fd.id -> tfd2.fd.id)) + case Some(fdid2) => + if(fdid2 == tfd2.fd.id) Some(apriori) else None + })) && + tfd1.tps.zip(tfd2.tps).mergeall{ + case (t1, t2) => if(t1 == t2) + (m: Apriori) => Option(m) + else (m: Apriori) => None} && (args1 zip args2).mergeall{ case (a1, a2) => isHomo(a1, a2) } case (Terminating(tfd1, args1), Terminating(tfd2, args2)) => - idHomo(tfd1.fd.id, tfd2.fd.id) && tfd1.tps.zip(tfd2.tps).mergeall{ case (t1, t2) => if(t1 == t2) Option(Map()) else None} && + idHomo(tfd1.fd.id, tfd2.fd.id)(apriori) && tfd1.tps.zip(tfd2.tps).mergeall{ case (t1, t2) => if(t1 == t2) (m: Apriori) => Option(m) else (m: Apriori) => None} && (args1 zip args2).mergeall{ case (a1, a2) => isHomo(a1, a2) } case (Lambda(defs, body), Lambda(defs2, body2)) => // We remove variables introduced by lambdas. - (isHomo(body, body2) && - (defs zip defs2).mergeall{ case (ValDef(a1), ValDef(a2)) => Option(Map(a1 -> a2)) } + ((defs zip defs2).mergeall{ case (ValDef(a1), ValDef(a2)) => + (m: Apriori) => + Some(m + (a1 -> a2)) }(apriori) + && isHomo(body, body2) ) -- (defs.map(_.id)) case (v1, v2) if isValue(v1) && isValue(v2) => - v1 == v2 && Some(Map[Identifier, Identifier]()) + v1 == v2 && Some(apriori) case Same(Operator(es1, _), Operator(es2, _)) => (es1.size == es2.size) && - (es1 zip es2).mergeall{ case (e1, e2) => isHomo(e1, e2) } + (es1 zip es2).mergeall{ case (e1, e2) => isHomo(e1, e2) }(apriori) case _ => None @@ -1453,9 +1462,7 @@ object ExprOps extends { val Deconstructor = Operator } with SubTreeOps[Expr] { res } - isHomo(t1,t2) - - + isHomo(t1,t2)(Map()) } // ensuring (res => res.isEmpty || isHomomorphic(t1, t2)(res.get)) /** Checks whether two trees are homomoprhic modulo an identifier map. diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala index 20502a13640e1ab3fd5820a9bcfc38de46ece0f5..8b681460826345dad6078145bb9cb01e1be73eb9 100644 --- a/src/main/scala/leon/purescala/PrettyPrinter.scala +++ b/src/main/scala/leon/purescala/PrettyPrinter.scala @@ -42,9 +42,12 @@ class PrettyPrinter(opts: PrinterOptions, body } } + + protected def getScope(implicit ctx: PrinterContext) = + ctx.parents.collectFirst { case (d: Definition) if !d.isInstanceOf[ValDef] => d } protected def printNameWithPath(df: Definition)(implicit ctx: PrinterContext) { - (opgm, ctx.parents.collectFirst { case (d: Definition) if !d.isInstanceOf[ValDef] => d }) match { + (opgm, getScope) match { case (Some(pgm), Some(scope)) => sb.append(fullNameFrom(df, scope, opts.printUniqueIds)(pgm)) @@ -133,7 +136,11 @@ class PrettyPrinter(opts: PrinterOptions, case h @ Hole(tpe, es) => if (es.isEmpty) { - p"???[$tpe]" + val hole = (for{scope <- getScope + program <- opgm } + yield simplifyPath("leon" :: "lang" :: "synthesis" :: "???" :: Nil, scope, false)(program)) + .getOrElse("leon.lang.synthesis.???") + p"$hole[$tpe]" } else { p"?($es)" } diff --git a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala index 6b7f7cc6ee3c00827289313ed60e111b6ec3a640..506cb86ce95bb044f709e23b6483ec1dcfb6ff7e 100644 --- a/src/main/scala/leon/xlang/ImperativeCodeElimination.scala +++ b/src/main/scala/leon/xlang/ImperativeCodeElimination.scala @@ -155,7 +155,7 @@ object ImperativeCodeElimination extends UnitPhase[Program] { val (rVal, rScope, rFun) = toFunction(e) val scope = (body: Expr) => { rVal match { - case FunctionInvocation(tfd, args) if tfd.hasPrecondition => + case FunctionInvocation(tfd, args) => rScope(replaceNames(rFun, Let(FreshIdentifier("tmp", tfd.returnType), rVal, accScope(body)))) case _ => rScope(replaceNames(rFun, accScope(body))) diff --git a/testcases/web/synthesis/24_String_DoubleList.scala b/testcases/web/synthesis/24_String_DoubleList.scala index 652f5e4b84ed8837dc56cdce10393a480232c1b0..021a11bcef87e883ea01424ed2024ca44bd255cd 100644 --- a/testcases/web/synthesis/24_String_DoubleList.scala +++ b/testcases/web/synthesis/24_String_DoubleList.scala @@ -21,14 +21,11 @@ object DoubleListRender { } holds def AtoString(a : A): String = { - ??? - } ensuring { - (res : String) => (a, res) passes { - case N() => - "[]" - case B(NN(), N()) => - "[()]" - } + ???[String] ask a + } + + def AAtoString(a : AA): String = { + ???[String] ask a } def structurallyEqualA(a: A, b: A): Boolean = (a, b) match {