Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment: Less duplication, more aggressive let simplification #860

Open
wants to merge 2 commits into
base: scala-2
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Experiment: Less duplication, more aggressive let simplification
gsps committed Nov 20, 2020
commit c5edcdfa96b76053c2485a2758791e0d89370c44
7 changes: 4 additions & 3 deletions core/src/main/scala/stainless/ast/SymbolOps.scala
Original file line number Diff line number Diff line change
@@ -144,9 +144,10 @@ trait SymbolOps extends inox.ast.SymbolOps { self: TypeOps =>

def rewritePM(e: Expr): Option[Expr] = e match {
case m @ MatchExpr(scrut, cases) =>
val scrutVd = ValDef.fresh("scrut", scrut.getType).setPos(m)
val condsAndRhs = for (cse <- cases) yield {
val map = mapForPattern(scrut, cse.pattern)
val patCond = conditionForPattern[Path](scrut, cse.pattern, includeBinders = false)
val map = mapForPattern(scrutVd.toVariable, cse.pattern)
val patCond = conditionForPattern[Path](scrutVd.toVariable, cse.pattern, includeBinders = false)
val realCond = cse.optGuard match {
case Some(g) => patCond withCond replaceFromSymbols(map, g)
case None => patCond
@@ -170,7 +171,7 @@ trait SymbolOps extends inox.ast.SymbolOps { self: TypeOps =>
}
})

Some(bigIte)
Some(Let(scrutVd, scrut, bigIte).setPos(m))

case _ => None
}
44 changes: 44 additions & 0 deletions core/src/main/scala/stainless/solvers/InoxEncoder.scala
Original file line number Diff line number Diff line change
@@ -73,6 +73,50 @@ trait InoxEncoder extends ProgramEncoder {
import sourceProgram._
import sourceProgram.symbols._

// Lowering to Inox erases various trees, which provides many opportunities for let simplifications.
override def transform(fd: s.FunDef): t.FunDef = {
import t._
import exprOps._

def singleOccurences(expr: Expr): Set[Identifier] = {
import collection.mutable.HashMap
val counts = HashMap.empty[Identifier, Int]
preTraversal {
case v: Variable => counts.update(v.id, counts.getOrElse(v.id, 0) + 1)
case _ =>
} (expr)
counts.iterator.collect { case (id, 1) => id } .toSet
}

// Copied from Inox's SymbolOps. Doesn't really depend on symbols!
def simplifyLetsExt(expr: Expr, forced: Set[Identifier]): Expr = preMap({
case l1 @ Let(v1, Let(v2, e2, b2), b1) => Some(Let(v2, e2, Let(v1, b2, b1).copiedFrom(l1)).copiedFrom(l1))

case Let(v, e, v2) if v.toVariable == v2 => Some(e)

case Let(v, ts @ (
(_: Variable) |
TupleSelect(_: Variable, _) |
ADTSelector(_: Variable, _) |
FiniteMap(Seq(), _, _, _) |
FiniteBag(Seq(), _) |
FiniteSet(Seq(), _) |
IsConstructor(_: Variable, _)
), b) => Some(replaceFromSymbols(Map(v -> ts), b))

case Let(v, e, b) if forced.contains(v.id) => Some(replaceFromSymbols(Map(v -> e), b))

case _ => None
}, applyRec = true)(expr)

val newFd = super.transform(fd)

timers.verification.simplify.run {
val simplBody = simplifyLetsExt(newFd.fullBody, singleOccurences(newFd.fullBody))
newFd.copy(fullBody = simplBody)
}
}

override def transform(e: s.Expr): t.Expr = e match {
case m: s.MatchExpr =>
transform(matchToIfThenElse(m))
23 changes: 17 additions & 6 deletions core/src/main/scala/stainless/verification/AssertionInjector.scala
Original file line number Diff line number Diff line change
@@ -10,10 +10,12 @@ package verification
*/
trait AssertionInjector extends transformers.TreeTransformer {
val s: ast.Trees
val t: ast.Trees
val t: s.type

implicit val symbols: s.Symbols

import t.dsl._

val strictArithmetic: Boolean

private[this] var inWrappingMode: Boolean = false
@@ -52,12 +54,21 @@ trait AssertionInjector extends transformers.TreeTransformer {
ti
).copiedFrom(i), transform(v)).copiedFrom(e)

// case sel @ s.ADTSelector(expr, _) =>
// t.Assert(
// t.IsConstructor(transform(expr), sel.constructor.id).copiedFrom(e),
// Some("Cast error"),
// super.transform(e)
// ).copiedFrom(e)

case sel @ s.ADTSelector(expr, _) =>
t.Assert(
t.IsConstructor(transform(expr), sel.constructor.id).copiedFrom(e),
Some("Cast error"),
super.transform(e)
).copiedFrom(e)
let("recv" :: expr.getType, transform(expr)) { recv =>
t.Assert(
t.IsConstructor(recv, sel.constructor.id).copiedFrom(e),
Some("Cast error"),
super.transform(sel.copy(adt = recv).copiedFrom(e))
).copiedFrom(e)
}.copiedFrom(e)

case BVTyped(true, size, e0 @ s.Plus(lhs0, rhs0)) if checkOverflow =>
val lhs = transform(lhs0)