diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index 99e2465120173fc72ed3e55d30606d5280fbfc50..d69bdbf94e608fed2a7d8fb3a2a60b1c0bd5f276 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -881,7 +881,7 @@ object ExprOps { val subTests = subps.zipWithIndex.map{case (p, i) => rec(tupleSelect(in, i+1, subps.size), p)} and(bind(ob, in) +: subTests: _*) - case up@UnapplyPattern(ob, fd, subps) => + case up @ UnapplyPattern(ob, fd, subps) => def someCase(e: Expr) = { // In the case where unapply returns a Some, it is enough that the subpatterns match andJoin(unwrapTuple(e, subps.size) zip subps map { case (ex, p) => rec(ex, p).setPos(p) }).setPos(e) diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index a45c4ee59a0bf518f320769286b00b95ce2fc42a..532d96d44ff00e8530b59e4728dfb34a0edfa3f9 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -331,7 +331,7 @@ object Expressions { // Hacky, but ok lazy val optionType = unapplyFun.returnType.asInstanceOf[AbstractClassType] lazy val Seq(noneType, someType) = optionType.knownCCDescendants.sortBy(_.fields.size) - lazy val someValue = someType.fields.head + lazy val someValue = someType.classDef.fields.head // Pattern match unapply(scrut) // In case of None, return noneCase. // In case of Some(v), return someCase(v). diff --git a/src/main/scala/leon/purescala/MethodLifting.scala b/src/main/scala/leon/purescala/MethodLifting.scala index 8cfae30d2ebfa070ceb5f50fb58e1eab1c70ccd6..2014739eaa3fba0c84ce10685087262e7e227872 100644 --- a/src/main/scala/leon/purescala/MethodLifting.scala +++ b/src/main/scala/leon/purescala/MethodLifting.scala @@ -27,7 +27,7 @@ object MethodLifting extends TransformationPhase { // Common for both cases val ct = ccd.typed val binder = FreshIdentifier(ccd.id.name.toLowerCase, ct, true) - val fBinders = ct.fields.map{ f => f.id -> f.id.freshen }.toMap + val fBinders = (ccd.fieldsIds zip ct.fields).map(p => p._1 -> p._2.id.freshen).toMap def subst(e: Expr): Expr = e match { case CaseClassSelector(`ct`, This(`ct`), i) => Variable(fBinders(i)).setPos(e) @@ -37,19 +37,19 @@ object MethodLifting extends TransformationPhase { e } - ccd.methods.find( _.id == fdId).map { m => + ccd.methods.find(_.id == fdId).map { m => // Ancestor's method is a method in the case class - val subPatts = ct.fields map (f => WildcardPattern(Some(fBinders(f.id)))) + val subPatts = ccd.fields map (f => WildcardPattern(Some(fBinders(f.id)))) val patt = CaseClassPattern(Some(binder), ct, subPatts) val newE = simplePreTransform(subst)(breakDown(m.fullBody)) val cse = SimpleCase(patt, newE).setPos(newE) (List(cse), true) - } orElse ccd.fields.find( _.id == fdId).map { f => + } orElse ccd.fields.find(_.id == fdId).map { f => // Ancestor's method is a case class argument in the case class - val subPatts = ct.fields map (fld => + val subPatts = ccd.fields map (fld => if (fld.id == f.id) WildcardPattern(Some(fBinders(f.id))) else diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala index ed8e75fbf79bcd8ea5016863ed845134ff59bc62..cc54bdd6c1b904c9aa4c419460ef011e674e5f53 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBTarget.scala @@ -630,6 +630,84 @@ trait SMTLIBTarget extends Interruptible { /* Translate an SMTLIB term back to a Leon Expr */ protected def fromSMT(t: Term, otpe: Option[TypeTree] = None)(implicit lets: Map[SSymbol, Term], letDefs: Map[SSymbol, DefineFun]): Expr = { + object EQ { + def unapply(t: Term): Option[(Term, Term)] = t match { + case Core.Equals(e1, e2) => Some((e1, e2)) + case FunctionApplication(f, Seq(e1, e2)) if f.toString == "=" => Some((e1, e2)) + case _ => None + } + } + + object AND { + def unapply(t: Term): Option[Seq[Term]] = t match { + case Core.And(e1, e2) => Some(Seq(e1, e2)) + case FunctionApplication(SimpleSymbol(SSymbol("and")), args) => Some(args) + case _ => None + } + def apply(ts: Seq[Term]): Term = ts match { + case Seq() => throw new IllegalArgumentException + case Seq(t) => t + case _ => FunctionApplication(SimpleSymbol(SSymbol("and")), ts) + } + } + + object Num { + def unapply(t: Term): Option[BigInt] = t match { + case SNumeral(n) => Some(n) + case FunctionApplication(f, Seq(SNumeral(n))) if f.toString == "-" => Some(-n) + case _ => None + } + } + + def extractLambda(n: BigInt, ft: FunctionType): Expr = { + val FunctionType(from, to) = ft + lambdas.getB(ft) match { + case None => simplestValue(ft) + case Some(dynLambda) => letDefs.get(dynLambda) match { + case None => simplestValue(ft) + case Some(DefineFun(SMTFunDef(a, SortedVar(dispatcher, dkind) +: args, rkind, body))) => + val d = symbolToQualifiedId(dispatcher) + def dispatch(t: Term): Term = t match { + case Core.ITE(EQ(di, Num(ni)), thenn, elze) if di == d => + if (ni == n) thenn else dispatch(elze) + case Core.ITE(AND(EQ(di, Num(ni)) +: rest), thenn, elze) if di == d => + if (ni == n) Core.ITE(AND(rest), thenn, dispatch(elze)) else dispatch(elze) + case _ => t + } + + def extract(t: Term): Expr = { + def recCond(term: Term, index: Int): Seq[Expr] = term match { + case AND(es) => + es.foldLeft(Seq.empty[Expr]) { case (seq, e) => seq ++ recCond(e, index + seq.size) } + case EQ(e1, e2) => + recCond(e2, index) + case _ => Seq(fromSMT(term, from(index))) + } + + def recCases(term: Term, matchers: Seq[Expr]): Seq[(Seq[Expr], Expr)] = term match { + case Core.ITE(cond, thenn, elze) => + val cs = recCond(cond, matchers.size) + recCases(thenn, matchers ++ cs) ++ recCases(elze, matchers) + case AND(es) if to == BooleanType => + Seq((matchers ++ recCond(term, matchers.size)) -> BooleanLiteral(true)) + case EQ(e1, e2) if to == BooleanType => + Seq((matchers ++ recCond(term, matchers.size)) -> BooleanLiteral(true)) + case _ => Seq(matchers -> fromSMT(term, to)) + } + + val cases = recCases(t, Seq.empty) + val (default, rest) = cases.partition(_._1.isEmpty) + val leonDefault = if (default.isEmpty && to == BooleanType) BooleanLiteral(false) else default.head._2 + PartialLambda(rest.filter(_._1.size == from.size), Some(leonDefault), ft) + } + + val lambdaTerm = dispatch(body) + val lambda = extract(lambdaTerm) + lambda + } + } + } + // Use as much information as there is, if there is an expected type, great, but it might not always be there (t, otpe) match { case (_, Some(UnitType)) => @@ -663,79 +741,8 @@ trait SMTLIBTarget extends Interruptible { } } - case (SNumeral(n), Some(ft @ FunctionType(from, to))) => - val dynLambda = lambdas.toB(ft) - val DefineFun(SMTFunDef(a, SortedVar(dispatcher, dkind) +: args, rkind, body)) = letDefs(dynLambda) - - object EQ { - def unapply(t: Term): Option[(Term, Term)] = t match { - case Core.Equals(e1, e2) => Some((e1, e2)) - case FunctionApplication(f, Seq(e1, e2)) if f.toString == "=" => Some((e1, e2)) - case _ => None - } - } - - object AND { - def unapply(t: Term): Option[Seq[Term]] = t match { - case Core.And(e1, e2) => Some(Seq(e1, e2)) - case FunctionApplication(SimpleSymbol(SSymbol("and")), args) => Some(args) - case _ => None - } - def apply(ts: Seq[Term]): Term = ts match { - case Seq() => throw new IllegalArgumentException - case Seq(t) => t - case _ => FunctionApplication(SimpleSymbol(SSymbol("and")), ts) - } - } - - object Num { - def unapply(t: Term): Option[BigInt] = t match { - case SNumeral(n) => Some(n) - case FunctionApplication(f, Seq(SNumeral(n))) if f.toString == "-" => Some(-n) - case _ => None - } - } - - val d = symbolToQualifiedId(dispatcher) - def dispatch(t: Term): Term = t match { - case Core.ITE(EQ(di, Num(ni)), thenn, elze) if di == d => - if (ni == n) thenn else dispatch(elze) - case Core.ITE(AND(EQ(di, Num(ni)) +: rest), thenn, elze) if di == d => - if (ni == n) Core.ITE(AND(rest), thenn, dispatch(elze)) else dispatch(elze) - case _ => t - } - - def extract(t: Term): Expr = { - def recCond(term: Term, index: Int): Seq[Expr] = term match { - case AND(es) => - es.foldLeft(Seq.empty[Expr]) { case (seq, e) => seq ++ recCond(e, index + seq.size) } - case EQ(e1, e2) => - recCond(e2, index) - case _ => Seq(fromSMT(term, from(index))) - } - - def recCases(term: Term, matchers: Seq[Expr]): Seq[(Seq[Expr], Expr)] = term match { - case Core.ITE(cond, thenn, elze) => - val cs = recCond(cond, matchers.size) - recCases(thenn, matchers ++ cs) ++ recCases(elze, matchers) - case AND(es) if to == BooleanType => - Seq((matchers ++ recCond(term, matchers.size)) -> BooleanLiteral(true)) - case EQ(e1, e2) if to == BooleanType => - Seq((matchers ++ recCond(term, matchers.size)) -> BooleanLiteral(true)) - case _ => Seq(matchers -> fromSMT(term, to)) - } - - val cases = recCases(t, Seq.empty) - val (default, rest) = cases.partition(_._1.isEmpty) - val leonDefault = if (default.isEmpty && to == BooleanType) BooleanLiteral(false) else default.head._2 - - assert(rest.forall(_._1.size == from.size)) - PartialLambda(rest, Some(leonDefault), ft) - } - - val lambdaTerm = dispatch(body) - val lambda = extract(lambdaTerm) - lambda + case (Num(n), Some(ft: FunctionType)) => + extractLambda(n, ft) case (SNumeral(n), Some(RealType)) => FractionalLiteral(n, 1) diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala index 334f9f0ad12820282da3569d028253662480104e..3d4a06a838a5057a693d85754bb5113e1ce7d0ae 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBZ3Target.scala @@ -76,11 +76,6 @@ trait SMTLIBZ3Target extends SMTLIBTarget { val n = s.name.split("!").toList.last GenericValue(tp, n.toInt) - // XXX: (NV) Z3 doesn't seem to produce models for uninterpreted functions that - // don't impact satisfiability... - case (SNumeral(n), Some(ft: FunctionType)) if !letDefs.isDefinedAt(lambdas.toB(ft)) => - purescala.ExprOps.simplestValue(ft) - case (QualifiedIdentifier(ExtendedIdentifier(SSymbol("as-array"), k: SSymbol), _), Some(tpe)) => if (letDefs contains k) { // Need to recover value form function model diff --git a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala index d7a3fd8fa22ac9ebeed9529aba1c17d00ca39e5a..bc6aaf207c071e694f6154fca23d128479aa50d0 100644 --- a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala @@ -657,9 +657,9 @@ trait AbstractZ3Solver extends Solver { } case ft @ FunctionType(fts, tt) => lambdas.getB(ft) match { - case None => throw new IllegalArgumentException + case None => simplestValue(ft) case Some(decl) => model.getModelFuncInterpretations.find(_._1 == decl) match { - case None => throw new IllegalArgumentException + case None => simplestValue(ft) case Some((_, mapping, elseValue)) => val leonElseValue = rec(elseValue, tt) PartialLambda(mapping.flatMap { case (z3Args, z3Result) => @@ -738,7 +738,8 @@ trait AbstractZ3Solver extends Solver { case _ => unsound(t, "unexpected AST") } } - rec(tree, tpe) + + rec(tree, normalizeType(tpe)) } protected[leon] def softFromZ3Formula(model: Z3Model, tree: Z3AST, tpe: TypeTree) : Option[Expr] = { diff --git a/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala b/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala index 227dd691c29734e0dd85feb179f40fac15ceea7a..6b2f9e8585a98ab9677aa5f1c439b2a1afa136ef 100644 --- a/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala +++ b/src/main/scala/leon/synthesis/rules/EquivalentInputs.scala @@ -24,7 +24,7 @@ case object EquivalentInputs extends NormalizingRule("EquivalentInputs") { val ccSubsts = for (IsInstanceOf(s, cct: CaseClassType) <- instanceOfs) yield { - val fieldsVals = (for (f <- cct.fields) yield { + val fieldsVals = (for (f <- cct.classDef.fields) yield { val id = f.id clauses.find { diff --git a/src/main/scala/leon/termination/Processor.scala b/src/main/scala/leon/termination/Processor.scala index bbd87b999e653a943598915e7d2e38a9aba20bff..3f7be09f145d2768ff00f8573f078d6e90bc1b2d 100644 --- a/src/main/scala/leon/termination/Processor.scala +++ b/src/main/scala/leon/termination/Processor.scala @@ -34,7 +34,7 @@ trait Solvable extends Processor { val sizeUnit : UnitDef = UnitDef(FreshIdentifier("$size"),Seq(sizeModule)) val newProgram : Program = program.copy( units = sizeUnit :: program.units) - SolverFactory.getFromSettings(context, newProgram).withTimeout(1000.millisecond) + SolverFactory.getFromSettings(context, newProgram).withTimeout(10.seconds) } type Solution = (Option[Boolean], Map[Identifier, Expr]) diff --git a/src/test/resources/regression/verification/purescala/invalid/Unapply1.scala b/src/test/resources/regression/verification/purescala/invalid/Unapply1.scala index 20ff95383b4f7042b0eba32fcc4e5c8b0cea3680..674ca7c69fa29333755d8908f707ae9931d20bf2 100644 --- a/src/test/resources/regression/verification/purescala/invalid/Unapply1.scala +++ b/src/test/resources/regression/verification/purescala/invalid/Unapply1.scala @@ -6,10 +6,14 @@ object Unap1 { } object Unapply1 { + + sealed abstract class Bool + case class True() extends Bool + case class False() extends Bool - def bar: Boolean = { (42, false, ()) match { - case Unap1(_, b) if b => b + def bar: Bool = { (42, False().asInstanceOf[Bool], ()) match { + case Unap1(_, b) if b == True() => b case Unap1((), b) => b - }} ensuring { res => res } + }} ensuring { res => res == True() } } diff --git a/src/test/resources/regression/verification/purescala/invalid/Unapply2.scala b/src/test/resources/regression/verification/purescala/invalid/Unapply2.scala index ae4167c20026f0e926494af6cf3a11f25e9399e5..7efd6e220fd1c7fcd08fbaa5b7016bf6801e151f 100644 --- a/src/test/resources/regression/verification/purescala/invalid/Unapply2.scala +++ b/src/test/resources/regression/verification/purescala/invalid/Unapply2.scala @@ -5,7 +5,12 @@ object Unap2 { } object Unapply { - def bar: Boolean = { (42, false, ()) match { - case Unap2(_, b) if b => b - }} ensuring { res => res } + + sealed abstract class Bool + case class True() extends Bool + case class False() extends Bool + + def bar: Bool = { (42, False().asInstanceOf[Bool], ()) match { + case Unap2(_, b) if b == True() => b + }} ensuring { res => res == True() } } diff --git a/src/test/resources/regression/verification/purescala/valid/Unapply.scala b/src/test/resources/regression/verification/purescala/valid/Unapply.scala index 941b1f370d740204e8fa850a9d5e5a787b1c401e..1885837d990c32185a42c1232a53de48c3935340 100644 --- a/src/test/resources/regression/verification/purescala/valid/Unapply.scala +++ b/src/test/resources/regression/verification/purescala/valid/Unapply.scala @@ -5,8 +5,18 @@ object Unap { } object Unapply { - def bar: Boolean = { (42, true, ()) match { - case Unap(_, b) if b => b - case Unap((), b) => !b - }} ensuring { res => res } + + sealed abstract class Bool + case class True() extends Bool + case class False() extends Bool + + def not(b: Bool): Bool = b match { + case True() => False() + case False() => True() + } + + def bar: Bool = { (42, True().asInstanceOf[Bool], ()) match { + case Unap(_, b) if b == True() => b + case Unap((), b) => not(b) + }} ensuring { res => res == True() } }