From 4cc65ae34e663b4f8d1bbad8953fc03c213d3620 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ali=20Sinan=20K=C3=B6ksal?= <alisinan@gmail.com>
Date: Thu, 18 Nov 2010 12:21:26 +0000
Subject: [PATCH] An implementation of InductionTactic and its example use in
 ListWithSize

---
 src/purescala/InductionTactic.scala | 64 ++++++++++++++++++++++++++++-
 testcases/ListWithSize.scala        |  4 ++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/src/purescala/InductionTactic.scala b/src/purescala/InductionTactic.scala
index dc4616a2f..d0567d4a4 100644
--- a/src/purescala/InductionTactic.scala
+++ b/src/purescala/InductionTactic.scala
@@ -2,13 +2,75 @@ package purescala
 
 import purescala.Common._
 import purescala.Trees._
+import purescala.TypeTrees._
 import purescala.Definitions._
 
 class InductionTactic(reporter: Reporter) extends DefaultTactic(reporter) {
   override val description = "Induction tactic for suitable functions"
   override val shortDescription = "induction"
 
+  private def singleAbsClassDef(args: VarDecls) : Option[AbstractClassDef] = {
+    val filtered = args.filter(arg =>
+      arg.getType match {
+        case AbstractClassType(_) => true
+        case _ => false
+      })
+    if (filtered.size != 1) None else (filtered.head.getType match {
+      case AbstractClassType(classDef) => Some(classDef)
+      case _ => scala.Predef.error("This should not happen.")
+    })
+  } 
+
+  private def selectorsOfParentType(parentType: ClassType, ccd: CaseClassDef, expr: Expr) : Seq[Expr] = {
+    val childrenOfSameType = ccd.fields.filter(field => field.getType == parentType)
+    for (field <- childrenOfSameType) yield {
+      CaseClassSelector(ccd, expr, field.id).setType(parentType)
+    }
+  }
+
   override def generatePostconditions(funDef: FunDef) : Seq[VerificationCondition] = {
-    Seq(new VerificationCondition(BooleanLiteral(false), funDef, VCKind.Postcondition, this))
+    assert(funDef.body.isDefined)
+    val defaultPost = super.generatePostconditions(funDef)
+    singleAbsClassDef(funDef.args) match {
+      case Some(classDef) =>
+        val prec = funDef.precondition
+        val post = funDef.postcondition
+        val body = matchToIfThenElse(funDef.body.get)
+        val arg = funDef.args.head
+        val argAsVar = arg.toVariable
+
+        if (post.isEmpty) {
+          Seq.empty
+        } else {
+          val children = classDef.knownChildren
+          val conditionsForEachChild = (for (child <- classDef.knownChildren) yield (child match {
+            case ccd @ CaseClassDef(id, prnt, vds) =>
+              val selectors = selectorsOfParentType(classDefToClassType(classDef), ccd, argAsVar)
+              // if no subtrees of parent type, assert property for base case
+              val resFresh = FreshIdentifier("result", true).setType(body.getType)
+              val bodyAndPostForArg = Let(resFresh, body, replace(Map(ResultVariable() -> Variable(resFresh)), matchToIfThenElse(post.get)))
+
+              val conditionForChild = 
+                if (selectors.size == 0) 
+                  bodyAndPostForArg
+                else {
+                  val inductiveHypothesis = (for (sel <- selectors) yield {
+                    val resFresh = FreshIdentifier("result", true).setType(body.getType)
+                    val bodyAndPost = Let(resFresh, replace(Map(argAsVar -> sel), body), replace(Map(ResultVariable() -> Variable(resFresh), argAsVar -> sel), matchToIfThenElse(post.get))) 
+                    bodyAndPost
+                  })
+                  Implies(And(inductiveHypothesis), bodyAndPostForArg)
+                }
+              Implies(CaseClassInstanceOf(ccd, argAsVar), conditionForChild)
+            case _ => error("Abstract class has non-case class subtype.")
+          }))
+          println("Induction tactic yields the following vc:")
+          println(And(conditionsForEachChild))
+          Seq(new VerificationCondition(And(conditionsForEachChild), funDef, VCKind.Postcondition, this))
+        }
+      case None =>
+        reporter.warning("Induction tactic currently supports exactly one argument of abstract class type")
+        defaultPost
+    }
   }
 }
diff --git a/testcases/ListWithSize.scala b/testcases/ListWithSize.scala
index 70f1291ca..7e3523bb5 100644
--- a/testcases/ListWithSize.scala
+++ b/testcases/ListWithSize.scala
@@ -1,5 +1,6 @@
 import scala.collection.immutable.Set
 import funcheck.Annotations._
+import funcheck.Utils._
 
 object ListWithSize {
     sealed abstract class List
@@ -49,6 +50,9 @@ object ListWithSize {
       case Cons(x,xs) => nilAppend(xs)
     }) ensuring(res => res && append(l, Nil()) == l)
 
+    @induct
+    def nilAppendInductive(l : List) : Boolean = (append(l, Nil()) == l) holds
+
     // unclear if we needed this--it was meant to force folding
     def appendFold(x : Int, xs : List, ys : List) : Boolean = {
       true
-- 
GitLab