diff --git a/library/lang/Rational.scala b/library/lang/Rational.scala index f4a10215b61032a73f20107c5aeddbac841e66bc..1bcb679a0d4abf6b168117b2af819fa41e027315 100644 --- a/library/lang/Rational.scala +++ b/library/lang/Rational.scala @@ -9,72 +9,65 @@ import scala.language.implicitConversions @library case class Rational(numerator: BigInt, denominator: BigInt) { + require(this.isRational) + def +(that: Rational): Rational = { - require(this.isRational && that.isRational) Rational(this.numerator*that.denominator + that.numerator*this.denominator, this.denominator*that.denominator) } ensuring(res => res.isRational) def -(that: Rational): Rational = { - require(this.isRational && that.isRational) Rational(this.numerator*that.denominator - that.numerator*this.denominator, this.denominator*that.denominator) } ensuring(res => res.isRational) def unary_- : Rational = { - require(this.isRational) Rational(-this.numerator, this.denominator) } ensuring(res => res.isRational) def *(that: Rational): Rational = { - require(this.isRational && that.isRational) Rational(this.numerator*that.numerator, this.denominator*that.denominator) } ensuring(res => res.isRational) def /(that: Rational): Rational = { - require(this.isRational && that.isRational && that.nonZero) + require(that.nonZero) val newNumerator = this.numerator*that.denominator val newDenominator = this.denominator*that.numerator normalize(newNumerator, newDenominator) } ensuring(res => res.isRational) def reciprocal: Rational = { - require(this.isRational && this.nonZero) + require(this.nonZero) normalize(this.denominator, this.numerator) } ensuring(res => res.isRational) def ~(that: Rational): Boolean = { - require(this.isRational && that.isRational) this.numerator*that.denominator == that.numerator*this.denominator } def <(that: Rational): Boolean = { - require(this.isRational && that.isRational) this.numerator*that.denominator < that.numerator*this.denominator } def <=(that: Rational): Boolean = { - require(this.isRational && that.isRational) this.numerator*that.denominator <= that.numerator*this.denominator } def >(that: Rational): Boolean = { - require(this.isRational && that.isRational) this.numerator*that.denominator > that.numerator*this.denominator } def >=(that: Rational): Boolean = { - require(this.isRational && that.isRational) this.numerator*that.denominator >= that.numerator*this.denominator } def nonZero: Boolean = { - require(this.isRational) numerator != 0 } - def isRational: Boolean = denominator > 0 + private def isRational: Boolean = denominator > 0 private def normalize(num: BigInt, den: BigInt): Rational = { + require(den != 0) if(den < 0) Rational(-num, -den) else @@ -88,4 +81,5 @@ object Rational { implicit def bigIntToRat(n: BigInt): Rational = Rational(n, 1) def apply(n: BigInt): Rational = Rational(n, 1) + } diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 041caba78a7d6eebe9cd6f81d3a5f1f56f3cd3ba..55ab79460e296e2b37c026261e29621488f6fa46 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -313,19 +313,45 @@ trait CodeExtraction extends ASTExtractors { } private def fillLeonUnit(u: ScalaUnit): Unit = { + def extractClassMembers(sym: Symbol, tpl: Template): Unit = { + for (t <- tpl.body if !t.isEmpty) { + extractFunOrMethodBody(Some(sym), t) + } + + classToInvariants.get(sym).foreach { bodies => + val fd = new FunDef(invId, Seq.empty, Seq.empty, BooleanType) + fd.addFlag(IsADTInvariant) + + val cd = classesToClasses(sym) + cd.registerMethod(fd) + cd.addFlag(IsADTInvariant) + val ctparams = sym.tpe match { + case TypeRef(_, _, tps) => + extractTypeParams(tps).map(_._1) + case _ => + Nil + } + + val tparamsMap = (ctparams zip cd.tparams.map(_.tp)).toMap + val dctx = DefContext(tparamsMap) + + val body = andJoin(bodies.toSeq.filter(_ != EmptyTree).map { + body => flattenBlocks(extractTreeOrNoTree(body)(dctx)) + }) + + fd.fullBody = body + } + } + for (t <- u.defs) t match { case t if isIgnored(t.symbol) => // ignore case ExAbstractClass(_, sym, tpl) => - for (t <- tpl.body if !t.isEmpty) { - extractFunOrMethodBody(Some(sym), t) - } + extractClassMembers(sym, tpl) case ExCaseClass(_, sym, _, tpl) => - for (t <- tpl.body if !t.isEmpty) { - extractFunOrMethodBody(Some(sym), t) - } + extractClassMembers(sym, tpl) case ExObjectDef(n, templ) => for (t <- templ.body if !t.isEmpty) t match { @@ -334,14 +360,10 @@ trait CodeExtraction extends ASTExtractors { None case ExAbstractClass(_, sym, tpl) => - for (t <- tpl.body if !t.isEmpty) { - extractFunOrMethodBody(Some(sym), t) - } + extractClassMembers(sym, tpl) case ExCaseClass(_, sym, _, tpl) => - for (t <- tpl.body if !t.isEmpty) { - extractFunOrMethodBody(Some(sym), t) - } + extractClassMembers(sym, tpl) case t => extractFunOrMethodBody(None, t) @@ -442,6 +464,7 @@ trait CodeExtraction extends ASTExtractors { private var isMethod = Set[Symbol]() private var methodToClass = Map[FunDef, LeonClassDef]() + private var classToInvariants = Map[Symbol, Set[Tree]]() /** * For the function in $defs with name $owner, find its parameter with index $index, @@ -543,8 +566,53 @@ trait CodeExtraction extends ASTExtractors { if (tpe != id.getType) println(tpe, id.getType) LeonValDef(id.setPos(t.pos)).setPos(t.pos) } + //println(s"Fields of $sym") ccd.setFields(fields) + + // checks whether this type definition could lead to an infinite type + def computeChains(tpe: LeonType): Map[TypeParameterDef, Set[LeonClassDef]] = { + var seen: Set[LeonClassDef] = Set.empty + var chains: Map[TypeParameterDef, Set[LeonClassDef]] = Map.empty + + def rec(tpe: LeonType): Set[LeonClassDef] = tpe match { + case ct: ClassType => + val root = ct.classDef.root + if (!seen(ct.classDef.root)) { + seen += ct.classDef.root + for (cct <- ct.root.knownCCDescendants; + (tp, tpe) <- cct.classDef.tparams zip cct.tps) { + val relevant = rec(tpe) + chains += tp -> (chains.getOrElse(tp, Set.empty) ++ relevant) + for (cd <- relevant; vd <- cd.fields) { + rec(vd.getType) + } + } + } + Set(root) + + case Types.NAryType(tpes, _) => + tpes.flatMap(rec).toSet + } + + rec(tpe) + chains + } + + val chains = computeChains(ccd.typed) + + def check(tp: TypeParameterDef, seen: Set[LeonClassDef]): Unit = chains.get(tp) match { + case Some(classDefs) => + if ((seen intersect classDefs).nonEmpty) { + outOfSubsetError(sym.pos, "Infinite types are not allowed") + } else { + for (cd <- classDefs; tp <- cd.tparams) check(tp, seen + cd) + } + case None => + } + + for (tp <- ccd.tparams) check(tp, Set.empty) + case _ => } @@ -568,6 +636,9 @@ trait CodeExtraction extends ASTExtractors { cd.registerMethod(fd) + case ExRequiredExpression(body) => + classToInvariants += sym -> (classToInvariants.getOrElse(sym, Set.empty) + body) + // Default values for parameters case t@ ExDefaultValueFunction(fsym, _, _, _, owner, index, _) => isMethod += fsym @@ -621,6 +692,8 @@ trait CodeExtraction extends ASTExtractors { } } + private val invId = FreshIdentifier("inv", BooleanType) + private var isLazy = Set[LeonValDef]() private var defsToDefs = Map[Symbol, FunDef]() diff --git a/src/main/scala/leon/purescala/CheckADTFieldsTypes.scala b/src/main/scala/leon/purescala/CheckADTFieldsTypes.scala deleted file mode 100644 index d4583e55311cfaf843983a3f8af70ac46f7b3675..0000000000000000000000000000000000000000 --- a/src/main/scala/leon/purescala/CheckADTFieldsTypes.scala +++ /dev/null @@ -1,29 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package purescala - -import Definitions._ -import TypeOps._ - -object CheckADTFieldsTypes extends UnitPhase[Program] { - - val name = "ADT Fields" - val description = "Check that fields of ADTs are hierarchy roots" - - def apply(ctx: LeonContext, program: Program) = { - program.definedClasses.foreach { - case ccd: CaseClassDef => - for(vd <- ccd.fields) { - val tpe = vd.getType - if (bestRealType(tpe) != tpe) { - ctx.reporter.warning(ccd.getPos, "Definition of "+ccd.id.asString(ctx)+" has a field of a sub-type ("+vd.asString(ctx)+"): " + - "this type is not supported as-is by solvers and will be up-cast. " + - "This may cause issues such as crashes.") - } - } - case _ => - } - } - -} diff --git a/src/main/scala/leon/purescala/Constructors.scala b/src/main/scala/leon/purescala/Constructors.scala index 3437d34efeef0d47a37eded5d85cb167d8ee2b6b..95218cb2857a97c6834ac039f20c426a0736be8f 100644 --- a/src/main/scala/leon/purescala/Constructors.scala +++ b/src/main/scala/leon/purescala/Constructors.scala @@ -131,7 +131,7 @@ object Constructors { */ def caseClassSelector(classType: CaseClassType, caseClass: Expr, selector: Identifier): Expr = { caseClass match { - case CaseClass(ct, fields) if ct.classDef == classType.classDef => + case CaseClass(ct, fields) if ct.classDef == classType.classDef && !ct.classDef.hasInvariant => fields(ct.classDef.selectorID2Index(selector)) case _ => CaseClassSelector(classType, caseClass, selector) diff --git a/src/main/scala/leon/purescala/DefOps.scala b/src/main/scala/leon/purescala/DefOps.scala index 78c2b93edecf113c717a625a3c974c8dcf43e474..856cdb37a21f3b73d07b03aa21ca62affd51971d 100644 --- a/src/main/scala/leon/purescala/DefOps.scala +++ b/src/main/scala/leon/purescala/DefOps.scala @@ -325,7 +325,7 @@ object DefOps { fdMapCache(fd).getOrElse(fd) } } - + val newP = p.copy(units = for (u <- p.units) yield { u.copy( defs = u.defs.map { @@ -636,6 +636,7 @@ object DefOps { } ) }) + if (!found) { println(s"addDefs could not find anchor definition! Not found: $after") p.definedFunctions.filter(f => f.id.name == after.id.name).map(fd => fd.id.name + " : " + fd) match { @@ -644,9 +645,10 @@ object DefOps { } println(Thread.currentThread().getStackTrace.map(_.toString).take(10).mkString("\n")) } + res } - + def addFunDefs(p: Program, fds: Traversable[FunDef], after: FunDef): Program = addDefs(p, fds, after) def addClassDefs(p: Program, fds: Traversable[ClassDef], after: ClassDef): Program = addDefs(p, fds, after) diff --git a/src/main/scala/leon/purescala/Definitions.scala b/src/main/scala/leon/purescala/Definitions.scala index dfc78d4c547ddf40fb6c2a1e39bdab40611dcbd1..733eaf124dbb6dd8499c81347f7fc864c23ca453 100644 --- a/src/main/scala/leon/purescala/Definitions.scala +++ b/src/main/scala/leon/purescala/Definitions.scala @@ -182,7 +182,6 @@ object Definitions { lazy val singleCaseClasses : Seq[CaseClassDef] = defs.collect { case c @ CaseClassDef(_, _, None, _) => c } - } // A class that represents flags that annotate a FunDef with different attributes @@ -217,7 +216,8 @@ object Definitions { case object IsSynthetic extends FunctionFlag // Is inlined case object IsInlined extends FunctionFlag - + // Is an ADT invariant method + case object IsADTInvariant extends FunctionFlag with ClassFlag /** Useful because case classes and classes are somewhat unified in some * patterns (of pattern-matching, that is) */ @@ -268,6 +268,15 @@ object Definitions { def flags = _flags + private var _invariant: Option[FunDef] = None + + def invariant = _invariant + def hasInvariant = flags contains IsADTInvariant + def setInvariant(fd: FunDef): Unit = { + addFlag(IsADTInvariant) + _invariant = Some(fd) + } + def annotations: Set[String] = extAnnotations.keySet def extAnnotations: Map[String, Seq[Option[Any]]] = flags.collect { case Annotation(s, args) => s -> args }.toMap @@ -289,6 +298,23 @@ object Definitions { ccd } + def isInductive: Boolean = { + def induct(tpe: TypeTree, seen: Set[ClassDef]): Boolean = tpe match { + case ct: ClassType => + val root = ct.classDef.root + seen(root) || ct.fields.forall(vd => induct(vd.getType, seen + root)) + case TupleType(tpes) => + tpes.forall(tpe => induct(tpe, seen)) + case _ => true + } + + if (this == root && !this.isAbstract) false + else if (this != root) root.isInductive + else knownCCDescendants.forall { ccd => + ccd.fields.forall(vd => induct(vd.getType, Set(root))) + } + } + val isAbstract: Boolean val isCaseObject: Boolean @@ -474,6 +500,7 @@ object Definitions { def canBeField = canBeLazyField || canBeStrictField def isRealFunction = !canBeField def isSynthetic = flags contains IsSynthetic + def isInvariant = flags contains IsADTInvariant def methodOwner = flags collectFirst { case IsMethod(cd) => cd } /* Wrapping in TypedFunDef */ diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index 3c597d26e76976d44a939754fdea8c1caf45d29e..e8e87c7976a907cfca16ddc79f0cd758fe7d8402 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -271,7 +271,7 @@ object Expressions { * @param rhs The expression to the right of `=>` * @see [[Expressions.MatchExpr]] */ - case class MatchCase(pattern : Pattern, optGuard : Option[Expr], rhs: Expr) extends Tree { + case class MatchCase(pattern: Pattern, optGuard: Option[Expr], rhs: Expr) extends Tree { def expressions: Seq[Expr] = optGuard.toList :+ rhs } diff --git a/src/main/scala/leon/purescala/MethodLifting.scala b/src/main/scala/leon/purescala/MethodLifting.scala index 2014739eaa3fba0c84ce10685087262e7e227872..0e9e11d171a15e2b0b5fb77d0b3f0e65d44be38b 100644 --- a/src/main/scala/leon/purescala/MethodLifting.scala +++ b/src/main/scala/leon/purescala/MethodLifting.scala @@ -251,6 +251,10 @@ object MethodLifting extends TransformationPhase { ) } + if (cd.methods.exists(md => md.id == fd.id && md.isInvariant)) { + cd.setInvariant(nfd) + } + mdToFds += fd -> nfd fdsOf += cd.id.name -> (fdsOf.getOrElse(cd.id.name, Set()) + nfd) } diff --git a/src/main/scala/leon/purescala/Quantification.scala b/src/main/scala/leon/purescala/Quantification.scala index 297ee1fc95105dc55509aaf74439b3640c0c4ca9..21608b0facdb2d2318320a0e58974d23f56dc491 100644 --- a/src/main/scala/leon/purescala/Quantification.scala +++ b/src/main/scala/leon/purescala/Quantification.scala @@ -87,7 +87,7 @@ object Quantification { object QuantificationMatcher { private def flatApplication(expr: Expr): Option[(Expr, Seq[Expr])] = expr match { - case Application(fi: FunctionInvocation, args) => Some((fi, args)) + case Application(fi: FunctionInvocation, args) => None case Application(caller: Application, args) => flatApplication(caller) match { case Some((c, prevArgs)) => Some((c, prevArgs ++ args)) case None => None diff --git a/src/main/scala/leon/purescala/Types.scala b/src/main/scala/leon/purescala/Types.scala index 9ec0e4b41f33aa0895ff160a5379d16a7cb88d87..6f2518d549be6b1d439ff9cb08594558d02449d5 100644 --- a/src/main/scala/leon/purescala/Types.scala +++ b/src/main/scala/leon/purescala/Types.scala @@ -114,6 +114,8 @@ object Types { } } + def invariant = classDef.invariant.map(_.typed(tps)) + def knownDescendants = classDef.knownDescendants.map( _.typed(tps) ) def knownCCDescendants: Seq[CaseClassType] = classDef.knownCCDescendants.map( _.typed(tps) ) @@ -128,8 +130,8 @@ object Types { case t => throw LeonFatalError("Unexpected translated parent type: "+t) } } - } + case class AbstractClassType(classDef: AbstractClassDef, tps: Seq[TypeTree]) extends ClassType case class CaseClassType(classDef: CaseClassDef, tps: Seq[TypeTree]) extends ClassType diff --git a/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala b/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala index 8c57de44eeb0978384dd5ae6076ce24c1b3ea498..95a8cdd67722660f666a9256a0020fab9d93b5d2 100644 --- a/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala +++ b/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala @@ -9,6 +9,7 @@ import purescala.Common._ import purescala.Definitions._ import purescala.Quantification._ import purescala.Constructors._ +import purescala.Extractors._ import purescala.Expressions._ import purescala.ExprOps._ import purescala.Types._ @@ -268,6 +269,69 @@ trait AbstractUnrollingSolver[T] private def getTotalModel: Model = { val wrapped = solverGetModel + def checkForalls(quantified: Set[Identifier], body: Expr): Option[String] = { + val matchers = collect[(Expr, Seq[Expr])] { + case QuantificationMatcher(e, args) => Set(e -> args) + case _ => Set.empty + } (body) + + if (matchers.isEmpty) + return Some("No matchers found.") + + val matcherToQuants = matchers.foldLeft(Map.empty[Expr, Set[Identifier]]) { + case (acc, (m, args)) => acc + (m -> (acc.getOrElse(m, Set.empty) ++ args.flatMap { + case Variable(id) if quantified(id) => Set(id) + case _ => Set.empty[Identifier] + })) + } + + val bijectiveMappings = matcherToQuants.filter(_._2.nonEmpty).groupBy(_._2) + if (bijectiveMappings.size > 1) + return Some("Non-bijective mapping for symbol " + bijectiveMappings.head._2.head._1.asString) + + def quantifiedArg(e: Expr): Boolean = e match { + case Variable(id) => quantified(id) + case QuantificationMatcher(_, args) => args.forall(quantifiedArg) + case _ => false + } + + postTraversal(m => m match { + case QuantificationMatcher(_, args) => + val qArgs = args.filter(quantifiedArg) + + if (qArgs.nonEmpty && qArgs.size < args.size) + return Some("Mixed ground and quantified arguments in " + m.asString) + + case Operator(es, _) if es.collect { case Variable(id) if quantified(id) => id }.nonEmpty => + return Some("Invalid operation on quantifiers " + m.asString) + + case (_: Equals) | (_: And) | (_: Or) | (_: Implies) => // OK + + case Operator(es, _) if (es.flatMap(variablesOf).toSet & quantified).nonEmpty => + return Some("Unandled implications from operation " + m.asString) + + case _ => + }) (body) + + body match { + case Variable(id) if quantified(id) => + Some("Unexpected free quantifier " + id.asString) + case _ => None + } + } + + val issues: Iterable[(Seq[Identifier], Expr, String)] = for { + q <- templateGenerator.manager.quantifications.view + if wrapped.eval(q.holds, BooleanType) == Some(BooleanLiteral(true)) + msg <- checkForalls(q.quantifiers.map(_._1).toSet, q.body) + } yield (q.quantifiers.map(_._1), q.body, msg) + + if (issues.nonEmpty) { + val (quantifiers, body, msg) = issues.head + reporter.warning("Model soundness not guaranteed for \u2200" + + quantifiers.map(_.asString).mkString(",") + ". " + body.asString+" :\n => " + msg) + } + val typeInsts = templateGenerator.manager.typeInstantiations val partialInsts = templateGenerator.manager.partialInstantiations diff --git a/src/main/scala/leon/solvers/templates/DatatypeManager.scala b/src/main/scala/leon/solvers/templates/DatatypeManager.scala new file mode 100644 index 0000000000000000000000000000000000000000..dcfa67e8343f88fb53dfa0a18212e0a2aa383016 --- /dev/null +++ b/src/main/scala/leon/solvers/templates/DatatypeManager.scala @@ -0,0 +1,219 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package solvers +package templates + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.Constructors._ +import purescala.Extractors._ +import purescala.ExprOps._ +import purescala.Types._ +import purescala.TypeOps.bestRealType + +import utils._ +import utils.SeqUtils._ +import Instantiation._ +import Template._ + +import scala.collection.mutable.{Map => MutableMap, Set => MutableSet} + +case class FreshFunction(expr: Expr) extends Expr with Extractable { + val getType = BooleanType + val extract = Some(Seq(expr), (exprs: Seq[Expr]) => FreshFunction(exprs.head)) +} + +object DatatypeTemplate { + + def apply[T]( + encoder: TemplateEncoder[T], + manager: DatatypeManager[T], + tpe: TypeTree + ) : DatatypeTemplate[T] = { + val id = FreshIdentifier("x", tpe, true) + val expr = manager.typeUnroller(Variable(id)) + + val pathVar = FreshIdentifier("b", BooleanType, true) + + var condVars = Map[Identifier, T]() + var condTree = Map[Identifier, Set[Identifier]](pathVar -> Set.empty).withDefaultValue(Set.empty) + def storeCond(pathVar: Identifier, id: Identifier): Unit = { + condVars += id -> encoder.encodeId(id) + condTree += pathVar -> (condTree(pathVar) + id) + } + + var guardedExprs = Map[Identifier, Seq[Expr]]() + def storeGuarded(pathVar: Identifier, expr: Expr): Unit = { + val prev = guardedExprs.getOrElse(pathVar, Nil) + guardedExprs += pathVar -> (expr +: prev) + } + + def requireDecomposition(e: Expr): Boolean = exists { + case _: FunctionInvocation => true + case _ => false + } (e) + + def rec(pathVar: Identifier, expr: Expr): Unit = expr match { + case i @ IfExpr(cond, thenn, elze) if requireDecomposition(i) => + val newBool1: Identifier = FreshIdentifier("b", BooleanType, true) + val newBool2: Identifier = FreshIdentifier("b", BooleanType, true) + + storeCond(pathVar, newBool1) + storeCond(pathVar, newBool2) + + storeGuarded(pathVar, or(Variable(newBool1), Variable(newBool2))) + storeGuarded(pathVar, or(not(Variable(newBool1)), not(Variable(newBool2)))) + storeGuarded(pathVar, Equals(Variable(newBool1), cond)) + + rec(newBool1, thenn) + rec(newBool2, elze) + + case a @ And(es) if requireDecomposition(a) => + val partitions = groupWhile(es)(!requireDecomposition(_)) + for (e <- partitions.map(andJoin)) rec(pathVar, e) + + case _ => + storeGuarded(pathVar, expr) + } + + rec(pathVar, expr) + + val (idT, pathVarT) = (encoder.encodeId(id), encoder.encodeId(pathVar)) + val (clauses, blockers, _, functions, _, _) = Template.encode(encoder, + pathVar -> pathVarT, Seq(id -> idT), condVars, Map.empty, guardedExprs, Seq.empty, Seq.empty) + + new DatatypeTemplate[T](encoder, manager, + pathVar -> pathVarT, idT, condVars, condTree, clauses, blockers, functions) + } +} + +class DatatypeTemplate[T] private ( + val encoder: TemplateEncoder[T], + val manager: DatatypeManager[T], + val pathVar: (Identifier, T), + val argument: T, + val condVars: Map[Identifier, T], + val condTree: Map[Identifier, Set[Identifier]], + val clauses: Seq[T], + val blockers: Map[T, Set[TemplateCallInfo[T]]], + val functions: Set[(T, FunctionType, T)]) extends Template[T] { + + val args = Seq(argument) + val exprVars = Map.empty[Identifier, T] + val applications = Map.empty[T, Set[App[T]]] + val lambdas = Seq.empty[LambdaTemplate[T]] + val matchers = Map.empty[T, Set[Matcher[T]]] + val quantifications = Seq.empty[QuantificationTemplate[T]] + + def instantiate(blocker: T, arg: T): Instantiation[T] = instantiate(blocker, Seq(Left(arg))) +} + +class DatatypeManager[T](encoder: TemplateEncoder[T]) extends TemplateManager(encoder) { + + private val classCache: MutableMap[ClassType, FunDef] = MutableMap.empty + + private def classTypeUnroller(ct: ClassType): FunDef = classCache.get(ct) match { + case Some(fd) => fd + case None => + val param = ValDef(FreshIdentifier("x", ct)) + val fd = new FunDef(FreshIdentifier("unroll"+ct.classDef.id), Seq.empty, Seq(param), BooleanType) + classCache += ct -> fd + + val matchers = ct match { + case (act: AbstractClassType) => act.knownCCDescendants + case (cct: CaseClassType) => Seq(cct) + } + + fd.body = Some(MatchExpr(param.toVariable, matchers.map { cct => + val pattern = CaseClassPattern(None, cct, cct.fields.map(vd => WildcardPattern(Some(vd.id)))) + val expr = andJoin(cct.fields.map(vd => typeUnroller(Variable(vd.id)))) + MatchCase(pattern, None, expr) + })) + + fd + } + + private val requireChecking: MutableSet[ClassType] = MutableSet.empty + private val requireCache: MutableMap[TypeTree, Boolean] = MutableMap.empty + + private def requireTypeUnrolling(tpe: TypeTree): Boolean = requireCache.get(tpe) match { + case Some(res) => res + case None => + val res = tpe match { + case ft: FunctionType => true + case ct: CaseClassType if ct.classDef.hasParent => true + case ct: ClassType if requireChecking(ct.root) => false + case ct: ClassType => + requireChecking += ct.root + val classTypes = ct.root +: (ct.root match { + case (act: AbstractClassType) => act.knownDescendants + case (cct: CaseClassType) => Seq.empty + }) + + classTypes.exists(ct => ct.classDef.hasInvariant || ct.fieldsTypes.exists(requireTypeUnrolling)) + + case BooleanType | UnitType | CharType | IntegerType | + RealType | Int32Type | StringType | (_: TypeParameter) => false + + case NAryType(tpes, _) => tpes.exists(requireTypeUnrolling) + } + + requireCache += tpe -> res + res + } + + def typeUnroller(expr: Expr): Expr = expr.getType match { + case tpe if !requireTypeUnrolling(tpe) => + BooleanLiteral(true) + + case ct: ClassType => + val inv = if (ct.classDef.root.hasInvariant) { + Seq(FunctionInvocation(ct.root.invariant.get, Seq(expr))) + } else { + Seq.empty + } + + val subtype = if (ct != ct.root) { + Seq(IsInstanceOf(expr, ct)) + } else { + Seq.empty + } + + val induct = if (!ct.classDef.isInductive) { + val matchers = ct.root match { + case (act: AbstractClassType) => act.knownCCDescendants + case (cct: CaseClassType) => Seq(cct) + } + + MatchExpr(expr, matchers.map { cct => + val pattern = CaseClassPattern(None, cct, cct.fields.map(vd => WildcardPattern(Some(vd.id)))) + val expr = andJoin(cct.fields.map(vd => typeUnroller(Variable(vd.id)))) + MatchCase(pattern, None, expr) + }) + } else { + val fd = classTypeUnroller(ct.root) + FunctionInvocation(fd.typed, Seq(expr)) + } + + andJoin(inv ++ subtype :+ induct) + + case TupleType(tpes) => + andJoin(tpes.zipWithIndex.map(p => typeUnroller(TupleSelect(expr, p._2 + 1)))) + + case FunctionType(_, _) => + FreshFunction(expr) + + case _ => scala.sys.error("TODO") + } + + private val typeCache: MutableMap[TypeTree, DatatypeTemplate[T]] = MutableMap.empty + + protected def typeTemplate(tpe: TypeTree): DatatypeTemplate[T] = typeCache.getOrElse(tpe, { + val template = DatatypeTemplate(encoder, this, tpe) + typeCache += tpe -> template + template + }) +} + diff --git a/src/main/scala/leon/solvers/templates/LambdaManager.scala b/src/main/scala/leon/solvers/templates/LambdaManager.scala index 036654c25ab64839efa5af4cb0d49a6566ffcf20..a63fdd7a6e06d7ea9cc0b9d8b7ac1f0b89219bab 100644 --- a/src/main/scala/leon/solvers/templates/LambdaManager.scala +++ b/src/main/scala/leon/solvers/templates/LambdaManager.scala @@ -5,16 +5,22 @@ package solvers package templates import purescala.Common._ +import purescala.Definitions._ import purescala.Expressions._ +import purescala.Constructors._ import purescala.Extractors._ import purescala.ExprOps._ import purescala.Types._ +import purescala.TypeOps.bestRealType import utils._ +import utils.SeqUtils._ import Instantiation._ import Template._ -case class App[T](caller: T, tpe: FunctionType, args: Seq[Arg[T]]) { +import scala.collection.mutable.{Map => MutableMap, Set => MutableSet} + +case class App[T](caller: T, tpe: FunctionType, args: Seq[Arg[T]], encoded: T) { override def toString = "(" + caller + " : " + tpe + ")" + args.map(_.encoded).mkString("(", ",", ")") } @@ -39,10 +45,12 @@ object LambdaTemplate { val id = ids._2 val tpe = ids._1.getType.asInstanceOf[FunctionType] - val (clauses, blockers, applications, matchers, templateString) = - Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, + val (clauses, blockers, applications, functions, matchers, templateString) = + Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, quantifications, substMap = baseSubstMap + ids, optApp = Some(id -> tpe)) + assert(functions.isEmpty, "Only synthetic type explorers should introduce functions!") + val lambdaString : () => String = () => { "Template for lambda " + ids._1 + ": " + lambda + " is :\n" + templateString() } @@ -63,11 +71,11 @@ object LambdaTemplate { clauses, blockers, applications, - quantifications, - matchers, lambdas, + matchers, + quantifications, keyDeps, - key, + key -> structSubst, lambdaString ) } @@ -75,7 +83,7 @@ object LambdaTemplate { trait KeyedTemplate[T, E <: Expr] { val dependencies: Map[Identifier, T] - val structuralKey: E + val structure: E lazy val key: (E, Seq[T]) = { def rec(e: Expr): Seq[Identifier] = e match { @@ -91,7 +99,7 @@ trait KeyedTemplate[T, E <: Expr] { case _ => Seq.empty } - structuralKey -> rec(structuralKey).distinct.map(dependencies) + structure -> rec(structure).map(dependencies) } } @@ -107,15 +115,17 @@ class LambdaTemplate[T] private ( val clauses: Seq[T], val blockers: Map[T, Set[TemplateCallInfo[T]]], val applications: Map[T, Set[App[T]]], - val quantifications: Seq[QuantificationTemplate[T]], - val matchers: Map[T, Set[Matcher[T]]], val lambdas: Seq[LambdaTemplate[T]], + val matchers: Map[T, Set[Matcher[T]]], + val quantifications: Seq[QuantificationTemplate[T]], val dependencies: Map[Identifier, T], - val structuralKey: Lambda, + val struct: (Lambda, Map[Identifier, Identifier]), stringRepr: () => String) extends Template[T] with KeyedTemplate[T, Lambda] { val args = arguments.map(_._2) - val tpe = ids._1.getType.asInstanceOf[FunctionType] + val tpe = bestRealType(ids._1.getType).asInstanceOf[FunctionType] + val functions: Set[(T, FunctionType, T)] = Set.empty + val (structure, structSubst) = struct def substitute(substituter: T => T, matcherSubst: Map[T, Matcher[T]]): LambdaTemplate[T] = { val newStart = substituter(start) @@ -135,14 +145,14 @@ class LambdaTemplate[T] private ( )) } - val newQuantifications = quantifications.map(_.substitute(substituter, matcherSubst)) + val newLambdas = lambdas.map(_.substitute(substituter, matcherSubst)) val newMatchers = matchers.map { case (b, ms) => val bp = if (b == start) newStart else b bp -> ms.map(_.substitute(substituter, matcherSubst)) } - val newLambdas = lambdas.map(_.substitute(substituter, matcherSubst)) + val newQuantifications = quantifications.map(_.substitute(substituter, matcherSubst)) val newDependencies = dependencies.map(p => p._1 -> substituter(p._2)) @@ -158,11 +168,11 @@ class LambdaTemplate[T] private ( newClauses, newBlockers, newApplications, - newQuantifications, - newMatchers, newLambdas, + newMatchers, + newQuantifications, newDependencies, - structuralKey, + struct, stringRepr ) } @@ -172,8 +182,8 @@ class LambdaTemplate[T] private ( new LambdaTemplate[T]( ids._1 -> idT, encoder, manager, pathVar, arguments, condVars, exprVars, condTree, clauses map substituter, // make sure the body-defining clause is inlined! - blockers, applications, quantifications, matchers, lambdas, - dependencies, structuralKey, stringRepr + blockers, applications, lambdas, matchers, quantifications, + dependencies, struct, stringRepr ) } @@ -185,27 +195,90 @@ class LambdaTemplate[T] private ( } } -class LambdaManager[T](encoder: TemplateEncoder[T]) extends TemplateManager(encoder) { +class LambdaManager[T](encoder: TemplateEncoder[T]) extends DatatypeManager(encoder) { private[templates] lazy val trueT = encoder.encodeExpr(Map.empty)(BooleanLiteral(true)) protected[templates] val byID = new IncrementalMap[T, LambdaTemplate[T]] protected val byType = new IncrementalMap[FunctionType, Map[(Expr, Seq[T]), LambdaTemplate[T]]].withDefaultValue(Map.empty) protected val applications = new IncrementalMap[FunctionType, Set[(T, App[T])]].withDefaultValue(Set.empty) - protected val freeLambdas = new IncrementalMap[FunctionType, Set[T]].withDefaultValue(Set.empty) + protected val knownFree = new IncrementalMap[FunctionType, Set[T]].withDefaultValue(Set.empty) + protected val maybeFree = new IncrementalMap[FunctionType, Set[(T, T)]].withDefaultValue(Set.empty) + protected val freeBlockers = new IncrementalMap[FunctionType, Set[(T, T)]].withDefaultValue(Set.empty) private val instantiated = new IncrementalSet[(T, App[T])] override protected def incrementals: List[IncrementalState] = - super.incrementals ++ List(byID, byType, applications, freeLambdas, instantiated) + super.incrementals ++ List(byID, byType, applications, knownFree, maybeFree, freeBlockers, instantiated) + + def registerFunction(b: T, tpe: FunctionType, f: T): Instantiation[T] = { + val ft = bestRealType(tpe).asInstanceOf[FunctionType] + val bs = fixpoint((bs: Set[T]) => bs ++ bs.flatMap(blockerParents))(Set(b)) + + val (known, neqClauses) = if ((bs intersect typeEnablers).nonEmpty) { + maybeFree += ft -> (maybeFree(ft) + (b -> f)) + (false, byType(ft).values.toSeq.map { t => + encoder.mkImplies(b, encoder.mkNot(encoder.mkEquals(t.ids._2, f))) + }) + } else { + knownFree += ft -> (knownFree(ft) + f) + (true, byType(ft).values.toSeq.map(t => encoder.mkNot(encoder.mkEquals(t.ids._2, f)))) + } - def registerFree(lambdas: Seq[(Identifier, T)]): Unit = { - for ((id, idT) <- lambdas) id.getType match { - case ft: FunctionType => - freeLambdas += ft -> (freeLambdas(ft) + idT) - case _ => + val extClauses = freeBlockers(tpe).map { case (oldB, freeF) => + val equals = encoder.mkEquals(f, freeF) + val nextB = encoder.encodeId(FreshIdentifier("b_or", BooleanType, true)) + val extension = encoder.mkOr(if (known) equals else encoder.mkAnd(b, equals), nextB) + encoder.mkEquals(oldB, extension) + } + + val instantiation = Instantiation.empty[T] withClauses (neqClauses ++ extClauses) + + applications(tpe).foldLeft(instantiation) { + case (instantiation, (app @ (_, App(caller, _, args, _)))) => + val equals = encoder.mkAnd(b, encoder.mkEquals(f, caller)) + instantiation withApp (app -> TemplateAppInfo(f, equals, args)) } } + def assumptions: Seq[T] = freeBlockers.toSeq.flatMap(_._2.map(p => encoder.mkNot(p._1))) + + private val typeBlockers = new IncrementalMap[T, T]() + private val typeEnablers: MutableSet[T] = MutableSet.empty + + private def typeUnroller(blocker: T, app: App[T]): Instantiation[T] = typeBlockers.get(app.encoded) match { + case Some(typeBlocker) => + implies(blocker, typeBlocker) + (Seq(encoder.mkImplies(blocker, typeBlocker)), Map.empty, Map.empty) + + case None => + val App(caller, tpe @ FunctionType(_, to), args, value) = app + val typeBlocker = encoder.encodeId(FreshIdentifier("t", BooleanType)) + typeBlockers += value -> typeBlocker + implies(blocker, typeBlocker) + + val template = typeTemplate(to) + val instantiation = template.instantiate(typeBlocker, value) + + val (b, extClauses) = if (knownFree(tpe) contains caller) { + (blocker, Seq.empty) + } else { + val firstB = encoder.encodeId(FreshIdentifier("b_free", BooleanType, true)) + implies(firstB, typeBlocker) + typeEnablers += firstB + + val nextB = encoder.encodeId(FreshIdentifier("b_or", BooleanType, true)) + freeBlockers += tpe -> (freeBlockers(tpe) + (nextB -> caller)) + + val clause = encoder.mkEquals(firstB, encoder.mkAnd(blocker, encoder.mkOr( + knownFree(tpe).map(idT => encoder.mkEquals(caller, idT)).toSeq ++ + maybeFree(tpe).map { case (b, idT) => encoder.mkAnd(b, encoder.mkEquals(caller, idT)) } :+ + nextB : _*))) + (firstB, Seq(clause)) + } + + instantiation withClauses extClauses withClause encoder.mkImplies(b, typeBlocker) + } + def instantiateLambda(template: LambdaTemplate[T]): (T, Instantiation[T]) = { byType(template.tpe).get(template.key) match { case Some(template) => @@ -215,51 +288,60 @@ class LambdaManager[T](encoder: TemplateEncoder[T]) extends TemplateManager(enco val idT = encoder.encodeId(template.ids._1) val newTemplate = template.withId(idT) - var clauses : Clauses[T] = equalityClauses(newTemplate) - var appBlockers : AppBlockers[T] = Map.empty.withDefaultValue(Set.empty) - // make sure the new lambda isn't equal to any free lambda var - clauses ++= freeLambdas(newTemplate.tpe).map(pIdT => encoder.mkNot(encoder.mkEquals(idT, pIdT))) + val instantiation = Instantiation.empty[T] withClauses ( + equalityClauses(newTemplate) ++ + knownFree(newTemplate.tpe).map(f => encoder.mkNot(encoder.mkEquals(idT, f))).toSeq ++ + maybeFree(newTemplate.tpe).map { p => + encoder.mkImplies(p._1, encoder.mkNot(encoder.mkEquals(idT, p._2))) + }) byID += idT -> newTemplate byType += newTemplate.tpe -> (byType(newTemplate.tpe) + (newTemplate.key -> newTemplate)) - for (blockedApp @ (_, App(caller, tpe, args)) <- applications(newTemplate.tpe)) { - val equals = encoder.mkEquals(idT, caller) - appBlockers += (blockedApp -> (appBlockers(blockedApp) + TemplateAppInfo(newTemplate, equals, args))) + val inst = applications(newTemplate.tpe).foldLeft(instantiation) { + case (instantiation, app @ (_, App(caller, _, args, _))) => + val equals = encoder.mkEquals(idT, caller) + instantiation withApp (app -> TemplateAppInfo(newTemplate, equals, args)) } - (idT, (clauses, Map.empty, appBlockers)) + (idT, inst) } } def instantiateApp(blocker: T, app: App[T]): Instantiation[T] = { - val App(caller, tpe, args) = app - val instantiation = Instantiation.empty[T] + val App(caller, tpe @ FunctionType(_, to), args, encoded) = app - if (freeLambdas(tpe).contains(caller)) instantiation else { - val key = blocker -> app + val instantiation: Instantiation[T] = if (byID contains caller) { + Instantiation.empty + } else { + typeUnroller(blocker, app) + } - if (instantiated(key)) instantiation else { - instantiated += key + val key = blocker -> app + if (instantiated(key)) { + instantiation + } else { + instantiated += key - if (byID contains caller) { - instantiation withApp (key -> TemplateAppInfo(byID(caller), trueT, args)) - } else { + if (knownFree(tpe) contains caller) { + instantiation withApp (key -> TemplateAppInfo(caller, trueT, args)) + } else if (byID contains caller) { + instantiation withApp (key -> TemplateAppInfo(byID(caller), trueT, args)) + } else { - // make sure that even if byType(tpe) is empty, app is recorded in blockers - // so that UnrollingBank will generate the initial block! - val init = instantiation withApps Map(key -> Set.empty) - val inst = byType(tpe).values.foldLeft(init) { - case (instantiation, template) => - val equals = encoder.mkEquals(template.ids._2, caller) - instantiation withApp (key -> TemplateAppInfo(template, equals, args)) - } + // make sure that even if byType(tpe) is empty, app is recorded in blockers + // so that UnrollingBank will generate the initial block! + val init = instantiation withApps Map(key -> Set.empty) + val inst = byType(tpe).values.foldLeft(init) { + case (instantiation, template) => + val equals = encoder.mkEquals(template.ids._2, caller) + instantiation withApp (key -> TemplateAppInfo(template, equals, args)) + } - applications += tpe -> (applications(tpe) + key) + applications += tpe -> (applications(tpe) + key) - inst - } + inst } } } diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala index 84a3017b8078faebc19fc606f5bd52f5be91fcb0..85a2add2721a8b03cb3399bdbc73d796d74853e4 100644 --- a/src/main/scala/leon/solvers/templates/QuantificationManager.scala +++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala @@ -55,8 +55,9 @@ class QuantificationTemplate[T]( val matchers: Map[T, Set[Matcher[T]]], val lambdas: Seq[LambdaTemplate[T]], val dependencies: Map[Identifier, T], - val structuralKey: Forall) extends KeyedTemplate[T, Forall] { + val struct: (Forall, Map[Identifier, Identifier])) extends KeyedTemplate[T, Forall] { + val structure = struct._1 lazy val start = pathVar._2 def substitute(substituter: T => T, matcherSubst: Map[T, Matcher[T]]): QuantificationTemplate[T] = { @@ -88,7 +89,7 @@ class QuantificationTemplate[T]( }, lambdas.map(_.substitute(substituter, matcherSubst)), dependencies.map { case (id, value) => id -> substituter(value) }, - structuralKey + struct ) } } @@ -117,8 +118,8 @@ object QuantificationTemplate { val insts: (Identifier, T) = inst -> encoder.encodeId(inst) val guards: (Identifier, T) = guard -> encoder.encodeId(guard) - val (clauses, blockers, applications, matchers, _) = - Template.encode(encoder, pathVar, quantifiers, condVars, exprVars, guardedExprs, lambdas, + val (clauses, blockers, applications, functions, matchers, _) = + Template.encode(encoder, pathVar, quantifiers, condVars, exprVars, guardedExprs, lambdas, Seq.empty, substMap = baseSubstMap + q2s + insts + guards + qs) val (structuralQuant, structSubst) = normalizeStructure(proposition) @@ -127,25 +128,25 @@ object QuantificationTemplate { new QuantificationTemplate[T](quantificationManager, pathVar, qs, q2s, insts, guards._2, quantifiers, condVars, exprVars, condTree, - clauses, blockers, applications, matchers, lambdas, keyDeps, key) + clauses, blockers, applications, matchers, lambdas, keyDeps, key -> structSubst) } } class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManager[T](encoder) { - private val quantifications = new IncrementalSeq[MatcherQuantification] + private[solvers] val quantifications = new IncrementalSeq[MatcherQuantification] + private val instCtx = new InstantiationContext private val ignoredMatchers = new IncrementalSeq[(Int, Set[T], Matcher[T])] private val ignoredSubsts = new IncrementalMap[MatcherQuantification, MutableSet[(Int, Set[T], Map[T,Arg[T]])]] private val handledSubsts = new IncrementalMap[MatcherQuantification, MutableSet[(Set[T], Map[T,Arg[T]])]] - private val known = new IncrementalSet[T] private val lambdaAxioms = new IncrementalSet[((Expr, Seq[T]), Seq[(Identifier, T)])] private val templates = new IncrementalMap[(Expr, Seq[T]), T] override protected def incrementals: List[IncrementalState] = List(quantifications, instCtx, ignoredMatchers, ignoredSubsts, - handledSubsts, known, lambdaAxioms, templates) ++ super.incrementals + handledSubsts, lambdaAxioms, templates) ++ super.incrementals private var currentGen = 0 @@ -155,8 +156,8 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage private case class TypeKey(tt: TypeTree) extends MatcherKey(tt) private def matcherKey(caller: T, tpe: TypeTree): MatcherKey = tpe match { - case _: FunctionType if known(caller) => CallerKey(caller, tpe) - case _: FunctionType if byID.isDefinedAt(caller) => LambdaKey(byID(caller).structuralKey, tpe) + case ft: FunctionType if knownFree(ft)(caller) => CallerKey(caller, tpe) + case _: FunctionType if byID.isDefinedAt(caller) => LambdaKey(byID(caller).structure, tpe) case _ => TypeKey(tpe) } @@ -201,7 +202,8 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage (qs.map(_._2) zip uniformQuants(qs.map(_._1))).toMap } - def assumptions: Seq[T] = quantifications.collect { case q: Quantification => q.currentQ2Var }.toSeq + override def assumptions: Seq[T] = super.assumptions ++ + quantifications.collect { case q: Quantification => q.currentQ2Var }.toSeq def typeInstantiations: Map[TypeTree, Matchers] = instCtx.map.instantiations.collect { case (TypeKey(tpe), matchers) => tpe -> matchers @@ -215,11 +217,6 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage case (CallerKey(caller, tpe), matchers) => caller -> (matchers ++ instCtx.map.get(TypeKey(tpe)).toMatchers) } - override def registerFree(ids: Seq[(Identifier, T)]): Unit = { - super.registerFree(ids) - known ++= ids.map(_._2) - } - private def matcherDepth(m: Matcher[T]): Int = 1 + (0 +: m.args.map { case Right(ma) => matcherDepth(ma) case _ => 0 @@ -239,7 +236,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage } def +(p: (Set[T], Matcher[T])): Context = if (apply(p)) this else { - val prev = ctx.getOrElse(p._2, Seq.empty) + val prev = ctx.getOrElse(p._2, Set.empty) val newSet = prev.filterNot(set => p._1.subsetOf(set)).toSet + p._1 new Context(ctx + (p._2 -> newSet)) } @@ -292,7 +289,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage def get(key: MatcherKey): Context = key match { case TypeKey(tpe) => tpeMap.getOrElse(tpe, new Context) - case key => funMap.getOrElse(key, new Context) + case key => funMap.getOrElse(key, new Context) ++ tpeMap.getOrElse(key.tpe, new Context) } def instantiations: Map[MatcherKey, Matchers] = @@ -349,7 +346,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage } } - private trait MatcherQuantification { + private[solvers] trait MatcherQuantification { val pathVar: (Identifier, T) val quantifiers: Seq[(Identifier, T)] val matchers: Set[Matcher[T]] @@ -362,6 +359,9 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val applications: Map[T, Set[App[T]]] val lambdas: Seq[LambdaTemplate[T]] + val holds: T + val body: Expr + lazy val quantified: Set[T] = quantifiers.map(_._2).toSet lazy val start = pathVar._2 @@ -477,23 +477,22 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage Instantiation.empty[T] } else { handledSubsts(this) += enablers -> subst - val allEnablers = fixpoint((enablers: Set[T]) => enablers.flatMap(blockerParents))(enablers) var instantiation = Instantiation.empty[T] - val (enabler, optEnabler) = freshBlocker(allEnablers) + val (enabler, optEnabler) = freshBlocker(enablers) if (optEnabler.isDefined) { instantiation = instantiation withClause encoder.mkEquals(enabler, optEnabler.get) } val baseSubst = subst ++ instanceSubst(enabler).mapValues(Left(_)) - val (substMap, inst) = Template.substitution(encoder, QuantificationManager.this, - condVars, exprVars, condTree, Seq.empty, lambdas, baseSubst, pathVar._1, enabler) + val (substMap, inst) = Template.substitution[T](encoder, QuantificationManager.this, + condVars, exprVars, condTree, Seq.empty, lambdas, Set.empty, baseSubst, pathVar._1, enabler) instantiation ++= inst val msubst = substMap.collect { case (c, Right(m)) => c -> m } val substituter = encoder.substitute(substMap.mapValues(_.encoded)) instantiation ++= Template.instantiate(encoder, QuantificationManager.this, - clauses, blockers, applications, Seq.empty, Map.empty, lambdas, substMap) + clauses, blockers, applications, Map.empty, substMap) for ((b,ms) <- allMatchers; m <- ms) { val sb = enablers ++ (if (b == start) Set.empty else Set(substituter(b))) @@ -541,9 +540,16 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val clauses: Seq[T], val blockers: Map[T, Set[TemplateCallInfo[T]]], val applications: Map[T, Set[App[T]]], - val lambdas: Seq[LambdaTemplate[T]]) extends MatcherQuantification { + val lambdas: Seq[LambdaTemplate[T]], + val template: QuantificationTemplate[T]) extends MatcherQuantification { var currentQ2Var: T = qs._2 + val holds = qs._2 + val body = { + val quantified = quantifiers.map(_._1).toSet + val mapping = template.struct._2.map(p => p._2 -> p._1.toVariable) + replaceFromIDs(mapping, template.structure.body) + } protected def instanceSubst(enabler: T): Map[T, T] = { val nextQ2Var = encoder.encodeId(q2s._1) @@ -563,7 +569,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage case Seq(b) if isBlocker(b) => (b, None) case _ => val last = enablersToBlocker.get(enablers).orElse { - val initialEnablers = enablers.flatMap(blockerToEnablers.get).flatten + val initialEnablers = enablers.flatMap(e => blockerToEnablers.getOrElse(e, Set(e))) enablersToBlocker.get(initialEnablers) } @@ -593,7 +599,16 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val clauses: Seq[T], val blockers: Map[T, Set[TemplateCallInfo[T]]], val applications: Map[T, Set[App[T]]], - val lambdas: Seq[LambdaTemplate[T]]) extends MatcherQuantification { + val lambdas: Seq[LambdaTemplate[T]], + val template: LambdaTemplate[T]) extends MatcherQuantification { + + val holds = start + + val body = { + val quantified = quantifiers.map(_._1).toSet + val mapping = template.structSubst.map(p => p._2 -> p._1.toVariable) + replaceFromIDs(mapping, template.structure) + } protected def instanceSubst(enabler: T): Map[T, T] = { Map(guardVar -> start, blocker -> enabler) @@ -735,75 +750,51 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val appT = encoder.encodeExpr((template.arguments.map(_._1) zip encArgs.map(_.encoded)).toMap + template.ids)(app) val selfMatcher = Matcher(template.ids._2, template.tpe, encArgs, appT) + val instMatchers = allMatchers + (template.start -> (allMatchers.getOrElse(template.start, Set.empty) + selfMatcher)) + val enablingClause = encoder.mkImplies(guardT, blockerT) - instantiateAxiom( - template.pathVar._1 -> substituter(template.start), - blockerT, - guardT, - quantifiers, - qMatchers, - allMatchers + (template.start -> (allMatchers.getOrElse(template.start, Set.empty) + selfMatcher)), - template.condVars map { case (id, idT) => id -> substituter(idT) }, - template.exprVars map { case (id, idT) => id -> substituter(idT) }, - template.condTree, - (template.clauses map substituter) :+ enablingClause, - template.blockers map { case (b, fis) => - substituter(b) -> fis.map(fi => fi.copy( - args = fi.args.map(_.substitute(substituter, msubst)) - )) - }, - template.applications map { case (b, apps) => - substituter(b) -> apps.map(app => app.copy( - caller = substituter(app.caller), - args = app.args.map(_.substitute(substituter, msubst)) - )) - }, - template.lambdas map (_.substitute(substituter, msubst)) - ) - } - } + val condVars = template.condVars map { case (id, idT) => id -> substituter(idT) } + val exprVars = template.exprVars map { case (id, idT) => id -> substituter(idT) } + val clauses = (template.clauses map substituter) :+ enablingClause + val blockers = template.blockers map { case (b, fis) => + substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(_.substitute(substituter, msubst)))) + } - def instantiateAxiom( - pathVar: (Identifier, T), - blocker: T, - guardVar: T, - quantifiers: Seq[(Identifier, T)], - matchers: Set[Matcher[T]], - allMatchers: Map[T, Set[Matcher[T]]], - condVars: Map[Identifier, T], - exprVars: Map[Identifier, T], - condTree: Map[Identifier, Set[Identifier]], - clauses: Seq[T], - blockers: Map[T, Set[TemplateCallInfo[T]]], - applications: Map[T, Set[App[T]]], - lambdas: Seq[LambdaTemplate[T]] - ): Instantiation[T] = { - val quantified = quantifiers.map(_._2).toSet - val matchQuorums = extractQuorums(quantified, matchers, lambdas) + val applications = template.applications map { case (b, apps) => + substituter(b) -> apps.map(app => app.copy( + caller = substituter(app.caller), + args = app.args.map(_.substitute(substituter, msubst)) + )) + } - var instantiation = Instantiation.empty[T] + val lambdas = template.lambdas map (_.substitute(substituter, msubst)) - for (matchers <- matchQuorums) { - val axiom = new LambdaAxiom(pathVar, blocker, guardVar, quantifiers, - matchers, allMatchers, condVars, exprVars, condTree, - clauses, blockers, applications, lambdas - ) + val quantified = quantifiers.map(_._2).toSet + val matchQuorums = extractQuorums(quantified, qMatchers, lambdas) - quantifications += axiom - handledSubsts += axiom -> MutableSet.empty - ignoredSubsts += axiom -> MutableSet.empty + var instantiation = Instantiation.empty[T] + + for (matchers <- matchQuorums) { + val axiom = new LambdaAxiom(template.pathVar._1 -> substituter(template.start), + blockerT, guardT, quantifiers, matchers, instMatchers, condVars, exprVars, template.condTree, + clauses, blockers, applications, lambdas, template) - val newCtx = new InstantiationContext() - for ((b,m) <- instCtx.instantiated) { - instantiation ++= newCtx.instantiate(b, m)(axiom) + quantifications += axiom + handledSubsts += axiom -> MutableSet.empty + ignoredSubsts += axiom -> MutableSet.empty + + val newCtx = new InstantiationContext() + for ((b,m) <- instCtx.instantiated) { + instantiation ++= newCtx.instantiate(b, m)(axiom) + } + instCtx.merge(newCtx) } - instCtx.merge(newCtx) - } - instantiation ++= instantiateConstants(quantifiers, matchers) + instantiation ++= instantiateConstants(quantifiers, qMatchers) - instantiation + instantiation + } } def instantiateQuantification(template: QuantificationTemplate[T]): (T, Instantiation[T]) = { @@ -829,8 +820,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage template.quantifiers, matchers, template.matchers, template.condVars, template.exprVars, template.condTree, template.clauses map substituter, // one clause depends on 'q' (and therefore 'newQ') - template.blockers, template.applications, template.lambdas - ) + template.blockers, template.applications, template.lambdas, template) quantifications += quantification handledSubsts += quantification -> MutableSet.empty @@ -928,8 +918,9 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val valuesP = values.map(v => v -> encoder.encodeId(v)) val exprT = encoder.encodeExpr(elemsP.toMap ++ valuesP + guardP)(expr) - val disjuncts = insts.toSeq.map { case (b, m) => - val subst = (elemsP.map(_._2) zip m.args.map(_.encoded)).toMap + (guardP._2 -> b) + val disjuncts = insts.toSeq.map { case (b, im) => + val bp = if (m.caller != im.caller) encoder.mkAnd(encoder.mkEquals(m.caller, im.caller), b) else b + val subst = (elemsP.map(_._2) zip im.args.map(_.encoded)).toMap + (guardP._2 -> bp) encoder.substitute(subst)(exprT) } diff --git a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala index 2b0e95a4d77de19253730a7fde9b2e04890ca5f6..4556f7dd6507f9a8c8a745b0323dbbdb9cf0032b 100644 --- a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala +++ b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala @@ -14,6 +14,7 @@ import purescala.Definitions._ import purescala.Constructors._ import purescala.Quantification._ +import utils.SeqUtils._ import Instantiation._ class TemplateGenerator[T](val encoder: TemplateEncoder[T], @@ -49,8 +50,10 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], return cacheExpr(body) } - val fakeFunDef = new FunDef(FreshIdentifier("fake", alwaysShowUniqueID = true), Nil, variablesOf(body).toSeq.map(ValDef(_)), body.getType) + val arguments = variablesOf(body).toSeq.map(ValDef(_)) + val fakeFunDef = new FunDef(FreshIdentifier("fake", alwaysShowUniqueID = true), Nil, arguments, body.getType) + fakeFunDef.precondition = Some(andJoin(arguments.map(vd => manager.typeUnroller(vd.toVariable)))) fakeFunDef.body = Some(body) val res = mkTemplate(fakeFunDef.typed, false) @@ -95,10 +98,11 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], val substMap : Map[Identifier, T] = arguments.toMap + pathVar + val (bodyConds, bodyExprs, bodyTree, bodyGuarded, bodyLambdas, bodyQuantifications) = if (isRealFunDef) { invocationEqualsBody.foldLeft(emptyClauses)((clsSet, cls) => clsSet ++ mkClauses(start, cls, substMap)) } else { - mkClauses(start, lambdaBody.get, substMap) + (prec.toSeq :+ lambdaBody.get).foldLeft(emptyClauses)((clsSet, cls) => clsSet ++ mkClauses(start, cls, substMap)) } // Now the postcondition. @@ -214,7 +218,7 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], // Represents clauses of the form: // id => expr && ... && expr var guardedExprs = Map[Identifier, Seq[Expr]]() - def storeGuarded(guardVar : Identifier, expr : Expr) : Unit = { + def storeGuarded(guardVar: Identifier, expr: Expr) : Unit = { assert(expr.getType == BooleanType, expr.asString(Program.empty)(LeonContext.empty) + " is not of type Boolean." + ( purescala.ExprOps.fold[String]{ (e, se) => s"$e is of type ${e.getType}" + se.map(child => "\n " + "\n".r.replaceAllIn(child, "\n ")).mkString @@ -222,7 +226,6 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], )) val prev = guardedExprs.getOrElse(guardVar, Nil) - guardedExprs += guardVar -> (expr +: prev) } @@ -249,25 +252,6 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], }(e) } - def groupWhile[T](es: Seq[T])(p: T => Boolean): Seq[Seq[T]] = { - var res: Seq[Seq[T]] = Nil - - var c = es - while (!c.isEmpty) { - val (span, rest) = c.span(p) - - if (span.isEmpty) { - res :+= Seq(rest.head) - c = rest.tail - } else { - res :+= span - c = rest - } - } - - res - } - def rec(pathVar: Identifier, expr: Expr): Expr = { expr match { case a @ Assert(cond, err, body) => diff --git a/src/main/scala/leon/solvers/templates/TemplateInfo.scala b/src/main/scala/leon/solvers/templates/TemplateInfo.scala index 27df9b25d13412c106f2bf30d6c75b1266b19d93..dfdd664cbdf1f110690d05e959bc1a14cc615327 100644 --- a/src/main/scala/leon/solvers/templates/TemplateInfo.scala +++ b/src/main/scala/leon/solvers/templates/TemplateInfo.scala @@ -16,11 +16,24 @@ case class TemplateCallInfo[T](tfd: TypedFunDef, args: Seq[Arg[T]]) { } } -case class TemplateAppInfo[T](template: LambdaTemplate[T], equals: T, args: Seq[Arg[T]]) { +case class TemplateAppInfo[T](template: Either[LambdaTemplate[T], T], equals: T, args: Seq[Arg[T]]) { override def toString = { - template.ids._2 + "|" + equals + args.map { + val caller = template match { + case Left(tmpl) => tmpl.ids._2 + case Right(c) => c + } + + caller + "|" + equals + args.map { case Right(m) => m.toString case Left(v) => v.toString }.mkString("(", ",", ")") } } + +object TemplateAppInfo { + def apply[T](template: LambdaTemplate[T], equals: T, args: Seq[Arg[T]]): TemplateAppInfo[T] = + TemplateAppInfo(Left(template), equals, args) + + def apply[T](caller: T, equals: T, args: Seq[Arg[T]]): TemplateAppInfo[T] = + TemplateAppInfo(Right(caller), equals, args) +} diff --git a/src/main/scala/leon/solvers/templates/TemplateManager.scala b/src/main/scala/leon/solvers/templates/TemplateManager.scala index 332d3fdf1442bfab25f72839d9ef00e04de73dd5..c81abc384934aea21fb1d5d183925e7c6d013fb7 100644 --- a/src/main/scala/leon/solvers/templates/TemplateManager.scala +++ b/src/main/scala/leon/solvers/templates/TemplateManager.scala @@ -8,6 +8,7 @@ import purescala.Common._ import purescala.Definitions._ import purescala.Expressions._ import purescala.Quantification._ +import purescala.Constructors._ import purescala.Extractors._ import purescala.ExprOps._ import purescala.Types._ @@ -18,9 +19,9 @@ import utils._ import scala.collection.generic.CanBuildFrom object Instantiation { - type Clauses[T] = Seq[T] - type CallBlockers[T] = Map[T, Set[TemplateCallInfo[T]]] - type AppBlockers[T] = Map[(T, App[T]), Set[TemplateAppInfo[T]]] + type Clauses[T] = Seq[T] + type CallBlockers[T] = Map[T, Set[TemplateCallInfo[T]]] + type AppBlockers[T] = Map[(T, App[T]), Set[TemplateAppInfo[T]]] type Instantiation[T] = (Clauses[T], CallBlockers[T], AppBlockers[T]) def empty[T] = (Seq.empty[T], Map.empty[T, Set[TemplateCallInfo[T]]], Map.empty[(T, App[T]), Set[TemplateAppInfo[T]]]) @@ -33,7 +34,7 @@ object Instantiation { implicit class MapSeqWrapper[A,B](map: Map[A,Seq[B]]) { def merge(that: Map[A,Seq[B]]): Map[A,Seq[B]] = (map.keys ++ that.keys).map { k => - k -> (map.getOrElse(k, Seq.empty) ++ that.getOrElse(k, Seq.empty)) + k -> (map.getOrElse(k, Seq.empty) ++ that.getOrElse(k, Seq.empty)).distinct }.toMap } @@ -61,32 +62,36 @@ import Template.Arg trait Template[T] { self => val encoder : TemplateEncoder[T] - val manager : QuantificationManager[T] + val manager : TemplateManager[T] + + val pathVar : (Identifier, T) + val args : Seq[T] - val pathVar: (Identifier, T) - val args : Seq[T] val condVars : Map[Identifier, T] val exprVars : Map[Identifier, T] val condTree : Map[Identifier, Set[Identifier]] - val clauses : Seq[T] - val blockers : Map[T, Set[TemplateCallInfo[T]]] + + val clauses : Seq[T] + val blockers : Map[T, Set[TemplateCallInfo[T]]] val applications : Map[T, Set[App[T]]] + val functions : Set[(T, FunctionType, T)] + val lambdas : Seq[LambdaTemplate[T]] + val quantifications : Seq[QuantificationTemplate[T]] - val matchers : Map[T, Set[Matcher[T]]] - val lambdas : Seq[LambdaTemplate[T]] + val matchers : Map[T, Set[Matcher[T]]] lazy val start = pathVar._2 def instantiate(aVar: T, args: Seq[Arg[T]]): Instantiation[T] = { val (substMap, instantiation) = Template.substitution(encoder, manager, - condVars, exprVars, condTree, quantifications, lambdas, + condVars, exprVars, condTree, quantifications, lambdas, functions, (this.args zip args).toMap + (start -> Left(aVar)), pathVar._1, aVar) instantiation ++ instantiate(substMap) } protected def instantiate(substMap: Map[T, Arg[T]]): Instantiation[T] = { - Template.instantiate(encoder, manager, - clauses, blockers, applications, quantifications, matchers, lambdas, substMap) + Template.instantiate(encoder, manager, clauses, + blockers, applications, matchers, substMap) } override def toString : String = "Instantiated template" @@ -127,6 +132,9 @@ object Template { Matcher(encodeExpr(caller), bestRealType(caller.getType), arguments.map(arg => Left(encodeExpr(arg))), encodeExpr(app)) } + type Apps[T] = Map[T, Set[App[T]]] + type Functions[T] = Set[(T, FunctionType, T)] + def encode[T]( encoder: TemplateEncoder[T], pathVar: (Identifier, T), @@ -135,34 +143,60 @@ object Template { exprVars: Map[Identifier, T], guardedExprs: Map[Identifier, Seq[Expr]], lambdas: Seq[LambdaTemplate[T]], + quantifications: Seq[QuantificationTemplate[T]], substMap: Map[Identifier, T] = Map.empty[Identifier, T], optCall: Option[TypedFunDef] = None, optApp: Option[(T, FunctionType)] = None - ) : (Seq[T], Map[T, Set[TemplateCallInfo[T]]], Map[T, Set[App[T]]], Map[T, Set[Matcher[T]]], () => String) = { + ) : (Clauses[T], CallBlockers[T], Apps[T], Functions[T], Map[T, Set[Matcher[T]]], () => String) = { val idToTrId : Map[Identifier, T] = - condVars ++ exprVars + pathVar ++ arguments ++ substMap ++ lambdas.map(_.ids) + condVars ++ exprVars + pathVar ++ arguments ++ substMap ++ lambdas.map(_.ids) ++ quantifications.map(_.qs) val encodeExpr : Expr => T = encoder.encodeExpr(idToTrId) - val clauses : Seq[T] = (for ((b,es) <- guardedExprs; e <- es) yield { - encodeExpr(Implies(Variable(b), e)) - }).toSeq + val (clauses, cleanGuarded, functions) = { + var functions: Set[(T, FunctionType, T)] = Set.empty + var clauses: Seq[T] = Seq.empty + + val cleanGuarded = guardedExprs.map { + case (b, es) => b -> es.map { e => + def clean(expr: Expr): Expr = postMap { + case FreshFunction(f) => Some(BooleanLiteral(true)) + case _ => None + } (expr) + + val withPaths = CollectorWithPaths { case FreshFunction(f) => f }.traverse(e) + functions ++= withPaths.map { case (f, TopLevelAnds(paths)) => + val tpe = bestRealType(f.getType).asInstanceOf[FunctionType] + val path = andJoin(paths.map(clean)) + (encodeExpr(and(Variable(b), path)), tpe, encodeExpr(f)) + } + + val cleanExpr = clean(e) + clauses :+= encodeExpr(Implies(Variable(b), cleanExpr)) + cleanExpr + } + } + + (clauses, cleanGuarded, functions) + } val optIdCall = optCall.map(tfd => TemplateCallInfo[T](tfd, arguments.map(p => Left(p._2)))) val optIdApp = optApp.map { case (idT, tpe) => - App(idT, bestRealType(tpe).asInstanceOf[FunctionType], arguments.map(p => Left(p._2))) + val id = FreshIdentifier("x", tpe, true) + val encoded = encoder.encodeExpr(Map(id -> idT) ++ arguments)(Application(Variable(id), arguments.map(_._1.toVariable))) + App(idT, bestRealType(tpe).asInstanceOf[FunctionType], arguments.map(p => Left(p._2)), encoded) } lazy val invocMatcher = optCall.filter(_.returnType.isInstanceOf[FunctionType]) .map(tfd => invocationMatcher(encodeExpr)(tfd, arguments.map(_._1.toVariable))) val (blockers, applications, matchers) = { - var blockers : Map[Identifier, Set[TemplateCallInfo[T]]] = Map.empty - var applications : Map[Identifier, Set[App[T]]] = Map.empty - var matchers : Map[Identifier, Set[Matcher[T]]] = Map.empty + var blockers : Map[Identifier, Set[TemplateCallInfo[T]]] = Map.empty + var applications : Map[Identifier, Set[App[T]]] = Map.empty + var matchers : Map[Identifier, Set[Matcher[T]]] = Map.empty - for ((b,es) <- guardedExprs) { + for ((b,es) <- cleanGuarded) { var funInfos : Set[TemplateCallInfo[T]] = Set.empty var appInfos : Set[App[T]] = Set.empty var matchInfos : Set[Matcher[T]] = Set.empty @@ -192,23 +226,22 @@ object Template { funInfos ++= firstOrderCallsOf(e).map(p => TemplateCallInfo(p._1, p._2.map(encodeArg))) appInfos ++= firstOrderAppsOf(e).map { case (c, args) => - App(encodeExpr(c), bestRealType(c.getType).asInstanceOf[FunctionType], args.map(encodeArg)) + val tpe = bestRealType(c.getType).asInstanceOf[FunctionType] + App(encodeExpr(c), tpe, args.map(encodeArg), encodeExpr(Application(c, args))) } matchInfos ++= exprToMatcher.values } - val calls = funInfos -- optIdCall + val calls = funInfos.filter(i => Some(i) != optIdCall) if (calls.nonEmpty) blockers += b -> calls - val apps = appInfos -- optIdApp + val apps = appInfos.filter(i => Some(i) != optIdApp) if (apps.nonEmpty) applications += b -> apps - val matchs = matchInfos.filter { case m @ Matcher(mc, mtpe, margs, _) => - !optIdApp.exists { case App(ac, atpe, aargs) => - mc == ac && mtpe == atpe && margs == aargs - } - } ++ (if (funInfos.exists(info => Some(info) == optIdCall)) invocMatcher else None) + val matchs = (matchInfos.filter { case m @ Matcher(_, _, _, menc) => + !optIdApp.exists { case App(_, _, _, aenc) => menc == aenc } + } ++ (if (funInfos.exists(info => Some(info) == optIdCall)) invocMatcher else None)) if (matchs.nonEmpty) matchers += b -> matchs } @@ -224,8 +257,8 @@ object Template { " * Activating boolean : " + pathVar._1 + "\n" + " * Control booleans : " + condVars.keys.mkString(", ") + "\n" + " * Expression vars : " + exprVars.keys.mkString(", ") + "\n" + - " * Clauses : " + (if (guardedExprs.isEmpty) "\n" else { - "\n " + (for ((b,es) <- guardedExprs; e <- es) yield (b + " ==> " + e)).mkString("\n ") + "\n" + " * Clauses : " + (if (cleanGuarded.isEmpty) "\n" else { + "\n " + (for ((b,es) <- cleanGuarded; e <- es) yield (b + " ==> " + e)).mkString("\n ") + "\n" }) + " * Invocation-blocks :" + (if (blockers.isEmpty) "\n" else { "\n " + blockers.map(p => p._1 + " ==> " + p._2).mkString("\n ") + "\n" @@ -241,17 +274,18 @@ object Template { }.mkString("\n") } - (clauses, encodedBlockers, encodedApps, encodedMatchers, stringRepr) + (clauses, encodedBlockers, encodedApps, functions, encodedMatchers, stringRepr) } def substitution[T]( encoder: TemplateEncoder[T], - manager: QuantificationManager[T], + manager: TemplateManager[T], condVars: Map[Identifier, T], exprVars: Map[Identifier, T], condTree: Map[Identifier, Set[Identifier]], quantifications: Seq[QuantificationTemplate[T]], lambdas: Seq[LambdaTemplate[T]], + functions: Set[(T, FunctionType, T)], baseSubst: Map[T, Arg[T]], pathVar: Identifier, aVar: T @@ -259,40 +293,56 @@ object Template { val freshSubst = exprVars.map { case (id, idT) => idT -> encoder.encodeId(id) } ++ manager.freshConds(pathVar -> aVar, condVars, condTree) val matcherSubst = baseSubst.collect { case (c, Right(m)) => c -> m } - var subst = freshSubst.mapValues(Left(_)) ++ baseSubst - // /!\ CAREFUL /!\ - // We have to be wary while computing the lambda subst map since lambdas can - // depend on each other. However, these dependencies cannot be cyclic so it - // suffices to make sure the traversal order is correct. + var subst = freshSubst.mapValues(Left(_)) ++ baseSubst var instantiation : Instantiation[T] = Instantiation.empty - var seen : Set[LambdaTemplate[T]] = Set.empty - - val lambdaKeys = lambdas.map(lambda => lambda.ids._1 -> lambda).toMap - def extractSubst(lambda: LambdaTemplate[T]): Unit = { - for { - dep <- lambda.dependencies.flatMap(p => lambdaKeys.get(p._1)) - if !seen(dep) - } extractSubst(dep) - - if (!seen(lambda)) { - val substMap = subst.mapValues(_.encoded) - val substLambda = lambda.substitute(encoder.substitute(substMap), matcherSubst) - val (idT, inst) = manager.instantiateLambda(substLambda) - instantiation ++= inst - subst += lambda.ids._2 -> Left(idT) - seen += lambda - } + + manager match { + case lmanager: LambdaManager[T] => + val funSubstituter = encoder.substitute(subst.mapValues(_.encoded)) + for ((b,tpe,f) <- functions) { + instantiation ++= lmanager.registerFunction(funSubstituter(b), tpe, funSubstituter(f)) + } + + // /!\ CAREFUL /!\ + // We have to be wary while computing the lambda subst map since lambdas can + // depend on each other. However, these dependencies cannot be cyclic so it + // suffices to make sure the traversal order is correct. + var seen : Set[LambdaTemplate[T]] = Set.empty + + val lambdaKeys = lambdas.map(lambda => lambda.ids._1 -> lambda).toMap + def extractSubst(lambda: LambdaTemplate[T]): Unit = { + for { + dep <- lambda.dependencies.flatMap(p => lambdaKeys.get(p._1)) + if !seen(dep) + } extractSubst(dep) + + if (!seen(lambda)) { + val substMap = subst.mapValues(_.encoded) + val substLambda = lambda.substitute(encoder.substitute(substMap), matcherSubst) + val (idT, inst) = lmanager.instantiateLambda(substLambda) + instantiation ++= inst + subst += lambda.ids._2 -> Left(idT) + seen += lambda + } + } + + for (l <- lambdas) extractSubst(l) + + case _ => } - for (l <- lambdas) extractSubst(l) + manager match { + case qmanager: QuantificationManager[T] => + for (q <- quantifications) { + val substMap = subst.mapValues(_.encoded) + val substQuant = q.substitute(encoder.substitute(substMap), matcherSubst) + val (qT, inst) = qmanager.instantiateQuantification(substQuant) + instantiation ++= inst + subst += q.qs._2 -> Left(qT) + } - for (q <- quantifications) { - val substMap = subst.mapValues(_.encoded) - val substQuant = q.substitute(encoder.substitute(substMap), matcherSubst) - val (qT, inst) = manager.instantiateQuantification(substQuant) - instantiation ++= inst - subst += q.qs._2 -> Left(qT) + case _ => } (subst, instantiation) @@ -300,13 +350,11 @@ object Template { def instantiate[T]( encoder: TemplateEncoder[T], - manager: QuantificationManager[T], + manager: TemplateManager[T], clauses: Seq[T], blockers: Map[T, Set[TemplateCallInfo[T]]], applications: Map[T, Set[App[T]]], - quantifications: Seq[QuantificationTemplate[T]], matchers: Map[T, Set[Matcher[T]]], - lambdas: Seq[LambdaTemplate[T]], substMap: Map[T, Arg[T]] ): Instantiation[T] = { @@ -314,20 +362,31 @@ object Template { val msubst = substMap.collect { case (c, Right(m)) => c -> m } val newClauses = clauses.map(substituter) + val newBlockers = blockers.map { case (b,fis) => substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(_.substitute(substituter, msubst)))) } var instantiation: Instantiation[T] = (newClauses, newBlockers, Map.empty) - for ((b,apps) <- applications; bp = substituter(b); app <- apps) { - val newApp = app.copy(caller = substituter(app.caller), args = app.args.map(_.substitute(substituter, msubst))) - instantiation ++= manager.instantiateApp(bp, newApp) + manager match { + case lmanager: LambdaManager[T] => + for ((b,apps) <- applications; bp = substituter(b); app <- apps) { + val newApp = app.copy(caller = substituter(app.caller), args = app.args.map(_.substitute(substituter, msubst))) + instantiation ++= lmanager.instantiateApp(bp, newApp) + } + + case _ => } - for ((b, matchs) <- matchers; bp = substituter(b); m <- matchs) { - val newMatcher = m.substitute(substituter, msubst) - instantiation ++= manager.instantiateMatcher(bp, newMatcher) + manager match { + case qmanager: QuantificationManager[T] => + for ((b, matchs) <- matchers; bp = substituter(b); m <- matchs) { + val newMatcher = m.substitute(substituter, msubst) + instantiation ++= qmanager.instantiateMatcher(bp, newMatcher) + } + + case _ => } instantiation @@ -339,7 +398,7 @@ object FunctionTemplate { def apply[T]( tfd: TypedFunDef, encoder: TemplateEncoder[T], - manager: QuantificationManager[T], + manager: TemplateManager[T], pathVar: (Identifier, T), arguments: Seq[(Identifier, T)], condVars: Map[Identifier, T], @@ -351,9 +410,8 @@ object FunctionTemplate { isRealFunDef: Boolean ) : FunctionTemplate[T] = { - val (clauses, blockers, applications, matchers, templateString) = - Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, - substMap = quantifications.map(q => q.qs).toMap, + val (clauses, blockers, applications, functions, matchers, templateString) = + Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, quantifications, optCall = Some(tfd)) val funString : () => String = () => { @@ -374,9 +432,10 @@ object FunctionTemplate { clauses, blockers, applications, - quantifications, - matchers, + functions, lambdas, + matchers, + quantifications, isRealFunDef, funString ) @@ -386,7 +445,7 @@ object FunctionTemplate { class FunctionTemplate[T] private( val tfd: TypedFunDef, val encoder: TemplateEncoder[T], - val manager: QuantificationManager[T], + val manager: TemplateManager[T], val pathVar: (Identifier, T), val args: Seq[T], val condVars: Map[Identifier, T], @@ -395,19 +454,15 @@ class FunctionTemplate[T] private( val clauses: Seq[T], val blockers: Map[T, Set[TemplateCallInfo[T]]], val applications: Map[T, Set[App[T]]], - val quantifications: Seq[QuantificationTemplate[T]], - val matchers: Map[T, Set[Matcher[T]]], + val functions: Set[(T, FunctionType, T)], val lambdas: Seq[LambdaTemplate[T]], + val matchers: Map[T, Set[Matcher[T]]], + val quantifications: Seq[QuantificationTemplate[T]], isRealFunDef: Boolean, stringRepr: () => String) extends Template[T] { private lazy val str : String = stringRepr() override def toString : String = str - - override def instantiate(aVar: T, args: Seq[Arg[T]]): Instantiation[T] = { - if (!isRealFunDef) manager.registerFree(tfd.params.map(_.id) zip args.map(_.left.get)) - super.instantiate(aVar, args) - } } class TemplateManager[T](protected[templates] val encoder: TemplateEncoder[T]) extends IncrementalState { diff --git a/src/main/scala/leon/solvers/templates/UnrollingBank.scala b/src/main/scala/leon/solvers/templates/UnrollingBank.scala index a84dc350e38275bf30b592d1e82badd0576b87c8..cb7fe90d280160d03a5b9fbc190a7851b3cd3c1b 100644 --- a/src/main/scala/leon/solvers/templates/UnrollingBank.scala +++ b/src/main/scala/leon/solvers/templates/UnrollingBank.scala @@ -28,7 +28,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat private val appInfos = new IncrementalMap[(T, App[T]), (Int, Int, T, T, Set[TemplateAppInfo[T]])]() private val appBlockers = new IncrementalMap[(T, App[T]), T]() private val blockerToApps = new IncrementalMap[T, (T, App[T])]() - private val functionVars = new IncrementalMap[TypeTree, Set[T]]() def push() { callInfos.push() @@ -37,7 +36,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat appInfos.push() appBlockers.push() blockerToApps.push() - functionVars.push() } def pop() { @@ -47,7 +45,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat appInfos.pop() appBlockers.pop() blockerToApps.pop() - functionVars.pop() } def clear() { @@ -57,7 +54,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat appInfos.clear() appBlockers.clear() blockerToApps.clear() - functionVars.clear() } def reset() { @@ -67,7 +63,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat appInfos.reset() appBlockers.reset() blockerToApps.clear() - functionVars.reset() } def dumpBlockers() = { @@ -142,14 +137,13 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat } private def freshAppBlocks(apps: Traversable[(T, App[T])]) : Seq[T] = { - apps.filter(!appBlockers.isDefinedAt(_)).toSeq.map { case app @ (blocker, App(caller, tpe, _)) => + apps.filter(!appBlockers.isDefinedAt(_)).toSeq.map { + case app @ (blocker, App(caller, tpe, _, _)) => + val firstB = encoder.encodeId(FreshIdentifier("b_lambda", BooleanType, true)) + val clause = encoder.mkImplies(encoder.mkNot(firstB), encoder.mkNot(blocker)) - val firstB = encoder.encodeId(FreshIdentifier("b_lambda", BooleanType, true)) - val freeEq = functionVars.getOrElse(tpe, Set()).toSeq.map(t => encoder.mkEquals(t, caller)) - val clause = encoder.mkImplies(encoder.mkNot(encoder.mkOr((freeEq :+ firstB) : _*)), encoder.mkNot(blocker)) - - appBlockers += app -> firstB - clause + appBlockers += app -> firstB + clause } } @@ -174,10 +168,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat val trArgs = template.tfd.params.map(vd => Left(bindings(Variable(vd.id)))) - for (vd <- template.tfd.params if vd.getType.isInstanceOf[FunctionType]) { - functionVars += vd.getType -> (functionVars.getOrElse(vd.getType, Set()) + bindings(vd.toVariable)) - } - // ...now this template defines clauses that are all guarded // by that activating boolean. If that activating boolean is // undefined (or false) these clauses have no effect... @@ -280,7 +270,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat newClauses :+= extension } - var fastAppInfos : Map[(T, App[T]), (Int, Set[TemplateAppInfo[T]])] = Map.empty + var fastAppInfos : Seq[((T, App[T]), (Int, Set[TemplateAppInfo[T]]))] = Seq.empty for ((id, (gen, _, _, infos)) <- newCallInfos; info @ TemplateCallInfo(tfd, args) <- infos) { var newCls = Seq[T]() @@ -294,7 +284,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat // we need to define this defBlocker and link it to definition val defBlocker = encoder.encodeId(FreshIdentifier("d", BooleanType)) defBlockers += info -> defBlocker - manager.implies(id, defBlocker) val template = templateGenerator.mkTemplate(tfd) //reporter.debug(template) @@ -328,6 +317,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat // We connect it to the defBlocker: blocker => defBlocker if (defBlocker != id) { newCls :+= encoder.mkImplies(id, defBlocker) + manager.implies(id, defBlocker) } reporter.debug("Unrolling behind "+info+" ("+newCls.size+")") @@ -338,7 +328,9 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat newClauses ++= newCls } - for ((app @ (b, _), (gen, infos)) <- thisAppInfos ++ fastAppInfos; info @ TemplateAppInfo(template, equals, args) <- infos) { + for ((app @ (b, _), (gen, infos)) <- thisAppInfos ++ fastAppInfos; + info @ TemplateAppInfo(tmpl, equals, args) <- infos; + template <- tmpl.left) { var newCls = Seq.empty[T] val lambdaBlocker = lambdaBlockers.get(info) match { @@ -347,7 +339,6 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat case None => val lambdaBlocker = encoder.encodeId(FreshIdentifier("d", BooleanType)) lambdaBlockers += info -> lambdaBlocker - manager.implies(b, lambdaBlocker) val (newExprs, callBlocks, appBlocks) = template.instantiate(lambdaBlocker, args) val blockExprs = freshAppBlocks(appBlocks.keys) @@ -367,6 +358,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat val enabler = if (equals == manager.trueT) b else encoder.mkAnd(equals, b) newCls :+= encoder.mkImplies(enabler, lambdaBlocker) + manager.implies(b, lambdaBlocker) reporter.debug("Unrolling behind "+info+" ("+newCls.size+")") for (cl <- newCls) { diff --git a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala index 3cadf39a6e3853196d4cdf6b7493ea91a6077c73..7ae02d83e0a6245c7345ef7349211e6c246221ac 100644 --- a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala @@ -257,7 +257,7 @@ trait AbstractZ3Solver extends Solver { } case other => - throw SolverUnsupportedError(other, this) + unsupported(other) } protected[leon] def toZ3Formula(expr: Expr, initialMap: Map[Identifier, Z3AST] = Map.empty): Z3AST = { diff --git a/src/main/scala/leon/termination/StructuralSize.scala b/src/main/scala/leon/termination/StructuralSize.scala index f59618e17ac4eec3881dbdfc30c2ad8133f5ae54..50de827c17168bacd3db23049ed76237f73f132b 100644 --- a/src/main/scala/leon/termination/StructuralSize.scala +++ b/src/main/scala/leon/termination/StructuralSize.scala @@ -32,7 +32,7 @@ trait StructuralSize { )) absFun.typed } - + def size(expr: Expr) : Expr = { def funDef(ct: ClassType, cases: ClassType => Seq[MatchCase]): FunDef = { // we want to reuse generic size functions for sub-types diff --git a/src/main/scala/leon/utils/PreprocessingPhase.scala b/src/main/scala/leon/utils/PreprocessingPhase.scala index 06f35dc3a47df0a48836e6137eaae96218745d39..72103a4f70098527f4f80dba71c47a4b46c5d2d1 100644 --- a/src/main/scala/leon/utils/PreprocessingPhase.scala +++ b/src/main/scala/leon/utils/PreprocessingPhase.scala @@ -36,7 +36,6 @@ class PreprocessingPhase(desugarXLang: Boolean = false, genc: Boolean = false) e MethodLifting andThen TypingPhase andThen synthesis.ConversionPhase andThen - CheckADTFieldsTypes andThen InliningPhase val pipeX = if (!genc && desugarXLang) { diff --git a/src/main/scala/leon/utils/SeqUtils.scala b/src/main/scala/leon/utils/SeqUtils.scala index f2290a68d11bc668c348af3954af16b95f0f7d88..ada7499120353737d34bc79e2c9cc312d6702580 100644 --- a/src/main/scala/leon/utils/SeqUtils.scala +++ b/src/main/scala/leon/utils/SeqUtils.scala @@ -8,7 +8,6 @@ import scala.collection.mutable.ArrayBuffer object SeqUtils { type Tuple[T] = Seq[T] - def cartesianProduct[T](seqs: Tuple[Seq[T]]): Seq[Tuple[T]] = { val sizes = seqs.map(_.size) val max = sizes.product @@ -59,6 +58,25 @@ object SeqUtils { rec(sum, arity) filterNot (_.head == 0) } + + def groupWhile[T](es: Seq[T])(p: T => Boolean): Seq[Seq[T]] = { + var res: Seq[Seq[T]] = Nil + + var c = es + while (!c.isEmpty) { + val (span, rest) = c.span(p) + + if (span.isEmpty) { + res :+= Seq(rest.head) + c = rest.tail + } else { + res :+= span + c = rest + } + } + + res + } } class CartesianView[+A](views: Seq[SeqView[A, Seq[A]]]) extends SeqView[Seq[A], Seq[Seq[A]]] { @@ -103,4 +121,4 @@ class CartesianView[+A](views: Seq[SeqView[A, Seq[A]]]) extends SeqView[Seq[A], ret } } -} \ No newline at end of file +} diff --git a/src/main/scala/leon/verification/InjectAsserts.scala b/src/main/scala/leon/verification/InjectAsserts.scala index 1bd9a695788877bd2a0034ec151294daddd5ab59..aa88b39dec1e45377aa77f87ded6a0535abac782 100644 --- a/src/main/scala/leon/verification/InjectAsserts.scala +++ b/src/main/scala/leon/verification/InjectAsserts.scala @@ -71,6 +71,12 @@ object InjectAsserts extends SimpleLeonPhase[Program, Program] { e ).setPos(e)) + case e @ CaseClass(cct, args) if cct.root.classDef.hasInvariant => + Some(assertion(FunctionInvocation(cct.root.invariant.get, Seq(e)), + Some("ADT invariant"), + e + ).setPos(e)) + case _ => None }) diff --git a/src/test/resources/regression/termination/valid/Ackermann.scala b/src/test/resources/regression/termination/valid/Ackermann.scala new file mode 100644 index 0000000000000000000000000000000000000000..11ea76bee4ae9ae9c8225642d413d0f9054691f1 --- /dev/null +++ b/src/test/resources/regression/termination/valid/Ackermann.scala @@ -0,0 +1,10 @@ +import leon.lang._ + +object Ackermann { + def ackermann(m: BigInt, n: BigInt): BigInt = { + require(m >= 0 && n >= 0) + if (m == 0) n + 1 + else if (n == 0) ackermann(m - 1, 1) + else ackermann(m - 1, ackermann(m, n - 1)) + } +} diff --git a/src/test/resources/regression/verification/purescala/invalid/InductiveQuantification.scala b/src/test/resources/regression/verification/purescala/invalid/InductiveQuantification.scala deleted file mode 100644 index 970b3b53bddb295db19308e3443309f478ee15cc..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/verification/purescala/invalid/InductiveQuantification.scala +++ /dev/null @@ -1,17 +0,0 @@ -import leon.lang._ - -object SizeInc { - - abstract class List[A] - case class Cons[A](head: A, tail: List[A]) extends List[A] - case class Nil[A]() extends List[A] - - def failling_1[A](x: List[A]): Int => Int = { - (i: Int) => x match { - case Cons(head, tail) => 1 + failling_1(tail)(i) - case Nil() => i - } - } ensuring { res => forall((a: Int) => res(a) > 0) } -} - -// vim: set ts=4 sw=4 et: diff --git a/src/test/resources/regression/verification/purescala/valid/InductiveQuantification.scala b/src/test/resources/regression/verification/purescala/valid/InductiveQuantification.scala deleted file mode 100644 index 4883043040fdb90df294aac09b076d16c7628cf4..0000000000000000000000000000000000000000 --- a/src/test/resources/regression/verification/purescala/valid/InductiveQuantification.scala +++ /dev/null @@ -1,26 +0,0 @@ -import leon.lang._ - -object SizeInc { - - abstract class List[A] - case class Cons[A](head: A, tail: List[A]) extends List[A] - case class Nil[A]() extends List[A] - - def sizeInc[A](x: List[A]): BigInt => BigInt = { - (i: BigInt) => x match { - case Cons(head, tail) => 1 + sizeInc(tail)(i) - case Nil() => i - } - } ensuring { res => forall((a: BigInt) => a > 0 ==> res(a) > 0) } - - def sizeInc2[A](x: BigInt): List[A] => BigInt = { - require(x > 0) - - (list: List[A]) => list match { - case Cons(head, tail) => 1 + sizeInc2(x)(tail) - case Nil() => x - } - } ensuring { res => forall((a: List[A]) => res(a) > 0) } -} - -// vim: set ts=4 sw=4 et: diff --git a/src/test/scala/leon/integration/solvers/QuantifierSolverSuite.scala b/src/test/scala/leon/integration/solvers/QuantifierSolverSuite.scala index 0e85b119bff11f1925d3a0177826701e0dd700d4..4ff5ccc71d159fd76ec1e48d99bce4be4dbfb125 100644 --- a/src/test/scala/leon/integration/solvers/QuantifierSolverSuite.scala +++ b/src/test/scala/leon/integration/solvers/QuantifierSolverSuite.scala @@ -108,6 +108,8 @@ class QuantifierSolverSuite extends LeonTestSuiteWithProgram { try { solver.assertCnstr(expr) solver.check match { + case Some(true) if sat && fix._1.reporter.warningCount > 0 => + fail(s"Solver $solver - Constraint ${expr.asString} doesn't guarantee model validity!?") case Some(true) if sat => // checkmodels ensures validity case Some(false) if !sat => // we were looking for unsat case res => fail(s"Solver $solver - Constraint ${expr.asString} has result $res!?") diff --git a/src/test/scala/leon/regression/termination/TerminationSuite.scala b/src/test/scala/leon/regression/termination/TerminationSuite.scala index 6c2df0820a2b0f0c19ee393c12e1d70b36bbf233..215c72272673c5c6e9bbd5173b2b334ed3fc753c 100644 --- a/src/test/scala/leon/regression/termination/TerminationSuite.scala +++ b/src/test/scala/leon/regression/termination/TerminationSuite.scala @@ -34,8 +34,7 @@ class TerminationSuite extends LeonRegressionSuite { val ignored = List( "verification/purescala/valid/MergeSort.scala", - "verification/purescala/valid/Nested14.scala", - "verification/purescala/valid/InductiveQuantification.scala" + "verification/purescala/valid/Nested14.scala" ) val t = if (ignored.exists(displayName.replaceAll("\\\\","/").endsWith)) { diff --git a/testcases/verification/math/RationalProps.scala b/testcases/verification/math/RationalProps.scala index aec07246e2e8f76b3a054cd202e7beaae32f3da5..0b13ff1aa3b665bdcfdc6fcb15180a1fa86eaa2d 100644 --- a/testcases/verification/math/RationalProps.scala +++ b/testcases/verification/math/RationalProps.scala @@ -7,63 +7,57 @@ import scala.language.postfixOps object RationalProps { def squarePos(r: Rational): Rational = { - require(r.isRational) r * r } ensuring(_ >= Rational(0)) def additionIsCommutative(p: Rational, q: Rational): Boolean = { - require(p.isRational && q.isRational) p + q == q + p } holds def multiplicationIsCommutative(p: Rational, q: Rational): Boolean = { - require(p.isRational && q.isRational) p * q == q * p } holds def lessThanIsTransitive(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational && p < q && q < r) + require(p < q && q < r) p < r } holds def lessEqualsIsTransitive(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational && p <= q && q <= r) + require(p <= q && q <= r) p <= r } holds def greaterThanIsTransitive(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational && p > q && q > r) + require(p > q && q > r) p > r } holds def greaterEqualsIsTransitive(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational && p >= q && q >= r) + require(p >= q && q >= r) p >= r } holds def distributionMult(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational) (p*(q + r)) ~ (p*q + p*r) } holds def reciprocalIsCorrect(p: Rational): Boolean = { - require(p.isRational && p.nonZero) + require(p.nonZero) (p * p.reciprocal) ~ Rational(1) } holds def additiveInverseIsCorrect(p: Rational): Boolean = { - require(p.isRational) (p + (-p)) ~ Rational(0) } holds //should not hold because q could be 0 def divByZero(p: Rational, q: Rational): Boolean = { - require(p.isRational && q.isRational) ((p / q) * q) ~ p } holds def divByNonZero(p: Rational, q: Rational): Boolean = { - require(p.isRational && q.isRational && q.nonZero) + require(q.nonZero) ((p / q) * q) ~ p } holds @@ -73,17 +67,16 @@ object RationalProps { */ def equivalentIsReflexive(p: Rational): Boolean = { - require(p.isRational) p ~ p } holds def equivalentIsSymmetric(p: Rational, q: Rational): Boolean = { - require(p.isRational && q.isRational && p ~ q) + require(p ~ q) q ~ p } holds def equivalentIsTransitive(p: Rational, q: Rational, r: Rational): Boolean = { - require(p.isRational && q.isRational && r.isRational && p ~ q && q ~ r) + require(p ~ q && q ~ r) p ~ r } holds } diff --git a/testcases/verification/quantification/invalid/SizeInc.scala b/testcases/verification/quantification/invalid/SizeInc.scala deleted file mode 100644 index 970b3b53bddb295db19308e3443309f478ee15cc..0000000000000000000000000000000000000000 --- a/testcases/verification/quantification/invalid/SizeInc.scala +++ /dev/null @@ -1,17 +0,0 @@ -import leon.lang._ - -object SizeInc { - - abstract class List[A] - case class Cons[A](head: A, tail: List[A]) extends List[A] - case class Nil[A]() extends List[A] - - def failling_1[A](x: List[A]): Int => Int = { - (i: Int) => x match { - case Cons(head, tail) => 1 + failling_1(tail)(i) - case Nil() => i - } - } ensuring { res => forall((a: Int) => res(a) > 0) } -} - -// vim: set ts=4 sw=4 et: diff --git a/testcases/verification/quantification/valid/SizeInc.scala b/testcases/verification/quantification/valid/SizeInc.scala deleted file mode 100644 index 9fe7ea96eb44bba951ff620507bad750c1560056..0000000000000000000000000000000000000000 --- a/testcases/verification/quantification/valid/SizeInc.scala +++ /dev/null @@ -1,26 +0,0 @@ -import leon.lang._ - -object SizeInc { - - abstract class List[A] - case class Cons[A](head: A, tail: List[A]) extends List[A] - case class Nil[A]() extends List[A] - - def sizeInc[A](x: List[A]): BigInt => BigInt = { - (i: BigInt) => x match { - case Cons(head, tail) => 1 + sizeInc(tail)(i) - case Nil() => i - } - } ensuring { res => forall((a: BigInt) => a > 0 ==> res(a) > 0) } - - def sizeInc2[A](x: BigInt): List[A] => BigInt = { - require(x > 0) - - (list: List[A]) => list match { - case Cons(head, tail) => 1 + sizeInc2(x)(tail) - case Nil() => x - } - } ensuring { res => forall((a: List[A]) => res(a) > 0) } -} - -// vim: set ts=4 sw=4 et: