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/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 {