From 727ec5f8c433415a106adb2de588fbe587f98f80 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 11 Jun 2020 23:21:08 +0800 Subject: [PATCH 001/211] Inline Coq load path directly in generated certificates --- certification.md | 11 +- certification/coq/examples/rose_tree.v | 118 ++++++++++++++++++ .../certification/targets/coq/Coq.scala | 5 +- .../targets/coq/CoqCertificationTests.scala | 29 ++--- 4 files changed, 139 insertions(+), 24 deletions(-) create mode 100644 certification/coq/examples/rose_tree.v diff --git a/certification.md b/certification.md index 36ddff3b0..445f68e75 100644 --- a/certification.md +++ b/certification.md @@ -21,7 +21,7 @@ opam pin add coq-htt git+https://github.com/TyGuS/htt\#master --no-action --yes opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt ``` -Each synthesized Coq certificate imports `SSL`, a module consisting of predefined tactics. The module source may be compiled by running `make clean && make` in the directory `certification/coq`. +Before verifying a generated Coq certificate, make sure to compile the predefined tactics by running `make clean && make` in the directory `certification/coq`. Each generated Coq certificate adds this physical directory to the load path of Coq, and then maps it to the logical directory `SSL`. ### Running Synthesis with Certification @@ -30,5 +30,14 @@ Add the following flags to run synthesis with certification. - `--certTarget `: Currently supports value `coq`. - `--certDest ` (optional): Specifies the directory in which to generate a certificate file. Logs output to console if not provided. +For example, the following command produces a Coq certificate of the specification `listfree.syn`, and logs its contents to the console. +```bash +./suslik examples/listfree.syn --assert false --certTarget coq +``` + +By providing the `--certDest` flag, SuSLik writes out this certificate as a file to the specified directory. The following example command writes a Coq certificate named `listfree.v` to the project root directory. +```bash +./suslik examples/listfree.syn --assert false --certTarget coq --certDest . +``` \ No newline at end of file diff --git a/certification/coq/examples/rose_tree.v b/certification/coq/examples/rose_tree.v new file mode 100644 index 000000000..dd7fcbc25 --- /dev/null +++ b/certification/coq/examples/rose_tree.v @@ -0,0 +1,118 @@ +From mathcomp +Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. +From fcsl +Require Import prelude pred pcm unionmap heap. +From HTT +Require Import stmod stsep stlog stlogR. +From SSL +Require Import core. + +(* PREDICATES *) + +(* + +predicate rose_tree(loc x) { +| x == 0 => { self_card == 0 ; emp } +| not (x == 0) => { z < self_card; [x, 1] ** x :-> b ** buds(b)} +} + +predicate buds(loc x) { +| x == 0 => { self_card == 0 ; emp } +| not (x == 0) => { y < self_card /\ z < self_card; + [x, 3] ** x :-> v ** (x + 1) :-> r ** rose_tree(r) ** (x + 2) :-> nxt ** buds(nxt) } +} + + +*) + +Inductive rose_tree (x: ptr) (h: heap) : Prop := +| rose_tree0 of x == null of + h = empty +| rose_tree1 of x != null of + exists b, + exists h', + h = x :-> b \+ h' /\ buds b h' +with buds (x: ptr) (h: heap) : Prop := +| buds0 of x == null of + h = empty +| buds1 of x != null of + exists (v: nat) r nxt, + exists h' h'', + h = x :-> v \+ x .+ 1 :-> r \+ x .+ 2 :-> nxt \+ h' \+ h'' /\ rose_tree r h' /\ buds nxt h'' + +. + +(* SPECIFICATION *) + +(* +{ rose_tree(x) } + void rose_tree_free(loc x) +{ emp } + + *) + +Definition rose_tree_free_type := + forall (x: ptr), + STsep ( + fun h => rose_tree x h, + [vfun (_: unit) h => h = empty]). + +Definition rose_tree_free10_type := + forall (x: ptr), + STsep ( + fun h => rose_tree x h, + [vfun (_: unit) h => h = empty]). + +(* PROGRAM *) + +(* + +void rose_tree_free (loc x) { + if (x == 0) { + } else { + rose_tree_free10(x); + } +} + +void rose_tree_free10 (loc x) { + let b = *x; + if (b == 0) { + free(x); + } else { + let r = *(b + 1); + let n = *(b + 2); + rose_tree_free(r); + *x = n; + rose_tree_free10(x); + free(b); + } +} + +*) + +Program Definition rose_tree_free : rose_tree_free_type := + Fix (fun (rose_tree_free : rose_tree_free_type) x => + Do ( + let: rose_tree_free10 := + Fix (fun (rose_tree_free10: rose_tree_free_type) (x: ptr) => + Do ( + b <-- @read ptr x; + if b == 0 + then + dealloc x;; + dealloc (x .+ 1);; + dealloc (x .+ 2) + else + r <-- @read ptr (b .+ 1); + n <-- @read nat (b .+ 2); + rose_tree_free r;; + x ::= n;; + rose_tree_free10 x;; + dealloc b;; + dealloc (b .+ 1);; + dealloc (b .+ 2) + )) + in + if x == 0 then ret tt else rose_tree_free10 x)). + + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala index 6f95e4d25..68888ccef 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala @@ -1,5 +1,7 @@ package org.tygus.suslik.certification.targets.coq +import java.nio.file.Paths + import org.tygus.suslik.certification._ import org.tygus.suslik.certification.targets.coq.translation.Translation import org.tygus.suslik.certification.targets.coq.translation.Translation.TranslationException @@ -8,7 +10,8 @@ import org.tygus.suslik.logic.Environment object Coq extends CertificationTarget { val name: String = "Coq" - private val prelude = """ + private val loadPath = Paths.get("certification/coq").toFile.getCanonicalPath + private val prelude = s"""Add LoadPath "$loadPath" as SSL. From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. From fcsl diff --git a/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala index d16ee83f9..00417539b 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.coq import java.io.File -import java.nio.file.{Files, Path, Paths} +import java.nio.file.{Files, Paths} import org.scalatest.{FunSpec, Matchers} import org.tygus.suslik.synthesis.instances.PhasedSynthesis @@ -15,31 +15,16 @@ import scala.sys.process._ class CoqCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { val synthesis: Synthesis = new PhasedSynthesis - val certRoot: String = Files.createTempDirectory("suslik-").toFile.getCanonicalPath - val certLibPath: File = Files.createDirectory(Paths.get(certRoot, "lib")).toFile - val certOutPath: File = Files.createDirectory(Paths.get(certRoot, "out")).toFile + val certRoot: File = Files.createTempDirectory("suslik-").toFile override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = it(s"certifies that it $desc") { - synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = Coq, certDest = certOutPath)) + synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = Coq, certDest = certRoot)) val fname = testName.split('/').last - val pathToCertificate = Paths.get(certOutPath.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath - - def copyIfMissing(filename: String): Unit = { - val path = Paths.get(certRoot, filename) - if (!Files.exists(path)) Files.copy(Paths.get("certification/coq", filename), path) - } - - // compile the tactics library if needed - if (!Files.exists(Paths.get(certRoot, "lib/core.vo"))) { - copyIfMissing("Makefile") - copyIfMissing("_CoqProject") - copyIfMissing("lib/core.v") - s"make -C $certRoot".! // compile tactics - } - - // verify with target directory in load path - val result = s"coqc -R $certRoot SSL -vok $pathToCertificate".! + val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath + + // verify + val result = s"coqc -vok $pathToCertificate".! // check that Coq compilation succeeded assert(result == 0) From f9347fdb5de45e431032cbe584828d0bbffd6f39 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 03:47:47 +0800 Subject: [PATCH 002/211] Translate the proof producers directly (breaks tests until the same is done for program producers) --- certification/coq/examples/listfree.v | 6 +- .../targets/coq/language/Expressions.scala | 23 +- .../targets/coq/language/Sentences.scala | 4 + .../targets/coq/logic/Proof.scala | 54 ++--- .../targets/coq/logic/ProofProducers.scala | 102 ++++++++ .../targets/coq/logic/ProofSteps.scala | 218 ++++++++++++++++++ .../targets/coq/logic/rules/FailRules.scala | 19 -- .../coq/logic/rules/LogicalRules.scala | 26 --- .../targets/coq/logic/rules/NativeRules.scala | 57 ----- .../coq/logic/rules/OperationalRules.scala | 24 -- .../targets/coq/logic/rules/Rules.scala | 25 -- .../coq/logic/rules/UnfoldingRules.scala | 95 -------- .../coq/logic/rules/UnificationRules.scala | 12 - .../coq/translation/ProofTranslation.scala | 206 +++++++---------- .../targets/coq/translation/Translation.scala | 9 +- .../tygus/suslik/synthesis/StmtProducer.scala | 13 +- .../synthesis/rules/OperationalRules.scala | 2 +- .../synthesis/rules/UnfoldingRules.scala | 6 +- .../certification/sll/sll-singleton.syn | 10 +- 19 files changed, 469 insertions(+), 442 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/FailRules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/LogicalRules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/NativeRules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/OperationalRules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/Rules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnfoldingRules.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnificationRules.scala diff --git a/certification/coq/examples/listfree.v b/certification/coq/examples/listfree.v index 4f6d15ce2..2ff117730 100644 --- a/certification/coq/examples/listfree.v +++ b/certification/coq/examples/listfree.v @@ -23,6 +23,7 @@ Definition listfree_type := lseg x S h, [vfun (_: unit) h => h = empty ]). + Program Definition listfree : listfree_type := Fix (fun (listfree : listfree_type) x => Do ( @@ -38,11 +39,6 @@ Program Definition listfree : listfree_type := )). Next Obligation. (* pull out precondition ghosts *) - (* apply: ghR. *) - - (* move precondition heap to context *) - (* move=>h//=. *) - ssl_ghostelim_pre. (* move precondition ghosts to context *) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala index acecddc2e..793e803c7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala @@ -30,7 +30,7 @@ object Expressions { val acc2 = collector(acc1)(cond) val acc3 = collector(acc2)(l) collector(acc3)(r) - case a@CSApp(_, args, _) => + case a@CSApp(_, args, _, _) => val acc1 = if (p(a)) acc + a.asInstanceOf[R] else acc args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) case CPointsTo(loc, _, value) => @@ -57,8 +57,8 @@ object Expressions { CSetLiteral(elems.map(e => e.simplify)) case CIfThenElse(cond, left, right) => CIfThenElse(cond.simplify, left.simplify, right.simplify) - case CSApp(pred, args, tag) => - CSApp(pred, args.map(_.simplify), tag) + case CSApp(pred, args, tag, card) => + CSApp(pred, args.map(_.simplify), tag, card) case other => other } @@ -132,12 +132,26 @@ object Expressions { override def pp: String = "empty" } - case class CSApp(pred: String, var args: Seq[CExpr], tag: Option[Int] = Some(0)) extends CExpr { + case class CSApp(pred: String, args: Seq[CExpr], tag: Option[Int] = Some(0), card: CExpr) extends CExpr { override def pp: String = s"$pred ${args.map(arg => arg.pp).mkString(" ")}" override def ppp: String = s"$pred ${args.map(arg => arg.ppp).mkString(" ")}" + + val uniqueName: String = s"$pred${card.pp}" + val heapName: String = s"heap_$uniqueName" + val hypName: String = s"H_$uniqueName" } case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { + def unify(source: CSFormula): Map[CVar, CExpr] = { + val initialMap: Map[CVar, CExpr] = Map.empty + val m1 = source.ptss.zip(ptss).foldLeft(initialMap) { + case (acc, (p1, p2)) => acc ++ p1.vars.zip(p2.vars).toMap + } + source.apps.zip(apps).foldLeft(m1) { + case (acc, (a1, a2)) => acc ++ a1.vars.zip(a2.vars).toMap + } + } + override def pp: String = { val hs = heapVars.map(_.pp) if (ptss.isEmpty) apps match { @@ -159,6 +173,7 @@ object Expressions { } } + // TODO: deprecate def heapVars: Seq[CVar] = if (ptss.isEmpty) Seq.empty else (1 to apps.length).map(i => CVar(s"$heapName${"'" * i}")) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala index cfde52eff..30fa7eafe 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala @@ -81,6 +81,10 @@ sealed abstract class Sentence extends PrettyPrinting { } case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { + def unify(source: CAssertion): Map[CVar, CExpr] = { + sigma.unify(source.sigma) + } + def pureEx: Seq[CVar] = phi.collect(_.isInstanceOf[CVar]).toSeq diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala index 40cd7d17a..b94c4d545 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala @@ -2,7 +2,6 @@ package org.tygus.suslik.certification.targets.coq.logic import org.tygus.suslik.certification.targets.coq.language._ import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp object Proof { type ProofScriptProducer = Seq[String] => String @@ -14,17 +13,25 @@ object Proof { universalGhosts: Seq[CVar], fname: String) + case class UnfoldedSApp(substArgs: Map[CVar, CExpr], substEx: Map[CVar, CExpr], asn: CAssertion) + case class CEnvironment(spec: CFunSpec, predicates: Seq[CInductivePredicate], - ctx: Set[CVar] = Set.empty, + usedVars: Set[CVar] = Set.empty, existentials: Map[CVar, CExpr] = Map.empty, - callHeapVars: Seq[CVar] = Seq.empty) { + callHeapVars: Seq[CVar] = Seq.empty, + predUnfoldings: Map[CSApp, UnfoldedSApp] = Map.empty, + assumptions: Seq[CAssertion] = Seq.empty + ) { def copy(spec: CFunSpec = this.spec, predicates: Seq[CInductivePredicate] = this.predicates, - ctx: Set[CVar] = this.ctx, + usedVars: Set[CVar] = this.usedVars, existentials: Map[CVar, CExpr] = this.existentials, - callHeapVars: Seq[CVar] = this.callHeapVars): CEnvironment = - CEnvironment(spec, predicates, ctx, existentials, callHeapVars) + callHeapVars: Seq[CVar] = this.callHeapVars, + predUnfoldings: Map[CSApp, UnfoldedSApp] = this.predUnfoldings, + assumptions: Seq[CAssertion] = this.assumptions + ): CEnvironment = + CEnvironment(spec, predicates, usedVars, existentials, callHeapVars, predUnfoldings, assumptions) private var counter = 0 def generateFreshVar(v: CVar): CVar = { @@ -33,39 +40,4 @@ object Proof { freshVar } } - - case class CProofStep(app: CRuleApp, next: List[CProofStep]) - - case class CProof(root: CProofStep) extends PrettyPrinting { - override def pp: String = { - def prependArgsProducer(steps: Seq[String], kont: ProofScriptProducer): ProofScriptProducer = - steps1 => kont(steps ++ steps1) - - def joinProducer(steps: List[CProofStep], kont: ProofScriptProducer): ProofScriptProducer = - steps.foldLeft(kont) { - case (kontAcc, step) => - steps1 => traverse(step, prependArgsProducer(steps1, kontAcc)) - } - - def composeProducer(f: ProofScriptProducer, g: ProofScriptProducer): ProofScriptProducer = - steps => g(Seq(f(steps))) - - @scala.annotation.tailrec - def traverse(step: CProofStep, kont: ProofScriptProducer): String = { - val nextKont = composeProducer(step.app.fn, kont) - step.next match { - case hd :: tl => - traverse(hd, joinProducer(tl, nextKont)) - case Nil => - nextKont(Seq.empty) - } - } - - val builder = new StringBuilder() - builder.append("Next Obligation.\n") - builder.append(traverse(root, _.head)) - builder.append("Qed.\n") - builder.toString() - } - } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala new file mode 100644 index 000000000..cc9a7cadd --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala @@ -0,0 +1,102 @@ +package org.tygus.suslik.certification.targets.coq.logic + +import org.tygus.suslik.certification.targets.coq.language.{CInductiveClause, CInductivePredicate} +import org.tygus.suslik.certification.targets.coq.language.Expressions._ +import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal +import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.{OpenPostStep, OpenStep, ProofStep, SeqCompStep} + +object ProofProducers { + sealed abstract class ProofProducer { + val arity: Int + val fn: Seq[ProofStep] => ProofStep + + def apply(children: Seq[ProofStep]): ProofStep = { + assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") + fn(children) + } + + def >>(p: ProofProducer): ProofProducer = { + ChainedProofProducer(this, p) + } + + def partApply(s: ProofStep): ProofProducer = { + PartiallyAppliedProofProducer(this, s) + } + + def simplify: ProofProducer = this match { + case ChainedProofProducer(p1, IdProofProducer) => p1.simplify + case ChainedProofProducer(IdProofProducer, p2) => p2.simplify + case ChainedProofProducer(_, p2@ConstProofProducer(_)) => p2.simplify + case _ => this + } + } + + case class ChainedProofProducer(p1: ProofProducer, p2: ProofProducer) extends ProofProducer { + val arity: Int = p1.arity + p2.arity - 1 + val fn: Seq[ProofStep] => ProofStep = steps => { + val (steps1, steps2) = steps.splitAt(p1.arity) + val step = p1.fn(steps1) + p2.fn(step +: steps2) + } + } + + case class PartiallyAppliedProofProducer(p: ProofProducer, s: ProofStep) extends ProofProducer { + val arity: Int = p.arity - 1 + val fn: Seq[ProofStep] => ProofStep = steps => { + p.apply(s +: steps) + } + } + + case object IdProofProducer extends ProofProducer { + val arity: Int = 1 + val fn: Seq[ProofStep] => ProofStep = _.head + } + + case class ConstProofProducer(step: ProofStep) extends ProofProducer { + val arity: Int = 0 + val fn: Seq[ProofStep] => ProofStep = _ => step + } + + case class PrependProofProducer(s: ProofStep) extends ProofProducer { + val arity: Int = 1 + val fn: Seq[ProofStep] => ProofStep = steps => SeqCompStep(s, steps.head).simplify + } + + case class AppendProofProducer(s: ProofStep) extends ProofProducer { + val arity: Int = 1 + val fn: Seq[ProofStep] => ProofStep = steps => SeqCompStep(steps.head, s).simplify + } + + case class BranchProofProducer(app: CSApp, pred: CInductivePredicate, clauses: Seq[CInductiveClause], subgoals: Seq[CGoal]) extends ProofProducer { + val arity: Int = clauses.length + val fn: Seq[ProofStep] => ProofStep = steps => + if (steps.length == 1) steps.head else { + val condBranches = clauses.zip(steps).zip(subgoals).reverse.map{ case ((c, s), g) => + SeqCompStep(OpenPostStep(app, pred, c, g), s) + } + val ctail = condBranches.tail + val finalBranch = condBranches.head + SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }) + } + } + + case class FoldProofProducer[T](op: (T, ProofProducer) => ProofStep, item: T, bp: ProofProducer) extends ProofProducer { + val arity: Int = 1 + val fn: Seq[ProofStep] => ProofStep = steps => { + // partially apply a produced step to the BranchProducer of the downstream `bp` + @scala.annotation.tailrec + def isBase(curr: ProofProducer): Boolean = curr match { + case PartiallyAppliedProofProducer(p, _) => isBase(p) + case BranchProofProducer(_, _, _, _) => true + case _ => false + } + def update(curr: ProofProducer): ProofProducer = curr match { + case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) + case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) + case _:PartiallyAppliedProofProducer | _:BranchProofProducer if isBase(curr) => curr.partApply(steps.head) + case _ => curr + } + op(item, update(bp)) + } + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala new file mode 100644 index 000000000..cb08cefcb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala @@ -0,0 +1,218 @@ +package org.tygus.suslik.certification.targets.coq.logic + +import org.tygus.suslik.certification.targets.coq.language._ +import org.tygus.suslik.certification.targets.coq.language.Expressions._ +import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal + +object ProofSteps { + case class Proof(root: ProofStep) { + def pp: String = s"Next Obligation.\n${root.pp}\nQed.\n" + } + sealed abstract class ProofStep { + def pp: String = "" + + protected def nestedDestruct(items: Seq[CVar]): String = items.toList match { + case v1 :: v2 :: rest => + s"[${v1.pp} ${nestedDestruct(v2 :: rest)}]" + case v :: _ => + v.pp + case Nil => + "" + } + + def collect[R <: CExpr](p: CExpr => Boolean): Set[R] = { + def collector(acc: Set[R])(st: ProofStep): Set[R] = st match { + case WriteStep(to) => + acc ++ to.collect(p) + case ReadStep(to) => + acc ++ to.collect(p) + case AllocStep(to, _, _) => + acc ++ to.collect(p) + case SeqCompStep(s1,s2) => + val acc1 = collector(acc)(s1) + collector(acc1)(s2) + case CallStep(app, _) => + val argVars = app.args.flatMap(arg => arg.collect(p)).toSet + acc ++ argVars + case _ => + acc + } + + collector(Set.empty)(this) + } + } + + case class SeqCompStep(s1: ProofStep, s2: ProofStep) extends ProofStep { + override def pp: String = s"${s1.pp}${s2.pp}" + + def simplify: ProofStep = { + (s1, s2) match { + case (ReadStep(to), _) => simplifyBinding(to) + case (WriteStep(to), _) => simplifyBinding(to) + case (AllocStep(to, _, _), _) => simplifyBinding(to) + case _ => this + } + } + + def simplifyBinding(newvar: CVar): ProofStep = { + val used: Set[CVar] = s2.collect(_.isInstanceOf[CVar]) + if (used.exists(v => newvar.name.startsWith(v.name))) { + this + } else s2 // Do not generate bindings for unused variables + } + } + + case class WriteStep(to: CVar) extends ProofStep { + override def pp: String = s"ssl_write.\nssl_write_post ${to.pp}.\n" + } + + case class ReadStep(to: CVar) extends ProofStep { + override def pp: String = "ssl_read.\n" + } + + case class AllocStep(to: CVar, tpe: CoqType, sz: Int) extends ProofStep { + override def pp: String = s"ssl_alloc $to.\n" + } + + case class FreeStep(size: Int) extends ProofStep { + override def pp: String = { + val deallocStmts = (1 to size).map(_ => "ssl_dealloc.") + s"${deallocStmts.mkString("\n")}\n" + } + } + + case object OpenStep extends ProofStep { + override def pp: String = "case: ifP=>H_cond.\n" + } + + case class OpenPostStep(app: CSApp, pred: CInductivePredicate, clause: CInductiveClause, goal: CGoal) extends ProofStep { + override def pp: String = { + val builder = new StringBuilder() + builder.append(s"case ${app.hypName}; rewrite H_cond=>//= _.\n") + + val asn = clause.asn + val subst = goal.pre.unify(asn) + val ve0 = (asn.spatialEx ++ asn.pureEx).diff(pred.params.map(_._2)).distinct + val ve = ve0.map(subst(_)) + val ptss = asn.sigma.ptss + val apps = asn.sigma.apps + + // move constructor's value existentials to context + if (ve.nonEmpty) { + builder.append(s"move=>${ve.map(v => s"[${v.pp}]").mkString(" ")}.\n") + } + + // move constructor's heap existentials to context + if (apps.nonEmpty) { + builder.append(s"move=>${apps.map(app => s"[${app.heapName}]").mkString(" ")}.\n") + } + + // move constructor's pure part to context + builder.append(s"move=>[phi_${app.uniqueName}].\n") + + // substitute constructor's points-to assertions to conclusion + if (ptss.nonEmpty || apps.isEmpty) { + builder.append("move=>[->].\n") + } + + // move constructor's predicate apps to context + if (apps.nonEmpty) { + val appNames = apps.map(app => CVar(s"${app.hypName}")) + val hApps = nestedDestruct(appNames) + builder.append(s"move=>$hApps.\n") + } + + builder.toString() + } + } + + case class CallStep(app: CSApp, pureEx: Seq[CExpr]) extends ProofStep { + override def pp: String = { + val builder = new StringBuilder() + + // rearrange heap to put recursive heap component to the head + builder.append(s"put_to_head ${app.heapName}.\n") + builder.append("apply: bnd_seq.\n") + + // identify how many ghost values to pass to the call + for (v <- pureEx) builder.append(s"apply: (gh_ex ${v.pp}).\n") + + builder.append("apply: val_do=>//= _ ? ->; rewrite unitL=>_.\n") + + builder.toString() + } + } + + case class GhostElimStep(goal: CGoal) extends ProofStep { + override def pp: String = { + val builder = new StringBuilder() + + // Pull out any precondition ghosts and move precondition heap to the context + builder.append("ssl_ghostelim_pre.\n") + + val ghosts = goal.universalGhosts + val pre = goal.pre + val ptss = pre.sigma.ptss + val apps = pre.sigma.apps + + // move precondition's ghosts to context + if (ghosts.nonEmpty) { + builder.append("move=>") + builder.append(nestedDestruct(ghosts)) + builder.append("//=.\n") + } + + // substitute precondition's points-to assertions to conclusion + if (ptss.nonEmpty || apps.isEmpty) { + builder.append("move=>[->].\n") + } + + // move precondition's predicate apps to context + if (apps.nonEmpty) { + val hApps = nestedDestruct(apps.map(app => CVar(app.hypName))) + builder.append(s"move=>$hApps.\n") + } + + // move heap validity assertion generated by ghR to context + if (ghosts.nonEmpty) { + builder.append("move=>H_valid.\n") + } + + builder.toString() + } + } + + case object AbduceBranchStep extends ProofStep { + override def pp: String = "case: ifP=>H_cond.\n" + } + + case class EmpStep(spec: CFunSpec) extends ProofStep { + override def pp: String = { + val builder = new StringBuilder() + builder.append("ssl_emp;\n") + + + // instantiate any existentials from the fun spec post + val post = spec.post + val programVars = spec.programVars + val ve = (post.spatialEx ++ post.pureEx).diff(programVars).distinct + +// if (ve.nonEmpty) { +// val subs = ve.map(e => { +// // first, see if it matches any existentials produced by the Pick rule +// env.existentials.get(e) match { +// case Some(v) => v.pp +// case None => +// // if that doesn't work, match from the unrolled predicates +// +// } +// }) +// builder.append(s"exists ${subs.mkString(", ")};\n") +// } + + builder.append("ssl_emp_post.\n") + + builder.toString() + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/FailRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/FailRules.scala deleted file mode 100644 index 2e0cce973..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/FailRules.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language.Expressions.CExpr -import org.tygus.suslik.certification.targets.coq.logic.Proof.CEnvironment -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp - -object FailRules { - case class CAbduceBranchApp(cond: CExpr, env: CEnvironment) extends CRuleApp(env) { - override val nextEnvs: Seq[CEnvironment] = Seq(env, env) - - override def fn(steps: Seq[String]): String = { - val builder = new StringBuilder() - builder.append("case: ifP=>H_cond.\n") - steps.foreach(builder.append) - builder.append("\n") - builder.toString() - } - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/LogicalRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/LogicalRules.scala deleted file mode 100644 index 3e0dd9c11..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/LogicalRules.scala +++ /dev/null @@ -1,26 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp - -object LogicalRules { - case class CEmpApp(env: CEnvironment) extends CRuleApp(env) { - override def fn(steps: Seq[String]): String = { - val builder = new StringBuilder() - builder.append("ssl_emp;\n") - - // instantiate any existentials from the fun spec post - val postEx = { - (env.spec.post.pureEx ++ env.spec.post.spatialEx ++ env.spec.post.heapEx).diff(env.spec.programVars).distinct - } - if (postEx.nonEmpty) { - val postExSubs = postEx.map(e => env.existentials(e).pp) - builder.append(s"exists ${postExSubs.mkString(", ")};\n") - } - - builder.append("ssl_emp_post.\n") - steps.headOption.map(builder.append) - builder.toString() - } - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/NativeRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/NativeRules.scala deleted file mode 100644 index e2c43d509..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/NativeRules.scala +++ /dev/null @@ -1,57 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp -import org.tygus.suslik.certification.targets.coq.logic.Proof._ - -object NativeRules { - case class CGhostElimApp(goal: CGoal, env: CEnvironment) extends CRuleApp(env) { - private val appNames: Seq[CVar] = goal.pre.sigma.apps.map(app => CVar(s"H_${app.pred}")) - - override val nextEnvs: Seq[CEnvironment] = { - val nextVars = goal.universalGhosts.toSet ++ appNames ++ Set(CVar("h")) - Seq(env.copy(ctx = nextVars)) - } - - override def fn(steps: Seq[String]): String = { - val builder = new StringBuilder() - - // Pull out any precondition ghosts and move precondition heap to the context - builder.append("ssl_ghostelim_pre.\n") - - val ghosts = goal.universalGhosts - val pre = goal.pre - val ptss = pre.sigma.ptss - val apps = pre.sigma.apps - - // move precondition's ghosts to context - if (ghosts.nonEmpty) { - builder.append("move=>") - builder.append(nestedDestruct(ghosts)) - builder.append("//=.\n") - } - - // substitute precondition's points-to assertions to conclusion - if (ptss.nonEmpty || apps.isEmpty) { - builder.append("move=>[->].\n") - } - - // move precondition's predicate apps to context - if (apps.nonEmpty) { - val hApps = nestedDestruct(appNames) - builder.append(s"move=>$hApps.\n") - } - - // move heap validity assertion generated by ghR to context - if (ghosts.nonEmpty) { - builder.append("move=>H_valid.\n") - } - - // print the rest - steps.headOption.foreach(builder.append) - - builder.toString() - } - } - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/OperationalRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/OperationalRules.scala deleted file mode 100644 index f6e3d2c1a..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/OperationalRules.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language.Expressions.CVar -import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp - -object OperationalRules { - case class CWriteApp(to: CVar, env: CEnvironment) extends CRuleApp(env) { - override def fn(steps: Seq[String]): String = - s"ssl_write.\nssl_write_post ${to.pp}.\n" + steps.headOption.getOrElse("") - } - - case class CReadApp(env: CEnvironment) extends CRuleApp(env) { - override def fn(steps: Seq[String]): String = - "ssl_read.\n" + steps.headOption.getOrElse("") - } - - case class CFreeApp(size: Int, env: CEnvironment) extends CRuleApp(env) { - override def fn(steps: Seq[String]): String = { - val deallocStmts = (1 to size).map(_ => "ssl_dealloc.") - s"${deallocStmts.mkString("\n")}\n" + steps.headOption.getOrElse("") - } - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/Rules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/Rules.scala deleted file mode 100644 index 6dab03f24..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/Rules.scala +++ /dev/null @@ -1,25 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language.Expressions.CVar -import org.tygus.suslik.certification.targets.coq.logic.Proof._ - -object Rules { - abstract class CRuleApp(env: CEnvironment) { - val nextEnvs: Seq[CEnvironment] = Seq(env.copy()) - - // whether this should appear as an explicit proof step in the Coq script - val isExplicit: Boolean = true - - def fn(steps: Seq[String]): String = steps.headOption.getOrElse("") - - protected def nestedDestruct(items: Seq[CVar]): String = items.toList match { - case v1 :: v2 :: rest => - s"[${v1.pp} ${nestedDestruct(v2 :: rest)}]" - case v :: _ => - v.pp - case Nil => - "" - } - - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnfoldingRules.scala deleted file mode 100644 index 2a9927f09..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnfoldingRules.scala +++ /dev/null @@ -1,95 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language._ -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp - -object UnfoldingRules { - case class COpenApp(selectors: Seq[CExpr], pred: CInductivePredicate, env: CEnvironment) extends CRuleApp(env) { - private val clauses: Seq[CInductiveClause] = selectors.map(s => pred.clauses.find(_.selector == s).get) - - // maps from apps to coq proof names - private val clauseAppNames: Seq[Seq[CVar]] = clauses.map { - _.asn.sigma.apps.map(app => env.generateFreshVar(CVar(s"H_${app.pred}"))) - } - - // run this to generate my descendants each with an updated context - override val nextEnvs: Seq[CEnvironment] = { - clauses.zip(clauseAppNames).map { case (clause, appNames) => - val asn = clause.asn - val ve = (asn.spatialEx ++ asn.pureEx).diff(pred.params.map(_._2)).distinct - val he = asn.heapEx - env.copy(ctx = env.ctx ++ ve ++ he ++ appNames, callHeapVars = he) - } - } - - // run this after all my descendants return - override def fn(steps: Seq[String]): String = { - val builder = new StringBuilder() - builder.append("case: ifP=>H_cond.\n") - clauses.zip(steps).zip(clauseAppNames).foreach { case ((clause, step), appNames) => - // match current condition to correct constructor - builder.append(s"case H_${pred.name}; rewrite H_cond=>//= _.\n") - - val asn = clause.asn - val ve = (asn.spatialEx ++ asn.pureEx).diff(pred.params.map(_._2)).distinct - val he = asn.heapEx - val ptss = asn.sigma.ptss - val apps = asn.sigma.apps - - // move constructor's value existentials to context - if (ve.nonEmpty) { - builder.append(s"move=>${ve.map(v => s"[${v.pp}]").mkString(" ")}.\n") - } - - // move constructor's heap existentials to context - if (he.nonEmpty) { - builder.append(s"move=>${he.map(v => s"[${v.pp}]").mkString(" ")}.\n") - } - - // move constructor's pure part to context - builder.append(s"move=>[${pred.name}_phi].\n") - - // substitute constructor's points-to assertions to conclusion - if (ptss.nonEmpty || apps.isEmpty) { - builder.append("move=>[->].\n") - } - - // move constructor's predicate apps to context - if (appNames.nonEmpty) { - val hApps = nestedDestruct(appNames) - builder.append(s"move=>$hApps.\n") - } - - builder.append(step) - - } - - builder.append("\n") - builder.toString() - } - } - - case class CCallApp(fun: String, sub: Map[CVar, CExpr], env: CEnvironment) extends CRuleApp(env) { - override val nextEnvs: Seq[CEnvironment] = - Seq(env.copy(callHeapVars = env.callHeapVars.tail)) - - override def fn(steps: Seq[String]): String = { - val builder = new StringBuilder() - // rearrange heap to put recursive heap component to the head - builder.append(s"put_to_head ${env.callHeapVars.head.pp}.\n") - builder.append("apply: bnd_seq.\n") - // identify how many ghost values to pass to the call - val pureEx = env.spec.pureParams.map { case (_, v) => sub(v).asInstanceOf[CVar] } - for (v <- pureEx) builder.append(s"apply: (gh_ex ${v.pp}).\n") - - builder.append("apply: val_do=>//= _ ? ->; rewrite unitL=>_.\n") - - // print the rest - steps.headOption.foreach(builder.append) - - builder.toString() - } - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnificationRules.scala deleted file mode 100644 index 5cb7eb3b0..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/rules/UnificationRules.scala +++ /dev/null @@ -1,12 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.logic.rules - -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.Proof.CEnvironment -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp - -object UnificationRules { - case class CPickApp(subst: Map[CVar, CExpr], env: CEnvironment) extends CRuleApp(env) { - override val nextEnvs: Seq[CEnvironment] = Seq(env.copy(existentials = env.existentials ++ subst)) - override val isExplicit: Boolean = false - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala index cdcca6268..7a0f6d313 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala @@ -3,141 +3,109 @@ package org.tygus.suslik.certification.targets.coq.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.coq.language.Expressions._ import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.logic.rules.FailRules.CAbduceBranchApp -import org.tygus.suslik.certification.targets.coq.logic.rules.LogicalRules._ -import org.tygus.suslik.certification.targets.coq.logic.rules.NativeRules.CGhostElimApp -import org.tygus.suslik.certification.targets.coq.logic.rules.OperationalRules._ -import org.tygus.suslik.certification.targets.coq.logic.rules.Rules.CRuleApp -import org.tygus.suslik.certification.targets.coq.logic.rules.UnfoldingRules._ -import org.tygus.suslik.certification.targets.coq.logic.rules.UnificationRules.CPickApp +import org.tygus.suslik.certification.targets.coq.logic.ProofProducers._ +import org.tygus.suslik.certification.targets.coq.logic.ProofSteps._ import org.tygus.suslik.certification.targets.coq.translation.Translation._ -import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.unification.SpatialUnification -import org.tygus.suslik.synthesis.rules.FailRules.AbduceBranch -import org.tygus.suslik.synthesis.rules.LogicalRules.EmpRule -import org.tygus.suslik.synthesis.rules.OperationalRules._ -import org.tygus.suslik.synthesis.rules.UnfoldingRules -import org.tygus.suslik.synthesis.rules.UnfoldingRules._ -import org.tygus.suslik.synthesis.{BranchProducer, ExistentialProducer, GuardedProducer, PrependProducer} +import org.tygus.suslik.synthesis._ object ProofTranslation { - type ProofProducer = Producer[CProofStep] + private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) extends Traversable - private case class TraversalItem(node: CertTree.Node, stmt: Statement, cenv: CEnvironment) extends Traversable - - def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): CProof = { - val ruleApp = CGhostElimApp(goal, cenv) - val nextEnv = ruleApp.nextEnvs.head - - val traversalItem = TraversalItem(node, proc.body, nextEnv) - val proofBody = traverseProof(traversalItem, ruleAppProducer(ruleApp)) - CProof(proofBody) + def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): Proof = { + val nextEnv = cenv.copy(usedVars = goal.universalGhosts.toSet) + val traversalItem = TraversalItem(node, nextEnv) + val proofBody = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal))) + Proof(proofBody) } - private def joinProofProducer(steps: List[TraversalItem], kont: ProofProducer): ProofProducer = - joinProducer[CProofStep, TraversalItem](traverseProof)(steps, kont) - - private def ruleAppProducer(app: CRuleApp): ProofProducer = steps => CProofStep(app, steps) - - private def deriveCRuleApp(item: TraversalItem, currStmt: Option[Statement]): Option[CRuleApp] = { - val TraversalItem(node, _, cenv) = item - - val footprint = node.consume - val goal = node.goal - val stmtProducer = unwrapStmtProducer(node.kont) - (node.rule, stmtProducer) match { - case (EmpRule, _) => - Some(CEmpApp(cenv)) - case (ReadRule, PrependProducer(Load(to, _, _, _))) => - currStmt match { - case Some(Load(to1, _, _, _)) if to.name.startsWith(to1.name) => - Some(CReadApp(cenv)) - case _ => - None // bound variable was eliminated by SeqComp.simplify - } - case (WriteRule, PrependProducer(Store(to, _, _))) => - Some(CWriteApp(CVar(to.name), cenv)) - case (FreeRule, PrependProducer(Free(v))) => - footprint.pre.blocks.find(_.loc == v).map(b => CFreeApp(b.sz, cenv)) - case (CallRule, PrependProducer(Call(fun, _, _))) => - val allCands = goal.companionCandidates.reverse - val cands = if (goal.env.config.auxAbduction) allCands else allCands.take(1) - val funLabels = cands.map(a => a.toFunSpec) ++ // companions - goal.env.functions.values // components - - val subs = for { - f <- funLabels - - // Find all subsets of the goal's pre that might be unified - lilHeap = f.pre.sigma - largHeap = goal.pre.sigma - largSubHeap <- UnfoldingRules.findLargestMatchingHeap(lilHeap, largHeap) - callSubPre = goal.pre.copy(sigma = largSubHeap) - - // Try to unify f's precondition and found goal pre's subheaps - sourceAsn = f.pre - targetAsn = callSubPre - sub <- SpatialUnification.unify(targetAsn, sourceAsn) - } yield sub + def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { + def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { + case Skip => + (EmpStep(cenv.spec), cenv) + case Load(to, _, _, _) => + (ReadStep(CVar(to.name)), cenv) + case Store(to, _, _) => + (WriteStep(CVar(to.name)), cenv) + case Malloc(to, tpe, sz) => + (AllocStep(CVar(to.name), translateSSLType(tpe), sz), cenv) + case Free(v) => + val block = item.node.consume.pre.blocks.find(_.loc == v) + assert(block.nonEmpty) + (FreeStep(block.get.sz), cenv) + case Call(_, _, _) => + val csub = cenv.existentials + val sapp = cenv.assumptions.flatMap(_.sigma.apps).head + val pureEx = cenv.spec.pureParams.map { case (_, v) => csub(v).asInstanceOf[CVar] } + (CallStep(sapp, pureEx), cenv) + } - val sub = subs.head - val existingVars = cenv.ctx - def longestPrefix(s: String, vset: Set[CVar]): Option[CVar] = { - val prefixes = vset.filter(v1 => s.startsWith(v1.name)) - if (prefixes.isEmpty) None - else Some(prefixes.maxBy(_.name.length)) - } + def translateProducer(stmtProducer: StmtProducer, cenv: CEnvironment): (ProofProducer, CEnvironment) = { + stmtProducer match { + case ChainedProducer(p1, p2) => + val (k1, cenv1) = translateProducer(p1, cenv) + val (k2, cenv2) = translateProducer(p2, cenv1) + (k1 >> k2, cenv2) + case PartiallyAppliedProducer(p, _) => + translateProducer(p, cenv) + case ExistentialProducer(subst) => + val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) } + val cenv1 = cenv.copy(existentials = cenv.existentials ++ csub) + (IdProofProducer, cenv1) + case ConstProducer(s) => + val (step, cenv1) = translateOperation(s, cenv) + (ConstProofProducer(step), cenv1) + case PrependProducer(s) => + val (step, cenv1) = translateOperation(s, cenv) + (PrependProofProducer(step), cenv1) + case BranchProducer(selectors) => + val app = item.node.consume.pre.apps.headOption + .getOrElse(throw TranslationException("Open rule was called, but no predicate applications found")) + val pred = cenv.predicates + .find(_.name == app.pred) + .getOrElse(throw TranslationException(s"No predicate matching label ${app.pred} found in environment")) + val clauses = selectors.map(s => pred.clauses.find(_.selector == translateExpr(s)).get) + val sapp = translateHeaplet(item.node.consume.pre.apps.head).asInstanceOf[CSApp] + val subgoals = item.node.children.map(n => translateGoal(n.goal)) + (BranchProofProducer(sapp, pred, clauses, subgoals), cenv) + case GuardedProducer(_, _) => + (PrependProofProducer(AbduceBranchStep), cenv) + case _ => + (IdProofProducer, cenv) + } + } - val csub = sub.map { case (src, dest) => - val csrc = CVar(src.name) - // if any variables were renamed, substitute them - val simplifyingMapping = dest.vars - .map(v => (v, longestPrefix(v.name, existingVars))) - .filter(_._2.isDefined) - .map(el => (el._1, Var(el._2.get.name))) - .toMap - (csrc, translateExpr(dest.subst(simplifyingMapping))) + def generateNextItems(p: ProofProducer, cenv: CEnvironment): Seq[TraversalItem] = p match { + case BranchProofProducer(_, _, clauses, _) => + item.node.children.zip(clauses).map { case (node, clause) => + val cenv1 = cenv.copy(assumptions = cenv.assumptions ++ Seq(clause.asn)) + TraversalItem(node, cenv1) } - Some(CCallApp(fun.name, csub, cenv)) - case (Open, BranchProducer(selectors)) => - val app = footprint.pre.apps.headOption - .getOrElse(throw TranslationException("Open rule was called, but no predicate applications found")) - val pred = cenv.predicates - .find(_.name == app.pred) - .getOrElse(throw TranslationException(s"No predicate matching label ${app.pred} found in environment")) - Some(COpenApp(selectors.map(translateExpr), pred, cenv)) - case (_, ExistentialProducer(subst)) => - val csubst = subst.map { case (k, v) => CVar(k.name) -> translateExpr(v) } - Some(CPickApp(csubst, cenv)) - case (AbduceBranch, GuardedProducer(cond, _)) => - Some(CAbduceBranchApp(translateExpr(cond), cenv)) case _ => - None // rule has no effect on certification + item.node.children.map(node => TraversalItem(node, cenv)) } - } - @scala.annotation.tailrec - private def traverseProof(item: TraversalItem, kont: ProofProducer): CProofStep = { - val (currStmt, nextStmts) = expandStmt(item.stmt) - val childNodes = item.node.children - val (nextTraversalItems, nextKont) = deriveCRuleApp(item, currStmt) match { - case Some(cruleApp) => - val nextTraversalItems = childNodes - .zip(if (cruleApp.isExplicit) nextStmts else Seq(item.stmt)) - .zip(cruleApp.nextEnvs) - .map(i => TraversalItem(i._1._1, i._1._2, i._2)) - val nextKont = composeProducer[CProofStep](ruleAppProducer(cruleApp), kont) - (nextTraversalItems, nextKont) + // handle guard + def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: ProofProducer, cenv: CEnvironment): ProofProducer = nextKont match { + case _: BranchProofProducer => + nextItems.tail.foldLeft(nextKont >> kont) { + case (foldedP, item) => FoldProofProducer(traverseProof, item, foldedP) + } case _ => - (childNodes.map(TraversalItem(_, item.stmt, item.cenv)), kont) + nextKont >> kont } - nextTraversalItems match { - case hd :: tl => - traverseProof(hd, joinProofProducer(tl, nextKont)) - case Nil => - nextKont(List.empty) + // generate nested continuations + environments for children + val (p0, cenv1) = translateProducer(item.node.kont, item.cenv) + val p = p0.simplify + val nextItems = generateNextItems(p, cenv1) + val nextKont = updateProducerPost(nextItems, p, cenv1) + + nextItems.headOption match { + case Some(childHead) => + traverseProof(childHead, nextKont) + case None => + nextKont(Nil) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala index cab75caa9..cd0f6b075 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.certification.targets.coq.language.Expressions._ import org.tygus.suslik.certification.targets.coq.language.Statements._ import org.tygus.suslik.certification.targets.coq.language._ import org.tygus.suslik.certification.targets.coq.logic.Proof.{CEnvironment, CGoal, CProof} +import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.Proof import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.language._ @@ -82,7 +83,7 @@ object Translation { * @return the inductive predicates, fun spec, proof, and program translated to Coq */ def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): - (Seq[CInductivePredicate], CFunSpec, CProof, CProcedure) = { + (Seq[CInductivePredicate], CFunSpec, Proof, CProcedure) = { val cpreds = for (label <- (node.goal.pre.sigma.apps ++ node.goal.post.sigma.apps).map(_.pred).distinct) yield { val predicate = env.predicates(label) translateInductivePredicate(predicate.resolveOverloading(env)) @@ -160,12 +161,12 @@ object Translation { case IfThenElse(c, t, e) => CIfThenElse(translateExpr(c), translateExpr(t), translateExpr(e)) } - private def translateHeaplet(el: Heaplet): CExpr = el match { + def translateHeaplet(el: Heaplet): CExpr = el match { case PointsTo(loc, offset, value) => CPointsTo(translateExpr(loc), offset, translateExpr(value)) - case SApp(pred, args, tag, card) => CSApp(pred, args.map(translateExpr), tag) + case SApp(pred, args, tag, card) => CSApp(pred, args.map(translateExpr), tag, translateExpr(card)) } - private def translateAsn(el: Assertion): CAssertion = { + def translateAsn(el: Assertion): CAssertion = { val phi: CExpr = translateExpr(el.phi.toExpr).simplify val sigma = translateSFormula(el.sigma) CAssertion(phi, sigma) diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index a1350e5f4..2c82b5cea 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,8 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.Specifications.Goal +import org.tygus.suslik.logic.SApp +import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils /** @@ -167,4 +168,12 @@ case class ExistentialProducer(subst: Map[Var, Expr]) extends StmtProducer { val fn: Kont = liftToSolutions(stmts => stmts.head) } - +// Captures a predicate unfolding due to the Close rule +case class UnfoldingProducer(sapp: SApp, // the unfolded predicate application + substArgs: Map[Var, Expr], // substitutions for predicate parameters + substEx: Map[Var, Expr], // substitutions for predicate post existentials + asn: Assertion // the unfolded result + ) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = liftToSolutions(stmts => stmts.head) +} diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala index 46d5f1e41..4399f00ec 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala @@ -154,7 +154,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post.subst(x, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = ExistentialProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, goal.allHeaplets - subGoal.allHeaplets, this)) case _ => Nil } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index a8af8ad70..c41b8d9d4 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -125,7 +125,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { // Check that the goal's subheap had at least one unfolding callGoal = mkCallGoal(f, sub, callSubPre, goal) } yield { - val kont: StmtProducer = PrependProducer(Call(Var(f.name), args, l)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = ExistentialProducer(sub) >> PrependProducer(Call(Var(f.name), args, l)) >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(callGoal), kont, Footprint(largSubHeap, emp), this) } nubBy[RuleResult, Assertion](results, r => r.subgoals.head.pre) @@ -265,7 +265,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val env = goal.env def heapletResults(h: Heaplet): Seq[RuleResult] = h match { - case SApp(pred, args, Some(t), card) => + case h@SApp(pred, args, Some(t), card) => if (t >= env.config.maxCloseDepth) return Nil ruleAssert(env.predicates.contains(pred), @@ -295,7 +295,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPhi = post.phi && actualConstraints && actualSelector val newPost = Assertion(newPhi, goal.post.sigma ** actualBody - h) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnfoldingProducer(h, substArgs, freshExistentialsSubst, actualAssertion) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(goal.spawnChild(post = newPost)), kont, Footprint(emp, singletonHeap(h)), this) } diff --git a/src/test/resources/synthesis/certification/sll/sll-singleton.syn b/src/test/resources/synthesis/certification/sll/sll-singleton.syn index da4c7b73c..063fc8e2a 100644 --- a/src/test/resources/synthesis/certification/sll/sll-singleton.syn +++ b/src/test/resources/synthesis/certification/sll/sll-singleton.syn @@ -3,15 +3,15 @@ singly-linked list: construct a list with one element ##### -{ true ; ret :-> a } -void sll_singleton (int x, loc ret) -{ elems =i {x} ; ret :-> y ** sll(y, elems) } +{ true ; p :-> a } +void sll_singleton (int x, loc p) +{ elems =i {x} ; p :-> y ** sll(y, elems) } ##### -void sll_singleton (int x, loc ret) { +void sll_singleton (int x, loc p) { let y = malloc(2); - *ret = y; + *p = y; *(y + 1) = 0; *y = x; } \ No newline at end of file From 9a7cff3c2cac465ffddfe8903e142d7ab38a319a Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 11:29:11 +0800 Subject: [PATCH 003/211] Add pretty printing for cert tree --- .../tygus/suslik/certification/CertTree.scala | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index cd78ab6b3..8adaac26b 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -2,9 +2,10 @@ package org.tygus.suslik.certification import org.tygus.suslik.logic.Specifications.{Footprint, Goal} import org.tygus.suslik.synthesis.SearchTree.{AndNode, NodeId, OrNode} -import org.tygus.suslik.synthesis.StmtProducer +import org.tygus.suslik.synthesis.{StmtProducer, SynthesisException} import org.tygus.suslik.synthesis.rules.Rules.{RuleResult, SynthesisRule} +import scala.Console._ import scala.collection.mutable object CertTree { @@ -72,4 +73,38 @@ object CertTree { childrenMap.clear parentMap.clear } + + // Pretty prints the tree roted at a node (tries the root node by default) + def pp(id: NodeId = Vector())(implicit ind: Int = 0): Unit = { + def getIndent(implicit ind: Int): String = if (ind <= 0) "" else "| " * ind + + def showFootprint(f: Footprint): String = s"$GREEN{${f.pre.pp}}$MAGENTA{${f.post.pp}}$RESET" + + def visit(n: Node)(implicit ind: Int): Unit = { + val children = n.children + + print(s"$RESET$getIndent") + println(s"${RED}NODE ${n.id}") + + print(s"$RESET$getIndent") + println(s"Goal to solve:") + + print(s"$RESET$getIndent") + println(s"$BLUE${n.goal.pp.replaceAll("\n", s"\n$RESET$getIndent$BLUE")}") + + print(s"$RESET$getIndent") + println(s"Rule applied: $YELLOW${n.rule}") + + print(s"$RESET$getIndent") + val childProds = if (children.nonEmpty) children.map(c => showFootprint(c.produce)).mkString(", ") else "nothing" + println(s"Footprint: ${showFootprint(n.consume)} --> $childProds") + + println() + + for (child <- children) visit(child)(ind + 1) + } + + val n = get(id).getOrElse(throw SynthesisException(s"No node found matching id $id")) + visit(n) + } } From 474c8b381962b03f9142f549f9a1e84d7aca84d2 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 16:40:19 +0800 Subject: [PATCH 004/211] Add flags to print CTree or dump to file --- .../tygus/suslik/certification/CertTree.scala | 61 ++++++++++--------- .../tygus/suslik/synthesis/SynConfig.scala | 2 + .../tygus/suslik/synthesis/Synthesis.scala | 16 ++++- .../suslik/synthesis/SynthesisRunner.scala | 8 +++ 4 files changed, 58 insertions(+), 29 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index 8adaac26b..3cf8c3eb9 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -30,6 +30,30 @@ object CertTree { override def equals(obj: Any): Boolean = obj.isInstanceOf[Node] && (obj.asInstanceOf[Node].id == this.id) override def hashCode(): Int = id.hashCode() + + private def getIndent(implicit ind: Int): String = if (ind <= 0) "" else "| " * ind + + private def showFootprint(f: Footprint): String = s"$GREEN{${f.pre.pp}}$MAGENTA{${f.post.pp}}$RESET" + + def pp(implicit ind: Int): String = { + val builder = new StringBuilder() + builder.append(s"$RESET$getIndent") + builder.append(s"${RED}NODE ${id}\n") + + builder.append(s"$RESET$getIndent") + builder.append(s"Goal to solve:\n") + + builder.append(s"$RESET$getIndent") + builder.append(s"$BLUE${goal.pp.replaceAll("\n", s"\n$RESET$getIndent$BLUE")}\n") + + builder.append(s"$RESET$getIndent") + builder.append(s"Rule applied: $YELLOW${rule}\n") + + builder.append(s"$RESET$getIndent") + val childProds = if (children.nonEmpty) children.map(c => showFootprint(c.produce)).mkString(", ") else "nothing" + builder.append(s"Footprint: ${showFootprint(consume)} --> $childProds\n") + builder.toString() + } } // [Certify]: Maintain a certification tree as a pair of bidirectional hash maps @@ -74,34 +98,15 @@ object CertTree { parentMap.clear } - // Pretty prints the tree roted at a node (tries the root node by default) - def pp(id: NodeId = Vector())(implicit ind: Int = 0): Unit = { - def getIndent(implicit ind: Int): String = if (ind <= 0) "" else "| " * ind - - def showFootprint(f: Footprint): String = s"$GREEN{${f.pre.pp}}$MAGENTA{${f.post.pp}}$RESET" - - def visit(n: Node)(implicit ind: Int): Unit = { - val children = n.children - - print(s"$RESET$getIndent") - println(s"${RED}NODE ${n.id}") - - print(s"$RESET$getIndent") - println(s"Goal to solve:") - - print(s"$RESET$getIndent") - println(s"$BLUE${n.goal.pp.replaceAll("\n", s"\n$RESET$getIndent$BLUE")}") - - print(s"$RESET$getIndent") - println(s"Rule applied: $YELLOW${n.rule}") - - print(s"$RESET$getIndent") - val childProds = if (children.nonEmpty) children.map(c => showFootprint(c.produce)).mkString(", ") else "nothing" - println(s"Footprint: ${showFootprint(n.consume)} --> $childProds") - - println() - - for (child <- children) visit(child)(ind + 1) + // Pretty prints the tree rooted at a node (tries the root node by default) + def pp(id: NodeId = Vector())(implicit ind: Int = 0): String = { + def visit(n: Node)(implicit ind: Int): String = { + val builder = new StringBuilder() + builder.append(n.pp) + builder.append("\n") + for (child <- n.children) + builder.append(visit(child)(ind + 1)) + builder.toString() } val n = get(id).getOrElse(throw SynthesisException(s"No node found matching id $id")) diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala index eb1539a3f..6e5a9c538 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala @@ -42,6 +42,8 @@ case class SynConfig( timeOut: Long = 120000, inputFormat: InputFormat = dotSyn, startTime: Deadline = Deadline.now, + printTree: Boolean = false, + treeDest: File = null, // Certification certTarget: CertificationTarget = null, certDest: File = null, diff --git a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala index 5ce56d88f..e6b5b3307 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala @@ -1,5 +1,7 @@ package org.tygus.suslik.synthesis +import java.io.PrintWriter + import org.tygus.suslik.certification.CertTree import org.tygus.suslik.language.Statements.{Solution, _} import org.tygus.suslik.logic.Specifications._ @@ -36,6 +38,7 @@ trait Synthesis extends SepLogicUtils { try { synthesize(goal)(stats = stats) match { case Some((body, helpers)) => + printTree val main = Procedure(fspec, body) (main :: helpers, stats) case None => @@ -143,7 +146,7 @@ trait Synthesis extends SepLogicUtils { // Check if any of the expansions is a terminal expansions.find(_.subgoals.isEmpty) match { case Some(e) => - if (config.certTarget != null) { + if (config.certTarget != null || config.printTree) { // [Certify]: Add a terminal node and its ancestors to the certification tree CertTree.addSuccessfulPath(node, e) } @@ -241,4 +244,15 @@ trait Synthesis extends SepLogicUtils { } } + protected def printTree(implicit config: SynConfig, ind: Int = 0): Unit = { + if (config.printTree) { + val tree = CertTree.pp() + println() + if (config.treeDest == null) println(tree) else { + new PrintWriter(config.treeDest) { write(tree); close() } + val msg = s"Successful derivations saved to ${config.treeDest.getCanonicalPath}" + println(s"$MAGENTA$msg") + } + } + } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index bbc8c759f..7655ae110 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -189,6 +189,14 @@ object SynthesisRunner extends SynthesisRunnerUtil { rc.copy(synConfig = rc.synConfig.copy(memoization = b)) }.text("enable memoization; default: true") + opt[Boolean](name="printTree").action { (b, rc) => + rc.copy(synConfig = rc.synConfig.copy(printTree = b)) + }.text("print tree of successful derivations to path; default: false") + + opt[File](name="treeDest").action { (f, rc) => + rc.copy(synConfig = rc.synConfig.copy(treeDest = f)) + }.text("write tree of successful derivations to path; default: none") + opt[CertificationTarget](name="certTarget").action { (t, rc) => rc.copy(synConfig = rc.synConfig.copy(certTarget = t)) }.text("set certification target; default: none") From fc5bc93e4f9505412fcf06cefba8816a3f111f0d Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 18:17:26 +0800 Subject: [PATCH 005/211] Goodbye colors and indentation --- certification.md | 17 +++++++++- .../tygus/suslik/certification/CertTree.scala | 34 +++++++++---------- .../tygus/suslik/synthesis/Synthesis.scala | 2 +- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/certification.md b/certification.md index 445f68e75..70af1d1e6 100644 --- a/certification.md +++ b/certification.md @@ -40,4 +40,19 @@ By providing the `--certDest` flag, SuSLik writes out this certificate as a file ```bash ./suslik examples/listfree.syn --assert false --certTarget coq --certDest . -``` \ No newline at end of file +``` + +### Viewing The Trace + +This package extracts the successful derivations from the synthesis process into a tree structure, for post-hoc access to information needed for certification. + +A standalone representation of this tree can be printed via the the following flags. + +- `--printTree `: Set to `true` to enable printing. +- `--treeDest ` (optional): Specifies the path to output the tree as a file. If not provided, logs output to console instead. + +For example, the following command logs the tree to a file called `trace.out` in the project root directory. + +```bash +./suslik examples/listfree.syn --assert false --printTree true --treeDest trace.out +``` diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index 3cf8c3eb9..12bf739ab 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -5,11 +5,10 @@ import org.tygus.suslik.synthesis.SearchTree.{AndNode, NodeId, OrNode} import org.tygus.suslik.synthesis.{StmtProducer, SynthesisException} import org.tygus.suslik.synthesis.rules.Rules.{RuleResult, SynthesisRule} -import scala.Console._ import scala.collection.mutable object CertTree { - + private var counter: Int = 0 /** * [Certify]: A utility for traversing a successful synthesis result during certification * @param id the NodeId of the associated or-node in the synthesis search tree @@ -25,33 +24,34 @@ object CertTree { rule: SynthesisRule, produce: Footprint, consume: Footprint) { + val cid: Int = { val n = counter; counter += 1; n } def children: List[Node] = childrenMap.getOrElse(this, List.empty).reverse def parent: Option[Node] = parentMap.get(this) override def equals(obj: Any): Boolean = obj.isInstanceOf[Node] && (obj.asInstanceOf[Node].id == this.id) override def hashCode(): Int = id.hashCode() - private def getIndent(implicit ind: Int): String = if (ind <= 0) "" else "| " * ind - private def showFootprint(f: Footprint): String = s"$GREEN{${f.pre.pp}}$MAGENTA{${f.post.pp}}$RESET" + private def showFootprint(f: Footprint): String = s"{${f.pre.pp}}{${f.post.pp}}" + + def pp: String = { + val cs = children - def pp(implicit ind: Int): String = { val builder = new StringBuilder() - builder.append(s"$RESET$getIndent") - builder.append(s"${RED}NODE ${id}\n") + builder.append(s"Node <${hashCode()}>\n") - builder.append(s"$RESET$getIndent") builder.append(s"Goal to solve:\n") - builder.append(s"$RESET$getIndent") - builder.append(s"$BLUE${goal.pp.replaceAll("\n", s"\n$RESET$getIndent$BLUE")}\n") + builder.append(s"${goal.pp}\n") - builder.append(s"$RESET$getIndent") - builder.append(s"Rule applied: $YELLOW${rule}\n") + builder.append(s"Rule applied: $rule\n") - builder.append(s"$RESET$getIndent") - val childProds = if (children.nonEmpty) children.map(c => showFootprint(c.produce)).mkString(", ") else "nothing" + val childProds = if (cs.nonEmpty) cs.map(c => showFootprint(c.produce)).mkString(", ") else "nothing" builder.append(s"Footprint: ${showFootprint(consume)} --> $childProds\n") + + builder.append(s"Parent node: ${parent.map(_.hashCode()).getOrElse("none")}\n") + builder.append(s"Child nodes: [${cs.map(_.hashCode()).mkString(", ")}]\n") + builder.toString() } } @@ -99,13 +99,13 @@ object CertTree { } // Pretty prints the tree rooted at a node (tries the root node by default) - def pp(id: NodeId = Vector())(implicit ind: Int = 0): String = { - def visit(n: Node)(implicit ind: Int): String = { + def pp(id: NodeId = Vector()): String = { + def visit(n: Node): String = { val builder = new StringBuilder() builder.append(n.pp) builder.append("\n") for (child <- n.children) - builder.append(visit(child)(ind + 1)) + builder.append(visit(child)) builder.toString() } diff --git a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala index f5002a9dc..bdedead54 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala @@ -238,7 +238,7 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof } } - protected def printTree(implicit config: SynConfig, ind: Int = 0): Unit = { + protected def printTree(implicit config: SynConfig): Unit = { if (config.printTree) { val tree = CertTree.pp() println() From 44df27950c74d80ff390a78de42c5eddbb3ee99b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 23:32:42 +0800 Subject: [PATCH 006/211] Translate the program producers directly --- .../coq/language/StatementProducers.scala | 100 +++++++++++++ .../coq/translation/ProgramTranslation.scala | 132 +++++++----------- 2 files changed, 154 insertions(+), 78 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala new file mode 100644 index 000000000..772a12f38 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala @@ -0,0 +1,100 @@ +package org.tygus.suslik.certification.targets.coq.language + +import org.tygus.suslik.certification.targets.coq.language.Expressions.CExpr +import org.tygus.suslik.certification.targets.coq.language.Statements.{CIf, CSeqComp, CStatement} + +object StatementProducers { + type Kont = Seq[CStatement] => CStatement + + sealed abstract class CStmtProducer { + val arity: Int + val fn: Kont + + def apply(children: Seq[CStatement]): CStatement = { + assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") + fn(children) + } + + def >>(p: CStmtProducer): CStmtProducer = { + ChainedCStmtProducer(this, p) + } + + def partApply(s: CStatement): CStmtProducer = { + PartiallyAppliedCStmtProducer(this, s) + } + + def simplify: CStmtProducer = this match { + case ChainedCStmtProducer(p1, IdCStmtProducer) => p1.simplify + case ChainedCStmtProducer(IdCStmtProducer, p2) => p2.simplify + case ChainedCStmtProducer(_, p2@ConstCStmtProducer(_)) => p2.simplify + case _ => this + } + } + + case class ChainedCStmtProducer(p1: CStmtProducer, p2: CStmtProducer) extends CStmtProducer { + val arity: Int = p1.arity + p2.arity - 1 + val fn: Kont = stmts => { + val (stmts1, stmts2) = stmts.splitAt(p1.arity) + val s = p1.fn(stmts1) + p2.fn(s +: stmts2) + } + } + + case class PartiallyAppliedCStmtProducer(p: CStmtProducer, s: CStatement) extends CStmtProducer { + val arity: Int = p.arity - 1 + val fn: Kont = stmts => { + p.apply(s +: stmts) + } + } + + case object IdCStmtProducer extends CStmtProducer { + val arity: Int = 1 + val fn: Kont = _.head + } + + case class ConstCStmtProducer(s: CStatement) extends CStmtProducer { + val arity: Int = 0 + val fn: Kont = _ => s + } + + case class PrependCStmtProducer(s: CStatement) extends CStmtProducer { + val arity: Int = 1 + val fn: Kont = stmts => CSeqComp(s, stmts.head).simplify + } + + case class AppendCStmtProducer(s: CStatement) extends CStmtProducer { + val arity: Int = 1 + val fn: Kont = stmts => CSeqComp(stmts.head, s).simplify + } + + case class BranchCStmtProducer(selectors: Seq[CExpr]) extends CStmtProducer { + val arity: Int = selectors.length + val fn: Kont = stmts => + if (stmts.length == 1) stmts.head else { + val cond_branches = selectors.zip(stmts).reverse + val ctail = cond_branches.tail + val finalBranch = cond_branches.head._2 + ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb).simplify } + } + } + + case class FoldCStmtProducer[T](op: (T, CStmtProducer) => CStatement, item: T, bp: CStmtProducer) extends CStmtProducer { + val arity: Int = 1 + val fn: Kont = steps => { + // partially apply a produced step to the BranchProducer of the downstream `bp` + @scala.annotation.tailrec + def isBase(curr: CStmtProducer): Boolean = curr match { + case PartiallyAppliedCStmtProducer(p, _) => isBase(p) + case _: BranchCStmtProducer => true + case _ => false + } + def update(curr: CStmtProducer): CStmtProducer = curr match { + case FoldCStmtProducer(op, item, bp) => FoldCStmtProducer(op, item, update(bp)) + case ChainedCStmtProducer(p1, p2) => ChainedCStmtProducer(update(p1), update(p2)) + case _: PartiallyAppliedCStmtProducer | _: BranchCStmtProducer if isBase(curr) => curr.partApply(steps.head) + case _ => curr + } + op(item, update(bp)) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala index 61621fc8d..291d997e2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala @@ -2,104 +2,80 @@ package org.tygus.suslik.certification.targets.coq.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.coq.language.Expressions._ +import org.tygus.suslik.certification.targets.coq.language.StatementProducers._ import org.tygus.suslik.certification.targets.coq.language.Statements._ import org.tygus.suslik.certification.targets.coq.logic.Proof._ import org.tygus.suslik.certification.targets.coq.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ -import org.tygus.suslik.synthesis.rules.FailRules.AbduceBranch -import org.tygus.suslik.synthesis.rules.LogicalRules._ -import org.tygus.suslik.synthesis.rules.OperationalRules._ -import org.tygus.suslik.synthesis.rules.UnfoldingRules._ object ProgramTranslation { - type CStmtProducer = Producer[CStatement] - - private case class TraversalItem(node: CertTree.Node, stmt: Statement) extends Traversable + private case class TraversalItem(node: CertTree.Node) extends Traversable def translate(node: CertTree.Node, proc: Procedure, goal: CGoal): CStatement = { - val traversalItem = TraversalItem(node, proc.body) - traverseStmt(traversalItem, _.head) + val traversalItem = TraversalItem(node) + traverseStmt(traversalItem, IdCStmtProducer) } - private def joinCStmtProducer(steps: List[TraversalItem], kont: CStmtProducer): CStmtProducer = - joinProducer[CStatement, TraversalItem](traverseStmt)(steps, kont) - - private def seqCompProducer(s: CStatement): CStmtProducer = { - case hd :: _ => CSeqComp(s, hd) - case Nil => s - } - - private def openProducer(selectors: Seq[CExpr]): CStmtProducer = stmts => - if (stmts.length == 1) stmts.head else { - val cond_branches = selectors.zip(stmts).reverse - val ctail = cond_branches.tail - val finalBranch = cond_branches.head._2 - ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb).simplify } + def traverseStmt(item: TraversalItem, kont: CStmtProducer): CStatement = { + def translateOperation(s: Statement): CStatement = s match { + case Skip => + CSkip + case Load(to, tpe, from, offset) => + CLoad(CVar(to.name), translateSSLType(tpe), CVar(from.name), offset) + case Store(to, offset, e) => + CStore(CVar(to.name), offset, translateExpr(e)) + case Malloc(to, tpe, sz) => + CMalloc(CVar(to.name), translateSSLType(tpe), sz) + case Free(v) => + val block = item.node.consume.pre.blocks.find(_.loc == v) + assert(block.nonEmpty) + CFree(CVar(v.name), block.get.sz) + case Call(v, args, _) => + CCall(CVar(v.name), args.map(translateExpr)) } - private def guardedProducer(cond: CExpr): CStmtProducer = stmts => - CGuarded(cond, stmts.head, stmts.last) + def translateProducer(stmtProducer: StmtProducer): CStmtProducer = stmtProducer match { + case ChainedProducer(p1, p2) => + val k1 = translateProducer(p1) + val k2 = translateProducer(p2) + k1 >> k2 + case PartiallyAppliedProducer(p, _) => + translateProducer(p) + case ConstProducer(s) => + val stmt = translateOperation(s) + ConstCStmtProducer(stmt) + case PrependProducer(s) => + val stmt = translateOperation(s) + PrependCStmtProducer(stmt) + case BranchProducer(selectors) => + BranchCStmtProducer(selectors.map(translateExpr)) + case _ => + IdCStmtProducer + } - private def deriveCStmtProducer(item: TraversalItem, currStmt: Option[Statement]): Option[CStmtProducer] = { - val node = item.node + def generateNextItems: Seq[TraversalItem] = + item.node.children.map(n => TraversalItem(n)) // TODO: remove stmt - val footprint = node.consume - val stmtProducer = unwrapStmtProducer(node.kont) - (node.rule, stmtProducer) match { - case (EmpRule, _) => - Some(seqCompProducer(CSkip)) - case (ReadRule, PrependProducer(Load(to, _, _, _))) => - currStmt match { - case Some(Load(to1, tpe, from, offset)) if to.name.startsWith(to1.name) => - val s = CLoad(CVar(to.name), translateSSLType(tpe), CVar(from.name), offset) - Some(seqCompProducer(s)) - case _ => - None // bound variable was eliminated by SeqComp.simplify - } - case (WriteRule, PrependProducer(Store(to, offset, e))) => - val s = CStore(CVar(to.name), offset, translateExpr(e)) - Some(seqCompProducer(s)) - case (FreeRule, PrependProducer(Free(v))) => - footprint.pre.blocks.find(_.loc == v).map { b => - val s = (1 until b.sz).foldLeft[CStatement](CFree(CVar(v.name))){ - (acc, n) => CSeqComp(CFree(CVar(v.name), n), acc) - } - seqCompProducer(s) + def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: CStmtProducer): CStmtProducer = nextKont match { + case _: BranchCStmtProducer => + nextItems.tail.foldLeft(nextKont >> kont) { + case (foldedP, item) => FoldCStmtProducer(traverseStmt, item, foldedP) } - case (CallRule, PrependProducer(Call(fun, args, _))) => - val s = CCall(CVar(fun.name), args.map(translateExpr)) - val cstmt = seqCompProducer(s) - Some(cstmt) - case (Open, BranchProducer(selectors)) => - val cstmt = openProducer(selectors.map(translateExpr)) - Some(cstmt) - case (AbduceBranch, GuardedProducer(cond, _)) => - val cstmt = guardedProducer(translateExpr(cond)) - Some(cstmt) case _ => - None // rule has no effect on certification + nextKont >> kont } - } - @scala.annotation.tailrec - private def traverseStmt(item: TraversalItem, kont: CStmtProducer): CStatement = { - val (currStmt, nextStmts) = expandStmt(item.stmt) - val childNodes = item.node.children - val (nextTraversalItems, nextKont) = deriveCStmtProducer(item, currStmt) match { - case Some(nextStmtKont) => - val nextTraversalItems = childNodes.zip(nextStmts).map(i => TraversalItem(i._1, i._2)) - val nextKont = composeProducer[CStatement](nextStmtKont, kont) - (nextTraversalItems, nextKont) - case _ => - (childNodes.map(TraversalItem(_, item.stmt)), kont) - } + // generated nested continuations for children + val p = translateProducer(item.node.kont).simplify + val nextItems = generateNextItems + val nextKont = updateProducerPost(nextItems, p) - nextTraversalItems match { - case hd :: tl => - traverseStmt(hd, joinCStmtProducer(tl, nextKont)) - case Nil => - nextKont(List.empty) + nextItems.headOption match { + case Some(childHead) => + traverseStmt(childHead, nextKont) + case None => + nextKont(Nil) } } } From ad970ace15f26c92e1ce4d872d2e9f9358be40cc Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Jun 2020 23:33:13 +0800 Subject: [PATCH 007/211] Fix variable ordering issues to make most tests pass again --- .../targets/coq/language/Expressions.scala | 25 ++++++++------- .../targets/coq/language/Sentences.scala | 15 +++++---- .../targets/coq/language/Statements.scala | 27 +++++++++------- .../targets/coq/logic/ProofProducers.scala | 32 ++++++++++--------- .../targets/coq/logic/ProofSteps.scala | 29 ++++++++--------- .../coq/translation/ProofTranslation.scala | 30 ++++++----------- .../targets/coq/translation/Translation.scala | 4 +-- 7 files changed, 79 insertions(+), 83 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala index 793e803c7..764187dff 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala @@ -9,29 +9,29 @@ object Expressions { private def isMetadata: Boolean = !this.vars.exists(v => v.name != selfCardVar.name && !v.name.startsWith(cardinalityPrefix)) - def collect[R <: CExpr](p: CExpr => Boolean): Set[R] = { + def collect[R <: CExpr](p: CExpr => Boolean): Seq[R] = { - def collector(acc: Set[R])(exp: CExpr): Set[R] = exp match { - case v@CVar(_) if p(v) => acc + v.asInstanceOf[R] - case c@CNatConst(_) if p(c) => acc + c.asInstanceOf[R] - case c@CBoolConst(_) if p(c) => acc + c.asInstanceOf[R] + def collector(acc: Seq[R])(exp: CExpr): Seq[R] = exp match { + case v@CVar(_) if p(v) => acc :+ v.asInstanceOf[R] + case c@CNatConst(_) if p(c) => acc :+ c.asInstanceOf[R] + case c@CBoolConst(_) if p(c) => acc :+ c.asInstanceOf[R] case b@CBinaryExpr(_, l, r) => - val acc1 = if (p(b)) acc + b.asInstanceOf[R] else acc + val acc1 = if (p(b)) acc :+ b.asInstanceOf[R] else acc val acc2 = collector(acc1)(l) collector(acc2)(r) case u@CUnaryExpr(_, arg) => - val acc1 = if (p(u)) acc + u.asInstanceOf[R] else acc + val acc1 = if (p(u)) acc :+ u.asInstanceOf[R] else acc collector(acc1)(arg) case s@CSetLiteral(elems) => - val acc1 = if (p(s)) acc + s.asInstanceOf[R] else acc + val acc1 = if (p(s)) acc :+ s.asInstanceOf[R] else acc elems.foldLeft(acc1)((a,e) => collector(a)(e)) case i@CIfThenElse(cond, l, r) => - val acc1 = if (p(i)) acc + i.asInstanceOf[R] else acc + val acc1 = if (p(i)) acc :+ i.asInstanceOf[R] else acc val acc2 = collector(acc1)(cond) val acc3 = collector(acc2)(l) collector(acc3)(r) case a@CSApp(_, args, _, _) => - val acc1 = if (p(a)) acc + a.asInstanceOf[R] else acc + val acc1 = if (p(a)) acc :+ a.asInstanceOf[R] else acc args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) case CPointsTo(loc, _, value) => collector(collector(acc)(loc))(value) @@ -41,7 +41,7 @@ object Expressions { case _ => acc } - collector(Set.empty)(this) + collector(Seq.empty)(this) } def simplify: CExpr = this match { @@ -62,11 +62,12 @@ object Expressions { case other => other } - def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]).toSeq + def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]) } case class CVar(name: String) extends CExpr { override def pp: String = name + val isCard: Boolean = name.startsWith(cardinalityPrefix) } case class CBoolConst(value: Boolean) extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala index 30fa7eafe..84dd84a70 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala @@ -14,7 +14,7 @@ sealed abstract class Sentence extends PrettyPrinting { def build(el: Sentence, offset: Int = 0, vars: Seq[CVar] = Seq.empty) : Unit = el match { case el@CAssertion(phi, sigma) => - val ve = (el.spatialEx ++ el.pureEx).diff(vars).distinct + val ve = el.valueEx.filterNot(vars.contains).distinct val he = el.heapEx // existentials if (ve.nonEmpty) { @@ -85,13 +85,16 @@ case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { sigma.unify(source.sigma) } - def pureEx: Seq[CVar] = - phi.collect(_.isInstanceOf[CVar]).toSeq + val pureEx: Seq[CVar] = + phi.collect(_.isInstanceOf[CVar]).asInstanceOf[Seq[CVar]].filterNot(_.isCard) - def spatialEx: Seq[CVar] = - sigma.collect(_.isInstanceOf[CVar]).toSeq + val spatialEx: Seq[CVar] = + sigma.collect(_.isInstanceOf[CVar]).asInstanceOf[Seq[CVar]].filterNot(_.isCard) - def heapEx: Seq[CVar] = + val valueEx: Seq[CVar] = + spatialEx ++ pureEx + + val heapEx: Seq[CVar] = sigma.heapVars } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala index e19d92526..f236911bd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala @@ -7,9 +7,9 @@ object Statements { sealed abstract class CStatement extends ProgramPrettyPrinting { // Expression-collector - def collectE[R <: CExpr](p: CExpr => Boolean): Set[R] = { + def collectE[R <: CExpr](p: CExpr => Boolean): Seq[R] = { - def collector(acc: Set[R])(st: CStatement): Set[R] = st match { + def collector(acc: Seq[R])(st: CStatement): Seq[R] = st match { case CSkip => acc case CHole => acc case CError => acc @@ -23,7 +23,7 @@ object Statements { case CFree(x, _) => acc ++ x.collect(p) case CCall(fun, args) => - acc ++ fun.collect(p) ++ args.flatMap(_.collect(p)).toSet + acc ++ fun.collect(p) ++ args.flatMap(_.collect(p)) case CSeqComp(s1,s2) => val acc1 = collector(acc)(s1) collector(acc1)(s2) @@ -35,10 +35,10 @@ object Statements { collector(acc1)(eb) } - collector(Set.empty)(this) + collector(Seq.empty)(this) } - def usedVars: Set[CVar] = this match { + def usedVars: Seq[CVar] = this match { case _ => collectE(_.isInstanceOf[CVar]) } @@ -56,13 +56,16 @@ object Statements { case CMalloc(to, _, sz) => builder.append(mkSpaces(offset)) builder.append(s"${to.ppp} <-- allocb ${to.ppp} $sz") - case CFree(v, off) => - builder.append(mkSpaces(offset)) - if (off > 0) { - builder.append(s"dealloc (${v.ppp} .+ $off)") - } else { - builder.append(s"dealloc ${v.ppp}") + case CFree(v, sz) => + val deallocs = for (off <- 0 until sz) yield { + if (off > 0) { + s"dealloc (${v.ppp} .+ $off)" + } else { + s"dealloc ${v.ppp}" + } } + builder.append(mkSpaces(offset)) + builder.append(deallocs.mkString(s";;\n${mkSpaces(offset)}")) case CStore(to, off, e) => builder.append(mkSpaces(offset)) val t = if (off <= 0) to.ppp else s"(${to.ppp} .+ $off)" @@ -123,7 +126,7 @@ object Statements { case class CMalloc(to: CVar, tpe: CoqType, sz: Int = 1) extends CStatement with ReturnsValue - case class CFree(v: CVar, offset: Int = 0) extends CStatement + case class CFree(v: CVar, sz: Int = 1) extends CStatement case class CLoad(to: CVar, tpe: CoqType, from: CVar, offset: Int = 0) extends CStatement with ReturnsValue diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala index cc9a7cadd..c0d77c227 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala @@ -6,9 +6,11 @@ import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.{OpenPostStep, OpenStep, ProofStep, SeqCompStep} object ProofProducers { + type Kont = Seq[ProofStep] => ProofStep + sealed abstract class ProofProducer { val arity: Int - val fn: Seq[ProofStep] => ProofStep + val fn: Kont def apply(children: Seq[ProofStep]): ProofStep = { assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") @@ -33,7 +35,7 @@ object ProofProducers { case class ChainedProofProducer(p1: ProofProducer, p2: ProofProducer) extends ProofProducer { val arity: Int = p1.arity + p2.arity - 1 - val fn: Seq[ProofStep] => ProofStep = steps => { + val fn: Kont = steps => { val (steps1, steps2) = steps.splitAt(p1.arity) val step = p1.fn(steps1) p2.fn(step +: steps2) @@ -42,37 +44,37 @@ object ProofProducers { case class PartiallyAppliedProofProducer(p: ProofProducer, s: ProofStep) extends ProofProducer { val arity: Int = p.arity - 1 - val fn: Seq[ProofStep] => ProofStep = steps => { + val fn: Kont = steps => { p.apply(s +: steps) } } case object IdProofProducer extends ProofProducer { val arity: Int = 1 - val fn: Seq[ProofStep] => ProofStep = _.head + val fn: Kont = _.head } case class ConstProofProducer(step: ProofStep) extends ProofProducer { val arity: Int = 0 - val fn: Seq[ProofStep] => ProofStep = _ => step + val fn: Kont = _ => step } case class PrependProofProducer(s: ProofStep) extends ProofProducer { val arity: Int = 1 - val fn: Seq[ProofStep] => ProofStep = steps => SeqCompStep(s, steps.head).simplify + val fn: Kont = steps => SeqCompStep(s, steps.head).simplify } case class AppendProofProducer(s: ProofStep) extends ProofProducer { val arity: Int = 1 - val fn: Seq[ProofStep] => ProofStep = steps => SeqCompStep(steps.head, s).simplify + val fn: Kont = steps => SeqCompStep(steps.head, s).simplify } - case class BranchProofProducer(app: CSApp, pred: CInductivePredicate, clauses: Seq[CInductiveClause], subgoals: Seq[CGoal]) extends ProofProducer { - val arity: Int = clauses.length - val fn: Seq[ProofStep] => ProofStep = steps => + case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal]) extends ProofProducer { + val arity: Int = subgoals.length + val fn: Kont = steps => if (steps.length == 1) steps.head else { - val condBranches = clauses.zip(steps).zip(subgoals).reverse.map{ case ((c, s), g) => - SeqCompStep(OpenPostStep(app, pred, c, g), s) + val condBranches = steps.zip(subgoals).reverse.map{ case (s, g) => + SeqCompStep(OpenPostStep(app, g), s) } val ctail = condBranches.tail val finalBranch = condBranches.head @@ -82,18 +84,18 @@ object ProofProducers { case class FoldProofProducer[T](op: (T, ProofProducer) => ProofStep, item: T, bp: ProofProducer) extends ProofProducer { val arity: Int = 1 - val fn: Seq[ProofStep] => ProofStep = steps => { + val fn: Kont = steps => { // partially apply a produced step to the BranchProducer of the downstream `bp` @scala.annotation.tailrec def isBase(curr: ProofProducer): Boolean = curr match { case PartiallyAppliedProofProducer(p, _) => isBase(p) - case BranchProofProducer(_, _, _, _) => true + case _: BranchProofProducer => true case _ => false } def update(curr: ProofProducer): ProofProducer = curr match { case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) - case _:PartiallyAppliedProofProducer | _:BranchProofProducer if isBase(curr) => curr.partApply(steps.head) + case _: PartiallyAppliedProofProducer | _: BranchProofProducer if isBase(curr) => curr.partApply(steps.head) case _ => curr } op(item, update(bp)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala index cb08cefcb..cfbb73058 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala @@ -22,8 +22,8 @@ object ProofSteps { def collect[R <: CExpr](p: CExpr => Boolean): Set[R] = { def collector(acc: Set[R])(st: ProofStep): Set[R] = st match { - case WriteStep(to) => - acc ++ to.collect(p) + case WriteStep(to, e) => + acc ++ to.collect(p) ++ e.collect(p) case ReadStep(to) => acc ++ to.collect(p) case AllocStep(to, _, _) => @@ -31,9 +31,10 @@ object ProofSteps { case SeqCompStep(s1,s2) => val acc1 = collector(acc)(s1) collector(acc1)(s2) - case CallStep(app, _) => + case CallStep(app, pureEx) => val argVars = app.args.flatMap(arg => arg.collect(p)).toSet - acc ++ argVars + val pureExVars = pureEx.flatMap(e => e.collect(p)).toSet + acc ++ argVars ++ pureExVars case _ => acc } @@ -48,8 +49,8 @@ object ProofSteps { def simplify: ProofStep = { (s1, s2) match { case (ReadStep(to), _) => simplifyBinding(to) - case (WriteStep(to), _) => simplifyBinding(to) - case (AllocStep(to, _, _), _) => simplifyBinding(to) +// case (WriteStep(to), _) => simplifyBinding(to) +// case (AllocStep(to, _, _), _) => simplifyBinding(to) case _ => this } } @@ -62,7 +63,7 @@ object ProofSteps { } } - case class WriteStep(to: CVar) extends ProofStep { + case class WriteStep(to: CVar, e: CExpr) extends ProofStep { override def pp: String = s"ssl_write.\nssl_write_post ${to.pp}.\n" } @@ -85,17 +86,15 @@ object ProofSteps { override def pp: String = "case: ifP=>H_cond.\n" } - case class OpenPostStep(app: CSApp, pred: CInductivePredicate, clause: CInductiveClause, goal: CGoal) extends ProofStep { + case class OpenPostStep(app: CSApp, goal: CGoal) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append(s"case ${app.hypName}; rewrite H_cond=>//= _.\n") - val asn = clause.asn - val subst = goal.pre.unify(asn) - val ve0 = (asn.spatialEx ++ asn.pureEx).diff(pred.params.map(_._2)).distinct - val ve = ve0.map(subst(_)) - val ptss = asn.sigma.ptss - val apps = asn.sigma.apps + val asn = goal.pre + val ve = asn.valueEx.filterNot(app.vars.contains).distinct + val ptss = goal.pre.sigma.ptss + val apps = goal.pre.sigma.apps // move constructor's value existentials to context if (ve.nonEmpty) { @@ -195,7 +194,7 @@ object ProofSteps { // instantiate any existentials from the fun spec post val post = spec.post val programVars = spec.programVars - val ve = (post.spatialEx ++ post.pureEx).diff(programVars).distinct + val ve = post.valueEx.filterNot(programVars.contains).distinct // if (ve.nonEmpty) { // val subs = ve.map(e => { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala index 7a0f6d313..925a28ef9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala @@ -25,8 +25,8 @@ object ProofTranslation { (EmpStep(cenv.spec), cenv) case Load(to, _, _, _) => (ReadStep(CVar(to.name)), cenv) - case Store(to, _, _) => - (WriteStep(CVar(to.name)), cenv) + case Store(to, _, e) => + (WriteStep(CVar(to.name), translateExpr(e)), cenv) case Malloc(to, tpe, sz) => (AllocStep(CVar(to.name), translateSSLType(tpe), sz), cenv) case Free(v) => @@ -35,8 +35,8 @@ object ProofTranslation { (FreeStep(block.get.sz), cenv) case Call(_, _, _) => val csub = cenv.existentials - val sapp = cenv.assumptions.flatMap(_.sigma.apps).head - val pureEx = cenv.spec.pureParams.map { case (_, v) => csub(v).asInstanceOf[CVar] } + val sapp = translateHeaplet(item.node.consume.pre.apps.head).asInstanceOf[CSApp] + val pureEx = cenv.spec.pureParams.filterNot(_._2.vars.exists(_.isCard)).map { case (_, v) => csub(v).asInstanceOf[CVar] } (CallStep(sapp, pureEx), cenv) } @@ -49,7 +49,7 @@ object ProofTranslation { case PartiallyAppliedProducer(p, _) => translateProducer(p, cenv) case ExistentialProducer(subst) => - val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) } + val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) }.filterKeys(!_.isCard) val cenv1 = cenv.copy(existentials = cenv.existentials ++ csub) (IdProofProducer, cenv1) case ConstProducer(s) => @@ -58,16 +58,10 @@ object ProofTranslation { case PrependProducer(s) => val (step, cenv1) = translateOperation(s, cenv) (PrependProofProducer(step), cenv1) - case BranchProducer(selectors) => - val app = item.node.consume.pre.apps.headOption - .getOrElse(throw TranslationException("Open rule was called, but no predicate applications found")) - val pred = cenv.predicates - .find(_.name == app.pred) - .getOrElse(throw TranslationException(s"No predicate matching label ${app.pred} found in environment")) - val clauses = selectors.map(s => pred.clauses.find(_.selector == translateExpr(s)).get) + case BranchProducer(_) => val sapp = translateHeaplet(item.node.consume.pre.apps.head).asInstanceOf[CSApp] val subgoals = item.node.children.map(n => translateGoal(n.goal)) - (BranchProofProducer(sapp, pred, clauses, subgoals), cenv) + (BranchProofProducer(sapp, subgoals), cenv) case GuardedProducer(_, _) => (PrependProofProducer(AbduceBranchStep), cenv) case _ => @@ -75,14 +69,8 @@ object ProofTranslation { } } - def generateNextItems(p: ProofProducer, cenv: CEnvironment): Seq[TraversalItem] = p match { - case BranchProofProducer(_, _, clauses, _) => - item.node.children.zip(clauses).map { case (node, clause) => - val cenv1 = cenv.copy(assumptions = cenv.assumptions ++ Seq(clause.asn)) - TraversalItem(node, cenv1) - } - case _ => - item.node.children.map(node => TraversalItem(node, cenv)) + def generateNextItems(p: ProofProducer, cenv: CEnvironment): Seq[TraversalItem] = { + item.node.children.map(node => TraversalItem(node, cenv)) } // handle guard diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala index f03c363e9..5d7d71133 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala @@ -103,7 +103,7 @@ object Translation { val goal = node.goal val pureParams = goal.universalGhosts .intersect(goal.gamma.keySet) - .map(v => translateParam((goal.gamma(v), v))).toList + .map(v => translateParam((goal.gamma(v), v))).filterNot(_._2.isCard).toList val ctp = translateSSLType(tp) val cparams = goal.formals.map(translateParam) val cpre = translateAsn(goal.pre) @@ -146,7 +146,7 @@ object Translation { val post = translateAsn(goal.post) val gamma = goal.gamma.map { case (value, lType) => (CVar(value.name), translateSSLType(lType)) } val programVars = goal.programVars.map(v => CVar(v.name)) - val universalGhosts = goal.universalGhosts.intersect(goal.gamma.keySet).map(v => CVar(v.name)).toSeq + val universalGhosts = goal.universalGhosts.intersect(goal.gamma.keySet).map(v => CVar(v.name)).toSeq.filterNot(_.isCard) CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) } From 3f0d8f1569fb894c2c3701c213a2eb0799374dbc Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 23 Jun 2020 15:56:54 +0800 Subject: [PATCH 008/211] Remove unused fields and methods --- .../targets/coq/logic/Proof.scala | 19 +------ .../coq/translation/ProofTranslation.scala | 3 +- .../targets/coq/translation/Translation.scala | 56 ------------------- 3 files changed, 3 insertions(+), 75 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala index b94c4d545..e71e25ad0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala @@ -4,8 +4,6 @@ import org.tygus.suslik.certification.targets.coq.language._ import org.tygus.suslik.certification.targets.coq.language.Expressions._ object Proof { - type ProofScriptProducer = Seq[String] => String - case class CGoal(pre: CAssertion, post: CAssertion, gamma: Map[CVar, CoqType], @@ -17,27 +15,14 @@ object Proof { case class CEnvironment(spec: CFunSpec, predicates: Seq[CInductivePredicate], - usedVars: Set[CVar] = Set.empty, existentials: Map[CVar, CExpr] = Map.empty, - callHeapVars: Seq[CVar] = Seq.empty, - predUnfoldings: Map[CSApp, UnfoldedSApp] = Map.empty, - assumptions: Seq[CAssertion] = Seq.empty + predUnfoldings: Map[CSApp, UnfoldedSApp] = Map.empty ) { def copy(spec: CFunSpec = this.spec, predicates: Seq[CInductivePredicate] = this.predicates, - usedVars: Set[CVar] = this.usedVars, existentials: Map[CVar, CExpr] = this.existentials, - callHeapVars: Seq[CVar] = this.callHeapVars, predUnfoldings: Map[CSApp, UnfoldedSApp] = this.predUnfoldings, - assumptions: Seq[CAssertion] = this.assumptions ): CEnvironment = - CEnvironment(spec, predicates, usedVars, existentials, callHeapVars, predUnfoldings, assumptions) - - private var counter = 0 - def generateFreshVar(v: CVar): CVar = { - val freshVar = CVar(s"${v.name}${counter}") - counter += 1 - freshVar - } + CEnvironment(spec, predicates, existentials, predUnfoldings) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala index 925a28ef9..3a0015798 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala @@ -13,8 +13,7 @@ object ProofTranslation { private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) extends Traversable def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): Proof = { - val nextEnv = cenv.copy(usedVars = goal.universalGhosts.toSet) - val traversalItem = TraversalItem(node, nextEnv) + val traversalItem = TraversalItem(node, cenv) val proofBody = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal))) Proof(proofBody) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala index 5d7d71133..4b3e65ef0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala @@ -19,62 +19,6 @@ object Translation { trait Traversable - type Producer[A] = List[A] => A - - /** - * Composes two producer continuations - * @param f the continuation to apply first - * @param g the continuation to apply to the result of f - * @return the composed result - */ - def composeProducer[A](f: Producer[A], g: Producer[A]): Producer[A] = - (steps: List[A]) => g(List(f(steps))) - - /** - * Creates a new continuation that prepends items to the argument of an existing continuation `kont` - * @param args arguments to prepend to `kont` - * @param kont the original continuation - * @return a new continuation - */ - def prependArgsProducer[A](args: List[A], kont: Producer[A]): Producer[A] = - (args1: List[A]) => kont(args ++ args1) - - /** - * Creates a new continuation that combines the result of multiple traversals at a branching point - * @param items a list of things to traverse - * @param kont the final (root) continuation to apply after collecting all child results - * @return a new continuation - */ - def joinProducer[A, T <: Traversable](op: (T, Producer[A]) => A)(items: List[T], kont: Producer[A]): Producer[A] = - items.foldLeft(kont) { - case (kontAcc, arg) => - items1 => op(arg, prependArgsProducer[A](items1, kontAcc)) - } - - /** - * Given a program point, derives the currently focused statement and its children - * @param stmt the program point - * @return a tuple of an optional current statement and a sequence of child statements - */ - def expandStmt(stmt: Statement) : (Option[Statement], Seq[Statement]) = stmt match { - case SeqComp(s1, s2) => (Some(s1), Seq(s2)) - case If(_, tb, eb) => (None, Seq(tb, eb)) - case Guarded(_, body, els, _) => (None, Seq(body, els)) - case _ => (Some(stmt), Seq()) - } - - /** - * Unwraps a statement producer to get to the part relevant to certification - * @param p the producer - * @return an unwrapped producer - */ - @scala.annotation.tailrec - def unwrapStmtProducer(p: StmtProducer) : StmtProducer = p match { - case PartiallyAppliedProducer(p, _) => unwrapStmtProducer(p) - case ChainedProducer(p1, _) => unwrapStmtProducer(p1) - case p => p - } - /** * Produces a Coq certificate from the tree of successful derivations and a synthesized procedure * @param node the root of the derivation tree From 6706a5b53bfe321616144c5bda8d863d7998db54 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 23 Jun 2020 19:35:26 +0800 Subject: [PATCH 009/211] Handle guards and simple existentials --- .../coq/language/StatementProducers.scala | 17 ++++++++---- .../targets/coq/logic/ProofProducers.scala | 21 ++++++++++++--- .../targets/coq/logic/ProofSteps.scala | 26 +++++++++---------- .../coq/translation/ProgramTranslation.scala | 6 +++-- .../coq/translation/ProofTranslation.scala | 6 ++--- 5 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala index 772a12f38..f2bb3fb78 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala @@ -1,11 +1,13 @@ package org.tygus.suslik.certification.targets.coq.language import org.tygus.suslik.certification.targets.coq.language.Expressions.CExpr -import org.tygus.suslik.certification.targets.coq.language.Statements.{CIf, CSeqComp, CStatement} +import org.tygus.suslik.certification.targets.coq.language.Statements.{CGuarded, CIf, CSeqComp, CStatement} object StatementProducers { type Kont = Seq[CStatement] => CStatement + trait Branching + sealed abstract class CStmtProducer { val arity: Int val fn: Kont @@ -40,7 +42,7 @@ object StatementProducers { } } - case class PartiallyAppliedCStmtProducer(p: CStmtProducer, s: CStatement) extends CStmtProducer { + case class PartiallyAppliedCStmtProducer(p: CStmtProducer, s: CStatement) extends CStmtProducer with Branching { val arity: Int = p.arity - 1 val fn: Kont = stmts => { p.apply(s +: stmts) @@ -67,7 +69,7 @@ object StatementProducers { val fn: Kont = stmts => CSeqComp(stmts.head, s).simplify } - case class BranchCStmtProducer(selectors: Seq[CExpr]) extends CStmtProducer { + case class BranchCStmtProducer(selectors: Seq[CExpr]) extends CStmtProducer with Branching { val arity: Int = selectors.length val fn: Kont = stmts => if (stmts.length == 1) stmts.head else { @@ -78,6 +80,11 @@ object StatementProducers { } } + case class GuardedCStmtProducer(cond: CExpr) extends CStmtProducer with Branching { + val arity: Int = 2 + val fn: Kont = stmts => CGuarded(cond, stmts.head, stmts.last) + } + case class FoldCStmtProducer[T](op: (T, CStmtProducer) => CStatement, item: T, bp: CStmtProducer) extends CStmtProducer { val arity: Int = 1 val fn: Kont = steps => { @@ -85,13 +92,13 @@ object StatementProducers { @scala.annotation.tailrec def isBase(curr: CStmtProducer): Boolean = curr match { case PartiallyAppliedCStmtProducer(p, _) => isBase(p) - case _: BranchCStmtProducer => true + case _: Branching => true case _ => false } def update(curr: CStmtProducer): CStmtProducer = curr match { case FoldCStmtProducer(op, item, bp) => FoldCStmtProducer(op, item, update(bp)) case ChainedCStmtProducer(p1, p2) => ChainedCStmtProducer(update(p1), update(p2)) - case _: PartiallyAppliedCStmtProducer | _: BranchCStmtProducer if isBase(curr) => curr.partApply(steps.head) + case _: PartiallyAppliedCStmtProducer | _: Branching if isBase(curr) => curr.partApply(steps.head) case _ => curr } op(item, update(bp)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala index c0d77c227..2ac8900b6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala @@ -3,11 +3,13 @@ package org.tygus.suslik.certification.targets.coq.logic import org.tygus.suslik.certification.targets.coq.language.{CInductiveClause, CInductivePredicate} import org.tygus.suslik.certification.targets.coq.language.Expressions._ import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal -import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.{OpenPostStep, OpenStep, ProofStep, SeqCompStep} +import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.{AbduceBranchStep, OpenPostStep, OpenStep, ProofStep, SeqCompStep} object ProofProducers { type Kont = Seq[ProofStep] => ProofStep + trait Branching + sealed abstract class ProofProducer { val arity: Int val fn: Kont @@ -69,7 +71,7 @@ object ProofProducers { val fn: Kont = steps => SeqCompStep(steps.head, s).simplify } - case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal]) extends ProofProducer { + case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal]) extends ProofProducer with Branching { val arity: Int = subgoals.length val fn: Kont = steps => if (steps.length == 1) steps.head else { @@ -82,6 +84,17 @@ object ProofProducers { } } + case object GuardedProofProducer extends ProofProducer with Branching { + val arity: Int = 2 + val fn: Kont = steps => + if (steps.length == 1) steps.head else { + val condBranches = steps.reverse + val ctail = condBranches.tail + val finalBranch = condBranches.head + SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }) + } + } + case class FoldProofProducer[T](op: (T, ProofProducer) => ProofStep, item: T, bp: ProofProducer) extends ProofProducer { val arity: Int = 1 val fn: Kont = steps => { @@ -89,13 +102,13 @@ object ProofProducers { @scala.annotation.tailrec def isBase(curr: ProofProducer): Boolean = curr match { case PartiallyAppliedProofProducer(p, _) => isBase(p) - case _: BranchProofProducer => true + case _: Branching => true case _ => false } def update(curr: ProofProducer): ProofProducer = curr match { case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) - case _: PartiallyAppliedProofProducer | _: BranchProofProducer if isBase(curr) => curr.partApply(steps.head) + case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => curr.partApply(steps.head) case _ => curr } op(item, update(bp)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala index cfbb73058..1f7262292 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala @@ -185,7 +185,7 @@ object ProofSteps { override def pp: String = "case: ifP=>H_cond.\n" } - case class EmpStep(spec: CFunSpec) extends ProofStep { + case class EmpStep(spec: CFunSpec, existentials: Map[CVar, CExpr]) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append("ssl_emp;\n") @@ -196,18 +196,18 @@ object ProofSteps { val programVars = spec.programVars val ve = post.valueEx.filterNot(programVars.contains).distinct -// if (ve.nonEmpty) { -// val subs = ve.map(e => { -// // first, see if it matches any existentials produced by the Pick rule -// env.existentials.get(e) match { -// case Some(v) => v.pp -// case None => -// // if that doesn't work, match from the unrolled predicates -// -// } -// }) -// builder.append(s"exists ${subs.mkString(", ")};\n") -// } + if (ve.nonEmpty) { + val subs = ve.map(e => { + // first, see if it matches any existentials produced by the Pick rule + existentials.get(e) match { + case Some(v) => v.pp + case None => + // TODO: if that doesn't work, match from the unrolled predicates + "" + } + }) + builder.append(s"exists ${subs.mkString(", ")};\n") + } builder.append("ssl_emp_post.\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala index 291d997e2..9245712d7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala @@ -50,15 +50,17 @@ object ProgramTranslation { PrependCStmtProducer(stmt) case BranchProducer(selectors) => BranchCStmtProducer(selectors.map(translateExpr)) + case GuardedProducer(cond, _) => + GuardedCStmtProducer(translateExpr(cond)) case _ => IdCStmtProducer } def generateNextItems: Seq[TraversalItem] = - item.node.children.map(n => TraversalItem(n)) // TODO: remove stmt + item.node.children.map(n => TraversalItem(n)) def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: CStmtProducer): CStmtProducer = nextKont match { - case _: BranchCStmtProducer => + case _: Branching => nextItems.tail.foldLeft(nextKont >> kont) { case (foldedP, item) => FoldCStmtProducer(traverseStmt, item, foldedP) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala index 3a0015798..8a3571a38 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala @@ -21,7 +21,7 @@ object ProofTranslation { def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => - (EmpStep(cenv.spec), cenv) + (EmpStep(cenv.spec, cenv.existentials), cenv) case Load(to, _, _, _) => (ReadStep(CVar(to.name)), cenv) case Store(to, _, e) => @@ -62,7 +62,7 @@ object ProofTranslation { val subgoals = item.node.children.map(n => translateGoal(n.goal)) (BranchProofProducer(sapp, subgoals), cenv) case GuardedProducer(_, _) => - (PrependProofProducer(AbduceBranchStep), cenv) + (GuardedProofProducer, cenv) case _ => (IdProofProducer, cenv) } @@ -74,7 +74,7 @@ object ProofTranslation { // handle guard def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: ProofProducer, cenv: CEnvironment): ProofProducer = nextKont match { - case _: BranchProofProducer => + case _: Branching => nextItems.tail.foldLeft(nextKont >> kont) { case (foldedP, item) => FoldProofProducer(traverseProof, item, foldedP) } From b8770006c7d14da439fc0d04b7cd70914874a839 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 24 Jun 2020 03:08:10 +0800 Subject: [PATCH 010/211] Populate post-condition existentials by collecting variable substitutions and predicate unfoldings --- .../targets/coq/language/Expressions.scala | 32 +++++++++++-- .../targets/coq/language/Sentences.scala | 6 +-- .../targets/coq/language/Statements.scala | 2 +- .../targets/coq/logic/Proof.scala | 12 ++--- .../targets/coq/logic/ProofSteps.scala | 47 ++++++++++++++----- .../coq/translation/ProofTranslation.scala | 21 +++++++-- .../targets/coq/translation/Translation.scala | 8 ++-- .../tygus/suslik/synthesis/StmtProducer.scala | 14 ++---- .../synthesis/rules/OperationalRules.scala | 2 +- .../synthesis/rules/UnfoldingRules.scala | 8 ++-- .../synthesis/rules/UnificationRules.scala | 6 +-- 11 files changed, 105 insertions(+), 53 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala index 764187dff..827ff5cf8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala @@ -44,6 +44,29 @@ object Expressions { collector(Seq.empty)(this) } + def subst(sigma: Map[CVar, CExpr]): CExpr = this match { + case v: CVar => + sigma.get(v).map(_.subst(sigma)).getOrElse(v) + case CBinaryExpr(op, l, r) => + CBinaryExpr(op, l.subst(sigma), r.subst(sigma)) + case CUnaryExpr(op, arg) => + CUnaryExpr(op, arg.subst(sigma)) + case CSetLiteral(elems) => + CSetLiteral(elems.map(_.subst(sigma))) + case CIfThenElse(cond, l, r) => + CIfThenElse(cond.subst(sigma), l.subst(sigma), r.subst(sigma)) + case CSApp(pred, args, tag, card) => + CSApp(pred, args.map(_.subst(sigma)), tag, card.subst(sigma)) + case CPointsTo(loc, offset, value) => + CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) + case CSFormula(heapName, apps, ptss) => + val apps1 = apps.map(_.subst(sigma).asInstanceOf[CSApp]) + val ptss1 = ptss.map(_.subst(sigma).asInstanceOf[CPointsTo]) + CSFormula(heapName, apps1, ptss1) + case _ => + this + } + def simplify: CExpr = this match { case CBinaryExpr(op, left, right) => if (op == COpAnd) { @@ -67,7 +90,7 @@ object Expressions { case class CVar(name: String) extends CExpr { override def pp: String = name - val isCard: Boolean = name.startsWith(cardinalityPrefix) + val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name } case class CBoolConst(value: Boolean) extends CExpr { @@ -174,10 +197,9 @@ object Expressions { } } - // TODO: deprecate - def heapVars: Seq[CVar] = - if (ptss.isEmpty) Seq.empty - else (1 to apps.length).map(i => CVar(s"$heapName${"'" * i}")) + val heapVars: Seq[CVar] = + if (ptss.isEmpty && apps.length == 1) Seq.empty + else apps.map(a => CVar(a.heapName)) override def vars: Seq[CVar] = super.vars ++ heapVars } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala index 84dd84a70..76143aef5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala @@ -31,9 +31,9 @@ sealed abstract class Sentence extends PrettyPrinting { case CBoolConst(true) => builder.append(sigma.pp) case _ => builder.append(s"${phi.pp} /\\ ${sigma.pp}") } - case CInductiveClause(name, selector, asn) => + case CInductiveClause(pred, idx, selector, asn) => builder.append(mkSpaces(offset)) - builder.append(s"| $name of ${selector.pp} of\n") + builder.append(s"| $pred$idx of ${selector.pp} of\n") build(asn, offset + 2, vars) case CInductivePredicate(name, params, clauses) => builder.append(mkSpaces(offset)) @@ -98,7 +98,7 @@ case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { sigma.heapVars } -case class CInductiveClause(name: String, selector: CExpr, asn: CAssertion) extends Sentence +case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion) extends Sentence case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends Sentence diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala index f236911bd..b7ecca30c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala @@ -55,7 +55,7 @@ object Statements { case CMagic => ??? case CMalloc(to, _, sz) => builder.append(mkSpaces(offset)) - builder.append(s"${to.ppp} <-- allocb ${to.ppp} $sz") + builder.append(s"${to.ppp} <-- allocb null $sz") case CFree(v, sz) => val deallocs = for (off <- 0 until sz) yield { if (off > 0) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala index e71e25ad0..1523cc850 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala @@ -11,18 +11,16 @@ object Proof { universalGhosts: Seq[CVar], fname: String) - case class UnfoldedSApp(substArgs: Map[CVar, CExpr], substEx: Map[CVar, CExpr], asn: CAssertion) - case class CEnvironment(spec: CFunSpec, predicates: Seq[CInductivePredicate], - existentials: Map[CVar, CExpr] = Map.empty, - predUnfoldings: Map[CSApp, UnfoldedSApp] = Map.empty + subst: Map[CVar, CExpr] = Map.empty, + heapSubst: Map[CSApp, (CSFormula, CInductiveClause)] = Map.empty, ) { def copy(spec: CFunSpec = this.spec, predicates: Seq[CInductivePredicate] = this.predicates, - existentials: Map[CVar, CExpr] = this.existentials, - predUnfoldings: Map[CSApp, UnfoldedSApp] = this.predUnfoldings, + subst: Map[CVar, CExpr] = this.subst, + heapSubst: Map[CSApp, (CSFormula, CInductiveClause)] = this.heapSubst ): CEnvironment = - CEnvironment(spec, predicates, existentials, predUnfoldings) + CEnvironment(spec, predicates, subst, heapSubst) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala index 1f7262292..28f7e0344 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala @@ -72,7 +72,7 @@ object ProofSteps { } case class AllocStep(to: CVar, tpe: CoqType, sz: Int) extends ProofStep { - override def pp: String = s"ssl_alloc $to.\n" + override def pp: String = s"ssl_alloc ${to.pp}.\n" } case class FreeStep(size: Int) extends ProofStep { @@ -185,7 +185,7 @@ object ProofSteps { override def pp: String = "case: ifP=>H_cond.\n" } - case class EmpStep(spec: CFunSpec, existentials: Map[CVar, CExpr]) extends ProofStep { + case class EmpStep(predicates: Seq[CInductivePredicate], spec: CFunSpec, subst: Map[CVar, CExpr], heapSubst: Map[CSApp, (CSFormula, CInductiveClause)]) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append("ssl_emp;\n") @@ -193,20 +193,43 @@ object ProofSteps { // instantiate any existentials from the fun spec post val post = spec.post + val postApps = post.sigma.apps val programVars = spec.programVars val ve = post.valueEx.filterNot(programVars.contains).distinct if (ve.nonEmpty) { - val subs = ve.map(e => { - // first, see if it matches any existentials produced by the Pick rule - existentials.get(e) match { - case Some(v) => v.pp - case None => - // TODO: if that doesn't work, match from the unrolled predicates - "" - } - }) - builder.append(s"exists ${subs.mkString(", ")};\n") + val subs = ve.map(e => e.subst(subst)) + builder.append(s"exists ${subs.map(s => s"(${s.pp})").mkString(", ")};\n") + } + + def expand(app: CSApp): Seq[CPointsTo] = { + val (expandedApp, _) = heapSubst(app) + val rest = expandedApp.apps.flatMap(expand) + expandedApp.ptss ++ rest + } + for (postApp <- postApps) { + val expandedHeap = expand(postApp).map(ptss => ptss.subst(subst).pp) + val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.mkString(" \\+ ") else "empty" + builder.append(s"exists $expandedHeapStr;\n") + } + + def expand2(app: CSApp): Unit = { + val (expandedApp, clause) = heapSubst(app) + val pred = predicates.find(_.name == clause.pred).get + builder.append(s"constructor ${clause.idx + 1}=>//;\n") + val valueEx = clause.asn.valueEx.filterNot(pred.params.map(_._2).contains).distinct.map(_.subst(subst).pp) + if (valueEx.nonEmpty) { + builder.append(s"exists ${valueEx.mkString(", ")};\n") + } + if (expandedApp.apps.nonEmpty) { + val expandedHeap = expandedApp.apps.flatMap(expand) + val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.map(_.pp).mkString(" \\+ ") else "empty" + builder.append(s"exists $expandedHeapStr;\n") + } + expandedApp.apps.foreach(expand2) + } + for (postApp <- postApps) { + expand2(postApp) } builder.append("ssl_emp_post.\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala index 8a3571a38..4a63d3376 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.coq.translation import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.coq.language.CInductiveClause import org.tygus.suslik.certification.targets.coq.language.Expressions._ import org.tygus.suslik.certification.targets.coq.logic.Proof._ import org.tygus.suslik.certification.targets.coq.logic.ProofProducers._ @@ -8,6 +9,7 @@ import org.tygus.suslik.certification.targets.coq.logic.ProofSteps._ import org.tygus.suslik.certification.targets.coq.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.rules.UnfoldingRules.Close object ProofTranslation { private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) extends Traversable @@ -21,7 +23,7 @@ object ProofTranslation { def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => - (EmpStep(cenv.spec, cenv.existentials), cenv) + (EmpStep(cenv.predicates, cenv.spec, cenv.subst, cenv.heapSubst), cenv) case Load(to, _, _, _) => (ReadStep(CVar(to.name)), cenv) case Store(to, _, e) => @@ -33,7 +35,7 @@ object ProofTranslation { assert(block.nonEmpty) (FreeStep(block.get.sz), cenv) case Call(_, _, _) => - val csub = cenv.existentials + val csub = cenv.subst val sapp = translateHeaplet(item.node.consume.pre.apps.head).asInstanceOf[CSApp] val pureEx = cenv.spec.pureParams.filterNot(_._2.vars.exists(_.isCard)).map { case (_, v) => csub(v).asInstanceOf[CVar] } (CallStep(sapp, pureEx), cenv) @@ -47,9 +49,20 @@ object ProofTranslation { (k1 >> k2, cenv2) case PartiallyAppliedProducer(p, _) => translateProducer(p, cenv) - case ExistentialProducer(subst) => + case SubstProducer(subst) => val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) }.filterKeys(!_.isCard) - val cenv1 = cenv.copy(existentials = cenv.existentials ++ csub) + val cenv1 = cenv.copy(subst = cenv.subst ++ csub) + (IdProofProducer, cenv1) + case UnrollProducer(predName, clause, substEx) => + val csub = substEx.map { case (v, e) => CVar(v.name) -> translateExpr(e) }.filterKeys(!_.isCard) + val src = translateHeaplet(item.node.consume.post.apps.head).asInstanceOf[CSApp] + val dst = translateSFormula(item.node.children.head.produce.post) + + val pred = cenv.predicates.find(_.name == predName).get + val selector = translateExpr(clause.selector) + val clauseIdx = pred.clauses.indexWhere(_.selector == selector) + val cclause = CInductiveClause(predName, clauseIdx, selector, translateAsn(clause.asn)) + val cenv1 = cenv.copy(subst = cenv.subst ++ csub, heapSubst = cenv.heapSubst ++ Map(src -> (dst, cclause))) (IdProofProducer, cenv1) case ConstProducer(s) => val (step, cenv1) = translateOperation(s, cenv) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala index 4b3e65ef0..75a5cb816 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala @@ -65,16 +65,16 @@ object Translation { private def translateInductivePredicate(el: InductivePredicate): CInductivePredicate = { val cParams = el.params.map(translateParam) :+ (CHeapType, CVar("h")) - val cClauses = el.clauses.zipWithIndex.map { case (c, i) => translateClause(s"${el.name}$i", c) } + val cClauses = el.clauses.zipWithIndex.map { case (c, i) => translateClause(c, el.name, i) } CInductivePredicate(el.name, cParams, cClauses) } private def translateParam(el: (SSLType, Var)): (CoqType, CVar) = (translateSSLType(el._1), CVar(el._2.name)) - private def translateClause(name: String, el: InductiveClause): CInductiveClause = { + def translateClause(el: InductiveClause, pred: String, idx: Int): CInductiveClause = { val selector = translateExpr(el.selector) - CInductiveClause(name, selector, translateAsn(el.asn)) + CInductiveClause(pred, idx, selector, translateAsn(el.asn)) } def translateSSLType(el: SSLType): CoqType = el match { @@ -116,7 +116,7 @@ object Translation { CAssertion(phi, sigma) } - private def translateSFormula(el: SFormula): CSFormula = { + def translateSFormula(el: SFormula): CSFormula = { val ptss = el.ptss.map(translateHeaplet).asInstanceOf[List[CPointsTo]] val apps = el.apps.map(translateHeaplet).asInstanceOf[List[CSApp]] CSFormula("h", apps, ptss) diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 2c82b5cea..51e312e17 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.SApp +import org.tygus.suslik.logic.{InductiveClause, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils @@ -162,18 +162,14 @@ case class GuardedProducer(cond: Expr, goal: Goal) extends StmtProducer { val fn: Kont = liftToSolutions(stmts => Guarded(cond, stmts.head, stmts.last, goal.label)) } -// Captures the existential substitution map produced by the Pick rule -case class ExistentialProducer(subst: Map[Var, Expr]) extends StmtProducer { +// Captures variable substitutions +case class SubstProducer(subst: Map[Var, Expr]) extends StmtProducer { val arity: Int = 1 val fn: Kont = liftToSolutions(stmts => stmts.head) } -// Captures a predicate unfolding due to the Close rule -case class UnfoldingProducer(sapp: SApp, // the unfolded predicate application - substArgs: Map[Var, Expr], // substitutions for predicate parameters - substEx: Map[Var, Expr], // substitutions for predicate post existentials - asn: Assertion // the unfolded result - ) extends StmtProducer { +// Captures an unrolled predicate +case class UnrollProducer(pred: String, clause: InductiveClause, substEx: Map[Var, Expr]) extends StmtProducer { val arity: Int = 1 val fn: Kont = liftToSolutions(stmts => stmts.head) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala index 4399f00ec..23f48c8b4 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala @@ -154,7 +154,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post.subst(x, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = ExistentialProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, goal.allHeaplets - subGoal.allHeaplets, this)) case _ => Nil } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index 1b228b8db..600e123c6 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -123,7 +123,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { // Check that the goal's subheap had at least one unfolding callGoal = mkCallGoal(f, sub, callSubPre, goal) } yield { - val kont: StmtProducer = ExistentialProducer(sub) >> PrependProducer(Call(Var(f.name), args, l)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstProducer(sub) >> PrependProducer(Call(Var(f.name), args, l)) >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(callGoal), kont, Footprint(largSubHeap, emp), this) } nubBy[RuleResult, Assertion](results, r => r.subgoals.head.pre) @@ -264,7 +264,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { ruleAssert(env.predicates.contains(pred), s"Close rule encountered undefined predicate: $pred") - val InductivePredicate(_, params, clauses) = env.predicates(pred).refreshExistentials(goal.vars) + val InductivePredicate(predName, params, clauses) = env.predicates(pred).refreshExistentials(goal.vars) //ruleAssert(clauses.lengthCompare(1) == 0, s"Predicates with multiple clauses not supported yet: $pred") val paramNames = params.map(_._2) @@ -273,7 +273,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val substArgs = paramNames.zip(args).toMap + (selfCardVar -> card) val subDerivations = for { - InductiveClause(selector, asn) <- clauses + clause@InductiveClause(selector, asn) <- clauses // Make sure that existential in the body are fresh asnExistentials = asn.vars -- paramNames.toSet -- Set(selfCardVar) freshSuffix = args.take(1).map(_.pp).mkString("_") @@ -289,7 +289,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPhi = post.phi && actualConstraints && actualSelector val newPost = Assertion(newPhi, goal.post.sigma ** actualBody - h) - val kont = UnfoldingProducer(h, substArgs, freshExistentialsSubst, actualAssertion) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnrollProducer(predName, clause, freshExistentialsSubst) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(goal.spawnChild(post = newPost)), kont, Footprint(emp, singletonHeap(h)), this) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 6449ffc3e..6d81f024e 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -104,7 +104,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils val _p2 = rest2.subst(x, e) val _s2 = s2.subst(x, e) val newGoal = goal.spawnChild(post = Assertion(_p2, _s2)) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(Map(x -> e)) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, goal.allHeaplets - newGoal.allHeaplets, this)) case _ => Nil } @@ -205,7 +205,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils sigma = Map(ex -> v) if sigma.nonEmpty newGoal = goal.spawnChild(post = goal.post.subst(sigma)) - kont = ExistentialProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, goal.allHeaplets - newGoal.allHeaplets, this) } } @@ -240,7 +240,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils sol = if (bounds.isEmpty) IntConst(0) else BinaryExpr(OpPlus, maxExpr(bounds), IntConst(1)) sigma = Map(ex -> sol) newGoal = goal.spawnChild(post = goal.post.subst(sigma)) - kont = ExistentialProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, goal.allHeaplets - newGoal.allHeaplets, this) } } From c55763093db11f886f43e5a6df9fef3bd475a112 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 24 Jun 2020 12:24:41 +0800 Subject: [PATCH 011/211] =?UTF-8?q?Rename=20package:=20=E2=80=9Ccoq?= =?UTF-8?q?=E2=80=9D=20->=20=E2=80=9Chtt=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- certification.md | 14 +++++++------- certification/{coq => htt}/Makefile | 0 certification/{coq => htt}/_CoqProject | 0 certification/{coq => htt}/examples/listfree.v | 0 certification/{coq => htt}/examples/min_max.v | 0 certification/{coq => htt}/examples/rose_tree.v | 0 certification/{coq => htt}/examples/sll_free.v | 0 certification/{coq => htt}/examples/sll_init.v | 0 .../{coq => htt}/examples/sll_singleton.v | 2 +- certification/{coq => htt}/examples/swap.v | 0 certification/{coq => htt}/examples/swap1.v | 0 certification/{coq => htt}/examples/swap2.v | 0 certification/{coq => htt}/examples/treefree.v | 0 certification/{coq => htt}/lib/core.v | 0 .../targets/coq/CoqCertificate.scala | 7 ------- .../targets/coq/language/PrettyPrinting.scala | 5 ----- .../targets/{coq/Coq.scala => htt/HTT.scala} | 16 ++++++++-------- .../targets/htt/HTTCertificate.scala | 7 +++++++ .../{coq => htt}/language/Expressions.scala | 2 +- .../targets/htt/language/PrettyPrinting.scala | 5 +++++ .../language/ProgramPrettyPrinting.scala | 2 +- .../{coq => htt}/language/Sentences.scala | 4 ++-- .../language/StatementProducers.scala | 6 +++--- .../{coq => htt}/language/Statements.scala | 2 +- .../targets/{coq => htt}/language/Types.scala | 2 +- .../targets/{coq => htt}/language/package.scala | 4 ++-- .../targets/{coq => htt}/logic/Proof.scala | 6 +++--- .../{coq => htt}/logic/ProofProducers.scala | 10 +++++----- .../targets/{coq => htt}/logic/ProofSteps.scala | 8 ++++---- .../translation/ProgramTranslation.scala | 12 ++++++------ .../translation/ProofTranslation.scala | 14 +++++++------- .../{coq => htt}/translation/Translation.scala | 16 ++++++++-------- .../tygus/suslik/synthesis/SynthesisRunner.scala | 2 +- .../HTTCertificationTests.scala} | 6 +++--- 34 files changed, 76 insertions(+), 76 deletions(-) rename certification/{coq => htt}/Makefile (100%) rename certification/{coq => htt}/_CoqProject (100%) rename certification/{coq => htt}/examples/listfree.v (100%) rename certification/{coq => htt}/examples/min_max.v (100%) rename certification/{coq => htt}/examples/rose_tree.v (100%) rename certification/{coq => htt}/examples/sll_free.v (100%) rename certification/{coq => htt}/examples/sll_init.v (100%) rename certification/{coq => htt}/examples/sll_singleton.v (96%) rename certification/{coq => htt}/examples/swap.v (100%) rename certification/{coq => htt}/examples/swap1.v (100%) rename certification/{coq => htt}/examples/swap2.v (100%) rename certification/{coq => htt}/examples/treefree.v (100%) rename certification/{coq => htt}/lib/core.v (100%) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/CoqCertificate.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/coq/language/PrettyPrinting.scala rename src/main/scala/org/tygus/suslik/certification/targets/{coq/Coq.scala => htt/HTT.scala} (70%) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/Expressions.scala (99%) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/ProgramPrettyPrinting.scala (58%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/Sentences.scala (96%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/StatementProducers.scala (94%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/Statements.scala (99%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/Types.scala (90%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/language/package.scala (50%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/logic/Proof.scala (83%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/logic/ProofProducers.scala (92%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/logic/ProofSteps.scala (96%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/translation/ProgramTranslation.scala (87%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/translation/ProofTranslation.scala (91%) rename src/main/scala/org/tygus/suslik/certification/targets/{coq => htt}/translation/Translation.scala (94%) rename src/test/scala/org/tygus/suslik/certification/targets/{coq/CoqCertificationTests.scala => htt/HTTCertificationTests.scala} (86%) diff --git a/certification.md b/certification.md index 70af1d1e6..e74e3ae1e 100644 --- a/certification.md +++ b/certification.md @@ -1,8 +1,8 @@ # Certified Synthesis -Generation of correctness certificates for synthesized programs. Currently, we support Coq as the certification backend. +Generation of correctness certificates for synthesized programs. Currently, we support HTT as the certification backend. -## Coq +## HTT ### Requirements @@ -27,19 +27,19 @@ Before verifying a generated Coq certificate, make sure to compile the predefine Add the following flags to run synthesis with certification. -- `--certTarget `: Currently supports value `coq`. +- `--certTarget `: Currently supports value `htt`. - `--certDest ` (optional): Specifies the directory in which to generate a certificate file. Logs output to console if not provided. -For example, the following command produces a Coq certificate of the specification `listfree.syn`, and logs its contents to the console. +For example, the following command produces a HTT certificate of the specification `listfree.syn`, and logs its contents to the console. ```bash -./suslik examples/listfree.syn --assert false --certTarget coq +./suslik examples/listfree.syn --assert false --certTarget htt ``` -By providing the `--certDest` flag, SuSLik writes out this certificate as a file to the specified directory. The following example command writes a Coq certificate named `listfree.v` to the project root directory. +By providing the `--certDest` flag, SuSLik writes out this certificate as a file to the specified directory. The following example command writes a HTT certificate named `listfree.v` to the project root directory. ```bash -./suslik examples/listfree.syn --assert false --certTarget coq --certDest . +./suslik examples/listfree.syn --assert false --certTarget htt --certDest . ``` ### Viewing The Trace diff --git a/certification/coq/Makefile b/certification/htt/Makefile similarity index 100% rename from certification/coq/Makefile rename to certification/htt/Makefile diff --git a/certification/coq/_CoqProject b/certification/htt/_CoqProject similarity index 100% rename from certification/coq/_CoqProject rename to certification/htt/_CoqProject diff --git a/certification/coq/examples/listfree.v b/certification/htt/examples/listfree.v similarity index 100% rename from certification/coq/examples/listfree.v rename to certification/htt/examples/listfree.v diff --git a/certification/coq/examples/min_max.v b/certification/htt/examples/min_max.v similarity index 100% rename from certification/coq/examples/min_max.v rename to certification/htt/examples/min_max.v diff --git a/certification/coq/examples/rose_tree.v b/certification/htt/examples/rose_tree.v similarity index 100% rename from certification/coq/examples/rose_tree.v rename to certification/htt/examples/rose_tree.v diff --git a/certification/coq/examples/sll_free.v b/certification/htt/examples/sll_free.v similarity index 100% rename from certification/coq/examples/sll_free.v rename to certification/htt/examples/sll_free.v diff --git a/certification/coq/examples/sll_init.v b/certification/htt/examples/sll_init.v similarity index 100% rename from certification/coq/examples/sll_init.v rename to certification/htt/examples/sll_init.v diff --git a/certification/coq/examples/sll_singleton.v b/certification/htt/examples/sll_singleton.v similarity index 96% rename from certification/coq/examples/sll_singleton.v rename to certification/htt/examples/sll_singleton.v index b1f7817d3..3af1513e0 100644 --- a/certification/coq/examples/sll_singleton.v +++ b/certification/htt/examples/sll_singleton.v @@ -67,7 +67,7 @@ ssl_write. ssl_write_post y2. apply: val_ret; rewrite !unitL=>v1 v2 v3 v4/=. -exists y2, [::x], (y2 :-> x \+ y2 .+ 1 :-> null). +exists y2, [::x] ++ [::], (y2 :-> x \+ y2 .+ 1 :-> null). split=>//; split=>//. - by rewrite joinA joinC joinA. - constructor 2=>//; first by rewrite defPtUnO in v2; case/andP: v2=>//. diff --git a/certification/coq/examples/swap.v b/certification/htt/examples/swap.v similarity index 100% rename from certification/coq/examples/swap.v rename to certification/htt/examples/swap.v diff --git a/certification/coq/examples/swap1.v b/certification/htt/examples/swap1.v similarity index 100% rename from certification/coq/examples/swap1.v rename to certification/htt/examples/swap1.v diff --git a/certification/coq/examples/swap2.v b/certification/htt/examples/swap2.v similarity index 100% rename from certification/coq/examples/swap2.v rename to certification/htt/examples/swap2.v diff --git a/certification/coq/examples/treefree.v b/certification/htt/examples/treefree.v similarity index 100% rename from certification/coq/examples/treefree.v rename to certification/htt/examples/treefree.v diff --git a/certification/coq/lib/core.v b/certification/htt/lib/core.v similarity index 100% rename from certification/coq/lib/core.v rename to certification/htt/lib/core.v diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/CoqCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/CoqCertificate.scala deleted file mode 100644 index 142242b06..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/CoqCertificate.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.tygus.suslik.certification.targets.coq - -import org.tygus.suslik.certification.Certificate - -case class CoqCertificate(body: String, name: String) extends Certificate { - val suffix: String = ".v" -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/coq/language/PrettyPrinting.scala deleted file mode 100644 index a95cb9828..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/PrettyPrinting.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.tygus.suslik.certification.targets.coq.language - -trait PrettyPrinting { - def pp: String = toString -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala similarity index 70% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 68888ccef..7dd595bcf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/Coq.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -1,16 +1,16 @@ -package org.tygus.suslik.certification.targets.coq +package org.tygus.suslik.certification.targets.htt import java.nio.file.Paths import org.tygus.suslik.certification._ -import org.tygus.suslik.certification.targets.coq.translation.Translation -import org.tygus.suslik.certification.targets.coq.translation.Translation.TranslationException +import org.tygus.suslik.certification.targets.htt.translation.Translation +import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment -object Coq extends CertificationTarget { - val name: String = "Coq" - private val loadPath = Paths.get("certification/coq").toFile.getCanonicalPath +object HTT extends CertificationTarget { + val name: String = "HTT" + private val loadPath = Paths.get("certification/htt").toFile.getCanonicalPath private val prelude = s"""Add LoadPath "$loadPath" as SSL. From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. @@ -23,7 +23,7 @@ Require Import core. """ - def certify(proc: Procedure, env: Environment): CoqCertificate = { + def certify(proc: Procedure, env: Environment): HTTCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) val builder = new StringBuilder builder.append(prelude) @@ -37,6 +37,6 @@ Require Import core. CertTree.clear() // [Certify]: Clear tree after certification complete - CoqCertificate(builder.toString(), proc.name) + HTTCertificate(builder.toString(), proc.name) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala new file mode 100644 index 000000000..ccb41aba9 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.targets.htt + +import org.tygus.suslik.certification.Certificate + +case class HTTCertificate(body: String, name: String) extends Certificate { + val suffix: String = ".v" +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala similarity index 99% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 827ff5cf8..b3c89a024 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.LanguageUtils.cardinalityPrefix import org.tygus.suslik.logic.Specifications.selfCardVar diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala new file mode 100644 index 000000000..b05295470 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala @@ -0,0 +1,5 @@ +package org.tygus.suslik.certification.targets.htt.language + +trait PrettyPrinting { + def pp: String = toString +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/ProgramPrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala similarity index 58% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/ProgramPrettyPrinting.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala index c1ff1ad28..41fbd6ae6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/ProgramPrettyPrinting.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language trait ProgramPrettyPrinting extends PrettyPrinting { def ppp: String = this.pp diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala similarity index 96% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala index 76143aef5..dadbb9091 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala @@ -1,6 +1,6 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language -import org.tygus.suslik.certification.targets.coq.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.util.StringUtil.mkSpaces sealed abstract class Sentence extends PrettyPrinting { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala similarity index 94% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala index f2bb3fb78..057c80423 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/StatementProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala @@ -1,7 +1,7 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language -import org.tygus.suslik.certification.targets.coq.language.Expressions.CExpr -import org.tygus.suslik.certification.targets.coq.language.Statements.{CGuarded, CIf, CSeqComp, CStatement} +import org.tygus.suslik.certification.targets.htt.language.Expressions.CExpr +import org.tygus.suslik.certification.targets.htt.language.Statements.{CGuarded, CIf, CSeqComp, CStatement} object StatementProducers { type Kont = Seq[CStatement] => CStatement diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala similarity index 99% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala index b7ecca30c..522664aa1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.util.StringUtil._ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala similarity index 90% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/Types.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala index 31bb6ca99..b02b0c7b1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.coq.language +package org.tygus.suslik.certification.targets.htt.language sealed abstract class CoqType extends PrettyPrinting diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala similarity index 50% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/language/package.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala index b79d764df..333422f9c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala @@ -1,6 +1,6 @@ -package org.tygus.suslik.certification.targets.coq +package org.tygus.suslik.certification.targets.htt -import org.tygus.suslik.certification.targets.coq.language.Expressions.CVar +import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar package object language { type CGamma = Map[CVar, CoqType] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala similarity index 83% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 1523cc850..c50c4ef43 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -1,7 +1,7 @@ -package org.tygus.suslik.certification.targets.coq.logic +package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.coq.language._ -import org.tygus.suslik.certification.targets.coq.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language._ +import org.tygus.suslik.certification.targets.htt.language.Expressions._ object Proof { case class CGoal(pre: CAssertion, diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala similarity index 92% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala index 2ac8900b6..b673c9d66 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala @@ -1,9 +1,9 @@ -package org.tygus.suslik.certification.targets.coq.logic +package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.coq.language.{CInductiveClause, CInductivePredicate} -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal -import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.{AbduceBranchStep, OpenPostStep, OpenStep, ProofStep, SeqCompStep} +import org.tygus.suslik.certification.targets.htt.language.{CInductiveClause, CInductivePredicate} +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.logic.Proof.CGoal +import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{AbduceBranchStep, OpenPostStep, OpenStep, ProofStep, SeqCompStep} object ProofProducers { type Kont = Seq[ProofStep] => ProofStep diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala similarity index 96% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 28f7e0344..d2eabba21 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -1,8 +1,8 @@ -package org.tygus.suslik.certification.targets.coq.logic +package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.coq.language._ -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.Proof.CGoal +import org.tygus.suslik.certification.targets.htt.language._ +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.logic.Proof.CGoal object ProofSteps { case class Proof(root: ProofStep) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala similarity index 87% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 9245712d7..8dabdc5f2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -1,11 +1,11 @@ -package org.tygus.suslik.certification.targets.coq.translation +package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.language.StatementProducers._ -import org.tygus.suslik.certification.targets.coq.language.Statements._ -import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.translation.Translation._ +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.StatementProducers._ +import org.tygus.suslik.certification.targets.htt.language.Statements._ +import org.tygus.suslik.certification.targets.htt.logic.Proof._ +import org.tygus.suslik.certification.targets.htt.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala similarity index 91% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 4a63d3376..843c42e80 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,12 +1,12 @@ -package org.tygus.suslik.certification.targets.coq.translation +package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.coq.language.CInductiveClause -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.logic.Proof._ -import org.tygus.suslik.certification.targets.coq.logic.ProofProducers._ -import org.tygus.suslik.certification.targets.coq.logic.ProofSteps._ -import org.tygus.suslik.certification.targets.coq.translation.Translation._ +import org.tygus.suslik.certification.targets.htt.language.CInductiveClause +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.logic.Proof._ +import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ +import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ +import org.tygus.suslik.certification.targets.htt.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ import org.tygus.suslik.synthesis.rules.UnfoldingRules.Close diff --git a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala similarity index 94% rename from src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 75a5cb816..2704f93c8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/coq/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -1,11 +1,11 @@ -package org.tygus.suslik.certification.targets.coq.translation +package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.coq.language.Expressions._ -import org.tygus.suslik.certification.targets.coq.language.Statements._ -import org.tygus.suslik.certification.targets.coq.language._ -import org.tygus.suslik.certification.targets.coq.logic.Proof.{CEnvironment, CGoal} -import org.tygus.suslik.certification.targets.coq.logic.ProofSteps.Proof +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.Statements._ +import org.tygus.suslik.certification.targets.htt.language._ +import org.tygus.suslik.certification.targets.htt.logic.Proof.{CEnvironment, CGoal} +import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.Proof import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.language._ @@ -20,11 +20,11 @@ object Translation { trait Traversable /** - * Produces a Coq certificate from the tree of successful derivations and a synthesized procedure + * Produces a HTT certificate from the tree of successful derivations and a synthesized procedure * @param node the root of the derivation tree * @param proc the synthesized procedure * @param env the synthesis environment - * @return the inductive predicates, fun spec, proof, and program translated to Coq + * @return the inductive predicates, fun spec, proof, and program translated to HTT */ def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): (Seq[CInductivePredicate], CFunSpec, Proof, CProcedure) = { diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 49f4cd08c..b4c50c49c 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -95,7 +95,7 @@ object SynthesisRunner extends SynthesisRunnerUtil { implicit val certTargetRead: scopt.Read[CertificationTarget] = scopt.Read.reads { - case "coq" => coq.Coq + case "htt" => htt.HTT case _ => ??? } diff --git a/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala similarity index 86% rename from src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala rename to src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index 3590c348b..3d7c93ea5 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/coq/CoqCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.coq +package org.tygus.suslik.certification.targets.htt import java.io.File import java.nio.file.{Files, Paths} @@ -12,12 +12,12 @@ import scala.sys.process._ * @author Yasunari Watanabe */ -class CoqCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { +class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { val certRoot: File = Files.createTempDirectory("suslik-").toFile override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = it(s"certifies that it $desc") { - synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = Coq, certDest = certRoot)) + synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = HTT, certDest = certRoot)) val fname = testName.split('/').last val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath From 05daa6dbf3007f77473d6b94d5f6493d9f217621 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 26 Jun 2020 01:59:34 +0800 Subject: [PATCH 012/211] Make sll_singleton pass by refining the Coq tactics to handle post-condition existentials --- certification/htt/examples/sll_singleton.v | 56 +++++--------- certification/htt/lib/core.v | 75 +++++++++++-------- .../targets/htt/language/Expressions.scala | 18 ++++- .../targets/htt/language/Sentences.scala | 19 ++++- .../targets/htt/language/Statements.scala | 3 +- .../targets/htt/logic/ProofSteps.scala | 18 +++-- .../htt/translation/ProofTranslation.scala | 4 +- 7 files changed, 112 insertions(+), 81 deletions(-) diff --git a/certification/htt/examples/sll_singleton.v b/certification/htt/examples/sll_singleton.v index 3af1513e0..0bca9dd08 100644 --- a/certification/htt/examples/sll_singleton.v +++ b/certification/htt/examples/sll_singleton.v @@ -12,8 +12,8 @@ Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := s = nil /\ h = empty | sll1 of x != 0 of exists nxt s1 v, - exists h', - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ h' /\ sll nxt s1 h' + exists heap_sll_alpha_513, + s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ heap_sll_alpha_513 /\ sll nxt s1 heap_sll_alpha_513 . Definition sll_singleton_type := forall (x : nat) (p : ptr), @@ -22,9 +22,9 @@ Definition sll_singleton_type := fun h => h = p :-> a, [vfun (_: unit) h => - exists y (elems : seq nat), - exists h', - elems = [:: x] /\ h = p :-> y \+ h' /\ sll y elems h' ]). + exists y (elems: seq nat), + exists heap_sll_alpha_514, + elems = [:: x] /\ h = p :-> y \+ heap_sll_alpha_514 /\ sll y elems heap_sll_alpha_514 ]). Program Definition sll_singleton : sll_singleton_type := fun x p => Do ( @@ -37,41 +37,23 @@ Next Obligation. ssl_ghostelim_pre. move=>a//=. move=>[->]. -move=>H_valid. -step. -move=>y2//=. - - +ssl_ghostelim_post. +ssl_alloc y2. ssl_write. -Fail ssl_write_post p. -(* TODO: further generalize ssl_write_post *) -rewrite ?unitL. -rewrite ?unitR. -put_to_head (p :-> y2). -match goal with -| [ |- verify (p :-> _ \+ _) _ _ ] => - rewrite ?(joinC (p :-> _)) -| [ |- verify (p :-> _) _ _ ] => - rewrite -(unitL (p :-> _)) -end. -rewrite ?joinA; -apply frame. - +ssl_write_post p. ssl_write. -Fail ssl_write_post (y2 .+ 1). -(* TODO: further generalize ssl_write_post *) -rewrite ?joinA; -apply frame. - +ssl_write_post (y2 .+ 1). ssl_write. ssl_write_post y2. +ssl_emp; +exists (y2), ([:: x] ++ nil); +exists (y2 :-> x \+ y2 .+ 1 :-> null); +ssl_emp_post. +constructor 2=>//; +exists 0, nil, x; +exists empty; +ssl_emp_post. +constructor 1=>//; +ssl_emp_post. -apply: val_ret; rewrite !unitL=>v1 v2 v3 v4/=. -exists y2, [::x] ++ [::], (y2 :-> x \+ y2 .+ 1 :-> null). -split=>//; split=>//. -- by rewrite joinA joinC joinA. -- constructor 2=>//; first by rewrite defPtUnO in v2; case/andP: v2=>//. - exists null, [::], x, Unit; split=>//; rewrite unitR. - split=>//. - constructor 1=>//. Qed. diff --git a/certification/htt/lib/core.v b/certification/htt/lib/core.v index 9d1a4fc75..f1a931a05 100644 --- a/certification/htt/lib/core.v +++ b/certification/htt/lib/core.v @@ -10,39 +10,25 @@ Notation empty := Unit. Coercion ptr_nat : nat >-> ptr. Definition skip := ret tt. -Ltac put_to_head h := - (* make everything left associative*) - repeat match goal with - | [|- context[?X \+ (?Y \+ ?Z)]] => rewrite -(joinA X Y Z) - end; - (* bring to head *) - match goal with - | [|- context[?X \+ h]] => rewrite (joinC _ h) - end; - (* make the remainder left associative*) - repeat match goal with - | [|- context[h \+ ?Y \+ ?Z]] => rewrite -(joinA h Y Z) - end. +Ltac put_to_tail_ptr p := + rewrite -?joinA; rewrite ?(joinC (p :-> _) _); rewrite ?joinA. -Ltac ssl_read_pre x := - match goal with - | [|- context[x :-> ?V]] => put_to_head (x :-> ?V) - end. +Ltac put_to_tail h := + rewrite -?joinA; rewrite ?(joinC h _); rewrite ?joinA. + +Ltac put_to_head h := + put_to_tail h; + rewrite (joinC _ h). Ltac ssl_read := apply: bnd_readR=>/=. Ltac ssl_write := apply: bnd_writeR=>/=. Ltac ssl_write_post x := - rewrite -?joinA; - match goal with - | [ |- verify (x :-> _ \+ _) _ _ ] => - rewrite ?(joinC (x :-> _)) - | [ |- verify (x :-> _) _ _ ] => - rewrite -(unitL (x :-> _)) - end; - rewrite ?joinA; - apply frame. + (put_to_tail_ptr x + rewrite -(unitL (x :-> _))); apply frame. + +Ltac ssl_alloc x := + apply: bnd_allocbR=>x//=. Ltac ssl_dealloc := apply: bnd_seq; @@ -58,15 +44,42 @@ Ltac ssl_dealloc := end . -Ltac ssl_emp := apply: val_ret=>*//=. +Ltac non_null := + rewrite defPtUnO; + case/andP; + let not_null := fresh "not_null" in move=>not_null; + case/andP; + rewrite ?unitL; + match goal with + | [|- context[valid empty] ] => + repeat match goal with + | [|- _ -> _] => move=>_ + end + | _ => non_null + end. -Ltac ssl_emp_post := - try repeat hhauto || +Ltac store_valid_hyps := match goal with - | [H_cond: _ |- _] => case: ltngtP H_cond + | [|- _ -> _ -> _ ] => + let hyp := fresh "H_valid" in move=>hyp; + store_valid_hyps + | [|- _ = _ -> _ ] => + move=>_ + | [|- _ -> _ ] => + let hyp := fresh "H_valid" in move=>hyp; + try (move:(hyp); non_null) + end. + +Ltac ssl_emp := apply: val_ret; rewrite ?unitL; store_valid_hyps; move=>//. + +Ltac ssl_emp_post := + repeat match goal with + | [|- _ /\ _] => split=>// + | [|- _ = _] => rewrite ?unitL; rewrite ?unitR; hhauto + | [H_cond: _ |- _] => case: ltngtP H_cond=>// end. Ltac ssl_ghostelim_pre := try apply: ghR; move=>h. -Ltac ssl_ghostelim_post := move=>->Vh. +Ltac ssl_ghostelim_post := try store_valid_hyps. diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index b3c89a024..0f5478eea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -9,6 +9,8 @@ object Expressions { private def isMetadata: Boolean = !this.vars.exists(v => v.name != selfCardVar.name && !v.name.startsWith(cardinalityPrefix)) + def typeOf: Option[CoqType] = None + def collect[R <: CExpr](p: CExpr => Boolean): Seq[R] = { def collector(acc: Seq[R])(exp: CExpr): Seq[R] = exp match { @@ -94,24 +96,36 @@ object Expressions { } case class CBoolConst(value: Boolean) extends CExpr { + override def typeOf: Option[CoqType] = Some(CBoolType) override def pp: String = value.toString } case class CNatConst(value: Int) extends CExpr { + override def typeOf: Option[CoqType] = Some(CNatType) override def pp: String = value.toString } case class CSetLiteral(elems: List[CExpr]) extends CExpr { + override def typeOf: Option[CoqType] = Some(CNatSeqType) override def pp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.pp).mkString("; ")}]" override def ppp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.ppp).mkString("; ")}]" } case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { + override def typeOf: Option[CoqType] = left.typeOf override def pp: String = s"if ${cond.pp} then ${left.pp} else ${right.pp}" override def ppp: String = s"if ${cond.ppp} then ${left.ppp} else ${right.ppp}" } case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { + override def typeOf: Option[CoqType] = op match { + case COpPlus | COpMinus | COpMinus => Some(CNatType) + case COpBoolEq | COpLeq | COpLt => Some(CBoolType) + case COpAnd | COpOr | COpSetEq => Some(CPropType) + case COpHeapJoin => Some(CHeapType) + case COpUnion | COpDiff => Some(CNatSeqType) + case _ => None + } override def equals(that: Any): Boolean = that match { case CUnaryExpr(COpNot, COverloadedBinaryExpr(COpOverloadedEq, left1, right1)) => left == left1 && right == right1 case CBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 @@ -148,8 +162,8 @@ object Expressions { case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" def locPPP: String = if (offset == 0) loc.ppp else s"${loc.ppp} .+ $offset" - override def pp: String = s"$locPP :-> ${value.pp}" - override def ppp: String = s"$locPPP :-> ${value.ppp}" + override def pp: String = if (value == CNatConst(0)) s"$locPP :-> null" else s"$locPP :-> ${value.pp}" + override def ppp: String = if (value == CNatConst(0)) s"$locPPP :-> null" else s"$locPPP :-> ${value.ppp}" } case object CEmpty extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala index dadbb9091..57a3b5c77 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala @@ -16,10 +16,11 @@ sealed abstract class Sentence extends PrettyPrinting { case el@CAssertion(phi, sigma) => val ve = el.valueEx.filterNot(vars.contains).distinct val he = el.heapEx + val types = el.inferredTypes // existentials if (ve.nonEmpty) { builder.append(mkSpaces(offset)) - builder.append(s"exists ${ve.map(_.pp).mkString(" ")},\n") + builder.append(s"exists ${ve.map(v => types.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") } if (he.nonEmpty) { builder.append(mkSpaces(offset)) @@ -96,6 +97,22 @@ case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { val heapEx: Seq[CVar] = sigma.heapVars + + val inferredTypes: Map[CVar, CoqType] = { + def collectPhi(el: CExpr, m: Map[CVar, CoqType]): Map[CVar, CoqType] = el match { + case CBinaryExpr(COpSetEq, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) + case CBinaryExpr(COpSetEq, left: CVar, right) => collectPhi(right, m ++ Map(left -> CNatSeqType)) + case CBinaryExpr(COpSetEq, left, right: CVar) => collectPhi(left, m ++ Map(right -> CNatSeqType)) + case _ => m + } + def collectSigma: Map[CVar, CoqType] = { + val ptss = sigma.ptss + ptss.foldLeft[Map[CVar, CoqType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} + } + val mPhi = collectPhi(phi, Map.empty) + val mSigma = collectSigma + mPhi ++ mSigma + } } case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion) extends Sentence diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala index 522664aa1..c5c236108 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala @@ -69,7 +69,8 @@ object Statements { case CStore(to, off, e) => builder.append(mkSpaces(offset)) val t = if (off <= 0) to.ppp else s"(${to.ppp} .+ $off)" - builder.append(s"$t ::= ${e.ppp}") + val v = if (e == CNatConst(0)) "null" else e.ppp + builder.append(s"$t ::= $v") case CLoad(to, tpe, from, off) => builder.append(mkSpaces(offset)) val f = if (off <= 0) from.ppp else s"(${from.ppp} .+ $off)" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index d2eabba21..cc504fca2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -22,7 +22,7 @@ object ProofSteps { def collect[R <: CExpr](p: CExpr => Boolean): Set[R] = { def collector(acc: Set[R])(st: ProofStep): Set[R] = st match { - case WriteStep(to, e) => + case WriteStep(to, _, e) => acc ++ to.collect(p) ++ e.collect(p) case ReadStep(to) => acc ++ to.collect(p) @@ -63,8 +63,11 @@ object ProofSteps { } } - case class WriteStep(to: CVar, e: CExpr) extends ProofStep { - override def pp: String = s"ssl_write.\nssl_write_post ${to.pp}.\n" + case class WriteStep(to: CVar, offset: Int, e: CExpr) extends ProofStep { + override def pp: String = { + val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" + s"ssl_write.\nssl_write_post $ptr.\n" + } } case class ReadStep(to: CVar) extends ProofStep { @@ -174,7 +177,7 @@ object ProofSteps { // move heap validity assertion generated by ghR to context if (ghosts.nonEmpty) { - builder.append("move=>H_valid.\n") + builder.append("ssl_ghostelim_post.\n") } builder.toString() @@ -210,9 +213,11 @@ object ProofSteps { for (postApp <- postApps) { val expandedHeap = expand(postApp).map(ptss => ptss.subst(subst).pp) val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.mkString(" \\+ ") else "empty" - builder.append(s"exists $expandedHeapStr;\n") + builder.append(s"exists ($expandedHeapStr);\n") } + builder.append("ssl_emp_post.\n") + def expand2(app: CSApp): Unit = { val (expandedApp, clause) = heapSubst(app) val pred = predicates.find(_.name == clause.pred).get @@ -226,14 +231,13 @@ object ProofSteps { val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.map(_.pp).mkString(" \\+ ") else "empty" builder.append(s"exists $expandedHeapStr;\n") } + builder.append("ssl_emp_post.\n") expandedApp.apps.foreach(expand2) } for (postApp <- postApps) { expand2(postApp) } - builder.append("ssl_emp_post.\n") - builder.toString() } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 843c42e80..dfb2b6850 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -26,8 +26,8 @@ object ProofTranslation { (EmpStep(cenv.predicates, cenv.spec, cenv.subst, cenv.heapSubst), cenv) case Load(to, _, _, _) => (ReadStep(CVar(to.name)), cenv) - case Store(to, _, e) => - (WriteStep(CVar(to.name), translateExpr(e)), cenv) + case Store(to, offset, e) => + (WriteStep(CVar(to.name), offset, translateExpr(e)), cenv) case Malloc(to, tpe, sz) => (AllocStep(CVar(to.name), translateSSLType(tpe), sz), cenv) case Free(v) => From f5c165ad49d0f5509bf5dcb79c09dbc044b11c37 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 6 Jul 2020 09:49:59 +0800 Subject: [PATCH 013/211] Finish sll_init and Add listcopy (WIP) --- certification/htt/examples/listcopy.v | 127 ++++++++++++++++++++++++++ certification/htt/examples/sll_init.v | 71 ++++++++++---- 2 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 certification/htt/examples/listcopy.v diff --git a/certification/htt/examples/listcopy.v b/certification/htt/examples/listcopy.v new file mode 100644 index 000000000..ece0b8689 --- /dev/null +++ b/certification/htt/examples/listcopy.v @@ -0,0 +1,127 @@ +From mathcomp +Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. +From fcsl +Require Import prelude pred pcm unionmap heap. +From HTT +Require Import stmod stsep stlog stlogR. +From SSL +Require Import core. + +Inductive lseg (x : ptr) (s : seq nat) (h : heap) : Prop := +| lseg0 of x == 0 of + s = nil /\ h = empty +| lseg1 of x != 0 of + exists nxt s1 v, + exists heap_lseg_alpha_513, + s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ heap_lseg_alpha_513 /\ lseg nxt s1 heap_lseg_alpha_513 +. + + +(* +void listcopy(loc r) [] +{true ; r :-> x ** lseg(x, S)<_alpha_519>} +{true ; r :-> y ** lseg(x, S)<_alpha_520> ** lseg(y, S)<_alpha_521>} + +void listcopy (loc r) { + let x = *r; + if (x == 0) { + } else { + let v = *x; + let n = *(x + 1); + *x = n; + listcopy(x); + let yx = *x; + let y = malloc(2); + *r = y; + *(y + 1) = yx; + *x = v; + *y = v; + } +} +*) + + +Definition listcopy_type := + forall (r : ptr), + {(arg: ptr * seq nat)}, + STsep ( + fun h => + let: (x, s) := arg in + exists heap_lseg_alpha_519, + h = r :-> x \+ heap_lseg_alpha_519 /\ lseg x s heap_lseg_alpha_519, + [vfun (_: unit) h => + exists y, + exists heap_lseg_alpha_520 heap_lseg_alpha_521, + let: (x, s) := arg in + h = r :-> y \+ heap_lseg_alpha_520 \+ heap_lseg_alpha_521 /\ lseg x s heap_lseg_alpha_520 /\ lseg y s heap_lseg_alpha_521 ]). + + +Program Definition listcopy : listcopy_type := + Fix (fun (listcopy: listcopy_type) r => + Do ( + x <-- @read ptr r; + if x == 0 + then + ret tt + else + v <-- @read nat x; + n <-- @read ptr (x .+ 1); + x ::= n;; + listcopy x;; + yx <-- @read nat x; + y <-- allocb null 2; + (y .+ 1) ::= yx;; + x ::= v;; + y ::= v;; + ret tt + )). + +Next Obligation. + ssl_ghostelim_pre. + move=>[x2 S]. + move=>[heap_lseg_alpha_519]. + move=>[->]=>/=. + move=>H_lseg_alpha_519. + ssl_ghostelim_post. + + ssl_read. + + case: ifP=>H_cond. + + case: (H_lseg_alpha_519); rewrite H_cond=>//= _. + move=>[phi_lseg_alpha_519]. + move=>[sigma_lseg_alpha_519]. + ssl_emp. + rewrite sigma_lseg_alpha_519 in H_lseg_alpha_519. + exists x2, empty, empty. + ssl_emp_post. + + case: (H_lseg_alpha_519); rewrite H_cond=>//= _. + move=>[nxtx22] [s1x2] [vx22] [heap_lseg_alpha_513x2]. + move=>[phi_lseg_alpha_519]. + move=>[sigma_lseg_alpha_519]. + rewrite sigma_lseg_alpha_519. + move=>H_lseg_alpha_513x2. + + ssl_read. + ssl_read. + ssl_write. + ssl_write_post x2. + + put_to_head heap_lseg_alpha_513x2. + apply: bnd_seq. + apply: (gh_ex (nxtx22, s1x2)). + apply: val_do=>//=. + exists heap_lseg_alpha_513x2. split. admit. done. + + move=>_ h'. + move=>[y] [heap_lseg_alpha_520x2] [heap_lseg_alpha_521x2]. + move=>[-> [H_lseg_alpha_520x2 H_lseg_alpha_521x2]]=>//=. + rewrite ?joinA. + move=>H_valid1. + + (* infinite loop *) + (* apply: bnd_readR. *) + + + diff --git a/certification/htt/examples/sll_init.v b/certification/htt/examples/sll_init.v index 886447c2e..8f9bcd31b 100644 --- a/certification/htt/examples/sll_init.v +++ b/certification/htt/examples/sll_init.v @@ -7,6 +7,16 @@ Require Import stmod stsep stlog stlogR. From SSL Require Import core. +Lemma subset_singleton : + forall (s1: seq nat) (x: nat), + {subset s1 <= [::x]} -> {subset x :: s1 <= [::x]}. +intros. +move=>n. +rewrite inE; move/orP. case. +move/eqP=>->. by rewrite inE eqxx. +move/H=>//. +Qed. + Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := | sll0 of x == 0 of s = nil /\ h = empty @@ -16,43 +26,72 @@ Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ h' /\ sll nxt s1 h' . -(* TODO translation error: how to translate Suslik subsets into Coq list types? *) Definition sll_init_type := - forall (x : ptr) (v : nat), - {(s : seq nat)}, + forall (arg: ptr * nat), + {(s : seq nat)}, STsep ( fun h => - sll x s h, - [vfun (_: unit) h => - exists s1, - s1 org.tygus.suslik.certification.targets.coq.language.Expressions$COpSubset$@f88bfbe [:: v] /\ sll x s1 h ]). + let: (a, b) := arg in + sll a s h, + [vfun (_: unit) h => + let: (a, b) := arg in + + exists s1, + {subset s1 <= [:: b]} /\ sll a s1 h ]). Program Definition sll_init : sll_init_type := - Fix (fun (sll_init : sll_init_type) x v => - Do ( - ret tt )). + Fix (fun (sll_init : sll_init_type) arg => + let: (a, b) := arg in + Do ( + if a == 0 + then ret tt + else + n <-- @read ptr (a .+ 1); + sll_init (n, b);; + a ::= b;; + ret tt + )). Next Obligation. ssl_ghostelim_pre. move=>s//=. move=>H_sll. move=>H_valid. case: ifP=>H_cond. + case H_sll; rewrite H_cond=>//= _. move=>[sll_phi]. move=>[->]. ssl_emp. +exists nil. +ssl_emp_post. +constructor 1=>//. + case H_sll; rewrite H_cond=>//= _. move=>[nxt] [s1] [v]. move=>[h']. move=>[sll_phi]. move=>[->]. move=>H_sll0. -case: ifP=>H_cond. -case H_sll; rewrite H_cond=>//= _. -move=>[sll_phi]. -move=>[->]. -ssl_emp. +ssl_read. +put_to_head h'. +apply: bnd_seq. +apply: (gh_ex s1). +apply: val_do=>//= _ h''. +move=>[s2] [H_subseq H_sll1]. +move=>H_valid0. +ssl_write. +ssl_write_post p. +ssl_emp. +move:H_valid2. rewrite joinC joinA. rewrite defPtUnO; case/andP. move=> H_pnotnull _. +exists (n :: s2). +split=>//=. +apply: subset_singleton=>//. + +constructor 2=>//=. +exists nxt. +exists s2, n. +exists h''. +ssl_emp_post. Qed. - From 7330e5f552ba4a8518b40e7bb767e380e1538987 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 8 Jul 2020 13:29:51 +0800 Subject: [PATCH 014/211] Install SSL HTT bindings using opam --- certification.md | 12 +- certification/htt/Makefile | 15 --- certification/htt/_CoqProject | 4 - certification/htt/examples/listcopy.v | 127 ------------------ certification/htt/examples/listfree.v | 102 -------------- certification/htt/examples/min_max.v | 82 ----------- certification/htt/examples/rose_tree.v | 118 ---------------- certification/htt/examples/sll_free.v | 64 --------- certification/htt/examples/sll_init.v | 97 ------------- certification/htt/examples/sll_singleton.v | 59 -------- certification/htt/examples/swap.v | 38 ------ certification/htt/examples/swap1.v | 36 ----- certification/htt/examples/swap2.v | 48 ------- certification/htt/examples/treefree.v | 75 ----------- certification/htt/lib/core.v | 85 ------------ .../certification/targets/htt/HTT.scala | 4 +- 16 files changed, 9 insertions(+), 957 deletions(-) delete mode 100644 certification/htt/Makefile delete mode 100644 certification/htt/_CoqProject delete mode 100644 certification/htt/examples/listcopy.v delete mode 100644 certification/htt/examples/listfree.v delete mode 100644 certification/htt/examples/min_max.v delete mode 100644 certification/htt/examples/rose_tree.v delete mode 100644 certification/htt/examples/sll_free.v delete mode 100644 certification/htt/examples/sll_init.v delete mode 100644 certification/htt/examples/sll_singleton.v delete mode 100644 certification/htt/examples/swap.v delete mode 100644 certification/htt/examples/swap1.v delete mode 100644 certification/htt/examples/swap2.v delete mode 100644 certification/htt/examples/treefree.v delete mode 100644 certification/htt/lib/core.v diff --git a/certification.md b/certification.md index e74e3ae1e..2cb953194 100644 --- a/certification.md +++ b/certification.md @@ -4,25 +4,29 @@ Generation of correctness certificates for synthesized programs. Currently, we s ## HTT +### Examples + +Visit the [`ssl-htt`](https://github.com/yasunariw/ssl-htt) repository to see examples of generated certificates. + ### Requirements - [Coq](https://coq.inria.fr/) (>= "8.9.0" & < "8.12~") - [Mathematical Components](http://math-comp.github.io/math-comp/) `ssreflect` (>= "1.10.0" & < "1.11~") - [FCSL PCM library](https://github.com/imdea-software/fcsl-pcm) (>= "1.0.0" & < "1.3~") - [HTT](https://github.com/TyGuS/htt) +- [SSL-HTT](https://github.com/yasunariw/ssl-htt) ### Building Definitions and Proofs -For Coq requirements available on [OPAM](https://opam.ocaml.org/doc/Install.html), we recommend installing with it: +We recommend installing with [OPAM](https://opam.ocaml.org/doc/Install.html): ``` opam repo add coq-released https://coq.inria.fr/opam/released opam pin add coq-htt git+https://github.com/TyGuS/htt\#master --no-action --yes -opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt +opam pin add coq-ssl-htt git+https://github.com/yasunariw/ssl-htt\#master --no-action --yes +opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt coq-ssl-htt ``` -Before verifying a generated Coq certificate, make sure to compile the predefined tactics by running `make clean && make` in the directory `certification/coq`. Each generated Coq certificate adds this physical directory to the load path of Coq, and then maps it to the logical directory `SSL`. - ### Running Synthesis with Certification Add the following flags to run synthesis with certification. diff --git a/certification/htt/Makefile b/certification/htt/Makefile deleted file mode 100644 index a80245c44..000000000 --- a/certification/htt/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -all: default doc -default: Makefile.coq - make -f Makefile.coq - -clean: Makefile.coq - make -f Makefile.coq clean - rm -f Makefile.coq - -install: Makefile.coq - make -f Makefile.coq install - -Makefile.coq: _CoqProject - coq_makefile -f _CoqProject -o Makefile.coq - -.PHONY: coq clean install doc diff --git a/certification/htt/_CoqProject b/certification/htt/_CoqProject deleted file mode 100644 index 6922310c2..000000000 --- a/certification/htt/_CoqProject +++ /dev/null @@ -1,4 +0,0 @@ --Q . SSL - -lib/core.v - diff --git a/certification/htt/examples/listcopy.v b/certification/htt/examples/listcopy.v deleted file mode 100644 index ece0b8689..000000000 --- a/certification/htt/examples/listcopy.v +++ /dev/null @@ -1,127 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Inductive lseg (x : ptr) (s : seq nat) (h : heap) : Prop := -| lseg0 of x == 0 of - s = nil /\ h = empty -| lseg1 of x != 0 of - exists nxt s1 v, - exists heap_lseg_alpha_513, - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ heap_lseg_alpha_513 /\ lseg nxt s1 heap_lseg_alpha_513 -. - - -(* -void listcopy(loc r) [] -{true ; r :-> x ** lseg(x, S)<_alpha_519>} -{true ; r :-> y ** lseg(x, S)<_alpha_520> ** lseg(y, S)<_alpha_521>} - -void listcopy (loc r) { - let x = *r; - if (x == 0) { - } else { - let v = *x; - let n = *(x + 1); - *x = n; - listcopy(x); - let yx = *x; - let y = malloc(2); - *r = y; - *(y + 1) = yx; - *x = v; - *y = v; - } -} -*) - - -Definition listcopy_type := - forall (r : ptr), - {(arg: ptr * seq nat)}, - STsep ( - fun h => - let: (x, s) := arg in - exists heap_lseg_alpha_519, - h = r :-> x \+ heap_lseg_alpha_519 /\ lseg x s heap_lseg_alpha_519, - [vfun (_: unit) h => - exists y, - exists heap_lseg_alpha_520 heap_lseg_alpha_521, - let: (x, s) := arg in - h = r :-> y \+ heap_lseg_alpha_520 \+ heap_lseg_alpha_521 /\ lseg x s heap_lseg_alpha_520 /\ lseg y s heap_lseg_alpha_521 ]). - - -Program Definition listcopy : listcopy_type := - Fix (fun (listcopy: listcopy_type) r => - Do ( - x <-- @read ptr r; - if x == 0 - then - ret tt - else - v <-- @read nat x; - n <-- @read ptr (x .+ 1); - x ::= n;; - listcopy x;; - yx <-- @read nat x; - y <-- allocb null 2; - (y .+ 1) ::= yx;; - x ::= v;; - y ::= v;; - ret tt - )). - -Next Obligation. - ssl_ghostelim_pre. - move=>[x2 S]. - move=>[heap_lseg_alpha_519]. - move=>[->]=>/=. - move=>H_lseg_alpha_519. - ssl_ghostelim_post. - - ssl_read. - - case: ifP=>H_cond. - - case: (H_lseg_alpha_519); rewrite H_cond=>//= _. - move=>[phi_lseg_alpha_519]. - move=>[sigma_lseg_alpha_519]. - ssl_emp. - rewrite sigma_lseg_alpha_519 in H_lseg_alpha_519. - exists x2, empty, empty. - ssl_emp_post. - - case: (H_lseg_alpha_519); rewrite H_cond=>//= _. - move=>[nxtx22] [s1x2] [vx22] [heap_lseg_alpha_513x2]. - move=>[phi_lseg_alpha_519]. - move=>[sigma_lseg_alpha_519]. - rewrite sigma_lseg_alpha_519. - move=>H_lseg_alpha_513x2. - - ssl_read. - ssl_read. - ssl_write. - ssl_write_post x2. - - put_to_head heap_lseg_alpha_513x2. - apply: bnd_seq. - apply: (gh_ex (nxtx22, s1x2)). - apply: val_do=>//=. - exists heap_lseg_alpha_513x2. split. admit. done. - - move=>_ h'. - move=>[y] [heap_lseg_alpha_520x2] [heap_lseg_alpha_521x2]. - move=>[-> [H_lseg_alpha_520x2 H_lseg_alpha_521x2]]=>//=. - rewrite ?joinA. - move=>H_valid1. - - (* infinite loop *) - (* apply: bnd_readR. *) - - - diff --git a/certification/htt/examples/listfree.v b/certification/htt/examples/listfree.v deleted file mode 100644 index 2ff117730..000000000 --- a/certification/htt/examples/listfree.v +++ /dev/null @@ -1,102 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Inductive lseg (x : ptr) (s : seq nat) (h : heap) : Prop := -| lseg0 of x == 0 of - s = nil /\ h = empty -| lseg1 of x != 0 of - exists nxt s1 v, - exists h', - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ h' /\ lseg nxt s1 h' -. -Definition listfree_type := - forall (x : ptr), - {(S : seq nat)}, - STsep ( - fun h => - lseg x S h, - [vfun (_: unit) h => - h = empty ]). - -Program Definition listfree : listfree_type := - Fix (fun (listfree : listfree_type) x => - Do ( - if x == 0 - then - ret tt - else - nxtx2 <-- @read ptr (x .+ 1); - listfree nxtx2;; - dealloc (x .+ 1);; - dealloc x;; - ret tt - )). -Next Obligation. - (* pull out precondition ghosts *) - ssl_ghostelim_pre. - - (* move precondition ghosts to context *) - move=>S. - - (* move precondition heaplets to context *) - move=>H_lseg. - - (* move heap validity assertion generated by ghR to context *) - move=>H_valid. - - (* [Open][AbduceBranch] Make subgoals out of each conditional branch *) - case: ifP=>Hcond. - - (* [Open] match current condition to correct constructor *) - case: H_lseg; rewrite Hcond=>//= _. - - (* move constructor's pure part to context *) - move=>[lseg_phi]. - - (* substitute constructor's spatial part (heap) to conclusion *) - move=>[->]. - - (* ret tt *) - ssl_emp. - - - (* [Open] match current condition to correct constructor *) - case: H_lseg; rewrite Hcond=>//= _. - - (* move constructor's value existentials to context *) - move=>[nxt] [s1] [v]. - - (* move constructor's heap existentials to context *) - move=>[h']. - - (* move constructor's pure part to context *) - move=>[lseg_phi]. - - (* substitute constructor's spatial part (heap) to conclusion *) - move=>[->]. - - (* move constructor's predicate part to context *) - move=>H_rec_lseg. - - (* nxtx2 <-- @read ptr (x .+ 1); *) - ssl_read. - - (* listfree nxtx2;; *) - put_to_head h'. - apply: bnd_seq. - apply: (gh_ex s1). - apply: val_do=>//= _ ? ->; rewrite unitL=>_. - - (* dealloc x.+1;; *) - ssl_dealloc. - - (* dealloc x;; *) - ssl_dealloc. - - (* ret tt *) - ssl_emp. -Qed. diff --git a/certification/htt/examples/min_max.v b/certification/htt/examples/min_max.v deleted file mode 100644 index aadbe8102..000000000 --- a/certification/htt/examples/min_max.v +++ /dev/null @@ -1,82 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Definition min2_type := - forall (r : ptr) (x : nat) (y : nat), - STsep ( - fun h => - h = r :-> 0, - [vfun (_: unit) h => - exists m, - m <= x /\ m <= y /\ h = r :-> m ]). -Program Definition min2 : min2_type := - fun r x y => - Do ( - if x <= y - then - r ::= x;; - ret tt - else - r ::= y;; - ret tt - ). -Next Obligation. -ssl_ghostelim_pre. -move=>[->]. -case: ifP=>H_cond. -ssl_write. -ssl_write_post r. -ssl_emp; -exists x; -ssl_emp_post. -ssl_write. -ssl_write_post r. -ssl_emp; -exists y; -ssl_emp_post. - -Qed. - -Definition max_type := - forall (r : ptr) (x : nat) (y : nat), - STsep ( - fun h => - h = r :-> 0, - [vfun (_: unit) h => - exists m, - x <= m /\ y <= m /\ h = r :-> m ]). -Program Definition max : max_type := - fun r x y => - Do ( - if y <= x - then - r ::= x;; - ret tt - else - r ::= y;; - ret tt - ). -Next Obligation. -ssl_ghostelim_pre. -move=>[->]. -case: ifP=>H_cond. -ssl_write. -ssl_write_post r. -ssl_emp; -exists x; -ssl_emp_post. -ssl_write. -ssl_write_post r. -ssl_emp; -exists y; -ssl_emp_post. - -Qed. - - diff --git a/certification/htt/examples/rose_tree.v b/certification/htt/examples/rose_tree.v deleted file mode 100644 index dd7fcbc25..000000000 --- a/certification/htt/examples/rose_tree.v +++ /dev/null @@ -1,118 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -(* PREDICATES *) - -(* - -predicate rose_tree(loc x) { -| x == 0 => { self_card == 0 ; emp } -| not (x == 0) => { z < self_card; [x, 1] ** x :-> b ** buds(b)} -} - -predicate buds(loc x) { -| x == 0 => { self_card == 0 ; emp } -| not (x == 0) => { y < self_card /\ z < self_card; - [x, 3] ** x :-> v ** (x + 1) :-> r ** rose_tree(r) ** (x + 2) :-> nxt ** buds(nxt) } -} - - -*) - -Inductive rose_tree (x: ptr) (h: heap) : Prop := -| rose_tree0 of x == null of - h = empty -| rose_tree1 of x != null of - exists b, - exists h', - h = x :-> b \+ h' /\ buds b h' -with buds (x: ptr) (h: heap) : Prop := -| buds0 of x == null of - h = empty -| buds1 of x != null of - exists (v: nat) r nxt, - exists h' h'', - h = x :-> v \+ x .+ 1 :-> r \+ x .+ 2 :-> nxt \+ h' \+ h'' /\ rose_tree r h' /\ buds nxt h'' - -. - -(* SPECIFICATION *) - -(* -{ rose_tree(x) } - void rose_tree_free(loc x) -{ emp } - - *) - -Definition rose_tree_free_type := - forall (x: ptr), - STsep ( - fun h => rose_tree x h, - [vfun (_: unit) h => h = empty]). - -Definition rose_tree_free10_type := - forall (x: ptr), - STsep ( - fun h => rose_tree x h, - [vfun (_: unit) h => h = empty]). - -(* PROGRAM *) - -(* - -void rose_tree_free (loc x) { - if (x == 0) { - } else { - rose_tree_free10(x); - } -} - -void rose_tree_free10 (loc x) { - let b = *x; - if (b == 0) { - free(x); - } else { - let r = *(b + 1); - let n = *(b + 2); - rose_tree_free(r); - *x = n; - rose_tree_free10(x); - free(b); - } -} - -*) - -Program Definition rose_tree_free : rose_tree_free_type := - Fix (fun (rose_tree_free : rose_tree_free_type) x => - Do ( - let: rose_tree_free10 := - Fix (fun (rose_tree_free10: rose_tree_free_type) (x: ptr) => - Do ( - b <-- @read ptr x; - if b == 0 - then - dealloc x;; - dealloc (x .+ 1);; - dealloc (x .+ 2) - else - r <-- @read ptr (b .+ 1); - n <-- @read nat (b .+ 2); - rose_tree_free r;; - x ::= n;; - rose_tree_free10 x;; - dealloc b;; - dealloc (b .+ 1);; - dealloc (b .+ 2) - )) - in - if x == 0 then ret tt else rose_tree_free10 x)). - - diff --git a/certification/htt/examples/sll_free.v b/certification/htt/examples/sll_free.v deleted file mode 100644 index deebd4799..000000000 --- a/certification/htt/examples/sll_free.v +++ /dev/null @@ -1,64 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := -| sll0 of x == 0 of - s = nil /\ h = empty -| sll1 of x != 0 of - exists nxt s1 v, - exists h', - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ h' /\ sll nxt s1 h' -. -Definition sll_free_type := - forall (x : ptr), - {(s : seq nat)}, - STsep ( - fun h => - sll x s h, - [vfun (_: unit) h => - h = empty ]). -Program Definition sll_free : sll_free_type := - Fix (fun (sll_free : sll_free_type) x => - Do ( - if x == 0 - then - ret tt - else - nxtx2 <-- @read ptr (x .+ 1); - sll_free nxtx2;; - dealloc (x .+ 1);; - dealloc x;; - ret tt - )). -Next Obligation. -ssl_ghostelim_pre. -move=>s//=. -move=>H_sll. -move=>H_valid. -case: ifP=>H_cond. -case H_sll; rewrite H_cond=>//= _. -move=>[sll_phi]. -move=>[->]. -ssl_emp. -case H_sll; rewrite H_cond=>//= _. -move=>[nxt] [s1] [v]. -move=>[h']. -move=>[sll_phi]. -move=>[->]. -move=>H_sll0. -ssl_read. -put_to_head h'. -apply: bnd_seq. -apply: (gh_ex s1). -apply: val_do=>//= _ ? ->; rewrite unitL=>_. -ssl_dealloc. -ssl_dealloc. -ssl_emp. - -Qed. diff --git a/certification/htt/examples/sll_init.v b/certification/htt/examples/sll_init.v deleted file mode 100644 index 8f9bcd31b..000000000 --- a/certification/htt/examples/sll_init.v +++ /dev/null @@ -1,97 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Lemma subset_singleton : - forall (s1: seq nat) (x: nat), - {subset s1 <= [::x]} -> {subset x :: s1 <= [::x]}. -intros. -move=>n. -rewrite inE; move/orP. case. -move/eqP=>->. by rewrite inE eqxx. -move/H=>//. -Qed. - -Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := -| sll0 of x == 0 of - s = nil /\ h = empty -| sll1 of x != 0 of - exists nxt s1 v, - exists h', - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ h' /\ sll nxt s1 h' -. - -Definition sll_init_type := - forall (arg: ptr * nat), - {(s : seq nat)}, - STsep ( - fun h => - let: (a, b) := arg in - sll a s h, - [vfun (_: unit) h => - let: (a, b) := arg in - - exists s1, - {subset s1 <= [:: b]} /\ sll a s1 h ]). - -Program Definition sll_init : sll_init_type := - Fix (fun (sll_init : sll_init_type) arg => - let: (a, b) := arg in - Do ( - if a == 0 - then ret tt - else - n <-- @read ptr (a .+ 1); - sll_init (n, b);; - a ::= b;; - ret tt - )). -Next Obligation. -ssl_ghostelim_pre. -move=>s//=. -move=>H_sll. -move=>H_valid. -case: ifP=>H_cond. - -case H_sll; rewrite H_cond=>//= _. -move=>[sll_phi]. -move=>[->]. -ssl_emp. -exists nil. -ssl_emp_post. -constructor 1=>//. - -case H_sll; rewrite H_cond=>//= _. -move=>[nxt] [s1] [v]. -move=>[h']. -move=>[sll_phi]. -move=>[->]. -move=>H_sll0. -ssl_read. -put_to_head h'. -apply: bnd_seq. -apply: (gh_ex s1). -apply: val_do=>//= _ h''. -move=>[s2] [H_subseq H_sll1]. -move=>H_valid0. - -ssl_write. -ssl_write_post p. - -ssl_emp. -move:H_valid2. rewrite joinC joinA. rewrite defPtUnO; case/andP. move=> H_pnotnull _. -exists (n :: s2). -split=>//=. -apply: subset_singleton=>//. - -constructor 2=>//=. -exists nxt. -exists s2, n. -exists h''. -ssl_emp_post. -Qed. diff --git a/certification/htt/examples/sll_singleton.v b/certification/htt/examples/sll_singleton.v deleted file mode 100644 index 0bca9dd08..000000000 --- a/certification/htt/examples/sll_singleton.v +++ /dev/null @@ -1,59 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Inductive sll (x : ptr) (s : seq nat) (h : heap) : Prop := -| sll0 of x == 0 of - s = nil /\ h = empty -| sll1 of x != 0 of - exists nxt s1 v, - exists heap_sll_alpha_513, - s = [:: v] ++ s1 /\ h = x :-> v \+ x .+ 1 :-> nxt \+ heap_sll_alpha_513 /\ sll nxt s1 heap_sll_alpha_513 -. -Definition sll_singleton_type := - forall (x : nat) (p : ptr), - {(a : nat)}, - STsep ( - fun h => - h = p :-> a, - [vfun (_: unit) h => - exists y (elems: seq nat), - exists heap_sll_alpha_514, - elems = [:: x] /\ h = p :-> y \+ heap_sll_alpha_514 /\ sll y elems heap_sll_alpha_514 ]). -Program Definition sll_singleton : sll_singleton_type := - fun x p => - Do ( - y2 <-- allocb null 2; - p ::= y2;; - (y2 .+ 1) ::= null;; - y2 ::= x;; - ret tt ). -Next Obligation. -ssl_ghostelim_pre. -move=>a//=. -move=>[->]. -ssl_ghostelim_post. -ssl_alloc y2. -ssl_write. -ssl_write_post p. -ssl_write. -ssl_write_post (y2 .+ 1). -ssl_write. -ssl_write_post y2. -ssl_emp; -exists (y2), ([:: x] ++ nil); -exists (y2 :-> x \+ y2 .+ 1 :-> null); -ssl_emp_post. -constructor 2=>//; -exists 0, nil, x; -exists empty; -ssl_emp_post. -constructor 1=>//; -ssl_emp_post. - -Qed. diff --git a/certification/htt/examples/swap.v b/certification/htt/examples/swap.v deleted file mode 100644 index 5d5b695a7..000000000 --- a/certification/htt/examples/swap.v +++ /dev/null @@ -1,38 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Definition swap_type := - forall (x : ptr) (y : ptr), - {(a : nat) (b : nat)}, - STsep ( - fun h => - h = x :-> a \+ y :-> b, - [vfun (_: unit) h => - h = x :-> b \+ y :-> a ]). -Program Definition swap : swap_type := - fun x y => - Do ( - a2 <-- @read nat x; - b2 <-- @read nat y; - x ::= b2;; - y ::= a2;; - ret tt ). -Next Obligation. -ssl_ghostelim_pre. -move=>[a b]//=. -move=>-> HValid. -ssl_read. -ssl_read. -ssl_write. -ssl_write_post x. -ssl_write. -ssl_write_post y. -ssl_emp. - -Qed. diff --git a/certification/htt/examples/swap1.v b/certification/htt/examples/swap1.v deleted file mode 100644 index e0a681fdf..000000000 --- a/certification/htt/examples/swap1.v +++ /dev/null @@ -1,36 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Definition swap1_type := - forall (x : ptr) (z : ptr) (y : ptr) (t : ptr), - {(a : nat) (b : nat) (q : nat) (c : nat)}, - STsep ( - fun h => - h = x :-> a \+ y :-> c \+ z :-> b \+ t :-> q, - [vfun (_: unit) h => - h = x :-> c \+ z :-> b \+ t :-> q \+ y :-> 41 ]). -Program Definition swap1 : swap1_type := - fun x z y t => - Do ( - c2 <-- @read nat y; - x ::= c2;; - y ::= 41;; - ret tt ). -Next Obligation. -ssl_ghostelim_pre. -move=>[q [a [b c]]]//=. -move=>-> HValid. -ssl_read. -ssl_write. -ssl_write_post x. -ssl_write. -ssl_write_post y. -ssl_emp. - -Qed. diff --git a/certification/htt/examples/swap2.v b/certification/htt/examples/swap2.v deleted file mode 100644 index 7383b8fce..000000000 --- a/certification/htt/examples/swap2.v +++ /dev/null @@ -1,48 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Definition swap2_type := - forall (x : ptr) (z : ptr) (y : ptr) (t : ptr), - {(a : nat) (b : nat) (q : nat) (c : nat)}, - STsep ( - fun h => - h = x :-> a \+ y :-> c \+ z :-> b \+ t :-> q, - [vfun (_: unit) h => - h = x :-> q \+ z :-> c \+ t :-> a \+ y :-> b ]). -Program Definition swap2 : swap2_type := - fun x z y t => - Do ( - a2 <-- @read nat x; - c2 <-- @read nat y; - b2 <-- @read nat z; - q2 <-- @read nat t; - x ::= q2;; - y ::= b2;; - z ::= c2;; - t ::= a2;; - ret tt ). -Next Obligation. -ssl_ghostelim_pre. -move=>[q [a [b c]]]//=. -move=>-> HValid. -ssl_read. -ssl_read. -ssl_read. -ssl_read. -ssl_write. -ssl_write_post x. -ssl_write. -ssl_write_post y. -ssl_write. -ssl_write_post z. -ssl_write. -ssl_write_post t. -ssl_emp. - -Qed. diff --git a/certification/htt/examples/treefree.v b/certification/htt/examples/treefree.v deleted file mode 100644 index 11b81afde..000000000 --- a/certification/htt/examples/treefree.v +++ /dev/null @@ -1,75 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -Inductive tree (x : ptr) (s : seq nat) (h : heap) : Prop := -| tree0 of x == 0 of - s = nil /\ h = empty -| tree1 of x != 0 of - exists v s1 s2 r l, - exists h' h'', - s = [:: v] ++ s1 ++ s2 /\ h = x :-> v \+ x .+ 1 :-> l \+ x .+ 2 :-> r \+ h' \+ h'' /\ tree l s1 h' /\ tree r s2 h'' -. -Definition treefree_type := - forall (x : ptr), - {(s : seq nat)}, - STsep ( - fun h => - tree x s h, - [vfun (_: unit) h => - h = empty ]). -Program Definition treefree : treefree_type := - Fix (fun (treefree : treefree_type) x => - Do ( - if x == 0 - then - ret tt - else - l2 <-- @read ptr (x .+ 1); - r2 <-- @read ptr (x .+ 2); - treefree l2;; - treefree r2;; - dealloc (x .+ 2);; - dealloc (x .+ 1);; - dealloc x;; - ret tt - )). -Next Obligation. -ssl_ghostelim_pre. -move=>s//=. -move=>H_tree HValid. -case: ifP=>cond; case H_tree; rewrite cond//==>_. -move=>[E]. -move=>[->]. - -ssl_emp. - -move=>[v] [s1] [s2] [r] [l]. -move=>[h'] [h'']. -move=>[E]. -move=>[->]. -move=>[H_rec_tree H_rec_tree']. - -ssl_read. -ssl_read. -put_to_head h'. -apply: bnd_seq. -apply: (gh_ex s1). - -apply: val_do=>//= _ ? ->; rewrite unitL=>_. -put_to_head h''. -apply: bnd_seq. -apply: (gh_ex s2). - -apply: val_do=>//= _ ? ->; rewrite unitL=>_. -ssl_dealloc. -ssl_dealloc. -ssl_dealloc. -ssl_emp. - -Qed. diff --git a/certification/htt/lib/core.v b/certification/htt/lib/core.v deleted file mode 100644 index f1a931a05..000000000 --- a/certification/htt/lib/core.v +++ /dev/null @@ -1,85 +0,0 @@ -From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. - -(* SuSLik bindings *) -Notation empty := Unit. -Coercion ptr_nat : nat >-> ptr. -Definition skip := ret tt. - -Ltac put_to_tail_ptr p := - rewrite -?joinA; rewrite ?(joinC (p :-> _) _); rewrite ?joinA. - -Ltac put_to_tail h := - rewrite -?joinA; rewrite ?(joinC h _); rewrite ?joinA. - -Ltac put_to_head h := - put_to_tail h; - rewrite (joinC _ h). - -Ltac ssl_read := apply: bnd_readR=>/=. - -Ltac ssl_write := apply: bnd_writeR=>/=. - -Ltac ssl_write_post x := - (put_to_tail_ptr x + rewrite -(unitL (x :-> _))); apply frame. - -Ltac ssl_alloc x := - apply: bnd_allocbR=>x//=. - -Ltac ssl_dealloc := - apply: bnd_seq; - match goal with - | [|- context[_ :-> _ \+ _]] => - apply: val_dealloc=>//=_ - | [|- context[_ :-> _]] => - apply: val_deallocR=>//=_ - end; - try match goal with - | [|- context[_ \+ empty]] => - rewrite unitR - end -. - -Ltac non_null := - rewrite defPtUnO; - case/andP; - let not_null := fresh "not_null" in move=>not_null; - case/andP; - rewrite ?unitL; - match goal with - | [|- context[valid empty] ] => - repeat match goal with - | [|- _ -> _] => move=>_ - end - | _ => non_null - end. - -Ltac store_valid_hyps := - match goal with - | [|- _ -> _ -> _ ] => - let hyp := fresh "H_valid" in move=>hyp; - store_valid_hyps - | [|- _ = _ -> _ ] => - move=>_ - | [|- _ -> _ ] => - let hyp := fresh "H_valid" in move=>hyp; - try (move:(hyp); non_null) - end. - -Ltac ssl_emp := apply: val_ret; rewrite ?unitL; store_valid_hyps; move=>//. - -Ltac ssl_emp_post := - repeat match goal with - | [|- _ /\ _] => split=>// - | [|- _ = _] => rewrite ?unitL; rewrite ?unitR; hhauto - | [H_cond: _ |- _] => case: ltngtP H_cond=>// - end. - -Ltac ssl_ghostelim_pre := try apply: ghR; move=>h. - -Ltac ssl_ghostelim_post := try store_valid_hyps. - diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 7dd595bcf..17394bfdd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -10,9 +10,7 @@ import org.tygus.suslik.logic.Environment object HTT extends CertificationTarget { val name: String = "HTT" - private val loadPath = Paths.get("certification/htt").toFile.getCanonicalPath - private val prelude = s"""Add LoadPath "$loadPath" as SSL. -From mathcomp + private val prelude = s"""From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. From fcsl Require Import prelude pred pcm unionmap heap. From 18f5011308958209f31397f703482785b8848048 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 9 Jul 2020 11:11:52 +0800 Subject: [PATCH 015/211] Update SSL tactics project path --- certification.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/certification.md b/certification.md index 2cb953194..8af2c7125 100644 --- a/certification.md +++ b/certification.md @@ -14,7 +14,7 @@ Visit the [`ssl-htt`](https://github.com/yasunariw/ssl-htt) repository to see ex - [Mathematical Components](http://math-comp.github.io/math-comp/) `ssreflect` (>= "1.10.0" & < "1.11~") - [FCSL PCM library](https://github.com/imdea-software/fcsl-pcm) (>= "1.0.0" & < "1.3~") - [HTT](https://github.com/TyGuS/htt) -- [SSL-HTT](https://github.com/yasunariw/ssl-htt) +- [SSL-HTT](https://github.com/TyGuS/ssl-htt) ### Building Definitions and Proofs @@ -23,7 +23,7 @@ We recommend installing with [OPAM](https://opam.ocaml.org/doc/Install.html): ``` opam repo add coq-released https://coq.inria.fr/opam/released opam pin add coq-htt git+https://github.com/TyGuS/htt\#master --no-action --yes -opam pin add coq-ssl-htt git+https://github.com/yasunariw/ssl-htt\#master --no-action --yes +opam pin add coq-ssl-htt git+https://github.com/TyGuS/ssl-htt\#master --no-action --yes opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt coq-ssl-htt ``` From 1481c244b41914ffb5d88955081205637a329ae2 Mon Sep 17 00:00:00 2001 From: Ilya Sergey Date: Thu, 23 Jul 2020 13:32:25 +0800 Subject: [PATCH 016/211] Update certification.md --- certification.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/certification.md b/certification.md index 8af2c7125..8b07b665f 100644 --- a/certification.md +++ b/certification.md @@ -10,7 +10,7 @@ Visit the [`ssl-htt`](https://github.com/yasunariw/ssl-htt) repository to see ex ### Requirements -- [Coq](https://coq.inria.fr/) (>= "8.9.0" & < "8.12~") +- [Coq](https://coq.inria.fr/) (>= "8.10.0" & < "8.12~") - [Mathematical Components](http://math-comp.github.io/math-comp/) `ssreflect` (>= "1.10.0" & < "1.11~") - [FCSL PCM library](https://github.com/imdea-software/fcsl-pcm) (>= "1.0.0" & < "1.3~") - [HTT](https://github.com/TyGuS/htt) From a7b39f0f9ded439605188dae749308f498971409 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 25 Jul 2020 20:31:02 +0800 Subject: [PATCH 017/211] Make existing tests pass again; more work on listcopy --- .../targets/htt/language/Expressions.scala | 38 +-- .../targets/htt/language/Sentences.scala | 38 ++- .../targets/htt/language/Statements.scala | 3 +- .../targets/htt/logic/Proof.scala | 14 +- .../targets/htt/logic/ProofSteps.scala | 269 ++++++++++-------- .../htt/translation/ProofTranslation.scala | 78 +++-- .../targets/htt/translation/Translation.scala | 5 +- .../tygus/suslik/synthesis/StmtProducer.scala | 27 +- .../suslik/synthesis/rules/LogicalRules.scala | 4 +- .../synthesis/rules/OperationalRules.scala | 4 +- .../synthesis/rules/UnfoldingRules.scala | 10 +- .../synthesis/rules/UnificationRules.scala | 2 +- .../synthesis/certification/list/common.def | 6 +- .../synthesis/certification/list/listcopy.syn | 11 + .../synthesis/certification/list/listfree.syn | 2 +- 15 files changed, 294 insertions(+), 217 deletions(-) create mode 100644 src/test/resources/synthesis/certification/list/listcopy.syn diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 2e3918bbc..acdef9c0c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -6,7 +6,8 @@ import org.tygus.suslik.logic.Specifications.selfCardVar object Expressions { sealed abstract class CExpr extends ProgramPrettyPrinting { - private def isCard: Boolean = this.vars.forall(_.isCard) + def isTrivial: Boolean = this == CBoolConst(true) + def isCard: Boolean = this.vars.exists(_.isCard) def typeOf: Option[CoqType] = None @@ -91,7 +92,7 @@ object Expressions { case class CVar(name: String) extends CExpr { override def pp: String = if (name.startsWith(cardinalityPrefix)) name.drop(cardinalityPrefix.length) else name - val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name + override val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name } case class CBoolConst(value: Boolean) extends CExpr { @@ -189,32 +190,19 @@ object Expressions { } } - override def pp: String = { - val hs = heapVars.map(_.pp) - if (ptss.isEmpty) apps match { - case Seq(_, _, _*) => - s"$heapName = ${hs.mkString(" ")} /\\ ${ - apps.zip(hs).map { case (a, h) => s"${a.pp} $h" } mkString " /\\ " - }" - case Seq(hd, _*) => - s"${hd.pp} $heapName" - case Seq() => - s"$heapName = empty" - } else apps match { - case Seq(_, _*) => - s"$heapName = ${ptss.map(_.pp).mkString(" \\+ ")} \\+ ${hs.mkString(" \\+ ")} /\\ ${ - apps.zip(hs).map { case (a, h) => s"${a.pp} $h" } mkString " /\\ " - }" - case Seq() => - s"$heapName = ${ptss.map(_.pp).mkString(" \\+ ")}" - } + def ppHeap: String = if (ptss.isEmpty && apps.isEmpty) "empty" else { + val ptssStr = ptss.map(_.pp) + val appsStr = apps.map(_.heapName) + (ptssStr ++ appsStr).mkString(" \\+ ") } - val heapVars: Seq[CVar] = - if (ptss.isEmpty && apps.length == 1) Seq.empty - else apps.map(a => CVar(a.heapName)) + override def pp: String = if (apps.isEmpty) s"$heapName = $ppHeap" else { + val heapVarsStr = heapVars.map(_.pp) + val appsStr = apps.zip(heapVarsStr).map { case (a, h) => s"${a.pp} $h"}.mkString(" /\\ ") + s"$heapName = $ppHeap /\\ $appsStr" + } - override def vars: Seq[CVar] = super.vars ++ heapVars + val heapVars: Seq[CVar] = apps.map(a => CVar(a.heapName)) } case class CExists(override val vars: Seq[CVar], e: CExpr) extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala index 57a3b5c77..1b4eab0ce 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala @@ -14,8 +14,8 @@ sealed abstract class Sentence extends PrettyPrinting { def build(el: Sentence, offset: Int = 0, vars: Seq[CVar] = Seq.empty) : Unit = el match { case el@CAssertion(phi, sigma) => - val ve = el.valueEx.filterNot(vars.contains).distinct - val he = el.heapEx + val ve = el.valueVars.diff(vars).distinct + val he = el.heapVars val types = el.inferredTypes // existentials if (ve.nonEmpty) { @@ -44,8 +44,9 @@ sealed abstract class Sentence extends PrettyPrinting { builder.append("\n") } builder.append(".") - case el@CFunSpec(name, rType, params, pureParams, pre, post, inductive) => + case el@CFunSpec(name, rType, params, pureParams, pre, post) => val paramsStr = params.map { case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ") + val pureParamsDestructuredStr = if (pureParams.length == 1) pureParams.head._2.pp else s"(${pureParams.map(_._2.pp).mkString(", ")})" builder.append(mkSpaces(offset)) builder.append(s"Definition ${name}_type ") builder.append(":=\n") @@ -53,8 +54,9 @@ sealed abstract class Sentence extends PrettyPrinting { // print pure params if (pureParams.nonEmpty) { + val pureParamsTypStr = pureParams.map(_._1.pp).mkString(" * ") builder.append(mkSpaces(offset + 2)) - builder.append(s"{${pureParams.map { case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")}},\n") + builder.append(s"{(args: $pureParamsTypStr)},\n") } // define goal @@ -64,13 +66,22 @@ sealed abstract class Sentence extends PrettyPrinting { // pre-condition builder.append(mkSpaces(offset + 6)) builder.append("fun h =>\n") + if (pureParams.nonEmpty) { + builder.append(mkSpaces(offset + 8)) + builder.append(s"let: $pureParamsDestructuredStr := args in\n") + } build(pre, offset + 8, el.programVars) builder.append(",\n") // post-condition builder.append(mkSpaces(offset + 6)) builder.append(s"[vfun (_: ${rType.pp}) h =>\n") + if (pureParams.nonEmpty) { + builder.append(mkSpaces(offset + 8)) + builder.append(s"let: $pureParamsDestructuredStr := args in\n") + } build(post, offset + 8, el.programVars) + builder.append("\n") builder.append(mkSpaces(offset + 6)) builder.append("]).") @@ -86,16 +97,19 @@ case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { sigma.unify(source.sigma) } - val pureEx: Seq[CVar] = - phi.collect(_.isInstanceOf[CVar]).asInstanceOf[Seq[CVar]].filterNot(_.isCard) + def subst(sub: Map[CVar, CExpr]): CAssertion = + CAssertion(phi.subst(sub), sigma.subst(sub).asInstanceOf[CSFormula]) + + val pureVars: Seq[CVar] = + phi.vars.filterNot(_.isCard).distinct - val spatialEx: Seq[CVar] = - sigma.collect(_.isInstanceOf[CVar]).asInstanceOf[Seq[CVar]].filterNot(_.isCard) + val spatialVars: Seq[CVar] = + sigma.vars.filterNot(_.isCard).distinct - val valueEx: Seq[CVar] = - spatialEx ++ pureEx + val valueVars: Seq[CVar] = + (spatialVars ++ pureVars).distinct - val heapEx: Seq[CVar] = + val heapVars: Seq[CVar] = sigma.heapVars val inferredTypes: Map[CVar, CoqType] = { @@ -119,6 +133,6 @@ case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAsser case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends Sentence -case class CFunSpec(name: String, rType: CoqType, params: CFormals, pureParams: CFormals, pre: CAssertion, post: CAssertion, inductive: Boolean) extends Sentence { +case class CFunSpec(name: String, rType: CoqType, params: CFormals, pureParams: CFormals, pre: CAssertion, post: CAssertion) extends Sentence { def programVars: Seq[CVar] = params.map(_._2) ++ pureParams.map(_._2) } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala index 187fdb635..dfd1ad0b5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala @@ -148,7 +148,8 @@ object Statements { case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement - case class CProcedure(name: String, tp: CoqType, formals: Seq[(CoqType, CVar)], body: CStatement, inductive: Boolean = false) extends CStatement { + case class CProcedure(name: String, tp: CoqType, formals: Seq[(CoqType, CVar)], body: CStatement) extends CStatement { + val inductive = body.usedVars.contains(CVar(name)) override def pp: String = { val builder = new StringBuilder builder.append(s"Program Definition $name : ${name}_type :=\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index c50c4ef43..fbdc12a46 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -11,16 +11,24 @@ object Proof { universalGhosts: Seq[CVar], fname: String) + case class AppExpansion(constructor: Int, heap: CSFormula, ex: Seq[CExpr]) + case class CEnvironment(spec: CFunSpec, predicates: Seq[CInductivePredicate], + ghostSubst: Map[CVar, CVar] = Map.empty, subst: Map[CVar, CExpr] = Map.empty, - heapSubst: Map[CSApp, (CSFormula, CInductiveClause)] = Map.empty, + call: Option[(CGoal, Map[CVar, CVar], Map[CVar, CExpr])] = None, + heapSubst: Map[CSApp, AppExpansion] = Map.empty, + framedHeaplets: Seq[CExpr] = Seq.empty ) { def copy(spec: CFunSpec = this.spec, predicates: Seq[CInductivePredicate] = this.predicates, + ghostSubst: Map[CVar, CVar] = this.ghostSubst, subst: Map[CVar, CExpr] = this.subst, - heapSubst: Map[CSApp, (CSFormula, CInductiveClause)] = this.heapSubst + call: Option[(CGoal, Map[CVar, CVar], Map[CVar, CExpr])] = this.call, + heapSubst: Map[CSApp, AppExpansion] = this.heapSubst, + framedHeaplets: Seq[CExpr] = this.framedHeaplets ): CEnvironment = - CEnvironment(spec, predicates, subst, heapSubst) + CEnvironment(spec, predicates, ghostSubst, subst, call, heapSubst) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 293ed8748..2e4c3affe 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language._ import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.Proof.CGoal +import org.tygus.suslik.certification.targets.htt.logic.Proof._ object ProofSteps { case class Proof(root: ProofStep) { @@ -11,30 +11,122 @@ object ProofSteps { sealed abstract class ProofStep { def pp: String = "" - protected def nestedDestruct(items: Seq[CVar]): String = items.toList match { + protected def nestedDestructR(items: Seq[CVar]): String = items.toList match { case v1 :: v2 :: rest => - s"[${v1.pp} ${nestedDestruct(v2 :: rest)}]" + s"[${v1.pp} ${nestedDestructR(v2 :: rest)}]" case v :: _ => v.pp case Nil => "" } - def collect[R <: CExpr](p: CExpr => Boolean): Set[R] = { - def collector(acc: Set[R])(st: ProofStep): Set[R] = st match { + protected def nestedDestructL(items: Seq[CVar]): String = { + def visit(items: Seq[CVar]): String = { + items.toList match { + case v1 :: v2 :: rest => + s"[${visit(v2 :: rest)} ${v1.pp}]" + case v :: _ => + v.pp + case Nil => + "" + } + } + visit(items.reverse) + } + + protected def buildValueExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar], nested: Boolean = false): Unit = { + val ve = asn.valueVars.diff(outsideVars) + + // move value existentials to context + if (ve.nonEmpty) { + if (nested) { + builder.append(s"move=>${nestedDestructL(ve)}.\n") + } else { + builder.append(s"move=>${ve.map(v => s"[${v.pp}]").mkString(" ")}.\n") + } + } + } + + protected def buildHeapExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar]): Unit = { + val he = asn.heapVars.diff(outsideVars) + + // move heap existentials to context + if (he.nonEmpty) { + builder.append(s"move=>${he.map(v => s"[${v.pp}]").mkString(" ")}.\n") + } + } + + protected def buildHeapExpansion(builder: StringBuilder, asn: CAssertion, uniqueName: String): Unit = { + val phi = asn.phi + val sigma = asn.sigma + val phiName = s"phi_$uniqueName" + val sigmaName = s"sigma_$uniqueName" + + // move pure part to context + if (!phi.isTrivial) { + builder.append(s"move=>[$phiName].\n") + } + + // move spatial part to context, and then substitute where appropriate + builder.append(s"move=>[$sigmaName].\n") + builder.append(s"rewrite->$sigmaName in *.\n") + + // move predicate apps to context, if any + if (sigma.apps.nonEmpty) { + val appNames = sigma.apps.map(app => CVar(app.hypName)) + val hApps = nestedDestructR(appNames) + builder.append(s"move=>$hApps.\n") + } + } + + protected def existentialInstantiation(builder: StringBuilder, asn: CAssertion, ve: Seq[CExpr], heapSubst: Map[CSApp, AppExpansion]): Unit = { + if (ve.nonEmpty) { + builder.append(s"exists ${ve.map(v => s"(${v.pp})").mkString(", ")}.\n") + } + + def expandSAppExistentials(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = heapSubst.get(app) match { + case Some(e) => + val (ptss, apps) = e.heap.apps.map(expandSAppExistentials).unzip + (e.heap.ptss ++ ptss.flatten, apps.flatten) + case None => + (Seq.empty, Seq(app)) + } + + val apps = asn.sigma.apps + for (app <- apps) { + val (expandedPtss, expandedApps) = expandSAppExistentials(app) + val h = CSFormula("", expandedApps, expandedPtss) + builder.append(s"exists (${h.ppHeap});\n") + } + + // solve everything except sapps + builder.append("ssl_emp_post.\n") + + for { + app <- apps + ae@AppExpansion(constructor, heap, _) <- heapSubst.get(app) + } { + builder.append(s"unfold_constructor ${constructor + 1};\n") + existentialInstantiation(builder, CAssertion(CBoolConst(true), heap), ae.ex, heapSubst) + } + } + + def vars: Set[CVar] = { + def collector(acc: Set[CVar])(st: ProofStep): Set[CVar] = st match { case WriteStep(to, _, e) => - acc ++ to.collect(p) ++ e.collect(p) + acc ++ to.vars ++ e.vars case ReadStep(to) => - acc ++ to.collect(p) + acc ++ to.vars case AllocStep(to, _, _) => - acc ++ to.collect(p) + acc ++ to.vars case SeqCompStep(s1,s2) => val acc1 = collector(acc)(s1) collector(acc1)(s2) - case CallStep(app, pureEx) => - val argVars = app.args.flatMap(arg => arg.collect(p)).toSet - val pureExVars = pureEx.flatMap(e => e.collect(p)).toSet - acc ++ argVars ++ pureExVars + case CallStep(pre, post, vars, pureEx) => + val c1 = pre.sigma.vars + val c2 = post.sigma.vars + val c3 = pureEx.flatMap(e => e.vars).toSet + acc ++ c1 ++ c2 ++ c3 case _ => acc } @@ -56,7 +148,7 @@ object ProofSteps { } def simplifyBinding(newvar: CVar): ProofStep = { - val used: Set[CVar] = s2.collect(_.isInstanceOf[CVar]) + val used = s2.vars if (used.exists(v => newvar.name.startsWith(v.name))) { this } else s2 // Do not generate bindings for unused variables @@ -94,52 +186,38 @@ object ProofSteps { val builder = new StringBuilder() builder.append(s"case ${app.hypName}; rewrite H_cond=>//= _.\n") - val asn = goal.pre - val ve = asn.valueEx.filterNot(app.vars.contains).distinct - val ptss = goal.pre.sigma.ptss - val apps = goal.pre.sigma.apps - - // move constructor's value existentials to context - if (ve.nonEmpty) { - builder.append(s"move=>${ve.map(v => s"[${v.pp}]").mkString(" ")}.\n") - } - - // move constructor's heap existentials to context - if (apps.nonEmpty) { - builder.append(s"move=>${apps.map(app => s"[${app.heapName}]").mkString(" ")}.\n") - } - - // move constructor's pure part to context - builder.append(s"move=>[phi_${app.uniqueName}].\n") + buildValueExistentials(builder, goal.pre, app.vars) + buildHeapExistentials(builder, goal.pre, app.vars) - // substitute constructor's points-to assertions to conclusion - if (ptss.nonEmpty || apps.isEmpty) { - builder.append("move=>[->].\n") - } - - // move constructor's predicate apps to context - if (apps.nonEmpty) { - val appNames = apps.map(app => CVar(s"${app.hypName}")) - val hApps = nestedDestruct(appNames) - builder.append(s"move=>$hApps.\n") - } + buildHeapExpansion(builder, goal.pre, app.uniqueName) builder.toString() } } - case class CallStep(app: CSApp, pureEx: Seq[CExpr]) extends ProofStep { + case class CallStep(pre: CAssertion, post: CAssertion, outsideVars: Seq[CVar], pureEx: Seq[CExpr]) extends ProofStep { override def pp: String = { val builder = new StringBuilder() - // rearrange heap to put recursive heap component to the head - builder.append(s"put_to_head ${app.heapName}.\n") - builder.append("apply: bnd_seq.\n") + val callHeap = pre.sigma - // identify how many ghost values to pass to the call - for (v <- pureEx) builder.append(s"apply: (gh_ex ${v.pp}).\n") + // put the part of the heap touched by the recursive call at the head + builder.append(s"ssl_call_pre (${callHeap.ppHeap}).\n") - builder.append("apply: val_do=>//= _ ? ->; rewrite unitL=>_.\n") + // provide universal ghosts and execute call + builder.append(s"ssl_call (${pureEx.map(_.pp).mkString(", ")}).\n") + + existentialInstantiation(builder, pre, pre.valueVars.diff(outsideVars), Map.empty) + + val callId = s"call${scala.math.abs(pre.hashCode())}" + builder.append(s"move=>h_$callId.\n") + + buildValueExistentials(builder, post, outsideVars) + buildHeapExistentials(builder, post, outsideVars) + buildHeapExpansion(builder, post, callId) + + // store validity hypotheses in context + builder.append("store_valid.\n") builder.toString() } @@ -152,33 +230,12 @@ object ProofSteps { // Pull out any precondition ghosts and move precondition heap to the context builder.append("ssl_ghostelim_pre.\n") - val ghosts = goal.universalGhosts - val pre = goal.pre - val ptss = pre.sigma.ptss - val apps = pre.sigma.apps + buildValueExistentials(builder, goal.pre, goal.programVars, nested = true) + buildHeapExistentials(builder, goal.pre, goal.programVars) + buildHeapExpansion(builder, goal.pre, "root") - // move precondition's ghosts to context - if (ghosts.nonEmpty) { - builder.append("move=>") - builder.append(nestedDestruct(ghosts)) - builder.append("//=.\n") - } - - // substitute precondition's points-to assertions to conclusion - if (ptss.nonEmpty || apps.isEmpty) { - builder.append("move=>[->].\n") - } - - // move precondition's predicate apps to context - if (apps.nonEmpty) { - val hApps = nestedDestruct(apps.map(app => CVar(app.hypName))) - builder.append(s"move=>$hApps.\n") - } - - // move heap validity assertion generated by ghR to context - if (ghosts.nonEmpty) { - builder.append("ssl_ghostelim_post.\n") - } + // store heap validity assertions + builder.append("ssl_ghostelim_post.\n") builder.toString() } @@ -188,60 +245,26 @@ object ProofSteps { override def pp: String = "case: ifP=>H_cond.\n" } - case class EmpStep(predicates: Seq[CInductivePredicate], spec: CFunSpec, subst: Map[CVar, CExpr], heapSubst: Map[CSApp, (CSFormula, CInductiveClause)]) extends ProofStep { + case class EmpStep(cenv: CEnvironment) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append("ssl_emp;\n") - - // instantiate any existentials from the fun spec post - val post = spec.post - val postApps = post.sigma.apps - val programVars = spec.programVars - val ve = post.valueEx.filterNot(programVars.contains).distinct - - if (ve.nonEmpty) { - val subs = ve.map(e => e.subst(subst)) - builder.append(s"exists ${subs.map(s => s"(${s.pp})").mkString(", ")};\n") - } - - // Update heapSubst with the variable substitutions - val heapSubst1 = heapSubst.map(el => (el._1.subst(subst).asInstanceOf[CSApp], el._2)) - - def expand(app: CSApp): Seq[CPointsTo] = { - val app1 = app.subst(subst).asInstanceOf[CSApp] - val expandedApp = heapSubst1(app1)._1 - val rest = expandedApp.apps.flatMap(expand) - expandedApp.ptss ++ rest - } - for (postApp <- postApps) { - val expandedHeap = expand(postApp).map(ptss => ptss.subst(subst).pp) - val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.mkString(" \\+ ") else "empty" - builder.append(s"exists ($expandedHeapStr);\n") - } - - builder.append("ssl_emp_post.\n") - - def expand2(app: CSApp): Unit = { - val app1 = app.subst(subst).asInstanceOf[CSApp] - val (expandedApp, clause) = heapSubst1(app1) - val pred = predicates.find(_.name == clause.pred).get - builder.append(s"constructor ${clause.idx + 1}=>//;\n") - val valueEx = clause.asn.valueEx.filterNot(pred.params.map(_._2).contains).distinct.map(_.subst(subst).pp) - if (valueEx.nonEmpty) { - builder.append(s"exists ${valueEx.mkString(", ")};\n") - } - if (expandedApp.apps.nonEmpty) { - val expandedHeap = expandedApp.apps.flatMap(expand) - val expandedHeapStr = if (expandedHeap.nonEmpty) expandedHeap.map(_.pp).mkString(" \\+ ") else "empty" - builder.append(s"exists $expandedHeapStr;\n") - } - builder.append("ssl_emp_post.\n") - expandedApp.apps.foreach(expand2) - } - for (postApp <- postApps) { - expand2(postApp) + val ghostSubst = cenv.ghostSubst + val subst = cenv.subst.mapValues(_.subst(ghostSubst)) + val post = cenv.spec.post.subst(ghostSubst) + val programVars = cenv.spec.programVars.map(v => ghostSubst.getOrElse(v, v)) + val ve = post.valueVars.diff(programVars).distinct.map(_.subst(subst)) + val heapSubst = cenv.heapSubst.map { case (app, e) => + val app1 = app.subst(ghostSubst).subst(subst).asInstanceOf[CSApp] + val e1 = AppExpansion( + e.constructor, + e.heap.subst(ghostSubst).subst(subst).asInstanceOf[CSFormula], + e.ex.map(_.subst(ghostSubst).subst(subst)) + ) + app1 -> e1 } + existentialInstantiation(builder, post.subst(subst), ve, heapSubst) builder.toString() } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 53b121112..1c5a1b142 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -7,8 +7,8 @@ import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ import org.tygus.suslik.certification.targets.htt.translation.Translation._ -import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements._ +import org.tygus.suslik.logic.Block import org.tygus.suslik.synthesis._ object ProofTranslation { @@ -23,7 +23,7 @@ object ProofTranslation { def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => - (EmpStep(cenv.predicates, cenv.spec, cenv.subst, cenv.heapSubst), cenv) + (EmpStep(cenv), cenv) case Load(to, _, _, _) => (ReadStep(CVar(to.name)), cenv) case Store(to, offset, e) => @@ -34,19 +34,6 @@ object ProofTranslation { val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) (FreeStep(block.get.sz), cenv) - case Call(_, _, _) => - assert(item.node.goal.callGoal.nonEmpty) - val callGoal = item.node.goal.callGoal.get - val actualValues = callGoal.freshToActual.values - val candidateApps = callGoal.callerPre.sigma.apps - val sapp = candidateApps.find(app => actualValues.exists(_ == app.card)).get - val csapp = translateHeaplet(sapp).asInstanceOf[CSApp] - val pureEx = cenv.spec.pureParams - .filterNot(_._1 == CCardType).map(_._2) - .flatMap(v => callGoal.companionToFresh.get(Var(v.name))) - .flatMap(v => callGoal.freshToActual.get(v)) - .map(v => translateExpr(v).asInstanceOf[CVar]) - (CallStep(csapp, pureEx), cenv) } def translateProducer(stmtProducer: StmtProducer, cenv: CEnvironment): (ProofProducer, CEnvironment) = { @@ -57,28 +44,63 @@ object ProofTranslation { (k1 >> k2, cenv2) case PartiallyAppliedProducer(p, _) => translateProducer(p, cenv) + case FrameProducer(h) => + h match { + case _: Block => + (IdProofProducer, cenv) + case _ => + val framedHeaplets = cenv.framedHeaplets :+ translateHeaplet(h) + (IdProofProducer, cenv.copy(framedHeaplets = framedHeaplets)) + } + case EnterCall(goal) => + val cgoal = translateGoal(goal) + (IdProofProducer, cenv.copy(subst = Map.empty, call = Some(cgoal, cenv.ghostSubst, cenv.subst))) + case ExitCall => + assert(item.node.goal.callGoal.isDefined) + val callGoal = item.node.goal.callGoal.get + val (initialGoal, prevGhostSubst, prevSubst) = cenv.call.get + + // create call step + val companionToFresh = callGoal.companionToFresh.map{ case (k, v) => CVar(k.name) -> CVar(v.name)} + val freshToActual = callGoal.freshToActual.map{ case (k, v) => CVar(k.name) -> translateExpr(v)} + val outsideVars = initialGoal.programVars ++ initialGoal.universalGhosts + val pureEx = cenv.spec.pureParams.map(_._2).map(v => v.subst(companionToFresh)).map(v => v.subst(freshToActual)) + val pre = initialGoal.post.subst(freshToActual) + val post = translateAsn(callGoal.calleePost).subst(freshToActual) + + // end of call; reset subst map + val cenv1 = cenv.copy(ghostSubst = prevGhostSubst, subst = prevSubst, call = None) + + (PrependProofProducer(CallStep(pre, post, outsideVars, pureEx)), cenv1) case SubstProducer(subst) => - val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) }.filterKeys(!_.isCard) + val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) } val cenv1 = cenv.copy(subst = cenv.subst ++ csub) (IdProofProducer, cenv1) - case UnrollProducer(predName, clause, substEx) => - val csub = substEx.map { case (v, e) => CVar(v.name) -> translateExpr(e) }.filterKeys(!_.isCard) - val srcFp = item.node.footprint - item.node.children.head.footprint - val dstFp = item.node.children.head.footprint - item.node.footprint + case GhostSubstProducer(subst) => + val csub = subst.map { case (v, e) => CVar(v.name) -> CVar(e.name) } + val cenv1 = cenv.copy(ghostSubst = cenv.ghostSubst ++ csub) + (IdProofProducer, cenv1) + case UnrollProducer(app, selector, expansion, substEx) => + val cselector = translateExpr(selector) + val capp = translateHeaplet(app).asInstanceOf[CSApp] + val csub = substEx.map { case (src, dst) => CVar(src.name) -> CVar(dst.name) } + val cexpansion = translateSFormula(expansion) - val csrc = translateHeaplet(srcFp.post.sigma.apps.head).asInstanceOf[CSApp] - val cdst = translateSFormula(dstFp.post.sigma) + // get clause existentials + val predicate = cenv.predicates.find(_.name == app.pred).get + val params = predicate.vars + val (clause, clauseIdx) = predicate.clauses.zipWithIndex.find(_._1.selector == cselector).get + val clauseEx = clause.asn.valueVars.diff(params).map(v => csub(v)) - val pred = cenv.predicates.find(_.name == predName).get - val selector = translateExpr(clause.selector) - val clauseIdx = pred.clauses.indexWhere(_.selector == selector) - val cclause = CInductiveClause(predName, clauseIdx, selector, translateAsn(clause.asn)) - val cenv1 = cenv.copy(subst = cenv.subst ++ csub, heapSubst = cenv.heapSubst ++ Map(csrc -> (cdst, cclause))) + val item = AppExpansion(clauseIdx, cexpansion, clauseEx) + val heapSubst = cenv.heapSubst ++ Map(capp -> item) + val cenv1 = cenv.copy(heapSubst = heapSubst) (IdProofProducer, cenv1) case ConstProducer(s) => val (step, cenv1) = translateOperation(s, cenv) (ConstProofProducer(step), cenv1) case PrependProducer(s) => + if (s.isInstanceOf[Call]) return (IdProofProducer, cenv) val (step, cenv1) = translateOperation(s, cenv) (PrependProofProducer(step), cenv1) case BranchProducer(_) => @@ -112,7 +134,7 @@ object ProofTranslation { val nextItems = generateNextItems(p, cenv1) val nextKont = updateProducerPost(nextItems, p, cenv1) - nextItems.headOption match { + nextItems.headOption match { case Some(childHead) => traverseProof(childHead, nextKont) case None => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 64ca6e375..96ea824f7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -38,7 +38,7 @@ object Translation { val proof = ProofTranslation.translate(node, proc, initialGoal, initialCEnv) val stmtBody = ProgramTranslation.translate(node, proc, initialGoal) - val cproc = CProcedure(proc.name, translateSSLType(proc.tp), proc.formals.map(translateParam), stmtBody, spec.inductive) + val cproc = CProcedure(proc.name, translateSSLType(proc.tp), proc.formals.map(translateParam), stmtBody) (cpreds, spec, proof, cproc) } @@ -60,8 +60,7 @@ object Translation { cparams, pureParams, cpre, - cpost, - node.rule == Open + cpost ) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 51e312e17..4920c88a3 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.{InductiveClause, SApp, SFormula} +import org.tygus.suslik.logic.{Heaplet, InductiveClause, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils @@ -162,14 +162,25 @@ case class GuardedProducer(cond: Expr, goal: Goal) extends StmtProducer { val fn: Kont = liftToSolutions(stmts => Guarded(cond, stmts.head, stmts.last, goal.label)) } -// Captures variable substitutions -case class SubstProducer(subst: Map[Var, Expr]) extends StmtProducer { +trait Noop { val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => stmts.head) + val fn: Kont = _.head } +// Captures variable substitutions +case class SubstProducer(subst: Map[Var, Expr]) extends StmtProducer with Noop + +// Captures ghost variable instantiations +case class GhostSubstProducer(subst: Map[Var, Var]) extends StmtProducer with Noop + // Captures an unrolled predicate -case class UnrollProducer(pred: String, clause: InductiveClause, substEx: Map[Var, Expr]) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => stmts.head) -} +case class UnrollProducer(app: SApp, selector: Expr, expansion: SFormula, substEx: Map[Var, Var]) extends StmtProducer with Noop + +// Captures a frame +case class FrameProducer(h: Heaplet) extends StmtProducer with Noop + +// Enters a call synthesis +case class EnterCall(goal: Goal) extends StmtProducer with Noop + +// Exits a call synthesis +case object ExitCall extends StmtProducer with Noop diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala index 682f1e3dd..2ffd57d35 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala @@ -131,7 +131,7 @@ object LogicalRules extends PureLogicUtils with SepLogicUtils with RuleUtils { val newPre = Assertion(pre.phi, newPreSigma) val newPost = Assertion(post.phi, newPostSigma) val newGoal = goal.spawnChild(newPre, newPost) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = FrameProducer(hPre) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) } } @@ -264,7 +264,7 @@ object LogicalRules extends PureLogicUtils with SepLogicUtils with RuleUtils { val _p1 = rest1.subst(x, e) val _s1 = s1.subst(x, e) val newGoal = goal.spawnChild(Assertion(_p1, _s1), goal.post.subst(x, e)) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(Map(x -> e)) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) assert(goal.callGoal.isEmpty) List(RuleResult(List(newGoal), kont, this, goal)) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala index 36e1e90d1..0fd11b23f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala @@ -96,7 +96,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post = post.subst(a, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = PrependProducer(Load(y, tpy, x, offset)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = GhostSubstProducer(Map(a -> y)) >> PrependProducer(Load(y, tpy, x, offset)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, this, goal)) case Some(h) => ruleAssert(false, s"Read rule matched unexpected heaplet ${h.pp}") @@ -148,7 +148,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post.subst(x, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = SubstProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = GhostSubstProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, this, goal)) case _ => Nil } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index 15b610ef4..584a5071b 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -94,7 +94,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { suspendedCallGoal = Some(SuspendedCallGoal(goal.pre, goal.post, callePost, call, freshSub)) newGoal = goal.spawnChild(post = f.pre, gamma = newGamma, callGoal = suspendedCallGoal) } yield { - val kont: StmtProducer = SubstProducer(freshSub) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = EnterCall(newGoal) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } } @@ -126,7 +126,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPost = callGoal.callerPost val newGoal = goal.spawnChild(pre = newPre, post = newPost, callGoal = None) val postCallTransition = Transition(goal, newGoal) - val kont: StmtProducer = PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = ExitCall >> PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, List(postCallTransition) ++ companionTransition(callGoal, goal))) } @@ -179,7 +179,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val env = goal.env def heapletResults(h: Heaplet): Seq[RuleResult] = h match { - case SApp(pred, args, PTag(cls, unf), card) => + case a@SApp(pred, args, PTag(cls, unf), card) => if (unf >= env.config.maxCloseDepth) return Nil ruleAssert(env.predicates.contains(pred), @@ -193,7 +193,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val substArgs = paramNames.zip(args).toMap + (selfCardVar -> card) val subDerivations = for { - clause@InductiveClause(selector, asn) <- clauses + InductiveClause(selector, asn) <- clauses // Make sure that existential in the body are fresh asnExistentials = asn.vars -- paramNames.toSet -- Set(selfCardVar) freshSuffix = args.take(1).map(_.pp).mkString("_") @@ -209,7 +209,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPhi = post.phi && actualConstraints && actualSelector val newPost = Assertion(newPhi, goal.post.sigma ** actualBody - h) - val kont = UnrollProducer(predName, clause, freshExistentialsSubst) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnrollProducer(a, selector, actualBody, freshExistentialsSubst) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(goal.spawnChild(post = newPost)), kont, this, goal) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index b6b40a2c7..4929af6b4 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -207,7 +207,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils newPost = goal.post.subst(sigma) newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) - kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, this, goal) } } diff --git a/src/test/resources/synthesis/certification/list/common.def b/src/test/resources/synthesis/certification/list/common.def index 3f8a4651a..35ce5133b 100644 --- a/src/test/resources/synthesis/certification/list/common.def +++ b/src/test/resources/synthesis/certification/list/common.def @@ -1,4 +1,4 @@ -predicate lseg(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } +predicate lseg(loc x, set n) { +| x == 0 => { n =i {} ; emp } +| not (x == 0) => { n =i {v} ++ n1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, n1) } } diff --git a/src/test/resources/synthesis/certification/list/listcopy.syn b/src/test/resources/synthesis/certification/list/listcopy.syn new file mode 100644 index 000000000..c2cfcc7ec --- /dev/null +++ b/src/test/resources/synthesis/certification/list/listcopy.syn @@ -0,0 +1,11 @@ +Copy a linked list + +### + +{ r :-> x ** lseg(x, N) } + + void listcopy(loc r) + +{ true ; r :-> y ** lseg(x, N) ** lseg(y, N) } + +##### \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/list/listfree.syn b/src/test/resources/synthesis/certification/list/listfree.syn index 4033ee4fe..16aba4238 100644 --- a/src/test/resources/synthesis/certification/list/listfree.syn +++ b/src/test/resources/synthesis/certification/list/listfree.syn @@ -2,7 +2,7 @@ Listfree example ### -{true; lseg(x, S)} +{true; lseg(x, s)} void listfree(loc x) {true ; emp } From c66f9fcc107e85d83523b93e240600ad55e53e02 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 27 Jul 2020 10:26:45 +0800 Subject: [PATCH 018/211] Add subset notation and fix the way clause existentials are calculated --- .../certification/targets/htt/language/Expressions.scala | 9 +++++++-- .../targets/htt/translation/ProofTranslation.scala | 7 ++++--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index acdef9c0c..3655a2d97 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -132,7 +132,10 @@ object Expressions { case COverloadedBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 case _ => false } - override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" + override def pp: String = op match { + case COpSubset => s"{subset ${left.pp} ${op.pp} ${right.pp}}" + case _ => s"${left.pp} ${op.pp} ${right.pp}" + } override def ppp: String = s"${left.ppp} ${op.ppp} ${right.ppp}" } @@ -333,7 +336,9 @@ object Expressions { override def pp: String = "=" } - object COpSubset extends CBinOp + object COpSubset extends CBinOp { + override def pp: String = "<=" + } object COpIntersect extends CBinOp diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 1c5a1b142..04dd579f7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -88,9 +88,10 @@ object ProofTranslation { // get clause existentials val predicate = cenv.predicates.find(_.name == app.pred).get - val params = predicate.vars - val (clause, clauseIdx) = predicate.clauses.zipWithIndex.find(_._1.selector == cselector).get - val clauseEx = clause.asn.valueVars.diff(params).map(v => csub(v)) + val clauseIdx = predicate.clauses.indexWhere(_.selector == cselector) + val varsInExpansion = cexpansion.vars.distinct + val clauseExUnordered = csub.values.toList + val clauseEx = varsInExpansion.filter(clauseExUnordered.contains) val item = AppExpansion(clauseIdx, cexpansion, clauseEx) val heapSubst = cenv.heapSubst ++ Map(capp -> item) From d01bf1e06b60bdd913ba3591a585a057d2f97793 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 27 Jul 2020 23:47:55 +0800 Subject: [PATCH 019/211] Fix clause existentials issue for real; minor fixes --- .../targets/htt/logic/ProofSteps.scala | 24 ++++++++++--------- .../htt/translation/ProofTranslation.scala | 7 +++--- .../synthesis/certification/list/common.def | 6 ++--- .../synthesis/certification/list/listcopy.syn | 4 ++-- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 2e4c3affe..ead59ba3a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -81,7 +81,7 @@ object ProofSteps { protected def existentialInstantiation(builder: StringBuilder, asn: CAssertion, ve: Seq[CExpr], heapSubst: Map[CSApp, AppExpansion]): Unit = { if (ve.nonEmpty) { - builder.append(s"exists ${ve.map(v => s"(${v.pp})").mkString(", ")}.\n") + builder.append(s"exists ${ve.map(v => s"(${v.pp})").mkString(", ")};\n") } def expandSAppExistentials(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = heapSubst.get(app) match { @@ -113,10 +113,10 @@ object ProofSteps { def vars: Set[CVar] = { def collector(acc: Set[CVar])(st: ProofStep): Set[CVar] = st match { - case WriteStep(to, _, e) => + case WriteStep(to, _, e, _) => acc ++ to.vars ++ e.vars - case ReadStep(to) => - acc ++ to.vars + case ReadStep(to, from) => + acc ++ to.vars ++ from.vars case AllocStep(to, _, _) => acc ++ to.vars case SeqCompStep(s1,s2) => @@ -140,7 +140,7 @@ object ProofSteps { def simplify: ProofStep = { (s1, s2) match { - case (ReadStep(to), _) => simplifyBinding(to) + case (ReadStep(to, _), _) => simplifyBinding(to) // case (WriteStep(to), _) => simplifyBinding(to) // case (AllocStep(to, _, _), _) => simplifyBinding(to) case _ => this @@ -155,14 +155,16 @@ object ProofSteps { } } - case class WriteStep(to: CVar, offset: Int, e: CExpr) extends ProofStep { + case class WriteStep(to: CVar, offset: Int, e: CExpr, frame: Boolean = true) extends ProofStep { override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" - s"ssl_write.\nssl_write_post $ptr.\n" + val writeStep = "ssl_write.\n" + val writePostStep = if (frame) s"ssl_write_post $ptr.\n" else "" + writeStep + writePostStep } } - case class ReadStep(to: CVar) extends ProofStep { + case class ReadStep(to: CVar, from: CVar) extends ProofStep { override def pp: String = "ssl_read.\n" } @@ -178,15 +180,15 @@ object ProofSteps { } case object OpenStep extends ProofStep { - override def pp: String = "case: ifP=>H_cond.\n" + override def pp: String = "ssl_open.\n" } case class OpenPostStep(app: CSApp, goal: CGoal) extends ProofStep { override def pp: String = { val builder = new StringBuilder() - builder.append(s"case ${app.hypName}; rewrite H_cond=>//= _.\n") + builder.append(s"ssl_open_post ${app.hypName}.\n") - buildValueExistentials(builder, goal.pre, app.vars) + buildValueExistentials(builder, goal.pre, app.vars ++ goal.programVars) buildHeapExistentials(builder, goal.pre, app.vars) buildHeapExpansion(builder, goal.pre, app.uniqueName) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 04dd579f7..2b348f7b8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -24,10 +24,11 @@ object ProofTranslation { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => (EmpStep(cenv), cenv) - case Load(to, _, _, _) => - (ReadStep(CVar(to.name)), cenv) + case Load(to, _, from, _) => + (ReadStep(CVar(to.name), CVar(from.name)), cenv) case Store(to, offset, e) => - (WriteStep(CVar(to.name), offset, translateExpr(e)), cenv) + val frame = item.node.goal.callGoal.isEmpty + (WriteStep(CVar(to.name), offset, translateExpr(e), frame), cenv) case Malloc(to, tpe, sz) => (AllocStep(CVar(to.name), translateSSLType(tpe), sz), cenv) case Free(v) => diff --git a/src/test/resources/synthesis/certification/list/common.def b/src/test/resources/synthesis/certification/list/common.def index 35ce5133b..3f8a4651a 100644 --- a/src/test/resources/synthesis/certification/list/common.def +++ b/src/test/resources/synthesis/certification/list/common.def @@ -1,4 +1,4 @@ -predicate lseg(loc x, set n) { -| x == 0 => { n =i {} ; emp } -| not (x == 0) => { n =i {v} ++ n1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, n1) } +predicate lseg(loc x, set s) { +| x == 0 => { s =i {} ; emp } +| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } } diff --git a/src/test/resources/synthesis/certification/list/listcopy.syn b/src/test/resources/synthesis/certification/list/listcopy.syn index c2cfcc7ec..ecdf2532d 100644 --- a/src/test/resources/synthesis/certification/list/listcopy.syn +++ b/src/test/resources/synthesis/certification/list/listcopy.syn @@ -2,10 +2,10 @@ Copy a linked list ### -{ r :-> x ** lseg(x, N) } +{ r :-> x ** lseg(x, s) } void listcopy(loc r) -{ true ; r :-> y ** lseg(x, N) ** lseg(y, N) } +{ true ; r :-> y ** lseg(x, s) ** lseg(y, s) } ##### \ No newline at end of file From f302198b84200afc5db10eedeeea40b702967601 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 28 Jul 2020 18:09:42 +0800 Subject: [PATCH 020/211] Handle multi-parameter functions by introducing custom simplification tactic for Program --- .../targets/htt/language/Sentences.scala | 40 +++++++++++--- .../targets/htt/language/Statements.scala | 11 ++-- .../targets/htt/logic/ProofSteps.scala | 53 +++++++++++-------- .../htt/translation/ProofTranslation.scala | 5 +- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala index 1b4eab0ce..8ff4f832e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala @@ -45,18 +45,34 @@ sealed abstract class Sentence extends PrettyPrinting { } builder.append(".") case el@CFunSpec(name, rType, params, pureParams, pre, post) => - val paramsStr = params.map { case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ") - val pureParamsDestructuredStr = if (pureParams.length == 1) pureParams.head._2.pp else s"(${pureParams.map(_._2.pp).mkString(", ")})" + val vprogs = "vprogs" + val vghosts = "vghosts" + + def formalsToStr(formals: CFormals): (String, String) = { + val (typs, names) = formals.unzip + val typsStr = typs.map(_.pp).mkString(" * ") + val namesStr = names.map(_.pp).mkString(", ") + (typsStr, namesStr) + } + + val (typsStr, namesStr) = formalsToStr(params) + val (pureTypsStr, pureNamesStr) = formalsToStr(pureParams) + + // first line builder.append(mkSpaces(offset)) builder.append(s"Definition ${name}_type ") builder.append(":=\n") - builder.append(s" forall $paramsStr,\n") - // print pure params + // program var signatures + if (params.nonEmpty) { + builder.append(mkSpaces(offset + 2)) + builder.append(s"forall ($vprogs : $typsStr),\n") + } + + // pure var signatures if (pureParams.nonEmpty) { - val pureParamsTypStr = pureParams.map(_._1.pp).mkString(" * ") builder.append(mkSpaces(offset + 2)) - builder.append(s"{(args: $pureParamsTypStr)},\n") + builder.append(s"{($vghosts : $pureTypsStr)},\n") } // define goal @@ -66,9 +82,13 @@ sealed abstract class Sentence extends PrettyPrinting { // pre-condition builder.append(mkSpaces(offset + 6)) builder.append("fun h =>\n") + if (params.nonEmpty) { + builder.append(mkSpaces(offset + 8)) + builder.append(s"let: ($namesStr) := $vprogs in\n") + } if (pureParams.nonEmpty) { builder.append(mkSpaces(offset + 8)) - builder.append(s"let: $pureParamsDestructuredStr := args in\n") + builder.append(s"let: ($pureNamesStr) := $vghosts in\n") } build(pre, offset + 8, el.programVars) builder.append(",\n") @@ -76,9 +96,13 @@ sealed abstract class Sentence extends PrettyPrinting { // post-condition builder.append(mkSpaces(offset + 6)) builder.append(s"[vfun (_: ${rType.pp}) h =>\n") + if (params.nonEmpty) { + builder.append(mkSpaces(offset + 8)) + builder.append(s"let: ($namesStr) := $vprogs in\n") + } if (pureParams.nonEmpty) { builder.append(mkSpaces(offset + 8)) - builder.append(s"let: $pureParamsDestructuredStr := args in\n") + builder.append(s"let: ($pureNamesStr) := $vghosts in\n") } build(post, offset + 8, el.programVars) builder.append("\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala index dfd1ad0b5..a04cf35fe 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala @@ -73,7 +73,7 @@ object Statements { builder.append(s"${to.ppp} <-- @read ${tpe.pp} $f") case CCall(fun, args) => builder.append(mkSpaces(offset)) - val function_call = s"${fun.ppp} ${args.map(_.ppp).mkString(" ")}" + val function_call = s"${fun.ppp} (${args.map(_.ppp).mkString(", ")})" // TODO: handle return types builder.append(function_call) case CSeqComp(s1, s2) => @@ -149,16 +149,17 @@ object Statements { case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement case class CProcedure(name: String, tp: CoqType, formals: Seq[(CoqType, CVar)], body: CStatement) extends CStatement { - val inductive = body.usedVars.contains(CVar(name)) + val inductive: Boolean = body.usedVars.contains(CVar(name)) override def pp: String = { + val vprogs = "vprogs" val builder = new StringBuilder builder.append(s"Program Definition $name : ${name}_type :=\n") - val fvs = formals.map(f => f._2.ppp) if (inductive) { - builder.append(s" Fix (fun ($name : ${name}_type) ${fvs.mkString(" ")} =>\n") + builder.append(s" Fix (fun ($name : ${name}_type) $vprogs =>\n") } else { - builder.append(s" fun ${fvs.mkString(" ")} =>\n") + builder.append(s" fun $vprogs =>\n") } + builder.append(s" let: (${formals.map(_._2.pp).mkString(", ")}) := $vprogs in\n") builder.append(" Do (\n") builder.append(body.ppp) builder.append(" )") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index ead59ba3a..2fa40e816 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -5,34 +5,41 @@ import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.Proof._ object ProofSteps { - case class Proof(root: ProofStep) { - def pp: String = s"Next Obligation.\n${root.pp}\nQed.\n" + def nestedDestructR(items: Seq[CVar]): String = items.toList match { + case v1 :: v2 :: rest => + s"[${v1.pp} ${nestedDestructR(v2 :: rest)}]" + case v :: _ => + v.pp + case Nil => + "" } - sealed abstract class ProofStep { - def pp: String = "" - protected def nestedDestructR(items: Seq[CVar]): String = items.toList match { - case v1 :: v2 :: rest => - s"[${v1.pp} ${nestedDestructR(v2 :: rest)}]" - case v :: _ => - v.pp - case Nil => - "" + def nestedDestructL(items: Seq[CVar]): String = { + def visit(items: Seq[CVar]): String = { + items.toList match { + case v1 :: v2 :: rest => + s"[${visit(v2 :: rest)} ${v1.pp}]" + case v :: _ => + v.pp + case Nil => + "" + } } + visit(items.reverse) + } - protected def nestedDestructL(items: Seq[CVar]): String = { - def visit(items: Seq[CVar]): String = { - items.toList match { - case v1 :: v2 :: rest => - s"[${visit(v2 :: rest)} ${v1.pp}]" - case v :: _ => - v.pp - case Nil => - "" - } - } - visit(items.reverse) + case class Proof(root: ProofStep, params: Seq[CVar], inductive: Boolean) { + def pp: String = { + val intro = if (inductive) "intro; " else "" + val obligationTactic = s"Obligation Tactic := ${intro}move=>${nestedDestructL(params)}; ssl_program_simpl." + val nextObligation = "Next Obligation." + val body = root.pp + val qed = "Qed.\n" + List(obligationTactic, nextObligation, body, qed).mkString("\n") } + } + sealed abstract class ProofStep { + def pp: String = "" protected def buildValueExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar], nested: Boolean = false): Unit = { val ve = asn.valueVars.diff(outsideVars) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 2b348f7b8..4951ea5a9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,12 +1,12 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.language.{CCardType, CInductiveClause} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ import org.tygus.suslik.certification.targets.htt.translation.Translation._ +import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements._ import org.tygus.suslik.logic.Block import org.tygus.suslik.synthesis._ @@ -17,7 +17,8 @@ object ProofTranslation { def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): Proof = { val traversalItem = TraversalItem(node, cenv) val proofBody = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal))) - Proof(proofBody) + val inductive = proc.body.vars.contains(Var(proc.name)) + Proof(proofBody, goal.programVars, inductive) } def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { From a73a085cc2f19098819356feba4ed6ff4375e6ca Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 30 Jul 2020 01:36:48 +0800 Subject: [PATCH 021/211] Make all tests pass by back-propagating the accumulated variable substitutions --- .../targets/htt/logic/ProofProducers.scala | 65 ++++++++++--------- .../targets/htt/logic/ProofSteps.scala | 45 ++++++++++--- .../htt/translation/ProofTranslation.scala | 13 ++-- .../targets/htt/translation/Translation.scala | 3 +- 4 files changed, 80 insertions(+), 46 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala index b673c9d66..0c1298a64 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala @@ -1,12 +1,12 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language.{CInductiveClause, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.Proof.CGoal +import org.tygus.suslik.certification.targets.htt.logic.Proof.{CEnvironment, CGoal} import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{AbduceBranchStep, OpenPostStep, OpenStep, ProofStep, SeqCompStep} object ProofProducers { - type Kont = Seq[ProofStep] => ProofStep + type KontResult = (ProofStep, CEnvironment) + type Kont = Seq[KontResult] => KontResult trait Branching @@ -14,7 +14,7 @@ object ProofProducers { val arity: Int val fn: Kont - def apply(children: Seq[ProofStep]): ProofStep = { + def apply(children: Seq[KontResult]): KontResult = { assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") fn(children) } @@ -30,24 +30,24 @@ object ProofProducers { def simplify: ProofProducer = this match { case ChainedProofProducer(p1, IdProofProducer) => p1.simplify case ChainedProofProducer(IdProofProducer, p2) => p2.simplify - case ChainedProofProducer(_, p2@ConstProofProducer(_)) => p2.simplify + case ChainedProofProducer(_, p2@ConstProofProducer(_, _)) => p2.simplify case _ => this } } case class ChainedProofProducer(p1: ProofProducer, p2: ProofProducer) extends ProofProducer { val arity: Int = p1.arity + p2.arity - 1 - val fn: Kont = steps => { - val (steps1, steps2) = steps.splitAt(p1.arity) - val step = p1.fn(steps1) - p2.fn(step +: steps2) + val fn: Kont = res => { + val (res1, res2) = res.splitAt(p1.arity) + val r = p1.fn(res1) + p2.fn(r +: res2) } } case class PartiallyAppliedProofProducer(p: ProofProducer, s: ProofStep) extends ProofProducer { val arity: Int = p.arity - 1 - val fn: Kont = steps => { - p.apply(s +: steps) + val fn: Kont = res => { + p.apply((s, res.head._2) +: res) } } @@ -56,48 +56,55 @@ object ProofProducers { val fn: Kont = _.head } - case class ConstProofProducer(step: ProofStep) extends ProofProducer { + case class ConstProofProducer(step: ProofStep, env: CEnvironment) extends ProofProducer { val arity: Int = 0 - val fn: Kont = _ => step + val fn: Kont = _ => (step, env) } case class PrependProofProducer(s: ProofStep) extends ProofProducer { val arity: Int = 1 - val fn: Kont = steps => SeqCompStep(s, steps.head).simplify + val fn: Kont = res => { + val (step, env) = res.head + (SeqCompStep(s.refreshVars(env), step).simplify, env) + } } case class AppendProofProducer(s: ProofStep) extends ProofProducer { val arity: Int = 1 - val fn: Kont = steps => SeqCompStep(steps.head, s).simplify + val fn: Kont = res => { + val (step, env) = res.head + (SeqCompStep(step, s.refreshVars(env)).simplify, env) + } } - case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal]) extends ProofProducer with Branching { + case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal], env: CEnvironment) extends ProofProducer with Branching { val arity: Int = subgoals.length - val fn: Kont = steps => - if (steps.length == 1) steps.head else { - val condBranches = steps.zip(subgoals).reverse.map{ case (s, g) => - SeqCompStep(OpenPostStep(app, g), s) + val fn: Kont = res => + if (res.length == 1) res.head else { + val condBranches = res.zip(subgoals).reverse.map{ case ((s, env), g) => + SeqCompStep(OpenPostStep(app, g.pre, g.programVars).refreshVars(env), s) } val ctail = condBranches.tail val finalBranch = condBranches.head - SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }) + (SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), env) } } - case object GuardedProofProducer extends ProofProducer with Branching { + case class GuardedProofProducer(env: CEnvironment) extends ProofProducer with Branching { val arity: Int = 2 - val fn: Kont = steps => - if (steps.length == 1) steps.head else { - val condBranches = steps.reverse + val fn: Kont = res => + if (res.length == 1) res.head else { + val condBranches = res.reverse.map(_._1) val ctail = condBranches.tail val finalBranch = condBranches.head - SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }) + (SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), env) } } - case class FoldProofProducer[T](op: (T, ProofProducer) => ProofStep, item: T, bp: ProofProducer) extends ProofProducer { + case class FoldProofProducer[T](op: (T, ProofProducer) => KontResult, item: T, bp: ProofProducer) extends ProofProducer { val arity: Int = 1 - val fn: Kont = steps => { + val fn: Kont = res => { + val step = res.head._1 // partially apply a produced step to the BranchProducer of the downstream `bp` @scala.annotation.tailrec def isBase(curr: ProofProducer): Boolean = curr match { @@ -108,7 +115,7 @@ object ProofProducers { def update(curr: ProofProducer): ProofProducer = curr match { case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) - case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => curr.partApply(steps.head) + case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => curr.partApply(step) case _ => curr } op(item, update(bp)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 2fa40e816..f9fb16e3b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -41,6 +41,8 @@ object ProofSteps { sealed abstract class ProofStep { def pp: String = "" + def refreshVars(env: CEnvironment): ProofStep = this + protected def buildValueExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar], nested: Boolean = false): Unit = { val ve = asn.valueVars.diff(outsideVars) @@ -156,13 +158,16 @@ object ProofSteps { def simplifyBinding(newvar: CVar): ProofStep = { val used = s2.vars - if (used.exists(v => newvar.name.startsWith(v.name))) { + if (used.contains(newvar)) { this } else s2 // Do not generate bindings for unused variables } } case class WriteStep(to: CVar, offset: Int, e: CExpr, frame: Boolean = true) extends ProofStep { + override def refreshVars(env: CEnvironment): WriteStep = + WriteStep(env.ghostSubst.getOrElse(to, to), offset, e.subst(env.ghostSubst), frame) + override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" val writeStep = "ssl_write.\n" @@ -172,10 +177,16 @@ object ProofSteps { } case class ReadStep(to: CVar, from: CVar) extends ProofStep { + override def refreshVars(env: CEnvironment): ReadStep = + ReadStep(env.ghostSubst.getOrElse(to, to), env.ghostSubst.getOrElse(from, from)) + override def pp: String = "ssl_read.\n" } case class AllocStep(to: CVar, tpe: CoqType, sz: Int) extends ProofStep { + override def refreshVars(env: CEnvironment): AllocStep = + AllocStep(env.ghostSubst.getOrElse(to, to), tpe, sz) + override def pp: String = s"ssl_alloc ${to.pp}.\n" } @@ -190,21 +201,34 @@ object ProofSteps { override def pp: String = "ssl_open.\n" } - case class OpenPostStep(app: CSApp, goal: CGoal) extends ProofStep { + case class OpenPostStep(app: CSApp, pre: CAssertion, programVars: Seq[CVar]) extends ProofStep { + override def refreshVars(env: CEnvironment): OpenPostStep = OpenPostStep( + app.subst(env.ghostSubst).asInstanceOf[CSApp], + pre.subst(env.ghostSubst), + programVars.map(v => env.ghostSubst.getOrElse(v, v)) + ) + override def pp: String = { val builder = new StringBuilder() builder.append(s"ssl_open_post ${app.hypName}.\n") - buildValueExistentials(builder, goal.pre, app.vars ++ goal.programVars) - buildHeapExistentials(builder, goal.pre, app.vars) + buildValueExistentials(builder, pre, app.vars ++ programVars) + buildHeapExistentials(builder, pre, app.vars) - buildHeapExpansion(builder, goal.pre, app.uniqueName) + buildHeapExpansion(builder, pre, app.uniqueName) builder.toString() } } case class CallStep(pre: CAssertion, post: CAssertion, outsideVars: Seq[CVar], pureEx: Seq[CExpr]) extends ProofStep { + override def refreshVars(env: CEnvironment): CallStep = CallStep ( + pre.subst(env.ghostSubst), + post.subst(env.ghostSubst), + outsideVars.map(v => env.ghostSubst.getOrElse(v, v)), + pureEx.map(_.subst(env.ghostSubst)) + ) + override def pp: String = { val builder = new StringBuilder() @@ -232,16 +256,19 @@ object ProofSteps { } } - case class GhostElimStep(goal: CGoal) extends ProofStep { + case class GhostElimStep(pre: CAssertion, programVars: Seq[CVar]) extends ProofStep { + override def refreshVars(env: CEnvironment): GhostElimStep = + GhostElimStep(pre.subst(env.ghostSubst), programVars.map(v => env.ghostSubst.getOrElse(v, v))) + override def pp: String = { val builder = new StringBuilder() // Pull out any precondition ghosts and move precondition heap to the context builder.append("ssl_ghostelim_pre.\n") - buildValueExistentials(builder, goal.pre, goal.programVars, nested = true) - buildHeapExistentials(builder, goal.pre, goal.programVars) - buildHeapExpansion(builder, goal.pre, "root") + buildValueExistentials(builder, pre, programVars, nested = true) + buildHeapExistentials(builder, pre, programVars) + buildHeapExpansion(builder, pre, "root") // store heap validity assertions builder.append("ssl_ghostelim_post.\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 4951ea5a9..ff51649d1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -16,12 +16,12 @@ object ProofTranslation { def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): Proof = { val traversalItem = TraversalItem(node, cenv) - val proofBody = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal))) + val (proofBody, _) = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal.pre, goal.programVars))) val inductive = proc.body.vars.contains(Var(proc.name)) Proof(proofBody, goal.programVars, inductive) } - def traverseProof(item: TraversalItem, kont: ProofProducer): ProofStep = { + def traverseProof(item: TraversalItem, kont: ProofProducer): (ProofStep, CEnvironment) = { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => (EmpStep(cenv), cenv) @@ -36,6 +36,7 @@ object ProofTranslation { val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) (FreeStep(block.get.sz), cenv) + case _ => ??? } def translateProducer(stmtProducer: StmtProducer, cenv: CEnvironment): (ProofProducer, CEnvironment) = { @@ -101,7 +102,7 @@ object ProofTranslation { (IdProofProducer, cenv1) case ConstProducer(s) => val (step, cenv1) = translateOperation(s, cenv) - (ConstProofProducer(step), cenv1) + (ConstProofProducer(step, cenv1), cenv1) case PrependProducer(s) => if (s.isInstanceOf[Call]) return (IdProofProducer, cenv) val (step, cenv1) = translateOperation(s, cenv) @@ -109,9 +110,9 @@ object ProofTranslation { case BranchProducer(_) => val sapp = translateHeaplet(item.node.footprint.pre.sigma.apps.head).asInstanceOf[CSApp] val subgoals = item.node.children.map(n => translateGoal(n.goal)) - (BranchProofProducer(sapp, subgoals), cenv) + (BranchProofProducer(sapp, subgoals, cenv), cenv) case GuardedProducer(_, _) => - (GuardedProofProducer, cenv) + (GuardedProofProducer(cenv), cenv) case _ => (IdProofProducer, cenv) } @@ -135,7 +136,7 @@ object ProofTranslation { val (p0, cenv1) = translateProducer(item.node.kont, item.cenv) val p = p0.simplify val nextItems = generateNextItems(p, cenv1) - val nextKont = updateProducerPost(nextItems, p, cenv1) + val nextKont = updateProducerPost(nextItems, p, cenv1).simplify nextItems.headOption match { case Some(childHead) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 96ea824f7..d19e5608b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -46,7 +46,6 @@ object Translation { val FunSpec(_, tp, _, _, _, _) = node.goal.toFunSpec val goal = node.goal val pureParams = goal.universalGhosts - .intersect(goal.gamma.keySet) .map(v => translateParam((v, goal.gamma(v)))) .filterNot(_._1 == CCardType) // exclude cardinality vars .toList @@ -92,7 +91,7 @@ object Translation { val post = translateAsn(goal.post) val gamma = goal.gamma.map { case (value, lType) => (CVar(value.name), translateSSLType(lType)) } val programVars = goal.programVars.map(v => CVar(v.name)) - val universalGhosts = goal.universalGhosts.intersect(goal.gamma.keySet).map(v => CVar(v.name)).toSeq.filterNot(_.isCard) + val universalGhosts = goal.universalGhosts.map(v => CVar(v.name)).toSeq.filterNot(_.isCard) CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) } From c2a2f82a3342b3292d1ce4236553c7afb8ce49a8 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 2 Aug 2020 17:11:43 +0800 Subject: [PATCH 022/211] Remove old code, fix bugs, simplify method signatures --- .../tygus/suslik/certification/CertTree.scala | 1 - .../certification/targets/htt/HTT.scala | 6 +- .../targets/htt/language/Expressions.scala | 165 +++---------- .../targets/htt/language/PrettyPrinting.scala | 2 + .../htt/language/ProgramPrettyPrinting.scala | 5 - .../targets/htt/language/Sentences.scala | 228 ++++++++---------- .../targets/htt/language/Statements.scala | 100 +++----- .../targets/htt/language/Types.scala | 18 +- .../targets/htt/language/package.scala | 4 +- .../targets/htt/logic/Proof.scala | 42 ++-- .../targets/htt/logic/ProofProducers.scala | 15 +- .../targets/htt/logic/ProofSteps.scala | 156 +++++------- .../htt/translation/ProgramTranslation.scala | 16 +- .../htt/translation/ProofTranslation.scala | 110 ++++----- .../targets/htt/translation/Translation.scala | 162 ++++--------- .../org/tygus/suslik/logic/Declarations.scala | 6 +- .../tygus/suslik/synthesis/StmtProducer.scala | 19 +- .../suslik/synthesis/rules/LogicalRules.scala | 2 +- .../synthesis/rules/UnfoldingRules.scala | 10 +- .../targets/htt/HTTCertificationTests.scala | 2 +- 20 files changed, 407 insertions(+), 662 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index f2a4e6f80..e4ed3be5b 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -8,7 +8,6 @@ import org.tygus.suslik.synthesis.rules.Rules.{RuleResult, SynthesisRule} import scala.collection.mutable object CertTree { - private var counter: Int = 0 /** * [Certify]: A utility for traversing a successful synthesis result during certification * @param nodeId the NodeId of the associated or-node in the synthesis search tree diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 17394bfdd..25be1f95c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -1,7 +1,5 @@ package org.tygus.suslik.certification.targets.htt -import java.nio.file.Paths - import org.tygus.suslik.certification._ import org.tygus.suslik.certification.targets.htt.translation.Translation import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException @@ -26,10 +24,10 @@ Require Import core. val builder = new StringBuilder builder.append(prelude) val (preds, spec, proof, cproc) = Translation.translate(root, proc)(env) - preds.foreach(pred => builder.append(pred.pp + "\n")) + preds.values.foreach(pred => builder.append(pred.pp + "\n")) builder.append(spec.pp) builder.append("\n") - builder.append(cproc.ppp) + builder.append(cproc.pp) builder.append("\n") builder.append(proof.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 3655a2d97..a03832281 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -5,12 +5,10 @@ import org.tygus.suslik.logic.Specifications.selfCardVar object Expressions { - sealed abstract class CExpr extends ProgramPrettyPrinting { + sealed abstract class CExpr extends PrettyPrinting { def isTrivial: Boolean = this == CBoolConst(true) def isCard: Boolean = this.vars.exists(_.isCard) - def typeOf: Option[CoqType] = None - def collect[R <: CExpr](p: CExpr => Boolean): Seq[R] = { def collector(acc: Seq[R])(exp: CExpr): Seq[R] = exp match { @@ -32,9 +30,10 @@ object Expressions { val acc2 = collector(acc1)(cond) val acc3 = collector(acc2)(l) collector(acc3)(r) - case a@CSApp(_, args, _) => + case a@CSApp(_, args, card) => val acc1 = if (p(a)) acc :+ a.asInstanceOf[R] else acc - args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) + val acc2 = args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) + collector(acc2)(card) case CPointsTo(loc, _, value) => collector(collector(acc)(loc))(value) case CSFormula(_, apps, ptss) => @@ -48,7 +47,11 @@ object Expressions { def subst(sigma: Map[CVar, CExpr]): CExpr = this match { case v: CVar => - sigma.get(v).map(_.subst(sigma)).getOrElse(v) + sigma.get(v) match { + case None => v + case Some(e) if v == e => v + case Some(e) => e.subst(sigma) + } case CBinaryExpr(op, l, r) => CBinaryExpr(op, l.subst(sigma), r.subst(sigma)) case CUnaryExpr(op, arg) => @@ -57,14 +60,6 @@ object Expressions { CSetLiteral(elems.map(_.subst(sigma))) case CIfThenElse(cond, l, r) => CIfThenElse(cond.subst(sigma), l.subst(sigma), r.subst(sigma)) - case CSApp(pred, args, card) => - CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) - case CPointsTo(loc, offset, value) => - CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) - case CSFormula(heapName, apps, ptss) => - val apps1 = apps.map(_.subst(sigma).asInstanceOf[CSApp]) - val ptss1 = ptss.map(_.subst(sigma).asInstanceOf[CPointsTo]) - CSFormula(heapName, apps1, ptss1) case _ => this } @@ -93,89 +88,51 @@ object Expressions { case class CVar(name: String) extends CExpr { override def pp: String = if (name.startsWith(cardinalityPrefix)) name.drop(cardinalityPrefix.length) else name override val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name + def substVar(sub: Map[CVar, CExpr]): CVar = sub.get(this) match { + case Some(v@CVar(_)) => v + case _ => this + } } case class CBoolConst(value: Boolean) extends CExpr { - override def typeOf: Option[CoqType] = Some(CBoolType) override def pp: String = value.toString } case class CNatConst(value: Int) extends CExpr { - override def typeOf: Option[CoqType] = Some(CNatType) override def pp: String = value.toString } case class CSetLiteral(elems: List[CExpr]) extends CExpr { - override def typeOf: Option[CoqType] = Some(CNatSeqType) override def pp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.pp).mkString("; ")}]" - override def ppp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.ppp).mkString("; ")}]" } case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { - override def typeOf: Option[CoqType] = left.typeOf override def pp: String = s"if ${cond.pp} then ${left.pp} else ${right.pp}" - override def ppp: String = s"if ${cond.ppp} then ${left.ppp} else ${right.ppp}" } case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { - override def typeOf: Option[CoqType] = op match { - case COpPlus | COpMinus | COpMinus => Some(CNatType) - case COpBoolEq | COpLeq | COpLt => Some(CBoolType) - case COpAnd | COpOr | COpSetEq => Some(CPropType) - case COpHeapJoin => Some(CHeapType) - case COpUnion | COpDiff => Some(CNatSeqType) - case _ => None - } - override def equals(that: Any): Boolean = that match { - case CUnaryExpr(COpNot, COverloadedBinaryExpr(COpOverloadedEq, left1, right1)) => left == left1 && right == right1 - case CBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 - case COverloadedBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 - case _ => false - } override def pp: String = op match { case COpSubset => s"{subset ${left.pp} ${op.pp} ${right.pp}}" case _ => s"${left.pp} ${op.pp} ${right.pp}" } - override def ppp: String = s"${left.ppp} ${op.ppp} ${right.ppp}" } case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { - override def equals(that: Any): Boolean = that match { - case CUnaryExpr(op1, e1) => op == op1 && e == e1 - case COverloadedBinaryExpr(COpNotEqual, left, right) => e match { - case COverloadedBinaryExpr(COpOverloadedEq, left1, right1) => left == left1 && right == right1 - case _ => false - } - case _ => false - } - override def pp: String = s"${op.pp} ${e.pp}" - override def ppp: String = s"${op.ppp} ${e.ppp}" - } - - case class COverloadedBinaryExpr(op: COverloadedBinOp, left: CExpr, right: CExpr) extends CExpr { - override def equals(that: Any): Boolean = that match { - case CBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 - case COverloadedBinaryExpr(op1, left1, right1) => op == op1 && left == left1 && right == right1 - case _ => false - } - override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" - override def ppp: String = s"${left.ppp} ${op.ppp} ${right.ppp}" + override def pp: String = s"${op.pp} (${e.pp})" } case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" - def locPPP: String = if (offset == 0) loc.ppp else s"${loc.ppp} .+ $offset" override def pp: String = if (value == CNatConst(0)) s"$locPP :-> null" else s"$locPP :-> ${value.pp}" - override def ppp: String = if (value == CNatConst(0)) s"$locPPP :-> null" else s"$locPPP :-> ${value.ppp}" - } - - case object CEmpty extends CExpr { - override def pp: String = "empty" + override def subst(sigma: Map[CVar, CExpr]): CPointsTo = + CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) } case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends CExpr { override def pp: String = s"$pred ${args.map(arg => arg.pp).mkString(" ")}" - override def ppp: String = s"$pred ${args.map(arg => arg.ppp).mkString(" ")}" + + override def subst(sigma: Map[CVar, CExpr]): CSApp = + CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) val uniqueName: String = s"$pred${card.pp}" val heapName: String = s"h_$uniqueName" @@ -183,13 +140,13 @@ object Expressions { } case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { - def unify(source: CSFormula): Map[CVar, CExpr] = { - val initialMap: Map[CVar, CExpr] = Map.empty + def unify(source: CSFormula): Map[CVar, CVar] = { + val initialMap: Map[CVar, CVar] = Map.empty val m1 = source.ptss.zip(ptss).foldLeft(initialMap) { - case (acc, (p1, p2)) => acc ++ p1.vars.zip(p2.vars).toMap + case (acc, (p1, p2)) => acc ++ p1.vars.zip(p2.vars).filterNot(v => v._1 == v._2).toMap } source.apps.zip(apps).foldLeft(m1) { - case (acc, (a1, a2)) => acc ++ a1.vars.zip(a2.vars).toMap + case (acc, (a1, a2)) => acc ++ a1.vars.zip(a2.vars).filterNot(v => v._1 == v._2).toMap } } @@ -205,72 +162,24 @@ object Expressions { s"$heapName = $ppHeap /\\ $appsStr" } - val heapVars: Seq[CVar] = apps.map(a => CVar(a.heapName)) - } - - case class CExists(override val vars: Seq[CVar], e: CExpr) extends CExpr { - override def pp: String = s"exists ${vars.map(v => v.pp).mkString(" ")}, ${e.pp}" - override def ppp: String = s"exists ${vars.map(v => v.ppp).mkString(" ")}, ${e.ppp}" - } - - case class CForAll(override val vars: Seq[CVar], e: CExpr) extends CExpr { - override def pp: String = s"forall ${vars.map(v => v.pp).mkString(" ")}, ${e.pp}" - override def ppp: String = s"forall ${vars.map(v => v.ppp).mkString(" ")}, ${e.ppp}" - } + override def subst(sigma: Map[CVar, CExpr]): CSFormula = { + val apps1 = apps.map(_.subst(sigma)) + val ptss1 = ptss.map(_.subst(sigma)) + CSFormula(heapName, apps1, ptss1) + } - case object Mystery extends CExpr { - override def pp: String = "_" - override def ppp: String = pp + val heapVars: Seq[CVar] = apps.map(a => CVar(a.heapName)) } - sealed abstract class CUnOp extends ProgramPrettyPrinting + sealed abstract class CUnOp extends PrettyPrinting object COpNot extends CUnOp { - override def pp: String = "not" + override def pp: String = "~~" } object COpUnaryMinus extends CUnOp - sealed abstract class COverloadedBinOp extends ProgramPrettyPrinting - - sealed abstract class CBinOp extends COverloadedBinOp - - object COpOverloadedEq extends COverloadedBinOp { - override def equals(that: Any): Boolean = that match { - case that: COpEq.type => true - case that: COpOverloadedEq.type => true - case _ => false - } - override def pp: String = "=" - } - - object COpNotEqual extends COverloadedBinOp { - override def pp: String = "!=" - } - - object COpGt extends COverloadedBinOp { - override def pp: String = ">" - } - - object COpGeq extends COverloadedBinOp { - override def pp: String = ">=" - } - - object COpOverloadedPlus extends COverloadedBinOp { - override def pp: String = "+" - } - - object COpOverloadedMinus extends COverloadedBinOp { - override def pp: String = "-" - } - - object COpOverloadedLeq extends COverloadedBinOp { - override def pp: String = "<=" - } - - object COpOverloadedStar extends COverloadedBinOp { - override def pp: String = "*" - } + sealed abstract class CBinOp extends PrettyPrinting object COpImplication extends CBinOp { override def pp: String = "->" @@ -289,13 +198,7 @@ object Expressions { } object COpEq extends CBinOp { - override def equals(that: Any): Boolean = that match { - case that: COpEq.type => true - case that: COpOverloadedEq.type => true - case _ => false - } override def pp: String = "==" - override def ppp: String = "==" } object COpBoolEq extends CBinOp { @@ -318,10 +221,6 @@ object Expressions { override def pp: String = "\\/" } - object COpHeapJoin extends CBinOp { - override def pp: String = "\\+" - } - object COpUnion extends CBinOp { override def pp: String = "++" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala index b05295470..f86523a78 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala @@ -2,4 +2,6 @@ package org.tygus.suslik.certification.targets.htt.language trait PrettyPrinting { def pp: String = toString + def ppIndent(depth: Int) = s"${getIndent(depth)}${pp.replaceAll("\n", s"\n${getIndent(depth)}")}" + def getIndent(depth: Int): String = " " * depth } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala deleted file mode 100644 index 41fbd6ae6..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/ProgramPrettyPrinting.scala +++ /dev/null @@ -1,5 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.language - -trait ProgramPrettyPrinting extends PrettyPrinting { - def ppp: String = this.pp -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala index 8ff4f832e..b8f73e1fb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala @@ -1,151 +1,51 @@ package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.util.StringUtil.mkSpaces -sealed abstract class Sentence extends PrettyPrinting { - def vars: Seq[CVar] = this match { - case CInductivePredicate(_, params, _) => params.map(_._2) - case _ => Seq.empty - } +sealed abstract class Sentence extends PrettyPrinting - override def pp: String = { +case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { + override def pp: String = if (phi.isTrivial) sigma.pp else s"${phi.pp} /\\ ${sigma.pp}" + + def existentials(quantifiedVars: Seq[CVar]): Seq[CVar] = valueVars.diff(quantifiedVars) + + def ppQuantified(quantifiedVars: Seq[CVar], depth: Int = 0): String = { val builder = new StringBuilder() - def build(el: Sentence, offset: Int = 0, vars: Seq[CVar] = Seq.empty) : Unit = el match { - case el@CAssertion(phi, sigma) => - val ve = el.valueVars.diff(vars).distinct - val he = el.heapVars - val types = el.inferredTypes - // existentials - if (ve.nonEmpty) { - builder.append(mkSpaces(offset)) - builder.append(s"exists ${ve.map(v => types.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") - } - if (he.nonEmpty) { - builder.append(mkSpaces(offset)) - builder.append(s"exists ${he.map(_.pp).mkString(" ")},\n") - } - // body - builder.append(mkSpaces(offset + 2)) - phi match { - case CBoolConst(true) => builder.append(sigma.pp) - case _ => builder.append(s"${phi.pp} /\\ ${sigma.pp}") - } - case CInductiveClause(pred, idx, selector, asn) => - builder.append(mkSpaces(offset)) - builder.append(s"| $pred$idx of ${selector.pp} of\n") - build(asn, offset + 2, vars) - case CInductivePredicate(name, params, clauses) => - builder.append(mkSpaces(offset)) - builder.append(s"Inductive $name ${params.map{ case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") - for (c <- clauses) { - build(c, offset, vars) - builder.append("\n") - } - builder.append(".") - case el@CFunSpec(name, rType, params, pureParams, pre, post) => - val vprogs = "vprogs" - val vghosts = "vghosts" - - def formalsToStr(formals: CFormals): (String, String) = { - val (typs, names) = formals.unzip - val typsStr = typs.map(_.pp).mkString(" * ") - val namesStr = names.map(_.pp).mkString(", ") - (typsStr, namesStr) - } - - val (typsStr, namesStr) = formalsToStr(params) - val (pureTypsStr, pureNamesStr) = formalsToStr(pureParams) - - // first line - builder.append(mkSpaces(offset)) - builder.append(s"Definition ${name}_type ") - builder.append(":=\n") - - // program var signatures - if (params.nonEmpty) { - builder.append(mkSpaces(offset + 2)) - builder.append(s"forall ($vprogs : $typsStr),\n") - } - - // pure var signatures - if (pureParams.nonEmpty) { - builder.append(mkSpaces(offset + 2)) - builder.append(s"{($vghosts : $pureTypsStr)},\n") - } - - // define goal - builder.append(mkSpaces(offset + 4)) - builder.append("STsep (\n") - - // pre-condition - builder.append(mkSpaces(offset + 6)) - builder.append("fun h =>\n") - if (params.nonEmpty) { - builder.append(mkSpaces(offset + 8)) - builder.append(s"let: ($namesStr) := $vprogs in\n") - } - if (pureParams.nonEmpty) { - builder.append(mkSpaces(offset + 8)) - builder.append(s"let: ($pureNamesStr) := $vghosts in\n") - } - build(pre, offset + 8, el.programVars) - builder.append(",\n") - - // post-condition - builder.append(mkSpaces(offset + 6)) - builder.append(s"[vfun (_: ${rType.pp}) h =>\n") - if (params.nonEmpty) { - builder.append(mkSpaces(offset + 8)) - builder.append(s"let: ($namesStr) := $vprogs in\n") - } - if (pureParams.nonEmpty) { - builder.append(mkSpaces(offset + 8)) - builder.append(s"let: ($pureNamesStr) := $vghosts in\n") - } - build(post, offset + 8, el.programVars) - builder.append("\n") - - builder.append(mkSpaces(offset + 6)) - builder.append("]).") + val valueEx = existentials(quantifiedVars) + val heapEx = heapVars + if (valueEx.nonEmpty) { + builder.append(s"exists ${valueEx.map(v => inferredTypes.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") + } + if (heapEx.nonEmpty) { + builder.append(s"exists ${heapEx.map(_.pp).mkString(" ")},\n") } - build(this, 0, vars) - builder.toString() - } -} + builder.append(pp) -case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { - def unify(source: CAssertion): Map[CVar, CExpr] = { - sigma.unify(source.sigma) + getIndent(depth) + builder.toString().replaceAll("\n", s"\n${getIndent(depth)}") } def subst(sub: Map[CVar, CExpr]): CAssertion = - CAssertion(phi.subst(sub), sigma.subst(sub).asInstanceOf[CSFormula]) - - val pureVars: Seq[CVar] = - phi.vars.filterNot(_.isCard).distinct - - val spatialVars: Seq[CVar] = - sigma.vars.filterNot(_.isCard).distinct + CAssertion(phi.subst(sub), sigma.subst(sub)) val valueVars: Seq[CVar] = - (spatialVars ++ pureVars).distinct + (phi.vars ++ sigma.vars).distinct.filterNot(_.isCard) val heapVars: Seq[CVar] = sigma.heapVars - val inferredTypes: Map[CVar, CoqType] = { - def collectPhi(el: CExpr, m: Map[CVar, CoqType]): Map[CVar, CoqType] = el match { + val inferredTypes: Map[CVar, HTTType] = { + @scala.annotation.tailrec + def collectPhi(el: CExpr, m: Map[CVar, HTTType]): Map[CVar, HTTType] = el match { case CBinaryExpr(COpSetEq, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) case CBinaryExpr(COpSetEq, left: CVar, right) => collectPhi(right, m ++ Map(left -> CNatSeqType)) case CBinaryExpr(COpSetEq, left, right: CVar) => collectPhi(left, m ++ Map(right -> CNatSeqType)) case _ => m } - def collectSigma: Map[CVar, CoqType] = { + def collectSigma: Map[CVar, HTTType] = { val ptss = sigma.ptss - ptss.foldLeft[Map[CVar, CoqType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} + ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} } val mPhi = collectPhi(phi, Map.empty) val mSigma = collectSigma @@ -153,10 +53,84 @@ case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { } } -case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion) extends Sentence +case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends Sentence { + def subst(sub: Map[CVar, CExpr]): CInductiveClause = + CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) +} + +case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends Sentence { + val paramVars: Seq[CVar] = params.map(_._2) + + def subst(sub: Map[CVar, CExpr]): CInductivePredicate = + CInductivePredicate(name, params.map(p => (p._1, p._2.substVar(sub))), clauses.map(_.subst(sub))) + + override def pp: String = { + val builder = new StringBuilder() + builder.append(s"Inductive $name ${params.map{ case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") + + // clauses + val clausesStr = for { + CInductiveClause(pred, idx, selector, asn, _) <- clauses + } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1)}" + + builder.append(s"${clausesStr.mkString("\n")}.\n") + builder.toString() + } +} + +case class CFunSpec(name: String, rType: HTTType, params: CFormals, ghostParams: CFormals, pre: CAssertion, post: CAssertion) extends Sentence { + val paramVars: Seq[CVar] = params.map(_._2) + val ghostParamVars: Seq[CVar] = ghostParams.map(_._2) + + val progVarsAlias = "vprogs" + val ghostVarsAlias = "vghosts" + + private def ppFormals(formals: CFormals): (String, String) = { + val (typs, names) = formals.unzip + val typsStr = typs.map(_.pp).mkString(" * ") + val namesStr = names.map(_.pp).mkString(", ") + (typsStr, namesStr) + } + + override def pp: String = { + val builder = new StringBuilder() + + val (typsStr, progVarsStr) = ppFormals(params) + val (ghostTypsStr, ghostVarsStr) = ppFormals(ghostParams) + val destructuredAliases = { + val destructuredParams = if (params.nonEmpty) s"${getIndent(3)}let: ($progVarsStr) := $progVarsAlias in\n" else "" + val destructuredGhostParams = if (ghostParams.nonEmpty) s"${getIndent(3)}let: ($ghostVarsStr) := $ghostVarsAlias in\n" else "" + destructuredParams + destructuredGhostParams + } -case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends Sentence + builder.append(s"Definition ${name}_type :=\n") -case class CFunSpec(name: String, rType: CoqType, params: CFormals, pureParams: CFormals, pre: CAssertion, post: CAssertion) extends Sentence { - def programVars: Seq[CVar] = params.map(_._2) ++ pureParams.map(_._2) + // program var signatures + if (params.nonEmpty) { + builder.append(s"${getIndent(1)}forall ($progVarsAlias : $typsStr),\n") + } + + // ghost var signatures + if (ghostParams.nonEmpty) { + builder.append(s"${getIndent(1)}{($ghostVarsAlias : $ghostTypsStr)},\n") + } + + builder.append(s"${getIndent(1)}STsep (\n") + + // pre-condition + builder.append(s"${getIndent(2)}fun h =>\n") + builder.append(destructuredAliases) + builder.append(pre.ppQuantified(paramVars ++ ghostParamVars, 3)) + builder.append(",\n") + + // post-condition + builder.append(s"${getIndent(2)}[vfun (_: ${rType.pp}) h =>\n") + builder.append(destructuredAliases) + builder.append(post.ppQuantified(paramVars ++ ghostParamVars, 3)) + builder.append("\n") + + builder.append(s"${getIndent(2)}]).") + + builder.toString() + } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala index a04cf35fe..842fdda36 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala @@ -5,13 +5,12 @@ import org.tygus.suslik.util.StringUtil._ object Statements { import Expressions._ - sealed abstract class CStatement extends ProgramPrettyPrinting { + sealed abstract class CStatement extends PrettyPrinting { // Expression-collector def collectE[R <: CExpr](p: CExpr => Boolean): Seq[R] = { def collector(acc: Seq[R])(st: CStatement): Seq[R] = st match { case CSkip => acc - case CError => acc case CStore(to, off, e) => acc ++ to.collect(p) ++ e.collect(p) case CLoad(_, _, from, off) => @@ -31,6 +30,8 @@ object Statements { case CGuarded(cond, b, eb) => val acc1 = collector(acc ++ cond.collect(p))(b) collector(acc1)(eb) + case _ => + acc } collector(Seq.empty)(this) @@ -43,66 +44,53 @@ object Statements { override def pp: String = { val builder = new StringBuilder - def build(s: CStatement, offset: Int = 2) : Unit = { + def build(s: CStatement, depth: Int = 0) : Unit = { + val indent = getIndent(depth) s match { case CSkip => - builder.append(mkSpaces(offset)) - builder.append("ret tt") - case CError => ??? + builder.append(s"${indent}ret tt") case CMalloc(to, _, sz) => - builder.append(mkSpaces(offset)) - builder.append(s"${to.ppp} <-- allocb null $sz") + builder.append(s"$indent${to.pp} <-- allocb null $sz") case CFree(v, sz) => val deallocs = for (off <- 0 until sz) yield { if (off > 0) { - s"dealloc (${v.ppp} .+ $off)" + s"dealloc (${v.pp} .+ $off)" } else { - s"dealloc ${v.ppp}" + s"dealloc ${v.pp}" } } - builder.append(mkSpaces(offset)) - builder.append(deallocs.mkString(s";;\n${mkSpaces(offset)}")) + builder.append(s"$indent${deallocs.mkString(s";;\n${mkSpaces(depth)}")}") case CStore(to, off, e) => - builder.append(mkSpaces(offset)) - val t = if (off <= 0) to.ppp else s"(${to.ppp} .+ $off)" - val v = if (e == CNatConst(0)) "null" else e.ppp - builder.append(s"$t ::= $v") + val t = if (off <= 0) to.pp else s"(${to.pp} .+ $off)" + val v = if (e == CNatConst(0)) "null" else e.pp + builder.append(s"$indent$t ::= $v") case CLoad(to, tpe, from, off) => - builder.append(mkSpaces(offset)) - val f = if (off <= 0) from.ppp else s"(${from.ppp} .+ $off)" - builder.append(s"${to.ppp} <-- @read ${tpe.pp} $f") + val f = if (off <= 0) from.pp else s"(${from.pp} .+ $off)" + builder.append(s"$indent${to.pp} <-- @read ${tpe.pp} $f") case CCall(fun, args) => - builder.append(mkSpaces(offset)) - val function_call = s"${fun.ppp} (${args.map(_.ppp).mkString(", ")})" + val function_call = s"$indent${fun.pp} (${args.map(_.pp).mkString(", ")})" // TODO: handle return types builder.append(function_call) case CSeqComp(s1, s2) => - build(s1, offset) + build(s1, depth) s1 match { case _: ReturnsValue => builder.append(";\n") case _ => builder.append(";;\n") } - build(s2, offset) + build(s2, depth) case CIf(cond, tb, eb) => - builder.append(mkSpaces(offset)) - builder.append(s"if ${cond.ppp}\n") - builder.append(mkSpaces(offset)) - builder.append(s"then\n") - build(tb, offset + 2) - builder.append("\n") - builder.append(mkSpaces(offset)).append(s"else\n") - build(eb, offset + 2) - builder.append("\n") + builder.append(s"${indent}if ${cond.pp}\n") + builder.append(s"${indent}then\n") + build(tb, depth + 1) + builder.append(s"\n${indent}else\n") + build(eb, depth + 1) case CGuarded(cond, body, els) => - builder.append(mkSpaces(offset)) - builder.append(s"if ${cond.ppp}\n") - builder.append(mkSpaces(offset)) - builder.append(s"then\n") - build(body, offset + 2) - builder.append("\n") - builder.append(mkSpaces(offset)).append(s"else\n") - build(els, offset + 2) - builder.append("\n") + builder.append(s"${indent}if ${cond.pp}\n") + builder.append(s"${indent}then\n") + build(body, depth + 1) + builder.append(s"\n${indent}else\n") + build(els, depth + 1) + case _ => } } @@ -115,13 +103,11 @@ object Statements { case object CSkip extends CStatement - case object CError extends CStatement - - case class CMalloc(to: CVar, tpe: CoqType, sz: Int = 1) extends CStatement with ReturnsValue + case class CMalloc(to: CVar, tpe: HTTType, sz: Int = 1) extends CStatement with ReturnsValue case class CFree(v: CVar, sz: Int = 1) extends CStatement - case class CLoad(to: CVar, tpe: CoqType, from: CVar, offset: Int = 0) extends CStatement with ReturnsValue + case class CLoad(to: CVar, tpe: HTTType, from: CVar, offset: Int = 0) extends CStatement with ReturnsValue case class CStore(to: CVar, offset: Int, e: CExpr) extends CStatement @@ -130,8 +116,6 @@ object Statements { case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement { def simplify: CStatement = (tb, eb) match { case (CSkip, CSkip) => CSkip - case (CError, _) => eb - case (_, CError) => tb case _ => this } } @@ -148,25 +132,17 @@ object Statements { case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement - case class CProcedure(name: String, tp: CoqType, formals: Seq[(CoqType, CVar)], body: CStatement) extends CStatement { - val inductive: Boolean = body.usedVars.contains(CVar(name)) + case class CProcedure(name: String, tp: HTTType, formals: Seq[(HTTType, CVar)], body: CStatement) extends CStatement { override def pp: String = { val vprogs = "vprogs" val builder = new StringBuilder builder.append(s"Program Definition $name : ${name}_type :=\n") - if (inductive) { - builder.append(s" Fix (fun ($name : ${name}_type) $vprogs =>\n") - } else { - builder.append(s" fun $vprogs =>\n") - } - builder.append(s" let: (${formals.map(_._2.pp).mkString(", ")}) := $vprogs in\n") - builder.append(" Do (\n") - builder.append(body.ppp) - builder.append(" )") - if (inductive) { - builder.append(")") - } - builder.append(".") + builder.append(s"${getIndent(1)}Fix (fun ($name : ${name}_type) $vprogs =>\n") + + builder.append(s"${getIndent(2)}let: (${formals.map(_._2.pp).mkString(", ")}) := $vprogs in\n") + builder.append(s"${getIndent(2)}Do (\n") + builder.append(body.ppIndent(3)) + builder.append(s"\n${getIndent(2)})).") builder.toString() } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala index 62455cbe7..44cb24c8e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala @@ -1,35 +1,35 @@ package org.tygus.suslik.certification.targets.htt.language -sealed abstract class CoqType extends PrettyPrinting +sealed abstract class HTTType extends PrettyPrinting -case object CBoolType extends CoqType { +case object CBoolType extends HTTType { override def pp: String = "bool" } -case object CNatType extends CoqType { +case object CNatType extends HTTType { override def pp: String = "nat" } -case object CPtrType extends CoqType { +case object CPtrType extends HTTType { override def pp: String = "ptr" } -case object CUnitType extends CoqType { +case object CUnitType extends HTTType { override def pp: String = "unit" } -case object CPropType extends CoqType { +case object CPropType extends HTTType { override def pp: String = "Prop" } -case object CHeapType extends CoqType { +case object CHeapType extends HTTType { override def pp: String = "heap" } -case object CNatSeqType extends CoqType { +case object CNatSeqType extends HTTType { override def pp: String = "seq nat" } -case object CCardType extends CoqType { +case object CCardType extends HTTType { override def pp: String = "" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala index 333422f9c..7cd5c8a1d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala @@ -3,6 +3,6 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar package object language { - type CGamma = Map[CVar, CoqType] - type CFormals = Seq[(CoqType, CVar)] + type CGamma = Map[CVar, HTTType] + type CFormals = Seq[(HTTType, CVar)] } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index fbdc12a46..6a21c2db6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -4,31 +4,43 @@ import org.tygus.suslik.certification.targets.htt.language._ import org.tygus.suslik.certification.targets.htt.language.Expressions._ object Proof { + private var currCallId = 0 + def freshCallId: String = { currCallId += 1; s"call$currCallId" } + case class CGoal(pre: CAssertion, post: CAssertion, - gamma: Map[CVar, CoqType], + gamma: Map[CVar, HTTType], programVars: Seq[CVar], universalGhosts: Seq[CVar], - fname: String) + fname: String) { + val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) + def subst(sub: Map[CVar, CExpr]): CGoal = CGoal( + pre.subst(sub), + post.subst(sub), + gamma.map { case (v, t) => v.substVar(sub) -> t }, + programVars.map(_.substVar(sub)), + universalGhosts.map(_.substVar(sub)), + fname + ) - case class AppExpansion(constructor: Int, heap: CSFormula, ex: Seq[CExpr]) + def toFunspec: CFunSpec = { + val params = programVars.map(v => (gamma(v), v)) + val ghosts = universalGhosts.map(v => (gamma(v), v)) + CFunSpec(fname, CUnitType, params, ghosts, pre, post) + } + } - case class CEnvironment(spec: CFunSpec, - predicates: Seq[CInductivePredicate], + case class CEnvironment(initialGoal: CGoal, + predicates: Map[String, CInductivePredicate], ghostSubst: Map[CVar, CVar] = Map.empty, subst: Map[CVar, CExpr] = Map.empty, - call: Option[(CGoal, Map[CVar, CVar], Map[CVar, CExpr])] = None, - heapSubst: Map[CSApp, AppExpansion] = Map.empty, - framedHeaplets: Seq[CExpr] = Seq.empty - ) { - def copy(spec: CFunSpec = this.spec, - predicates: Seq[CInductivePredicate] = this.predicates, + unfoldings: Map[CSApp, CInductiveClause] = Map.empty) { + def copy(initialGoal: CGoal = this.initialGoal, + predicates: Map[String, CInductivePredicate] = this.predicates, ghostSubst: Map[CVar, CVar] = this.ghostSubst, subst: Map[CVar, CExpr] = this.subst, - call: Option[(CGoal, Map[CVar, CVar], Map[CVar, CExpr])] = this.call, - heapSubst: Map[CSApp, AppExpansion] = this.heapSubst, - framedHeaplets: Seq[CExpr] = this.framedHeaplets + unfoldings: Map[CSApp, CInductiveClause] = this.unfoldings, ): CEnvironment = - CEnvironment(spec, predicates, ghostSubst, subst, call, heapSubst) + CEnvironment(initialGoal, predicates, ghostSubst, subst, unfoldings) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala index 0c1298a64..b94d4ccc4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala @@ -1,8 +1,7 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.Proof.{CEnvironment, CGoal} -import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{AbduceBranchStep, OpenPostStep, OpenStep, ProofStep, SeqCompStep} +import org.tygus.suslik.certification.targets.htt.logic.Proof.CEnvironment +import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ object ProofProducers { type KontResult = (ProofStep, CEnvironment) @@ -77,16 +76,16 @@ object ProofProducers { } } - case class BranchProofProducer(app: CSApp, subgoals: Seq[CGoal], env: CEnvironment) extends ProofProducer with Branching { - val arity: Int = subgoals.length + case class BranchProofProducer(steps: Seq[ProofStep], branchEnv: CEnvironment) extends ProofProducer with Branching { + val arity: Int = steps.length val fn: Kont = res => if (res.length == 1) res.head else { - val condBranches = res.zip(subgoals).reverse.map{ case ((s, env), g) => - SeqCompStep(OpenPostStep(app, g.pre, g.programVars).refreshVars(env), s) + val condBranches = res.zip(steps).reverse.map { case ((s, env), openPost) => + SeqCompStep(openPost.refreshVars(env), s) } val ctail = condBranches.tail val finalBranch = condBranches.head - (SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), env) + (SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), branchEnv) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index f9fb16e3b..267822593 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -5,33 +5,30 @@ import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.Proof._ object ProofSteps { - def nestedDestructR(items: Seq[CVar]): String = items.toList match { - case v1 :: v2 :: rest => - s"[${v1.pp} ${nestedDestructR(v2 :: rest)}]" - case v :: _ => - v.pp - case Nil => + def nestedDestructR(items: Seq[CExpr]): String = items match { + case Seq(e1, e2, rest @ _*) => + s"[${e1.pp} ${nestedDestructR(e2 +: rest)}]" + case Seq(e, _*) => + e.pp + case Seq() => "" } - def nestedDestructL(items: Seq[CVar]): String = { - def visit(items: Seq[CVar]): String = { - items.toList match { - case v1 :: v2 :: rest => - s"[${visit(v2 :: rest)} ${v1.pp}]" - case v :: _ => - v.pp - case Nil => + def nestedDestructL(items: Seq[CExpr]): String = { + def visit(items: Seq[CExpr]): String = items match { + case Seq(e1, e2, rest @ _*) => + s"[${visit(e2 +: rest)} ${e1.pp}]" + case Seq(e, _*) => + e.pp + case Seq() => "" } - } visit(items.reverse) } - case class Proof(root: ProofStep, params: Seq[CVar], inductive: Boolean) { + case class Proof(root: ProofStep, params: Seq[CVar]) { def pp: String = { - val intro = if (inductive) "intro; " else "" - val obligationTactic = s"Obligation Tactic := ${intro}move=>${nestedDestructL(params)}; ssl_program_simpl." + val obligationTactic = s"Obligation Tactic := intro; move=>${nestedDestructL(params)}; ssl_program_simpl." val nextObligation = "Next Obligation." val body = root.pp val qed = "Qed.\n" @@ -43,28 +40,16 @@ object ProofSteps { def refreshVars(env: CEnvironment): ProofStep = this - protected def buildValueExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar], nested: Boolean = false): Unit = { - val ve = asn.valueVars.diff(outsideVars) - - // move value existentials to context - if (ve.nonEmpty) { + protected def buildExistentials(builder: StringBuilder, ex: Seq[CExpr], nested: Boolean = false): Unit = { + if (ex.nonEmpty) { if (nested) { - builder.append(s"move=>${nestedDestructL(ve)}.\n") + builder.append(s"move=>${nestedDestructL(ex)}.\n") } else { - builder.append(s"move=>${ve.map(v => s"[${v.pp}]").mkString(" ")}.\n") + builder.append(s"move=>${ex.map(v => s"[${v.pp}]").mkString(" ")}.\n") } } } - protected def buildHeapExistentials(builder: StringBuilder, asn: CAssertion, outsideVars: Seq[CVar]): Unit = { - val he = asn.heapVars.diff(outsideVars) - - // move heap existentials to context - if (he.nonEmpty) { - builder.append(s"move=>${he.map(v => s"[${v.pp}]").mkString(" ")}.\n") - } - } - protected def buildHeapExpansion(builder: StringBuilder, asn: CAssertion, uniqueName: String): Unit = { val phi = asn.phi val sigma = asn.sigma @@ -88,23 +73,24 @@ object ProofSteps { } } - protected def existentialInstantiation(builder: StringBuilder, asn: CAssertion, ve: Seq[CExpr], heapSubst: Map[CSApp, AppExpansion]): Unit = { - if (ve.nonEmpty) { - builder.append(s"exists ${ve.map(v => s"(${v.pp})").mkString(", ")};\n") + protected def existentialInstantiation(builder: StringBuilder, asn: CAssertion, vars: Seq[CExpr], unfoldings: Map[CSApp, CInductiveClause]): Unit = { + if (vars.nonEmpty) { + builder.append(s"exists ${vars.map(v => s"(${v.pp})").mkString(", ")};\n") } - def expandSAppExistentials(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = heapSubst.get(app) match { - case Some(e) => - val (ptss, apps) = e.heap.apps.map(expandSAppExistentials).unzip - (e.heap.ptss ++ ptss.flatten, apps.flatten) + def unfoldSAppExistentials(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { + case Some(a) => + val sigma = a.asn.sigma + val (ptss, apps) = sigma.apps.map(unfoldSAppExistentials).unzip + (sigma.ptss ++ ptss.flatten, apps.flatten) case None => (Seq.empty, Seq(app)) } val apps = asn.sigma.apps for (app <- apps) { - val (expandedPtss, expandedApps) = expandSAppExistentials(app) - val h = CSFormula("", expandedApps, expandedPtss) + val (unfoldedPtss, unfoldedApps) = unfoldSAppExistentials(app) + val h = CSFormula("", unfoldedApps, unfoldedPtss) builder.append(s"exists (${h.ppHeap});\n") } @@ -113,10 +99,10 @@ object ProofSteps { for { app <- apps - ae@AppExpansion(constructor, heap, _) <- heapSubst.get(app) + c <- unfoldings.get(app) } { - builder.append(s"unfold_constructor ${constructor + 1};\n") - existentialInstantiation(builder, CAssertion(CBoolConst(true), heap), ae.ex, heapSubst) + builder.append(s"unfold_constructor ${c.idx + 1};\n") + existentialInstantiation(builder, c.asn, c.existentials, unfoldings) } } @@ -131,11 +117,8 @@ object ProofSteps { case SeqCompStep(s1,s2) => val acc1 = collector(acc)(s1) collector(acc1)(s2) - case CallStep(pre, post, vars, pureEx) => - val c1 = pre.sigma.vars - val c2 = post.sigma.vars - val c3 = pureEx.flatMap(e => e.vars).toSet - acc ++ c1 ++ c2 ++ c3 + case CallStep(goal, freshToActual, _) => + acc ++ goal.existentials ++ freshToActual.values.flatMap(_.vars) case _ => acc } @@ -183,7 +166,7 @@ object ProofSteps { override def pp: String = "ssl_read.\n" } - case class AllocStep(to: CVar, tpe: CoqType, sz: Int) extends ProofStep { + case class AllocStep(to: CVar, tpe: HTTType, sz: Int) extends ProofStep { override def refreshVars(env: CEnvironment): AllocStep = AllocStep(env.ghostSubst.getOrElse(to, to), tpe, sz) @@ -201,19 +184,19 @@ object ProofSteps { override def pp: String = "ssl_open.\n" } - case class OpenPostStep(app: CSApp, pre: CAssertion, programVars: Seq[CVar]) extends ProofStep { + case class OpenPostStep(app: CSApp, pre: CAssertion, existentials: Seq[CExpr]) extends ProofStep { override def refreshVars(env: CEnvironment): OpenPostStep = OpenPostStep( - app.subst(env.ghostSubst).asInstanceOf[CSApp], + app.subst(env.ghostSubst), pre.subst(env.ghostSubst), - programVars.map(v => env.ghostSubst.getOrElse(v, v)) + existentials.map(v => v.subst(env.ghostSubst)) ) override def pp: String = { val builder = new StringBuilder() builder.append(s"ssl_open_post ${app.hypName}.\n") - buildValueExistentials(builder, pre, app.vars ++ programVars) - buildHeapExistentials(builder, pre, app.vars) + buildExistentials(builder, existentials) + buildExistentials(builder, pre.heapVars) buildHeapExpansion(builder, pre, app.uniqueName) @@ -221,33 +204,35 @@ object ProofSteps { } } - case class CallStep(pre: CAssertion, post: CAssertion, outsideVars: Seq[CVar], pureEx: Seq[CExpr]) extends ProofStep { + case class CallStep(goal: CGoal, freshToActual: Map[CVar, CExpr], callId: String) extends ProofStep { override def refreshVars(env: CEnvironment): CallStep = CallStep ( - pre.subst(env.ghostSubst), - post.subst(env.ghostSubst), - outsideVars.map(v => env.ghostSubst.getOrElse(v, v)), - pureEx.map(_.subst(env.ghostSubst)) + goal.subst(env.ghostSubst), + freshToActual.mapValues(e => e.subst(env.ghostSubst).subst(env.subst)), + callId ) override def pp: String = { val builder = new StringBuilder() - val callHeap = pre.sigma + val refreshedPre = goal.pre.subst(freshToActual) + val refreshedPost = goal.post.subst(freshToActual) + val refreshedGhosts = goal.universalGhosts.map(_.subst(freshToActual)) + val callHeap = refreshedPre.sigma // put the part of the heap touched by the recursive call at the head builder.append(s"ssl_call_pre (${callHeap.ppHeap}).\n") // provide universal ghosts and execute call - builder.append(s"ssl_call (${pureEx.map(_.pp).mkString(", ")}).\n") + builder.append(s"ssl_call (${refreshedGhosts.map(_.pp).mkString(", ")}).\n") - existentialInstantiation(builder, pre, pre.valueVars.diff(outsideVars), Map.empty) + // pre has no value existentials + existentialInstantiation(builder, refreshedPre, Seq.empty, Map.empty) - val callId = s"call${scala.math.abs(pre.hashCode())}" builder.append(s"move=>h_$callId.\n") - buildValueExistentials(builder, post, outsideVars) - buildHeapExistentials(builder, post, outsideVars) - buildHeapExpansion(builder, post, callId) + buildExistentials(builder, goal.existentials) + buildExistentials(builder, refreshedPost.heapVars) + buildHeapExpansion(builder, refreshedPost, callId) // store validity hypotheses in context builder.append("store_valid.\n") @@ -256,9 +241,9 @@ object ProofSteps { } } - case class GhostElimStep(pre: CAssertion, programVars: Seq[CVar]) extends ProofStep { + case class GhostElimStep(goal: CGoal) extends ProofStep { override def refreshVars(env: CEnvironment): GhostElimStep = - GhostElimStep(pre.subst(env.ghostSubst), programVars.map(v => env.ghostSubst.getOrElse(v, v))) + GhostElimStep(goal.subst(env.ghostSubst)) override def pp: String = { val builder = new StringBuilder() @@ -266,9 +251,9 @@ object ProofSteps { // Pull out any precondition ghosts and move precondition heap to the context builder.append("ssl_ghostelim_pre.\n") - buildValueExistentials(builder, pre, programVars, nested = true) - buildHeapExistentials(builder, pre, programVars) - buildHeapExpansion(builder, pre, "root") + buildExistentials(builder, goal.universalGhosts, nested = true) + buildExistentials(builder, goal.pre.heapVars) + buildHeapExpansion(builder, goal.pre, "self") // store heap validity assertions builder.append("ssl_ghostelim_post.\n") @@ -278,29 +263,18 @@ object ProofSteps { } case object AbduceBranchStep extends ProofStep { - override def pp: String = "case: ifP=>H_cond.\n" + override def pp: String = "ssl_abduce_branch.\n" } - case class EmpStep(cenv: CEnvironment) extends ProofStep { + case class EmpStep(goal: CGoal, sub: Map[CVar, CExpr], unfoldings: Map[CSApp, CInductiveClause]) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append("ssl_emp;\n") - val ghostSubst = cenv.ghostSubst - val subst = cenv.subst.mapValues(_.subst(ghostSubst)) - val post = cenv.spec.post.subst(ghostSubst) - val programVars = cenv.spec.programVars.map(v => ghostSubst.getOrElse(v, v)) - val ve = post.valueVars.diff(programVars).distinct.map(_.subst(subst)) - val heapSubst = cenv.heapSubst.map { case (app, e) => - val app1 = app.subst(ghostSubst).subst(subst).asInstanceOf[CSApp] - val e1 = AppExpansion( - e.constructor, - e.heap.subst(ghostSubst).subst(subst).asInstanceOf[CSFormula], - e.ex.map(_.subst(ghostSubst).subst(subst)) - ) - app1 -> e1 - } - existentialInstantiation(builder, post.subst(subst), ve, heapSubst) + val post = goal.post.subst(sub) + val postEx = goal.existentials.map(_.subst(sub)) + val unfoldings1 = unfoldings.map { case (app, e) => app.subst(sub) -> e.subst(sub) } + existentialInstantiation(builder, post, postEx, unfoldings1) builder.toString() } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 7afb6c2c6..c225f41f7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -1,10 +1,8 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.StatementProducers._ import org.tygus.suslik.certification.targets.htt.language.Statements._ -import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ @@ -12,7 +10,7 @@ import org.tygus.suslik.synthesis._ object ProgramTranslation { private case class TraversalItem(node: CertTree.Node) extends Traversable - def translate(node: CertTree.Node, proc: Procedure, goal: CGoal): CStatement = { + def translate(node: CertTree.Node, proc: Procedure): CStatement = { val traversalItem = TraversalItem(node) traverseStmt(traversalItem, IdCStmtProducer) } @@ -22,17 +20,19 @@ object ProgramTranslation { case Skip => CSkip case Load(to, tpe, from, offset) => - CLoad(CVar(to.name), translateSSLType(tpe), CVar(from.name), offset) + CLoad(translateVar(to), translateType(tpe), translateVar(from), offset) case Store(to, offset, e) => - CStore(CVar(to.name), offset, translateExpr(e)) + CStore(translateVar(to), offset, translateExpr(e)) case Malloc(to, tpe, sz) => - CMalloc(CVar(to.name), translateSSLType(tpe), sz) + CMalloc(translateVar(to), translateType(tpe), sz) case Free(v) => val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) - CFree(CVar(v.name), block.get.sz) + CFree(translateVar(v), block.get.sz) case Call(v, args, _) => - CCall(CVar(v.name), args.map(translateExpr)) + CCall(translateVar(v), args.map(translateExpr)) + case _ => + throw TranslationException("Operation not supported") } def translateProducer(stmtProducer: StmtProducer): CStmtProducer = stmtProducer match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index ff51649d1..000b16226 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -6,37 +6,47 @@ import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ import org.tygus.suslik.certification.targets.htt.translation.Translation._ -import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.Block import org.tygus.suslik.synthesis._ object ProofTranslation { private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) extends Traversable - def translate(node: CertTree.Node, proc: Procedure, goal: CGoal, cenv: CEnvironment): Proof = { + def translate(node: CertTree.Node, cenv: CEnvironment): Proof = { + val initialGoal = translateGoal(node.goal) val traversalItem = TraversalItem(node, cenv) - val (proofBody, _) = traverseProof(traversalItem, PrependProofProducer(GhostElimStep(goal.pre, goal.programVars))) - val inductive = proc.body.vars.contains(Var(proc.name)) - Proof(proofBody, goal.programVars, inductive) + val initialKont = PrependProofProducer(GhostElimStep(initialGoal)) + val (proofBody, _) = traverseProof(traversalItem, initialKont) + Proof(proofBody, initialGoal.programVars) } def traverseProof(item: TraversalItem, kont: ProofProducer): (ProofStep, CEnvironment) = { def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { case Skip => - (EmpStep(cenv), cenv) + val goal = cenv.initialGoal.subst(cenv.ghostSubst) + (EmpStep(goal, cenv.subst, cenv.unfoldings), cenv) case Load(to, _, from, _) => - (ReadStep(CVar(to.name), CVar(from.name)), cenv) + (ReadStep(translateVar(to), translateVar(from)), cenv) case Store(to, offset, e) => val frame = item.node.goal.callGoal.isEmpty - (WriteStep(CVar(to.name), offset, translateExpr(e), frame), cenv) + (WriteStep(translateVar(to), offset, translateExpr(e), frame), cenv) case Malloc(to, tpe, sz) => - (AllocStep(CVar(to.name), translateSSLType(tpe), sz), cenv) + (AllocStep(translateVar(to), translateType(tpe), sz), cenv) case Free(v) => val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) (FreeStep(block.get.sz), cenv) - case _ => ??? + case Call(_, _, _) => + assert(item.node.goal.callGoal.isDefined) + val callGoal = item.node.goal.callGoal.get + + // create call step + val companionToFresh = callGoal.companionToFresh.map{ case (k, v) => translateVar(k) -> translateVar(v)} + val freshToActual = callGoal.freshToActual.map{ case (k, v) => translateVar(k) -> translateExpr(v)} + val cgoal = cenv.initialGoal.subst(companionToFresh) + + (CallStep(cgoal, freshToActual, freshCallId), cenv) + case _ => throw TranslationException("Operation not supported") } def translateProducer(stmtProducer: StmtProducer, cenv: CEnvironment): (ProofProducer, CEnvironment) = { @@ -47,70 +57,46 @@ object ProofTranslation { (k1 >> k2, cenv2) case PartiallyAppliedProducer(p, _) => translateProducer(p, cenv) - case FrameProducer(h) => - h match { - case _: Block => - (IdProofProducer, cenv) - case _ => - val framedHeaplets = cenv.framedHeaplets :+ translateHeaplet(h) - (IdProofProducer, cenv.copy(framedHeaplets = framedHeaplets)) - } - case EnterCall(goal) => - val cgoal = translateGoal(goal) - (IdProofProducer, cenv.copy(subst = Map.empty, call = Some(cgoal, cenv.ghostSubst, cenv.subst))) - case ExitCall => - assert(item.node.goal.callGoal.isDefined) - val callGoal = item.node.goal.callGoal.get - val (initialGoal, prevGhostSubst, prevSubst) = cenv.call.get - - // create call step - val companionToFresh = callGoal.companionToFresh.map{ case (k, v) => CVar(k.name) -> CVar(v.name)} - val freshToActual = callGoal.freshToActual.map{ case (k, v) => CVar(k.name) -> translateExpr(v)} - val outsideVars = initialGoal.programVars ++ initialGoal.universalGhosts - val pureEx = cenv.spec.pureParams.map(_._2).map(v => v.subst(companionToFresh)).map(v => v.subst(freshToActual)) - val pre = initialGoal.post.subst(freshToActual) - val post = translateAsn(callGoal.calleePost).subst(freshToActual) - - // end of call; reset subst map - val cenv1 = cenv.copy(ghostSubst = prevGhostSubst, subst = prevSubst, call = None) - - (PrependProofProducer(CallStep(pre, post, outsideVars, pureEx)), cenv1) case SubstProducer(subst) => - val csub = subst.map { case (v, e) => CVar(v.name) -> translateExpr(e) } - val cenv1 = cenv.copy(subst = cenv.subst ++ csub) - (IdProofProducer, cenv1) - case GhostSubstProducer(subst) => - val csub = subst.map { case (v, e) => CVar(v.name) -> CVar(e.name) } - val cenv1 = cenv.copy(ghostSubst = cenv.ghostSubst ++ csub) + if (item.node.goal.callGoal.isEmpty) { + val csub = subst.map { case (v, e) => translateVar(v) -> translateExpr(e).subst(cenv.ghostSubst) } + val cenv1 = cenv.copy(subst = cenv.subst ++ csub) + (IdProofProducer, cenv1) + } else (IdProofProducer, cenv) + case GhostSubstProducer(ghostSubst) => + val newGhostSubst = ghostSubst.map { case (v, e) => translateVar(v) -> translateVar(e) } + val newSubst = cenv.subst.map { case (v, e) => v.substVar(newGhostSubst) -> e.subst(newGhostSubst)} + val newUnfoldings = cenv.unfoldings.map { case (app, e) => app.subst(newGhostSubst) -> e.subst(newGhostSubst) } + val cenv1 = cenv.copy(subst = newSubst, ghostSubst = cenv.ghostSubst ++ newGhostSubst, unfoldings = newUnfoldings) (IdProofProducer, cenv1) - case UnrollProducer(app, selector, expansion, substEx) => + case UnfoldProducer(app, selector, substPred, substEx, substArgs) => val cselector = translateExpr(selector) - val capp = translateHeaplet(app).asInstanceOf[CSApp] - val csub = substEx.map { case (src, dst) => CVar(src.name) -> CVar(dst.name) } - val cexpansion = translateSFormula(expansion) - - // get clause existentials - val predicate = cenv.predicates.find(_.name == app.pred).get - val clauseIdx = predicate.clauses.indexWhere(_.selector == cselector) - val varsInExpansion = cexpansion.vars.distinct - val clauseExUnordered = csub.values.toList - val clauseEx = varsInExpansion.filter(clauseExUnordered.contains) + val capp = translateSApp(app) + val csubPred = substPred.map { case (src, dst) => translateVar(src) -> translateVar(dst) } + val csubEx = substEx.map { case (src, dst) => translateVar(src) -> translateVar(dst) } + val csubArgs = substArgs.map { case (src, dst) => translateVar(src) -> translateExpr(dst) } - val item = AppExpansion(clauseIdx, cexpansion, clauseEx) - val heapSubst = cenv.heapSubst ++ Map(capp -> item) - val cenv1 = cenv.copy(heapSubst = heapSubst) + // get clause with substitutions + val predicate = cenv.predicates(app.pred) + val cclause = predicate.clauses.find(_.selector == cselector).get + val actualClause = cclause.subst(csubPred).subst(csubEx).subst(csubArgs).subst(cenv.ghostSubst) + val cenv1 = cenv.copy(unfoldings = cenv.unfoldings ++ Map(capp -> actualClause)) (IdProofProducer, cenv1) case ConstProducer(s) => val (step, cenv1) = translateOperation(s, cenv) (ConstProofProducer(step, cenv1), cenv1) case PrependProducer(s) => - if (s.isInstanceOf[Call]) return (IdProofProducer, cenv) val (step, cenv1) = translateOperation(s, cenv) (PrependProofProducer(step), cenv1) case BranchProducer(_) => - val sapp = translateHeaplet(item.node.footprint.pre.sigma.apps.head).asInstanceOf[CSApp] + val sapp = translateSApp(item.node.footprint.pre.sigma.apps.head) + val pred = cenv.predicates(sapp.pred) val subgoals = item.node.children.map(n => translateGoal(n.goal)) - (BranchProofProducer(sapp, subgoals, cenv), cenv) + val initialSub: Map[CVar, CVar] = Map.empty + val sub = pred.clauses.zip(subgoals).foldLeft(initialSub){ case (acc, (c, g)) => acc ++ g.pre.sigma.unify(c.asn.sigma) } + val actualPred = pred.subst(sub) + val openPostSteps = actualPred.clauses.map(c => OpenPostStep(sapp, c.asn, c.existentials)) + (BranchProofProducer(openPostSteps, cenv), cenv) case GuardedProducer(_, _) => (GuardedProofProducer(cenv), cenv) case _ => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index d19e5608b..1946ff9c0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -11,8 +11,6 @@ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.language._ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic._ -import org.tygus.suslik.synthesis.rules.UnfoldingRules.Open -import org.tygus.suslik.synthesis.{ChainedProducer, PartiallyAppliedProducer, StmtProducer} object Translation { case class TranslationException(msg: String) extends Exception(msg) @@ -27,57 +25,33 @@ object Translation { * @return the inductive predicates, fun spec, proof, and program translated to HTT */ def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): - (Seq[CInductivePredicate], CFunSpec, Proof, CProcedure) = { - val cpreds = for (label <- (node.goal.pre.sigma.apps ++ node.goal.post.sigma.apps).map(_.pred).distinct) yield { - val predicate = env.predicates(label) - translateInductivePredicate(predicate.resolveOverloading(env)) - } - val initialGoal = translateGoal(node.goal) - val spec = translateFunSpec(node) - val initialCEnv = CEnvironment(spec, cpreds) - val proof = ProofTranslation.translate(node, proc, initialGoal, initialCEnv) - val stmtBody = ProgramTranslation.translate(node, proc, initialGoal) - - val cproc = CProcedure(proc.name, translateSSLType(proc.tp), proc.formals.map(translateParam), stmtBody) - (cpreds, spec, proof, cproc) - } - - private def translateFunSpec(node: CertTree.Node)(implicit env: Environment): CFunSpec = { - val FunSpec(_, tp, _, _, _, _) = node.goal.toFunSpec - val goal = node.goal - val pureParams = goal.universalGhosts - .map(v => translateParam((v, goal.gamma(v)))) - .filterNot(_._1 == CCardType) // exclude cardinality vars - .toList - val ctp = translateSSLType(tp) - val cparams = goal.formals.map(translateParam) - val cpre = translateAsn(goal.pre) - val cpost = translateAsn(goal.post) - CFunSpec( - goal.fname, - ctp, - cparams, - pureParams, - cpre, - cpost - ) + (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedure) = { + val cpreds = env.predicates.mapValues(p => translateInductivePredicate(p.resolveOverloading(env))) + val goal = translateGoal(node.goal) + val initialCEnv = CEnvironment(goal, cpreds) + val proof = ProofTranslation.translate(node, initialCEnv) + val stmtBody = ProgramTranslation.translate(node, proc) + + val cproc = CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), stmtBody) + (cpreds, goal.toFunspec, proof, cproc) } private def translateInductivePredicate(el: InductivePredicate): CInductivePredicate = { val cParams = el.params.map(translateParam) :+ (CHeapType, CVar("h")) - val cClauses = el.clauses.zipWithIndex.map { case (c, i) => translateClause(c, el.name, i) } + val cClauses = el.clauses.zipWithIndex.map { case (c, i) => translateClause(c, el.name, i, cParams) } CInductivePredicate(el.name, cParams, cClauses) } - private def translateParam(el: (Var, SSLType)): (CoqType, CVar) = - (translateSSLType(el._2), CVar(el._1.name)) + private def translateParam(el: (Var, SSLType)): (HTTType, CVar) = + (translateType(el._2), translateVar(el._1)) - def translateClause(el: InductiveClause, pred: String, idx: Int): CInductiveClause = { + private def translateClause(el: InductiveClause, pred: String, idx: Int, predParams: CFormals): CInductiveClause = { val selector = translateExpr(el.selector) - CInductiveClause(pred, idx, selector, translateAsn(el.asn)) + val asn = translateAsn(el.asn) + CInductiveClause(pred, idx, selector, translateAsn(el.asn), asn.existentials(predParams.map(_._2))) } - def translateSSLType(el: SSLType): CoqType = el match { + def translateType(el: SSLType): HTTType = el match { case BoolType => CBoolType case IntType => CNatType case LocType => CPtrType @@ -89,9 +63,9 @@ object Translation { def translateGoal(goal: Goal): CGoal = { val pre = translateAsn(goal.pre) val post = translateAsn(goal.post) - val gamma = goal.gamma.map { case (value, lType) => (CVar(value.name), translateSSLType(lType)) } - val programVars = goal.programVars.map(v => CVar(v.name)) - val universalGhosts = goal.universalGhosts.map(v => CVar(v.name)).toSeq.filterNot(_.isCard) + val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } + val programVars = goal.programVars.map(translateVar) + val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(_.isCard) CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) } @@ -101,15 +75,15 @@ object Translation { case IntConst(value) => CNatConst(value) case el@UnaryExpr(_, _) => translateUnaryExpr(el) case el@BinaryExpr(_, _, _) => translateBinaryExpr(el) - case el@OverloadedBinaryExpr(_, _, _) => translateOverloadedBinaryExpr(el) case SetLiteral(elems) => CSetLiteral(elems.map(e => translateExpr(e))) case IfThenElse(c, t, e) => CIfThenElse(translateExpr(c), translateExpr(t), translateExpr(e)) + case _ => throw TranslationException("Operation not supported") } - def translateHeaplet(el: Heaplet): CExpr = el match { - case PointsTo(loc, offset, value) => CPointsTo(translateExpr(loc), offset, translateExpr(value)) - case SApp(pred, args, tag, card) => CSApp(pred, args.map(translateExpr), translateExpr(card)) - } + def translateVar(el: Var): CVar = CVar(el.name) + + def translateSApp(el: SApp): CSApp = CSApp(el.pred, el.args.map(translateExpr), translateExpr(el.card)) + def translatePointsTo(el: PointsTo): CPointsTo = CPointsTo(translateExpr(el.loc), el.offset, translateExpr(el.value)) def translateAsn(el: Assertion): CAssertion = { val phi: CExpr = translateExpr(el.phi.toExpr).simplify @@ -118,72 +92,38 @@ object Translation { } def translateSFormula(el: SFormula): CSFormula = { - val ptss = el.ptss.map(translateHeaplet).asInstanceOf[List[CPointsTo]] - val apps = el.apps.map(translateHeaplet).asInstanceOf[List[CSApp]] + val ptss = el.ptss.map(translatePointsTo) + val apps = el.apps.map(translateSApp) CSFormula("h", apps, ptss) } - private def translateUnaryExpr(el: UnaryExpr) : CExpr = el match { - case UnaryExpr(OpNot, e) => e match { - case BinaryExpr(OpEq, left, right) => COverloadedBinaryExpr(COpNotEqual, translateExpr(left), translateExpr(right)) - case _ => CUnaryExpr(COpNot, translateExpr(e)) - } - case UnaryExpr(OpUnaryMinus, e) => ??? + private def translateUnOp(op: UnOp): CUnOp = op match { + case OpNot => COpNot + case OpUnaryMinus => COpUnaryMinus } - private def translateOverloadedBinaryExpr(el: OverloadedBinaryExpr) : CExpr = el match { - case OverloadedBinaryExpr(OpOverloadedEq, l, r) => - COverloadedBinaryExpr(COpOverloadedEq, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpNotEqual, l, r) => - COverloadedBinaryExpr(COpNotEqual, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpGt, l, r) => - COverloadedBinaryExpr(COpGt, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpGeq, l, r) => - COverloadedBinaryExpr(COpGeq, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpGeq, l, r) => - COverloadedBinaryExpr(COpGeq, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpOverloadedPlus, l, r) => - COverloadedBinaryExpr(COpOverloadedPlus, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpOverloadedMinus, l, r) => - COverloadedBinaryExpr(COpOverloadedMinus, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpOverloadedLeq, l, r) => - COverloadedBinaryExpr(COpOverloadedLeq, translateExpr(l), translateExpr(r)) - case OverloadedBinaryExpr(OpOverloadedStar, l, r) => - COverloadedBinaryExpr(COpOverloadedStar, translateExpr(l), translateExpr(r)) + private def translateBinOp(op: BinOp): CBinOp = op match { + case OpImplication => COpImplication + case OpPlus => COpPlus + case OpMinus => COpMinus + case OpMultiply => COpMultiply + case OpEq => COpEq + case OpBoolEq => COpBoolEq + case OpLeq => COpLeq + case OpLt => COpLt + case OpAnd => COpAnd + case OpOr => COpOr + case OpUnion => COpUnion + case OpDiff => COpDiff + case OpIn => COpIn + case OpSetEq => COpSetEq + case OpSubset => COpSubset + case OpIntersect => COpIntersect } - private def translateBinaryExpr(el: BinaryExpr) : CExpr = el match { - case BinaryExpr(OpImplication, l, r) => - CBinaryExpr(COpImplication, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpPlus, l, r) => - CBinaryExpr(COpPlus, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpMinus, l, r) => - CBinaryExpr(COpMinus, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpMultiply, l, r) => - CBinaryExpr(COpMultiply, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpEq, l, r) => - CBinaryExpr(COpEq, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpBoolEq, l, r) => - CBinaryExpr(COpBoolEq, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpLeq, l, r) => - CBinaryExpr(COpLeq, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpLt, l, r) => - CBinaryExpr(COpLt, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpAnd, l, r) => - CBinaryExpr(COpAnd, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpOr, l, r) => - CBinaryExpr(COpOr, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpUnion, l, r) => - CBinaryExpr(COpUnion, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpDiff, l, r) => - CBinaryExpr(COpDiff, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpIn, l, r) => - CBinaryExpr(COpIn, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpSetEq, l, r) => - CBinaryExpr(COpSetEq, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpSubset, l, r) => - CBinaryExpr(COpSubset, translateExpr(l), translateExpr(r)) - case BinaryExpr(OpIntersect, l, r) => - CBinaryExpr(COpIntersect, translateExpr(l), translateExpr(r)) - } + private def translateUnaryExpr(el: UnaryExpr) : CExpr = + CUnaryExpr(translateUnOp(el.op), translateExpr(el.arg)) + + private def translateBinaryExpr(el: BinaryExpr) : CExpr = + CBinaryExpr(translateBinOp(el.op), translateExpr(el.left), translateExpr(el.right)) } diff --git a/src/main/scala/org/tygus/suslik/logic/Declarations.scala b/src/main/scala/org/tygus/suslik/logic/Declarations.scala index 3cdb75709..3b88e5976 100644 --- a/src/main/scala/org/tygus/suslik/logic/Declarations.scala +++ b/src/main/scala/org/tygus/suslik/logic/Declarations.scala @@ -151,12 +151,12 @@ case class InductivePredicate(name: Ident, params: Formals, clauses: Seq[Inducti * Renames existentials so they wouldn't capture the parameters and `vars` * * @param vars additional contextual variables that can be captures - * @return inductive predicate + * @return inductive predicate and substitution used */ - def refreshExistentials(vars: Set[Var], suffix: String = ""): InductivePredicate = { + def refreshExistentials(vars: Set[Var], suffix: String = ""): (InductivePredicate, SubstVar) = { val bound = Set(selfCardVar) ++ vars ++ params.map(_._1).toSet val sbst = refreshVars(existentials.toList, bound, suffix) - this.copy(clauses = this.clauses.map(c => InductiveClause(c.selector.subst(sbst), c.asn.subst(sbst)))) + (this.copy(clauses = this.clauses.map(c => InductiveClause(c.selector.subst(sbst), c.asn.subst(sbst)))), sbst) } def vars: Set[Var] = clauses.flatMap(c => c.selector.vars ++ c.asn.vars).toSet diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 4920c88a3..8122d08d4 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.synthesis -import org.tygus.suslik.language.Expressions.{Expr, Var} +import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements._ import org.tygus.suslik.logic.{Heaplet, InductiveClause, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} @@ -168,19 +168,10 @@ trait Noop { } // Captures variable substitutions -case class SubstProducer(subst: Map[Var, Expr]) extends StmtProducer with Noop +case class SubstProducer(subst: Subst) extends StmtProducer with Noop // Captures ghost variable instantiations -case class GhostSubstProducer(subst: Map[Var, Var]) extends StmtProducer with Noop +case class GhostSubstProducer(subst: SubstVar) extends StmtProducer with Noop -// Captures an unrolled predicate -case class UnrollProducer(app: SApp, selector: Expr, expansion: SFormula, substEx: Map[Var, Var]) extends StmtProducer with Noop - -// Captures a frame -case class FrameProducer(h: Heaplet) extends StmtProducer with Noop - -// Enters a call synthesis -case class EnterCall(goal: Goal) extends StmtProducer with Noop - -// Exits a call synthesis -case object ExitCall extends StmtProducer with Noop +// Captures an unfolded predicate application +case class UnfoldProducer(app: SApp, selector: Expr, substPred: SubstVar, substEx: SubstVar, substArgs: Subst) extends StmtProducer with Noop diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala index 2ffd57d35..fe0581938 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala @@ -131,7 +131,7 @@ object LogicalRules extends PureLogicUtils with SepLogicUtils with RuleUtils { val newPre = Assertion(pre.phi, newPreSigma) val newPost = Assertion(post.phi, newPostSigma) val newGoal = goal.spawnChild(newPre, newPost) - val kont = FrameProducer(hPre) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) } } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index 584a5071b..ed535fde2 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -32,7 +32,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { case SApp(pred, args, PTag(cls, unf), card) if unf < env.config.maxOpenDepth => ruleAssert(env.predicates.contains(pred), s"Open rule encountered undefined predicate: $pred") val freshSuffix = args.take(1).map(_.pp).mkString("_") - val InductivePredicate(_, params, clauses) = env.predicates(pred).refreshExistentials(goal.vars, freshSuffix) + val (InductivePredicate(_, params, clauses), _) = env.predicates(pred).refreshExistentials(goal.vars, freshSuffix) // [Cardinality] adjust cardinality of sub-clauses val sbst = params.map(_._1).zip(args).toMap + (selfCardVar -> card) val remainingSigma = pre.sigma - h @@ -94,7 +94,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { suspendedCallGoal = Some(SuspendedCallGoal(goal.pre, goal.post, callePost, call, freshSub)) newGoal = goal.spawnChild(post = f.pre, gamma = newGamma, callGoal = suspendedCallGoal) } yield { - val kont: StmtProducer = EnterCall(newGoal) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } } @@ -126,7 +126,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPost = callGoal.callerPost val newGoal = goal.spawnChild(pre = newPre, post = newPost, callGoal = None) val postCallTransition = Transition(goal, newGoal) - val kont: StmtProducer = ExitCall >> PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, List(postCallTransition) ++ companionTransition(callGoal, goal))) } @@ -184,7 +184,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { ruleAssert(env.predicates.contains(pred), s"Close rule encountered undefined predicate: $pred") - val InductivePredicate(predName, params, clauses) = env.predicates(pred).refreshExistentials(goal.vars) + val (InductivePredicate(predName, params, clauses), predSbst) = env.predicates(pred).refreshExistentials(goal.vars) //ruleAssert(clauses.lengthCompare(1) == 0, s"Predicates with multiple clauses not supported yet: $pred") val paramNames = params.map(_._1) @@ -209,7 +209,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPhi = post.phi && actualConstraints && actualSelector val newPost = Assertion(newPhi, goal.post.sigma ** actualBody - h) - val kont = UnrollProducer(a, selector, actualBody, freshExistentialsSubst) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnfoldProducer(a, selector, predSbst, freshExistentialsSubst, substArgs) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(goal.spawnChild(post = newPost)), kont, this, goal) } diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index 3d7c93ea5..d3521acaa 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -22,7 +22,7 @@ class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUt val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath // verify - val result = s"coqc -vok $pathToCertificate".! + val result = s"coqc -vok -w none $pathToCertificate".! // check that Coq compilation succeeded assert(result == 0) From 40542e9465aec4db2099db6a0b63ed6564bea024 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 2 Aug 2020 18:08:09 +0800 Subject: [PATCH 023/211] Move around some files --- .../targets/htt/language/Sentences.scala | 136 ----------------- .../targets/htt/language/Types.scala | 52 +++---- .../targets/htt/logic/Proof.scala | 14 +- .../targets/htt/logic/ProofSteps.scala | 12 +- .../targets/htt/logic/Sentences.scala | 138 ++++++++++++++++++ .../StatementProducers.scala | 4 +- .../{language => program}/Statements.scala | 9 +- .../htt/translation/ProgramTranslation.scala | 4 +- .../targets/htt/translation/Translation.scala | 8 +- 9 files changed, 193 insertions(+), 184 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala rename src/main/scala/org/tygus/suslik/certification/targets/htt/{language => program}/StatementProducers.scala (95%) rename src/main/scala/org/tygus/suslik/certification/targets/htt/{language => program}/Statements.scala (94%) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala deleted file mode 100644 index b8f73e1fb..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Sentences.scala +++ /dev/null @@ -1,136 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.language - -import org.tygus.suslik.certification.targets.htt.language.Expressions._ - -sealed abstract class Sentence extends PrettyPrinting - -case class CAssertion(phi: CExpr, sigma: CSFormula) extends Sentence { - override def pp: String = if (phi.isTrivial) sigma.pp else s"${phi.pp} /\\ ${sigma.pp}" - - def existentials(quantifiedVars: Seq[CVar]): Seq[CVar] = valueVars.diff(quantifiedVars) - - def ppQuantified(quantifiedVars: Seq[CVar], depth: Int = 0): String = { - val builder = new StringBuilder() - - val valueEx = existentials(quantifiedVars) - val heapEx = heapVars - if (valueEx.nonEmpty) { - builder.append(s"exists ${valueEx.map(v => inferredTypes.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") - } - if (heapEx.nonEmpty) { - builder.append(s"exists ${heapEx.map(_.pp).mkString(" ")},\n") - } - - builder.append(pp) - - getIndent(depth) + builder.toString().replaceAll("\n", s"\n${getIndent(depth)}") - } - - def subst(sub: Map[CVar, CExpr]): CAssertion = - CAssertion(phi.subst(sub), sigma.subst(sub)) - - val valueVars: Seq[CVar] = - (phi.vars ++ sigma.vars).distinct.filterNot(_.isCard) - - val heapVars: Seq[CVar] = - sigma.heapVars - - val inferredTypes: Map[CVar, HTTType] = { - @scala.annotation.tailrec - def collectPhi(el: CExpr, m: Map[CVar, HTTType]): Map[CVar, HTTType] = el match { - case CBinaryExpr(COpSetEq, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) - case CBinaryExpr(COpSetEq, left: CVar, right) => collectPhi(right, m ++ Map(left -> CNatSeqType)) - case CBinaryExpr(COpSetEq, left, right: CVar) => collectPhi(left, m ++ Map(right -> CNatSeqType)) - case _ => m - } - def collectSigma: Map[CVar, HTTType] = { - val ptss = sigma.ptss - ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} - } - val mPhi = collectPhi(phi, Map.empty) - val mSigma = collectSigma - mPhi ++ mSigma - } -} - -case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends Sentence { - def subst(sub: Map[CVar, CExpr]): CInductiveClause = - CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) -} - -case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends Sentence { - val paramVars: Seq[CVar] = params.map(_._2) - - def subst(sub: Map[CVar, CExpr]): CInductivePredicate = - CInductivePredicate(name, params.map(p => (p._1, p._2.substVar(sub))), clauses.map(_.subst(sub))) - - override def pp: String = { - val builder = new StringBuilder() - builder.append(s"Inductive $name ${params.map{ case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") - - // clauses - val clausesStr = for { - CInductiveClause(pred, idx, selector, asn, _) <- clauses - } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1)}" - - builder.append(s"${clausesStr.mkString("\n")}.\n") - builder.toString() - } -} - -case class CFunSpec(name: String, rType: HTTType, params: CFormals, ghostParams: CFormals, pre: CAssertion, post: CAssertion) extends Sentence { - val paramVars: Seq[CVar] = params.map(_._2) - val ghostParamVars: Seq[CVar] = ghostParams.map(_._2) - - val progVarsAlias = "vprogs" - val ghostVarsAlias = "vghosts" - - private def ppFormals(formals: CFormals): (String, String) = { - val (typs, names) = formals.unzip - val typsStr = typs.map(_.pp).mkString(" * ") - val namesStr = names.map(_.pp).mkString(", ") - (typsStr, namesStr) - } - - override def pp: String = { - val builder = new StringBuilder() - - val (typsStr, progVarsStr) = ppFormals(params) - val (ghostTypsStr, ghostVarsStr) = ppFormals(ghostParams) - val destructuredAliases = { - val destructuredParams = if (params.nonEmpty) s"${getIndent(3)}let: ($progVarsStr) := $progVarsAlias in\n" else "" - val destructuredGhostParams = if (ghostParams.nonEmpty) s"${getIndent(3)}let: ($ghostVarsStr) := $ghostVarsAlias in\n" else "" - destructuredParams + destructuredGhostParams - } - - builder.append(s"Definition ${name}_type :=\n") - - // program var signatures - if (params.nonEmpty) { - builder.append(s"${getIndent(1)}forall ($progVarsAlias : $typsStr),\n") - } - - // ghost var signatures - if (ghostParams.nonEmpty) { - builder.append(s"${getIndent(1)}{($ghostVarsAlias : $ghostTypsStr)},\n") - } - - builder.append(s"${getIndent(1)}STsep (\n") - - // pre-condition - builder.append(s"${getIndent(2)}fun h =>\n") - builder.append(destructuredAliases) - builder.append(pre.ppQuantified(paramVars ++ ghostParamVars, 3)) - builder.append(",\n") - - // post-condition - builder.append(s"${getIndent(2)}[vfun (_: ${rType.pp}) h =>\n") - builder.append(destructuredAliases) - builder.append(post.ppQuantified(paramVars ++ ghostParamVars, 3)) - builder.append("\n") - - builder.append(s"${getIndent(2)}]).") - - builder.toString() - } -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala index 44cb24c8e..27bf9f7a6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala @@ -1,35 +1,37 @@ package org.tygus.suslik.certification.targets.htt.language -sealed abstract class HTTType extends PrettyPrinting +object Types { + sealed abstract class HTTType extends PrettyPrinting -case object CBoolType extends HTTType { - override def pp: String = "bool" -} + case object CBoolType extends HTTType { + override def pp: String = "bool" + } -case object CNatType extends HTTType { - override def pp: String = "nat" -} + case object CNatType extends HTTType { + override def pp: String = "nat" + } -case object CPtrType extends HTTType { - override def pp: String = "ptr" -} + case object CPtrType extends HTTType { + override def pp: String = "ptr" + } -case object CUnitType extends HTTType { - override def pp: String = "unit" -} + case object CUnitType extends HTTType { + override def pp: String = "unit" + } -case object CPropType extends HTTType { - override def pp: String = "Prop" -} + case object CPropType extends HTTType { + override def pp: String = "Prop" + } -case object CHeapType extends HTTType { - override def pp: String = "heap" -} + case object CHeapType extends HTTType { + override def pp: String = "heap" + } -case object CNatSeqType extends HTTType { - override def pp: String = "seq nat" -} + case object CNatSeqType extends HTTType { + override def pp: String = "seq nat" + } -case object CCardType extends HTTType { - override def pp: String = "" -} + case object CCardType extends HTTType { + override def pp: String = "" + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 6a21c2db6..849b31854 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -1,12 +1,24 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language._ +import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{ProofStep, nestedDestructL} +import org.tygus.suslik.certification.targets.htt.logic.Sentences._ object Proof { private var currCallId = 0 def freshCallId: String = { currCallId += 1; s"call$currCallId" } + case class Proof(root: ProofStep, params: Seq[CVar]) { + def pp: String = { + val obligationTactic = s"Obligation Tactic := intro; move=>${nestedDestructL(params)}; ssl_program_simpl." + val nextObligation = "Next Obligation." + val body = root.pp + val qed = "Qed.\n" + List(obligationTactic, nextObligation, body, qed).mkString("\n") + } + } + case class CGoal(pre: CAssertion, post: CAssertion, gamma: Map[CVar, HTTType], diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 267822593..225588c99 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -1,8 +1,9 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language._ +import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.Proof._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences._ object ProofSteps { def nestedDestructR(items: Seq[CExpr]): String = items match { @@ -26,15 +27,6 @@ object ProofSteps { visit(items.reverse) } - case class Proof(root: ProofStep, params: Seq[CVar]) { - def pp: String = { - val obligationTactic = s"Obligation Tactic := intro; move=>${nestedDestructL(params)}; ssl_program_simpl." - val nextObligation = "Next Obligation." - val body = root.pp - val qed = "Qed.\n" - List(obligationTactic, nextObligation, body, qed).mkString("\n") - } - } sealed abstract class ProofStep { def pp: String = "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala new file mode 100644 index 000000000..d7b61f0ef --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -0,0 +1,138 @@ +package org.tygus.suslik.certification.targets.htt.logic + +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.{CFormals, PrettyPrinting} +import org.tygus.suslik.certification.targets.htt.language.Types._ + +object Sentences { + case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { + override def pp: String = if (phi.isTrivial) sigma.pp else s"${phi.pp} /\\ ${sigma.pp}" + + def existentials(quantifiedVars: Seq[CVar]): Seq[CVar] = valueVars.diff(quantifiedVars) + + def ppQuantified(quantifiedVars: Seq[CVar], depth: Int = 0): String = { + val builder = new StringBuilder() + + val valueEx = existentials(quantifiedVars) + val heapEx = heapVars + if (valueEx.nonEmpty) { + builder.append(s"exists ${valueEx.map(v => inferredTypes.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") + } + if (heapEx.nonEmpty) { + builder.append(s"exists ${heapEx.map(_.pp).mkString(" ")},\n") + } + + builder.append(pp) + + getIndent(depth) + builder.toString().replaceAll("\n", s"\n${getIndent(depth)}") + } + + def subst(sub: Map[CVar, CExpr]): CAssertion = + CAssertion(phi.subst(sub), sigma.subst(sub)) + + val valueVars: Seq[CVar] = + (phi.vars ++ sigma.vars).distinct.filterNot(_.isCard) + + val heapVars: Seq[CVar] = + sigma.heapVars + + val inferredTypes: Map[CVar, HTTType] = { + @scala.annotation.tailrec + def collectPhi(el: CExpr, m: Map[CVar, HTTType]): Map[CVar, HTTType] = el match { + case CBinaryExpr(COpSetEq, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) + case CBinaryExpr(COpSetEq, left: CVar, right) => collectPhi(right, m ++ Map(left -> CNatSeqType)) + case CBinaryExpr(COpSetEq, left, right: CVar) => collectPhi(left, m ++ Map(right -> CNatSeqType)) + case _ => m + } + def collectSigma: Map[CVar, HTTType] = { + val ptss = sigma.ptss + ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} + } + val mPhi = collectPhi(phi, Map.empty) + val mSigma = collectSigma + mPhi ++ mSigma + } + } + + case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { + def subst(sub: Map[CVar, CExpr]): CInductiveClause = + CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) + } + + case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends PrettyPrinting { + val paramVars: Seq[CVar] = params.map(_._2) + + def subst(sub: Map[CVar, CExpr]): CInductivePredicate = + CInductivePredicate(name, params.map(p => (p._1, p._2.substVar(sub))), clauses.map(_.subst(sub))) + + override def pp: String = { + val builder = new StringBuilder() + builder.append(s"Inductive $name ${params.map{ case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") + + // clauses + val clausesStr = for { + CInductiveClause(pred, idx, selector, asn, _) <- clauses + } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1)}" + + builder.append(s"${clausesStr.mkString("\n")}.\n") + builder.toString() + } + } + + case class CFunSpec(name: String, rType: HTTType, params: CFormals, ghostParams: CFormals, pre: CAssertion, post: CAssertion) extends PrettyPrinting { + val paramVars: Seq[CVar] = params.map(_._2) + val ghostParamVars: Seq[CVar] = ghostParams.map(_._2) + + val progVarsAlias = "vprogs" + val ghostVarsAlias = "vghosts" + + private def ppFormals(formals: CFormals): (String, String) = { + val (typs, names) = formals.unzip + val typsStr = typs.map(_.pp).mkString(" * ") + val namesStr = names.map(_.pp).mkString(", ") + (typsStr, namesStr) + } + + override def pp: String = { + val builder = new StringBuilder() + + val (typsStr, progVarsStr) = ppFormals(params) + val (ghostTypsStr, ghostVarsStr) = ppFormals(ghostParams) + val destructuredAliases = { + val destructuredParams = if (params.nonEmpty) s"${getIndent(3)}let: ($progVarsStr) := $progVarsAlias in\n" else "" + val destructuredGhostParams = if (ghostParams.nonEmpty) s"${getIndent(3)}let: ($ghostVarsStr) := $ghostVarsAlias in\n" else "" + destructuredParams + destructuredGhostParams + } + + builder.append(s"Definition ${name}_type :=\n") + + // program var signatures + if (params.nonEmpty) { + builder.append(s"${getIndent(1)}forall ($progVarsAlias : $typsStr),\n") + } + + // ghost var signatures + if (ghostParams.nonEmpty) { + builder.append(s"${getIndent(1)}{($ghostVarsAlias : $ghostTypsStr)},\n") + } + + builder.append(s"${getIndent(1)}STsep (\n") + + // pre-condition + builder.append(s"${getIndent(2)}fun h =>\n") + builder.append(destructuredAliases) + builder.append(pre.ppQuantified(paramVars ++ ghostParamVars, 3)) + builder.append(",\n") + + // post-condition + builder.append(s"${getIndent(2)}[vfun (_: ${rType.pp}) h =>\n") + builder.append(destructuredAliases) + builder.append(post.ppQuantified(paramVars ++ ghostParamVars, 3)) + builder.append("\n") + + builder.append(s"${getIndent(2)}]).") + + builder.toString() + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala similarity index 95% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala index 057c80423..140089a44 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/StatementProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala @@ -1,7 +1,7 @@ -package org.tygus.suslik.certification.targets.htt.language +package org.tygus.suslik.certification.targets.htt.program import org.tygus.suslik.certification.targets.htt.language.Expressions.CExpr -import org.tygus.suslik.certification.targets.htt.language.Statements.{CGuarded, CIf, CSeqComp, CStatement} +import org.tygus.suslik.certification.targets.htt.program.Statements.{CGuarded, CIf, CSeqComp, CStatement} object StatementProducers { type Kont = Seq[CStatement] => CStatement diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala similarity index 94% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 842fdda36..120686fbc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -1,10 +1,11 @@ -package org.tygus.suslik.certification.targets.htt.language +package org.tygus.suslik.certification.targets.htt.program -import org.tygus.suslik.util.StringUtil._ +import org.tygus.suslik.certification.targets.htt.language.PrettyPrinting +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.util.StringUtil.mkSpaces object Statements { - import Expressions._ - sealed abstract class CStatement extends PrettyPrinting { // Expression-collector def collectE[R <: CExpr](p: CExpr => Boolean): Seq[R] = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index c225f41f7..ebd041ea9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -1,8 +1,8 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.language.StatementProducers._ -import org.tygus.suslik.certification.targets.htt.language.Statements._ +import org.tygus.suslik.certification.targets.htt.program.StatementProducers._ +import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.translation.Translation._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 1946ff9c0..cc01383f6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -2,10 +2,10 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.language.Statements._ -import org.tygus.suslik.certification.targets.htt.language._ -import org.tygus.suslik.certification.targets.htt.logic.Proof.{CEnvironment, CGoal} -import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.Proof +import org.tygus.suslik.certification.targets.htt.program.Statements._ +import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.targets.htt.logic.Proof._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences._ import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.language._ From 74d67192f28a86ca6e4b80e78cd9daa72ad56f07 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 3 Aug 2020 01:43:37 +0800 Subject: [PATCH 024/211] Add documentation --- .../suslik/certification/Certificate.scala | 8 +- .../certification/CertificationTarget.scala | 5 + .../certification/targets/htt/HTT.scala | 3 + .../targets/htt/HTTCertificate.scala | 7 +- .../targets/htt/language/Expressions.scala | 15 +- .../targets/htt/language/package.scala | 1 + .../targets/htt/logic/Proof.scala | 26 ++- .../targets/htt/logic/ProofProducers.scala | 41 +++- .../targets/htt/logic/ProofSteps.scala | 214 ++++++++++++------ .../targets/htt/logic/Sentences.scala | 7 +- .../targets/htt/program/Statements.scala | 5 + .../htt/translation/ProgramTranslation.scala | 21 +- .../htt/translation/ProofTranslation.scala | 21 +- .../targets/htt/translation/Translation.scala | 27 +-- .../targets/htt/HTTCertificationTests.scala | 11 +- 15 files changed, 274 insertions(+), 138 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 5080ad3cb..234ef5184 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -1,8 +1,12 @@ package org.tygus.suslik.certification +/** + * A generic interface for certificates, to be implemented by all + * certification backends + */ trait Certificate { val body: String val name: String - val suffix: String - def fileName: String = name + suffix + val target: CertificationTarget + def fileName: String = name + target.suffix } diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 1cfee185a..75c90f108 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -3,7 +3,12 @@ package org.tygus.suslik.certification import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +/** + * A generic interface for certification targets. The user can specify a target + * by setting the command-line flag `certTarget` to the target's `name` property. + */ trait CertificationTarget { val name: String + val suffix: String def certify(proc: Procedure, env: Environment): Certificate } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 25be1f95c..31ebf9b70 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -8,6 +8,9 @@ import org.tygus.suslik.logic.Environment object HTT extends CertificationTarget { val name: String = "HTT" + val suffix: String = ".v" + + // Import Coq dependencies private val prelude = s"""From mathcomp Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. From fcsl diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index ccb41aba9..d6e7bf1c3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -1,7 +1,10 @@ package org.tygus.suslik.certification.targets.htt -import org.tygus.suslik.certification.Certificate +import org.tygus.suslik.certification.{Certificate, CertificationTarget} case class HTTCertificate(body: String, name: String) extends Certificate { - val suffix: String = ".v" + val target: CertificationTarget = HTT + + // Replace hyphens with underscores + override def fileName: String = super.fileName.replace('-', '_') } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index a03832281..9a642313d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.LanguageUtils.cardinalityPrefix +import org.tygus.suslik.certification.targets.htt.logic.Proof.{Subst, SubstVar} import org.tygus.suslik.logic.Specifications.selfCardVar object Expressions { @@ -45,7 +46,7 @@ object Expressions { collector(Seq.empty)(this) } - def subst(sigma: Map[CVar, CExpr]): CExpr = this match { + def subst(sigma: Subst): CExpr = this match { case v: CVar => sigma.get(v) match { case None => v @@ -88,7 +89,7 @@ object Expressions { case class CVar(name: String) extends CExpr { override def pp: String = if (name.startsWith(cardinalityPrefix)) name.drop(cardinalityPrefix.length) else name override val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name - def substVar(sub: Map[CVar, CExpr]): CVar = sub.get(this) match { + def substVar(sub: Subst): CVar = sub.get(this) match { case Some(v@CVar(_)) => v case _ => this } @@ -124,14 +125,14 @@ object Expressions { case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" override def pp: String = if (value == CNatConst(0)) s"$locPP :-> null" else s"$locPP :-> ${value.pp}" - override def subst(sigma: Map[CVar, CExpr]): CPointsTo = + override def subst(sigma: Subst): CPointsTo = CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) } case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends CExpr { override def pp: String = s"$pred ${args.map(arg => arg.pp).mkString(" ")}" - override def subst(sigma: Map[CVar, CExpr]): CSApp = + override def subst(sigma: Subst): CSApp = CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) val uniqueName: String = s"$pred${card.pp}" @@ -140,8 +141,8 @@ object Expressions { } case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { - def unify(source: CSFormula): Map[CVar, CVar] = { - val initialMap: Map[CVar, CVar] = Map.empty + def unify(source: CSFormula): SubstVar = { + val initialMap: SubstVar = Map.empty val m1 = source.ptss.zip(ptss).foldLeft(initialMap) { case (acc, (p1, p2)) => acc ++ p1.vars.zip(p2.vars).filterNot(v => v._1 == v._2).toMap } @@ -162,7 +163,7 @@ object Expressions { s"$heapName = $ppHeap /\\ $appsStr" } - override def subst(sigma: Map[CVar, CExpr]): CSFormula = { + override def subst(sigma: Subst): CSFormula = { val apps1 = apps.map(_.subst(sigma)) val ptss1 = ptss.map(_.subst(sigma)) CSFormula(heapName, apps1, ptss1) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala index 7cd5c8a1d..885e32466 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar +import org.tygus.suslik.certification.targets.htt.language.Types.HTTType package object language { type CGamma = Map[CVar, HTTType] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 849b31854..668e0dc31 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -9,6 +9,12 @@ object Proof { private var currCallId = 0 def freshCallId: String = { currCallId += 1; s"call$currCallId" } + type Unfoldings = Map[CSApp, CInductiveClause] + type Subst = Map[CVar, CExpr] + type SubstVar = Map[CVar, CVar] + type Gamma = Map[CVar, HTTType] + type PredicateEnv = Map[String, CInductivePredicate] + case class Proof(root: ProofStep, params: Seq[CVar]) { def pp: String = { val obligationTactic = s"Obligation Tactic := intro; move=>${nestedDestructL(params)}; ssl_program_simpl." @@ -21,12 +27,12 @@ object Proof { case class CGoal(pre: CAssertion, post: CAssertion, - gamma: Map[CVar, HTTType], + gamma: Gamma, programVars: Seq[CVar], universalGhosts: Seq[CVar], fname: String) { val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) - def subst(sub: Map[CVar, CExpr]): CGoal = CGoal( + def subst(sub: Subst): CGoal = CGoal( pre.subst(sub), post.subst(sub), gamma.map { case (v, t) => v.substVar(sub) -> t }, @@ -43,15 +49,15 @@ object Proof { } case class CEnvironment(initialGoal: CGoal, - predicates: Map[String, CInductivePredicate], - ghostSubst: Map[CVar, CVar] = Map.empty, - subst: Map[CVar, CExpr] = Map.empty, - unfoldings: Map[CSApp, CInductiveClause] = Map.empty) { + predicates: PredicateEnv, + ghostSubst: SubstVar = Map.empty, + subst: Subst = Map.empty, + unfoldings: Unfoldings = Map.empty) { def copy(initialGoal: CGoal = this.initialGoal, - predicates: Map[String, CInductivePredicate] = this.predicates, - ghostSubst: Map[CVar, CVar] = this.ghostSubst, - subst: Map[CVar, CExpr] = this.subst, - unfoldings: Map[CSApp, CInductiveClause] = this.unfoldings, + predicates: PredicateEnv = this.predicates, + ghostSubst: SubstVar = this.ghostSubst, + subst: Subst = this.subst, + unfoldings: Unfoldings = this.unfoldings, ): CEnvironment = CEnvironment(initialGoal, predicates, ghostSubst, subst, unfoldings) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala index b94d4ccc4..83d8bc011 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala @@ -64,7 +64,7 @@ object ProofProducers { val arity: Int = 1 val fn: Kont = res => { val (step, env) = res.head - (SeqCompStep(s.refreshVars(env), step).simplify, env) + (SeqCompStep(s.refreshGhostVars(env.ghostSubst), step).simplify, env) } } @@ -72,51 +72,74 @@ object ProofProducers { val arity: Int = 1 val fn: Kont = res => { val (step, env) = res.head - (SeqCompStep(step, s.refreshVars(env)).simplify, env) + (SeqCompStep(step, s.refreshGhostVars(env.ghostSubst)).simplify, env) } } - case class BranchProofProducer(steps: Seq[ProofStep], branchEnv: CEnvironment) extends ProofProducer with Branching { - val arity: Int = steps.length + /** + * + * @param openPostSteps the steps to perform upon entering each branch + * @param branchEnv the state of the environment at the time of branching + */ + case class BranchProofProducer(openPostSteps: Seq[OpenPostStep], branchEnv: CEnvironment) extends ProofProducer with Branching { + val arity: Int = openPostSteps.length val fn: Kont = res => if (res.length == 1) res.head else { - val condBranches = res.zip(steps).reverse.map { case ((s, env), openPost) => - SeqCompStep(openPost.refreshVars(env), s) + // For each certified branch, prepend the step that prepares the branch's execution + val condBranches = res.zip(openPostSteps).reverse.map { case ((s, env), openPost) => + SeqCompStep(openPost.refreshGhostVars(env.ghostSubst), s) } val ctail = condBranches.tail val finalBranch = condBranches.head + + // Compose the branch certification code in order, and prepend the Open step at the very beginning. + // Discard envs propagated from child branches, and return the env that was preserved at the time of branching. (SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), branchEnv) } } - case class GuardedProofProducer(env: CEnvironment) extends ProofProducer with Branching { + case class GuardedProofProducer(branchEnv: CEnvironment) extends ProofProducer with Branching { val arity: Int = 2 val fn: Kont = res => if (res.length == 1) res.head else { val condBranches = res.reverse.map(_._1) val ctail = condBranches.tail val finalBranch = condBranches.head - (SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), env) + (SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), branchEnv) } } + /** + * One step in the CPS traversal of multiple branches. Partially applies the current branch's result to the + * branch producer (`bp`), and then initiates the certification of the next branch. A BranchProofProducer of arity N + * will have been fully applied after the N-th invocation of this FoldProofProducer. + * @param op a procedure that generates a KontResult + * @param item The start of the next branch to traverse + * @param bp the branch producer or its partially applied form + * @tparam T the unit of traversal in the algorithm + */ case class FoldProofProducer[T](op: (T, ProofProducer) => KontResult, item: T, bp: ProofProducer) extends ProofProducer { val arity: Int = 1 val fn: Kont = res => { val step = res.head._1 - // partially apply a produced step to the BranchProducer of the downstream `bp` + + // Partially apply a produced step to the BranchProducer of the downstream `bp` @scala.annotation.tailrec def isBase(curr: ProofProducer): Boolean = curr match { case PartiallyAppliedProofProducer(p, _) => isBase(p) case _: Branching => true case _ => false } + + // Find the BranchProducer in the `bp`, and then partially apply `step` def update(curr: ProofProducer): ProofProducer = curr match { case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => curr.partApply(step) case _ => curr } + + // Update the `bp` with the new result and step into the next branch op(item, update(bp)) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 225588c99..a6f241463 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -6,6 +6,8 @@ import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.Sentences._ object ProofSteps { + + // Generate right-associative destructing pattern for a tuple of Coq expressions def nestedDestructR(items: Seq[CExpr]): String = items match { case Seq(e1, e2, rest @ _*) => s"[${e1.pp} ${nestedDestructR(e2 +: rest)}]" @@ -15,6 +17,7 @@ object ProofSteps { "" } + // Generate left-associative destructing pattern for a tuple of Coq expressions def nestedDestructL(items: Seq[CExpr]): String = { def visit(items: Seq[CExpr]): String = items match { case Seq(e1, e2, rest @ _*) => @@ -30,9 +33,21 @@ object ProofSteps { sealed abstract class ProofStep { def pp: String = "" - def refreshVars(env: CEnvironment): ProofStep = this - - protected def buildExistentials(builder: StringBuilder, ex: Seq[CExpr], nested: Boolean = false): Unit = { + /** + * Retroactively refresh any ghost variables that were instantiated after this proof step was first generated + * + * @param sbst the certification environment + * @return the new proof step + */ + def refreshGhostVars(sbst: SubstVar): ProofStep = this + + /** + * Move a sequence of existential terms from goal to context + * @param builder the string builder + * @param ex the existential terms + * @param nested whether or not to format the existentials as tuples + */ + protected def elimExistentials(builder: StringBuilder, ex: Seq[CExpr], nested: Boolean = false): Unit = { if (ex.nonEmpty) { if (nested) { builder.append(s"move=>${nestedDestructL(ex)}.\n") @@ -42,7 +57,13 @@ object ProofSteps { } } - protected def buildHeapExpansion(builder: StringBuilder, asn: CAssertion, uniqueName: String): Unit = { + /** + * Given a precondition assertion, move all terms to the context + * @param builder the string builder + * @param asn the precondition assertion + * @param uniqueName a unique identifier + */ + protected def initPre(builder: StringBuilder, asn: CAssertion, uniqueName: String): Unit = { val phi = asn.phi val sigma = asn.sigma val phiName = s"phi_$uniqueName" @@ -65,36 +86,55 @@ object ProofSteps { } } - protected def existentialInstantiation(builder: StringBuilder, asn: CAssertion, vars: Seq[CExpr], unfoldings: Map[CSApp, CInductiveClause]): Unit = { - if (vars.nonEmpty) { - builder.append(s"exists ${vars.map(v => s"(${v.pp})").mkString(", ")};\n") + /** + * Execute the correct sequence of existential eliminations and constructor unfoldings needed to + * transform the goal into a given post condition assertion + * @param builder the string builder + * @param asn an assertion (or part of an assertion) in the post condition + * @param ex a sequence of existentials to eliminate + * @param unfoldings a map from predicate applications to their unfolded state + */ + protected def solvePost(builder: StringBuilder, asn: CAssertion, ex: Seq[CExpr], unfoldings: Unfoldings): Unit = { + // Eliminate value existentials + if (ex.nonEmpty) { + builder.append(s"exists ${ex.map(v => s"(${v.pp})").mkString(", ")};\n") } - def unfoldSAppExistentials(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { + /** + * Existentials for the current assertion need to be provided eagerly, so look ahead to find out + * the maximally unfolded, "flattened" form of the heap + * @param app the predicate app to flatten + * @return the points-to's and pred apps in the unfolded heap; any pred apps that remain have already been + * established as facts earlier on, so there is no need to unfold further + */ + def toUnfoldedHeap(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { case Some(a) => val sigma = a.asn.sigma - val (ptss, apps) = sigma.apps.map(unfoldSAppExistentials).unzip + val (ptss, apps) = sigma.apps.map(toUnfoldedHeap).unzip (sigma.ptss ++ ptss.flatten, apps.flatten) case None => (Seq.empty, Seq(app)) } + // Eliminate heap existentials (one for each predicate application in the assertion) val apps = asn.sigma.apps for (app <- apps) { - val (unfoldedPtss, unfoldedApps) = unfoldSAppExistentials(app) + val (unfoldedPtss, unfoldedApps) = toUnfoldedHeap(app) val h = CSFormula("", unfoldedApps, unfoldedPtss) builder.append(s"exists (${h.ppHeap});\n") } - // solve everything except sapps + // Solve everything except the predicate applications builder.append("ssl_emp_post.\n") + // For each predicate application, unfold the correct constructor and recursively solve its expanded form for { app <- apps + // If `app` isn't found in the unfoldings, its truth was already established earlier on c <- unfoldings.get(app) } { - builder.append(s"unfold_constructor ${c.idx + 1};\n") - existentialInstantiation(builder, c.asn, c.existentials, unfoldings) + builder.append(s"unfold_constructor ${c.idx};\n") + solvePost(builder, c.asn, c.existentials, unfoldings) } } @@ -109,8 +149,8 @@ object ProofSteps { case SeqCompStep(s1,s2) => val acc1 = collector(acc)(s1) collector(acc1)(s2) - case CallStep(goal, freshToActual, _) => - acc ++ goal.existentials ++ freshToActual.values.flatMap(_.vars) + case CallStep(goal, _) => + acc ++ goal.programVars ++ goal.universalGhosts ++ goal.existentials case _ => acc } @@ -119,9 +159,19 @@ object ProofSteps { } } + /** + * Sequentially compose two proof steps + * @param s1 the first step + * @param s2 the second step + */ case class SeqCompStep(s1: ProofStep, s2: ProofStep) extends ProofStep { override def pp: String = s"${s1.pp}${s2.pp}" + /** + * The synthesis removes some extraneous program statements that are unused in the final result, but + * the CertTree retains them as nodes. So, we remove them from our proof script here. + * @return A simplified proof step + */ def simplify: ProofStep = { (s1, s2) match { case (ReadStep(to, _), _) => simplifyBinding(to) @@ -139,103 +189,138 @@ object ProofSteps { } } + /** + * Perform a write + * @param to the variable to write to + * @param offset the variable offset + * @param e the expression to write + * @param frame whether or not to frame out the heaplet after writing + */ case class WriteStep(to: CVar, offset: Int, e: CExpr, frame: Boolean = true) extends ProofStep { - override def refreshVars(env: CEnvironment): WriteStep = - WriteStep(env.ghostSubst.getOrElse(to, to), offset, e.subst(env.ghostSubst), frame) + override def refreshGhostVars(sbst: SubstVar): WriteStep = + WriteStep(to.substVar(sbst), offset, e.subst(sbst), frame) override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" val writeStep = "ssl_write.\n" + + // SSL's `Write` rule does an implicit frame under normal circumstances, but not during a call synthesis val writePostStep = if (frame) s"ssl_write_post $ptr.\n" else "" + writeStep + writePostStep } } + /** + * Perform a read + * @param to the dst variable + * @param from the src variable + */ case class ReadStep(to: CVar, from: CVar) extends ProofStep { - override def refreshVars(env: CEnvironment): ReadStep = - ReadStep(env.ghostSubst.getOrElse(to, to), env.ghostSubst.getOrElse(from, from)) + override def refreshGhostVars(sbst: SubstVar): ReadStep = + ReadStep(to.substVar(sbst), from.substVar(sbst)) override def pp: String = "ssl_read.\n" } + /** + * Allocate a memory block of size `sz` and bind it to a variable + * @param to the dst variable + * @param tpe the alloc type + * @param sz the size allocated + */ case class AllocStep(to: CVar, tpe: HTTType, sz: Int) extends ProofStep { - override def refreshVars(env: CEnvironment): AllocStep = - AllocStep(env.ghostSubst.getOrElse(to, to), tpe, sz) + override def refreshGhostVars(sbst: SubstVar): AllocStep = + AllocStep(to.substVar(sbst), tpe, sz) override def pp: String = s"ssl_alloc ${to.pp}.\n" } + /** + * Free a memory block of size `sz` + * @param size the size of the block to free + */ case class FreeStep(size: Int) extends ProofStep { override def pp: String = { + // In HTT, each location offset needs to be freed individually val deallocStmts = (1 to size).map(_ => "ssl_dealloc.") s"${deallocStmts.mkString("\n")}\n" } } + /** + * Execute the Open rule + */ case object OpenStep extends ProofStep { override def pp: String = "ssl_open.\n" } - case class OpenPostStep(app: CSApp, pre: CAssertion, existentials: Seq[CExpr]) extends ProofStep { - override def refreshVars(env: CEnvironment): OpenPostStep = OpenPostStep( - app.subst(env.ghostSubst), - pre.subst(env.ghostSubst), - existentials.map(v => v.subst(env.ghostSubst)) + /** + * Perform clean-up tasks at the start of each branch generated by the Open rule + * @param app the predicate application used in the Open rule + * @param pre the precondition for the predicate's constructor that was used for this branch of the Open rule + * @param preEx the value existentials for this precondition + */ + case class OpenPostStep(app: CSApp, pre: CAssertion, preEx: Seq[CExpr]) extends ProofStep { + override def refreshGhostVars(sbst: SubstVar): OpenPostStep = OpenPostStep( + app.subst(sbst), + pre.subst(sbst), + preEx.map(v => v.subst(sbst)) ) override def pp: String = { val builder = new StringBuilder() builder.append(s"ssl_open_post ${app.hypName}.\n") - buildExistentials(builder, existentials) - buildExistentials(builder, pre.heapVars) - - buildHeapExpansion(builder, pre, app.uniqueName) + elimExistentials(builder, preEx) + elimExistentials(builder, pre.heapVars) + initPre(builder, pre, app.uniqueName) builder.toString() } } - case class CallStep(goal: CGoal, freshToActual: Map[CVar, CExpr], callId: String) extends ProofStep { - override def refreshVars(env: CEnvironment): CallStep = CallStep ( - goal.subst(env.ghostSubst), - freshToActual.mapValues(e => e.subst(env.ghostSubst).subst(env.subst)), - callId - ) + /** + * Call a function + * @param goal the goal of the call synthesis + * @param callId a unique call identifier + */ + case class CallStep(goal: CGoal, callId: String) extends ProofStep { + override def refreshGhostVars(sbst: SubstVar): CallStep = + CallStep(goal.subst(sbst), callId) override def pp: String = { val builder = new StringBuilder() - val refreshedPre = goal.pre.subst(freshToActual) - val refreshedPost = goal.post.subst(freshToActual) - val refreshedGhosts = goal.universalGhosts.map(_.subst(freshToActual)) - val callHeap = refreshedPre.sigma - - // put the part of the heap touched by the recursive call at the head - builder.append(s"ssl_call_pre (${callHeap.ppHeap}).\n") - - // provide universal ghosts and execute call - builder.append(s"ssl_call (${refreshedGhosts.map(_.pp).mkString(", ")}).\n") + // Move the part of the heap relevant to the call abduction to the beginning + builder.append(s"ssl_call_pre (${goal.pre.sigma.ppHeap}).\n") - // pre has no value existentials - existentialInstantiation(builder, refreshedPre, Seq.empty, Map.empty) + // Provide the necessary existentials so that the precondition of the call goal is met, + // and then execute the call + builder.append(s"ssl_call (${goal.universalGhosts.map(_.pp).mkString(", ")}).\n") + solvePost(builder, goal.pre, Seq.empty, Map.empty) builder.append(s"move=>h_$callId.\n") - buildExistentials(builder, goal.existentials) - buildExistentials(builder, refreshedPost.heapVars) - buildHeapExpansion(builder, refreshedPost, callId) + // The postcondition of the call abduction becomes the precondition of the companion + elimExistentials(builder, goal.existentials) + elimExistentials(builder, goal.post.heapVars) + initPre(builder, goal.post, callId) - // store validity hypotheses in context + // Store validity hypotheses in context builder.append("store_valid.\n") builder.toString() } } + /** + * Eliminate the program's ghost variables and move them to the proof context + * @param goal the goal + */ case class GhostElimStep(goal: CGoal) extends ProofStep { - override def refreshVars(env: CEnvironment): GhostElimStep = - GhostElimStep(goal.subst(env.ghostSubst)) + override def refreshGhostVars(sbst: SubstVar): GhostElimStep = + GhostElimStep(goal.subst(sbst)) override def pp: String = { val builder = new StringBuilder() @@ -243,9 +328,9 @@ object ProofSteps { // Pull out any precondition ghosts and move precondition heap to the context builder.append("ssl_ghostelim_pre.\n") - buildExistentials(builder, goal.universalGhosts, nested = true) - buildExistentials(builder, goal.pre.heapVars) - buildHeapExpansion(builder, goal.pre, "self") + elimExistentials(builder, goal.universalGhosts, nested = true) + elimExistentials(builder, goal.pre.heapVars) + initPre(builder, goal.pre, "self") // store heap validity assertions builder.append("ssl_ghostelim_post.\n") @@ -258,16 +343,17 @@ object ProofSteps { override def pp: String = "ssl_abduce_branch.\n" } - case class EmpStep(goal: CGoal, sub: Map[CVar, CExpr], unfoldings: Map[CSApp, CInductiveClause]) extends ProofStep { + /** + * At the end of a branch, transform the goal into the funspec's post-condition + * @param post the post-condition + * @param postEx the post-condition's value existentials + * @param unfoldings a map from predicate applications to their unfolded state + */ + case class EmpStep(post: CAssertion, postEx: Seq[CExpr], unfoldings: Unfoldings) extends ProofStep { override def pp: String = { val builder = new StringBuilder() builder.append("ssl_emp;\n") - - val post = goal.post.subst(sub) - val postEx = goal.existentials.map(_.subst(sub)) - val unfoldings1 = unfoldings.map { case (app, e) => app.subst(sub) -> e.subst(sub) } - existentialInstantiation(builder, post, postEx, unfoldings1) - + solvePost(builder, post, postEx, unfoldings) builder.toString() } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index d7b61f0ef..921997fd4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.{CFormals, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.targets.htt.logic.Proof.Subst object Sentences { case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { @@ -27,7 +28,7 @@ object Sentences { getIndent(depth) + builder.toString().replaceAll("\n", s"\n${getIndent(depth)}") } - def subst(sub: Map[CVar, CExpr]): CAssertion = + def subst(sub: Subst): CAssertion = CAssertion(phi.subst(sub), sigma.subst(sub)) val valueVars: Seq[CVar] = @@ -55,14 +56,14 @@ object Sentences { } case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { - def subst(sub: Map[CVar, CExpr]): CInductiveClause = + def subst(sub: Subst): CInductiveClause = CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) } case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends PrettyPrinting { val paramVars: Seq[CVar] = params.map(_._2) - def subst(sub: Map[CVar, CExpr]): CInductivePredicate = + def subst(sub: Subst): CInductivePredicate = CInductivePredicate(name, params.map(p => (p._1, p._2.substVar(sub))), clauses.map(_.subst(sub))) override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 120686fbc..19b630e76 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -122,6 +122,11 @@ object Statements { } case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement { + /** + * The synthesis removes some extraneous program statements that are unused in the final result, but + * the CertTree retains them as nodes. So, we remove them from our statements here. + * @return A simplified statement + */ def simplify: CStatement = (s1, s2) match { case (CGuarded(cond, b, eb), _) => CGuarded(cond, CSeqComp(b, s2).simplify, eb) case (CLoad(y, _, _, _), CGuarded(cond, _, _)) if cond.vars.contains(y) => this diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index ebd041ea9..cd40b62ac 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -8,14 +8,12 @@ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ object ProgramTranslation { - private case class TraversalItem(node: CertTree.Node) extends Traversable - - def translate(node: CertTree.Node, proc: Procedure): CStatement = { - val traversalItem = TraversalItem(node) - traverseStmt(traversalItem, IdCStmtProducer) + def translate(node: CertTree.Node, proc: Procedure): CProcedure = { + val stmtBody = traverseStmt(node, IdCStmtProducer) + CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), stmtBody) } - def traverseStmt(item: TraversalItem, kont: CStmtProducer): CStatement = { + def traverseStmt(node: CertTree.Node, kont: CStmtProducer): CStatement = { def translateOperation(s: Statement): CStatement = s match { case Skip => CSkip @@ -26,7 +24,7 @@ object ProgramTranslation { case Malloc(to, tpe, sz) => CMalloc(translateVar(to), translateType(tpe), sz) case Free(v) => - val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) + val block = node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) CFree(translateVar(v), block.get.sz) case Call(v, args, _) => @@ -56,10 +54,7 @@ object ProgramTranslation { IdCStmtProducer } - def generateNextItems: Seq[TraversalItem] = - item.node.children.map(n => TraversalItem(n)) - - def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: CStmtProducer): CStmtProducer = nextKont match { + def updateProducerPost(nextItems: Seq[CertTree.Node], nextKont: CStmtProducer): CStmtProducer = nextKont match { case _: Branching => nextItems.tail.foldLeft(nextKont >> kont) { case (foldedP, item) => FoldCStmtProducer(traverseStmt, item, foldedP) @@ -69,8 +64,8 @@ object ProgramTranslation { } // generated nested continuations for children - val p = translateProducer(item.node.kont).simplify - val nextItems = generateNextItems + val p = translateProducer(node.kont).simplify + val nextItems = node.children val nextKont = updateProducerPost(nextItems, p).simplify nextItems.headOption match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 000b16226..50d3bb8a8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,7 +1,6 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ @@ -10,7 +9,7 @@ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ object ProofTranslation { - private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) extends Traversable + private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) def translate(node: CertTree.Node, cenv: CEnvironment): Proof = { val initialGoal = translateGoal(node.goal) @@ -20,11 +19,14 @@ object ProofTranslation { Proof(proofBody, initialGoal.programVars) } - def traverseProof(item: TraversalItem, kont: ProofProducer): (ProofStep, CEnvironment) = { - def translateOperation(s: Statement, cenv: CEnvironment): (ProofStep, CEnvironment) = s match { + def traverseProof(item: TraversalItem, kont: ProofProducer): KontResult = { + def translateOperation(s: Statement, cenv: CEnvironment): KontResult = s match { case Skip => val goal = cenv.initialGoal.subst(cenv.ghostSubst) - (EmpStep(goal, cenv.subst, cenv.unfoldings), cenv) + val post = goal.post.subst(cenv.subst) + val postEx = goal.existentials.map(_.subst(cenv.subst)) + val unfoldings = cenv.unfoldings.map { case (app, c) => app.subst(cenv.subst) -> c.subst(cenv.subst) } + (EmpStep(post, postEx, unfoldings), cenv) case Load(to, _, from, _) => (ReadStep(translateVar(to), translateVar(from)), cenv) case Store(to, offset, e) => @@ -43,9 +45,9 @@ object ProofTranslation { // create call step val companionToFresh = callGoal.companionToFresh.map{ case (k, v) => translateVar(k) -> translateVar(v)} val freshToActual = callGoal.freshToActual.map{ case (k, v) => translateVar(k) -> translateExpr(v)} - val cgoal = cenv.initialGoal.subst(companionToFresh) + val cgoal = cenv.initialGoal.subst(companionToFresh).subst(freshToActual) - (CallStep(cgoal, freshToActual, freshCallId), cenv) + (CallStep(cgoal, freshCallId), cenv) case _ => throw TranslationException("Operation not supported") } @@ -92,7 +94,7 @@ object ProofTranslation { val sapp = translateSApp(item.node.footprint.pre.sigma.apps.head) val pred = cenv.predicates(sapp.pred) val subgoals = item.node.children.map(n => translateGoal(n.goal)) - val initialSub: Map[CVar, CVar] = Map.empty + val initialSub: SubstVar = Map.empty val sub = pred.clauses.zip(subgoals).foldLeft(initialSub){ case (acc, (c, g)) => acc ++ g.pre.sigma.unify(c.asn.sigma) } val actualPred = pred.subst(sub) val openPostSteps = actualPred.clauses.map(c => OpenPostStep(sapp, c.asn, c.existentials)) @@ -108,7 +110,7 @@ object ProofTranslation { item.node.children.map(node => TraversalItem(node, cenv)) } - // handle guard + // If at a branching point, queue up the traversal of each branch as nested calls of `traverseProof` in the continuation def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: ProofProducer, cenv: CEnvironment): ProofProducer = nextKont match { case _: Branching => nextItems.tail.foldLeft(nextKont >> kont) { @@ -118,7 +120,6 @@ object ProofTranslation { nextKont >> kont } - // generate nested continuations + environments for children val (p0, cenv1) = translateProducer(item.node.kont, item.cenv) val p = p0.simplify val nextItems = generateNextItems(p, cenv1) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index cc01383f6..00c869bd2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -15,10 +15,8 @@ import org.tygus.suslik.logic._ object Translation { case class TranslationException(msg: String) extends Exception(msg) - trait Traversable - /** - * Produces a HTT certificate from the tree of successful derivations and a synthesized procedure + * Produces the components of a HTT certificate, from the tree of successful derivations and a synthesized procedure * @param node the root of the derivation tree * @param proc the synthesized procedure * @param env the synthesis environment @@ -30,27 +28,26 @@ object Translation { val goal = translateGoal(node.goal) val initialCEnv = CEnvironment(goal, cpreds) val proof = ProofTranslation.translate(node, initialCEnv) - val stmtBody = ProgramTranslation.translate(node, proc) - - val cproc = CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), stmtBody) + val cproc = ProgramTranslation.translate(node, proc) (cpreds, goal.toFunspec, proof, cproc) } private def translateInductivePredicate(el: InductivePredicate): CInductivePredicate = { val cParams = el.params.map(translateParam) :+ (CHeapType, CVar("h")) - val cClauses = el.clauses.zipWithIndex.map { case (c, i) => translateClause(c, el.name, i, cParams) } + + val cClauses = el.clauses.zipWithIndex.map { case (c, idx) => + val selector = translateExpr(c.selector) + val asn = translateAsn(c.asn) + + // Include the clause number so that we can use Coq's `constructor n` tactic + CInductiveClause(el.name, idx + 1, selector, asn, asn.existentials(cParams.map(_._2))) + } CInductivePredicate(el.name, cParams, cClauses) } - private def translateParam(el: (Var, SSLType)): (HTTType, CVar) = + def translateParam(el: (Var, SSLType)): (HTTType, CVar) = (translateType(el._2), translateVar(el._1)) - private def translateClause(el: InductiveClause, pred: String, idx: Int, predParams: CFormals): CInductiveClause = { - val selector = translateExpr(el.selector) - val asn = translateAsn(el.asn) - CInductiveClause(pred, idx, selector, translateAsn(el.asn), asn.existentials(predParams.map(_._2))) - } - def translateType(el: SSLType): HTTType = el match { case BoolType => CBoolType case IntType => CNatType @@ -86,7 +83,7 @@ object Translation { def translatePointsTo(el: PointsTo): CPointsTo = CPointsTo(translateExpr(el.loc), el.offset, translateExpr(el.value)) def translateAsn(el: Assertion): CAssertion = { - val phi: CExpr = translateExpr(el.phi.toExpr).simplify + val phi = translateExpr(el.phi.toExpr).simplify val sigma = translateSFormula(el.sigma) CAssertion(phi, sigma) } diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index d3521acaa..6004d2096 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -13,18 +13,23 @@ import scala.sys.process._ */ class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { + // Create a temporary directory to store the generated certificates + // (The directory and its contents remain accessible after the test ends, but are eventually deleted) val certRoot: File = Files.createTempDirectory("suslik-").toFile override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = it(s"certifies that it $desc") { synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = HTT, certDest = certRoot)) val fname = testName.split('/').last - val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath - // verify + // Find the path to the certificated that was written out to the temp directory + val fileName = s"${fname.replace('-', '_')}.v" + val pathToCertificate = Paths.get(certRoot.getCanonicalPath, fileName).toFile.getCanonicalPath + + // Try compiling the ".v" file with coqc val result = s"coqc -vok -w none $pathToCertificate".! - // check that Coq compilation succeeded + // Check that Coq compilation succeeded with exit code 0 assert(result == 0) } From 33b24ded0c220ef9fb2be97eef83a4e6f5ee933b Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 7 Aug 2020 12:55:22 +0100 Subject: [PATCH 025/211] setup boilerplate for vst certification --- .../certification/CertificationTarget.scala | 7 +- .../certification/targets/vst/VST.scala | 90 +++++++++++++++++++ .../targets/vst/language/Expressions.scala | 25 ++++++ .../targets/vst/language/Types.scala | 26 ++++++ .../targets/vst/language/package.scala | 11 +++ .../targets/vst/logic/Proof.scala | 10 +++ .../targets/vst/logic/ProofSteps.scala | 9 ++ .../targets/vst/logic/Sentences.scala | 27 ++++++ .../targets/vst/program/Statements.scala | 17 ++++ .../targets/vst/translation/Translation.scala | 19 ++++ .../targets/vst/VSTCertificationTests.scala | 42 +++++++++ src/untitled.sc | 0 12 files changed, 281 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala create mode 100644 src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala create mode 100644 src/untitled.sc diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 75c90f108..1b412932e 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -4,10 +4,13 @@ import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment /** - * A generic interface for certification targets. The user can specify a target - * by setting the command-line flag `certTarget` to the target's `name` property. + * A generic interface for certification targets. + * The user can specify a target by setting the command-line flag `certTarget` to the target's `name` property. */ trait CertificationTarget { + /** uniquely identifies the certification target - users can specify + * this certification target from the CLI passing `name` via the + * `certTarget` flag */ val name: String val suffix: String def certify(proc: Procedure, env: Environment): Certificate diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala new file mode 100644 index 000000000..e1db51d76 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -0,0 +1,90 @@ +package org.tygus.suslik.certification.targets.vst + +import java.nio.file.Paths +import java.io.{File, PrintWriter} + +import org.tygus.suslik.certification.{Certificate, CertificationTarget} +import org.tygus.suslik.language.Statements +import org.tygus.suslik.language.Statements.Statement +import org.tygus.suslik.logic.{Environment, FunSpec, FunctionEnv, PredicateEnv, Preprocessor, Program} +import org.tygus.suslik.parsing.SSLParser +import org.tygus.suslik.synthesis.{SynConfig, SynthesisException, SynthesisRunner} +import org.tygus.suslik.util.SynStats +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException + +object VST extends CertificationTarget { + override val name: String = "VST" + override val suffix: String = ".v" + + /** prelude for Coq file */ + private def coq_prelude (fun_name: String) = s""" +Require Import VST.floyd.proofauto. +Require Import $fun_name. +Instance CompSpecs : compspecs. make_compspecs prog. Defined. +Definition Vprog : varspecs. mk_varspecs prog. Defined. + +""" + + override def certify(proc: Statements.Procedure, env: Environment): Certificate = { + // retrieve the search tree + val root = + CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) + val funspec = proc.f + val funbody = proc.body + val fun_name : String = funspec.name + + val builder = new StringBuilder + // append the coq prelude + builder.append(coq_prelude(fun_name)) + val (preds, spec, proof, cproc) = Translation.translate(root, proc, env) + + ??? + } + + def main(args: Array[String]) : Unit = { + val certDest = "/tmp/cert-test" + val certTarget = VST + val parser = new SSLParser + + val res = parser.parseGoalSYN( + "{ x :-> a ** y :-> b } void swap(loc x, loc y) { x :-> b ** y :-> a }" + ) + val prog = res.get + + val (specs, predEnv, funcEnv, body) = + Preprocessor.preprocessProgram(prog, new SynConfig) + + val spec = specs.head + + val config = (new SynConfig).copy(certTarget = VST) + + val env = Environment(predEnv, funcEnv, config, new SynStats(120000)) + + val synthesizer = SynthesisRunner.createSynthesizer(env) + val sresult = synthesizer.synthesizeProc(spec, env, body) + + + println("Root:") + println(CertTree.pp()) + + sresult._1 match { + case Nil => throw SynthesisException(s"Failed to synthesize:\n$sresult") + + case procs => + + print("hello?\n") + print(procs.head) + print("\nhello?") + + + val targetName = certTarget.name + val certificate = certTarget.certify(procs.head, env) + println(s"synthesized:\n${certificate.body}") + } + + } + + +} + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala new file mode 100644 index 000000000..7575afe53 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala @@ -0,0 +1,25 @@ +package org.tygus.suslik.certification.targets.vst.language + +import org.tygus.suslik.language.PrettyPrinting + +object Expressions { + + sealed abstract class CExpr extends PrettyPrinting { + + } + + case class CVar(name: String) extends CExpr { + } + + case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends CExpr { + + } + + case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { + + } + + case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala new file mode 100644 index 000000000..8a1b90733 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala @@ -0,0 +1,26 @@ +package org.tygus.suslik.certification.targets.vst.language + +import org.tygus.suslik.language.PrettyPrinting + +object Types { + + sealed abstract class VSTType extends PrettyPrinting + + case object CBoolType extends VSTType { + override def pp: String = "tbool" + } + + case object CNatType extends VSTType { + override def pp: String = "tint" + } + + case class CPtrType(of:VSTType) extends VSTType { + override def pp: String = s"(tptr ${of.pp})" + } + + case object CUnitType extends VSTType { + override def pp: String = "tvoid" + } + + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala new file mode 100644 index 000000000..a81b8aff0 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala @@ -0,0 +1,11 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.language.Types.VSTType + +package object language { + /** Typing context - maps variables to types */ + type CGamma = Map[CVar, VSTType] + /** Formal parameter specification - types and names of parameters */ + type CFormals = Seq[(VSTType, CVar)] +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala new file mode 100644 index 000000000..ca4d6a366 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -0,0 +1,10 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.ProofStep + +object Proof { + + case class Proof(root: ProofStep, params: Seq[CVar]) { + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala new file mode 100644 index 000000000..064a987ce --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala @@ -0,0 +1,9 @@ +package org.tygus.suslik.certification.targets.vst.logic + +object ProofSteps { + + sealed abstract class ProofStep { + def pp: String = "" + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala new file mode 100644 index 000000000..b336a5084 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala @@ -0,0 +1,27 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +import org.tygus.suslik.certification.targets.vst.language.Expressions.{ +CExpr, CSFormula +} +import org.tygus.suslik.certification.targets.vst.language.CFormals +import org.tygus.suslik.language.PrettyPrinting + +object Sentences { + case class CInductivePredicate( + pred: String, idx: Int, selector: CExpr, + asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { + + } + + case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { + + } + + case class CFunSpec( + name: String, rType: VSTType, + params: CFormals, ghostParams: CFormals, + pre: CAssertion, post: CAssertion) extends PrettyPrinting { + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala new file mode 100644 index 000000000..6eda32d0d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala @@ -0,0 +1,17 @@ +package org.tygus.suslik.certification.targets.vst.program + +import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar +import org.tygus.suslik.language.PrettyPrinting + +object Statements { + + sealed abstract class CStatement extends PrettyPrinting { + } + + case class CProcedure( + name: String, tp: VSTType, formals: Seq[(VSTType, CVar)], body: CStatement) extends + CStatement { + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala new file mode 100644 index 000000000..52294ec12 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -0,0 +1,19 @@ +package org.tygus.suslik.certification.targets.vst.translation + + + +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.language.Statements.Procedure +import org.tygus.suslik.logic.Environment +import org.tygus.suslik.certification.targets.vst.logic.Sentences.CInductivePredicate +import org.tygus.suslik.certification.targets.vst.logic.Sentences.CFunSpec +import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof +import org.tygus.suslik.certification.targets.vst.program.Statements.CProcedure + +object Translation { + + def translate(root:CertTree.Node, proc: Procedure, env: Environment) : + (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedure) = { + ??? + } +} diff --git a/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala new file mode 100644 index 000000000..b820720bc --- /dev/null +++ b/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala @@ -0,0 +1,42 @@ +package org.tygus.suslik.certification.targets.vst + +import java.io.File +import java.nio.file.{Files, Paths} + +import org.scalatest.{FunSpec, Matchers} +import org.tygus.suslik.certification.targets.vst.VST +import org.tygus.suslik.synthesis.{SynConfig, SynthesisRunnerUtil, defaultConfig} + +import scala.sys.process._ + + +class VSTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { + val certRoot: File = Files.createTempDirectory("suslik-").toFile + + override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = + it(s"certifies that it $desc") { + synthesizeFromSpec( + testName, in, out, + params.copy( + assertSuccess = false, certTarget = VST, + certDest = certRoot + )) + val fname = testName.split('/').last + val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath + + // verify + val result = s"coqc -vok $pathToCertificate".! + + // check that Coq compilation succeeded + assert(result == 0) + } + + describe("SL-based synthesizer with certification") { +// runAllTestsFromDir("certification/ints") + runAllTestsFromDir("certification/list") +// runAllTestsFromDir("certification/tree") +// runAllTestsFromDir("certification/sll") + } + + +} diff --git a/src/untitled.sc b/src/untitled.sc new file mode 100644 index 000000000..e69de29bb From e639d9a2aa503e52fea4e366e06ee1b6aa956710 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 10 Aug 2020 11:18:28 +0100 Subject: [PATCH 026/211] implemented conversion to C-format --- .../certification/targets/vst/VST.scala | 20 +- .../targets/vst/language/Expressions.scala | 114 +++++- .../targets/vst/language/PrettyPrinting.scala | 7 + .../targets/vst/language/Statements.scala | 114 ++++++ .../targets/vst/language/Types.scala | 64 ++- .../targets/vst/language/package.scala | 8 +- .../targets/vst/logic/Formulae.scala | 59 +++ .../targets/vst/logic/Proof.scala | 14 + .../targets/vst/logic/Sentences.scala | 27 -- .../targets/vst/program/Statements.scala | 17 - .../targets/vst/translation/Translation.scala | 382 +++++++++++++++++- 11 files changed, 743 insertions(+), 83 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index e1db51d76..6b4f4f6ba 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -12,6 +12,7 @@ import org.tygus.suslik.synthesis.{SynConfig, SynthesisException, SynthesisRunne import org.tygus.suslik.util.SynStats import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException +import org.tygus.suslik.certification.targets.vst.translation.Translation object VST extends CertificationTarget { override val name: String = "VST" @@ -30,13 +31,15 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. // retrieve the search tree val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val funspec = proc.f - val funbody = proc.body - val fun_name : String = funspec.name + val fun_name : String = proc.f.name val builder = new StringBuilder // append the coq prelude builder.append(coq_prelude(fun_name)) + + + val c_prelude ="""#include \n\nextern void free(void *p);\n\n""" + val (preds, spec, proof, cproc) = Translation.translate(root, proc, env) ??? @@ -64,22 +67,15 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. val synthesizer = SynthesisRunner.createSynthesizer(env) val sresult = synthesizer.synthesizeProc(spec, env, body) - - println("Root:") - println(CertTree.pp()) - sresult._1 match { case Nil => throw SynthesisException(s"Failed to synthesize:\n$sresult") case procs => - print("hello?\n") - print(procs.head) - print("\nhello?") - - val targetName = certTarget.name + val certificate = certTarget.certify(procs.head, env) + println(s"synthesized:\n${certificate.body}") } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala index 7575afe53..8408607f2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala @@ -1,25 +1,125 @@ package org.tygus.suslik.certification.targets.vst.language -import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.certification.targets.vst.language.Types.{CBoolType, CIntType, VSTType} +import org.tygus.suslik.language.Expressions.SubstVar +/** Encodes VST specific C expressions */ object Expressions { - sealed abstract class CExpr extends PrettyPrinting { + sealed abstract class CExpr extends PrettyPrinting { } + /** VST specific Encoding of variables */ + case class CVar(name: String) extends CExpr { + override def pp: String = s"${name}" } - case class CVar(name: String) extends CExpr { + case class CBoolConst(value: Boolean) extends CExpr { + override def pp: String = value.toString + } + + case class CNatConst(value: Int) extends CExpr { + override def pp: String = value.toString + } + + case class CSetLiteral(elems: List[CExpr]) extends CExpr { + } + + case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { + override def pp: String = + s"(${cond.pp} ? ${left.pp} : ${right.pp})" + } + + case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { + override def pp: String = + op match { + case COpBoolEq => s"(${left.pp} == ${right.pp})" + case COpEq => s"(${left.pp} == ${right.pp})" + case COpLt => s"(${left.pp} < ${right.pp})" + case COpLeq => s"(${left.pp} <= ${right.pp})" + case COpOr => s"${left.pp} || ${right.pp}" + case COpAnd => s"${left.pp} && ${right.pp}" + case COpPlus => s"(${left.pp} + ${right.pp})" + case COpMinus => s"(${left.pp} - ${right.pp})" + case COpMultiply => s"(${left.pp} * ${right.pp})" + } + } + + case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { + override def pp: String = + op match { + case COpNot => s"!(${e.pp})" + case COpUnaryMinus => s"-(${e.pp})" + } + } + + sealed abstract class CUnOp extends PrettyPrinting + + object COpNot extends CUnOp { + override def pp: String = "~~" + } + + object COpUnaryMinus extends CUnOp + + sealed abstract class CBinOp extends PrettyPrinting + + object COpImplication extends CBinOp { + override def pp: String = "->" + } + + object COpPlus extends CBinOp { + override def pp: String = "+" + } + + object COpMinus extends CBinOp { + override def pp: String = "-" + } + + object COpMultiply extends CBinOp { + override def pp: String = "*" + } + + object COpEq extends CBinOp { + override def pp: String = "==" + } + + object COpBoolEq extends CBinOp { + override def pp: String = "=" + } + + object COpLeq extends CBinOp { + override def pp: String = "<=" + } + + object COpLt extends CBinOp { + override def pp: String = "<" + } + + object COpAnd extends CBinOp { + override def pp: String = "/\\" } - case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends CExpr { + object COpOr extends CBinOp { + override def pp: String = "\\/" + } + + object COpUnion extends CBinOp { + override def pp: String = "++" + } + object COpDiff extends CBinOp { + override def pp: String = "--" } - case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { + object COpIn extends CBinOp + object COpSetEq extends CBinOp { + override def pp: String = "=" } - case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { + object COpSubset extends CBinOp { + override def pp: String = "<=" } - + + object COpIntersect extends CBinOp + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala new file mode 100644 index 000000000..e0e884849 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.targets.vst.language + +trait PrettyPrinting { + def pp: String = toString + def ppIndent(depth: Int) = s"${getIndent(depth)}${pp.replaceAll("\n", s"\n${getIndent(depth)}")}" + def getIndent(depth: Int): String = " " * depth +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala new file mode 100644 index 000000000..55377bd84 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala @@ -0,0 +1,114 @@ +package org.tygus.suslik.certification.targets.vst.program + +import org.tygus.suslik.certification.targets.vst.language.Types.{VSTType} +import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.language.{PrettyPrinting, Types} +import org.tygus.suslik.logic.Specifications.GoalLabel + +object Statements { + + /** pretty printing a VST C Statement returns a C embedding */ + sealed abstract class CStatement extends PrettyPrinting { } + + // skip + case object CSkip extends CStatement { + override def pp: String = { + "return;" + } + } + + // ?? + case object CHole extends CStatement { + override def pp: String = { ??? } + } + + // assert false + case object CError extends CStatement { + override def pp: String = ??? + } + + // let to = malloc(n) + case class CMalloc(to: CVar, tpe: VSTType, sz: Int = 1) extends CStatement { + override def pp: String = + s"${tpe.pp} ${to.pp} = (${tpe.pp})malloc(${sz.toString} * sizeof(${tpe.pp}));" + } + + // free(v) + case class CFree(v: CVar) extends CStatement { + override def pp: String = s"free(${v.pp});" + } + + /** encoding of a load operation + * let to = *from.offset + * */ + case class CLoad(to: CVar, elem_ty: VSTType, from: CVar, + offset: Int = 0) extends CStatement { + override def pp: String = + if (offset == 0) { + s"${elem_ty.pp} ${to.pp} = *(${elem_ty.pp}*)${from.pp};" + }else { + s"${elem_ty.pp} ${to.pp} = *((${elem_ty.pp}*)${from.pp} + ${offset.toString});" + } + } + + /** Encoding of a store operation + * *to.offset = e */ + case class CStore(to: CVar, elem_ty: VSTType, offset: Int, e: CExpr) extends CStatement { + override def pp: String = { + val cast = s"(${elem_ty.pp})" + val ptr = s"(${elem_ty.pp}*)${to.pp}" + if (offset == 0) { + s"*${ptr} = ${cast} ${e.pp};" + } else { + s"*(${ptr} + ${offset.toString}) = ${cast} ${e.pp};" + } + } + } + + /** encoding of a function call f(args) */ + case class CCall(fun: CVar, args: Seq[CExpr], companion: Option[GoalLabel]) extends CStatement { + override def pp: String = s"${fun.pp}(${args.map(_.pp).mkString(", ")});" + } + + /** Encoding of sequential composition + * + * s1; s2 */ + case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement { + override def ppIndent(depth: Int): String = + s"${s1.ppIndent(depth)}\n${s2.ppIndent(depth)}" + } + + /** Encoding of statement + * if (cond) { tb } else { eb } + * */ + case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement { + override def ppIndent(depth: Int): String = + s"${getIndent(depth)}if(${cond.pp}) {\n${tb.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${eb.ppIndent(depth+1)}\n${getIndent(depth)}}\n" + } + + /** + * Encoding of a statement: + * assume cond { body } else { els } + * */ + case class CGuarded(cond: CExpr, body: CStatement, els: CStatement, branchPoint: GoalLabel) extends CStatement { + override def ppIndent(depth: Int): String = + s"${getIndent(depth)}if(${cond.pp}) {\n${body.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${els.ppIndent(depth+1)}\n${getIndent(depth)}}\n" + } + + /** Definition of a CProcedure */ + case class CProcedureDefinition( + name: String, rt: VSTType, params: Seq[(CVar, VSTType)], body: CStatement) extends PrettyPrinting { + override def pp: String = { + val body_string = body.ppIndent(1) + val function_def = + s"${rt.pp} ${name}(${ + params.map({case (variable_name, variable_ty) => + s"${variable_ty.pp} ${variable_name.pp}" + }).mkString(", ") + }) {\n${body_string}\n}\n" + + function_def + } + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala index 8a1b90733..5beff8f01 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala @@ -1,26 +1,68 @@ package org.tygus.suslik.certification.targets.vst.language -import org.tygus.suslik.language.PrettyPrinting - object Types { - sealed abstract class VSTType extends PrettyPrinting - case object CBoolType extends VSTType { - override def pp: String = "tbool" + sealed trait AnalysisVSTTypes extends PrettyPrinting { + + /** the Analysis VST type encodes more precise information about pointers + * than is stored in a VST proof - after the analysis, we can drop this + * additional information */ + def downcast : VSTType = this match { + case tType: VSTType => tType + case _: PtrType => CVoidPtrType + case pureType: PureType =>pureType.asInstanceOf[VSTType] + } + } + + /** types used in a VST program */ + sealed abstract class VSTType extends PrettyPrinting with AnalysisVSTTypes + sealed trait PtrType extends AnalysisVSTTypes + sealed trait PureType extends AnalysisVSTTypes + + case object CBoolType extends VSTType with PureType { + override def pp: String = "bool" + } + + case object CIntType extends VSTType with PureType { + override def pp: String = "int" } - case object CNatType extends VSTType { - override def pp: String = "tint" + case object CUnitType extends VSTType with PureType { + override def pp: String = "void" } - case class CPtrType(of:VSTType) extends VSTType { - override def pp: String = s"(tptr ${of.pp})" + /** encoding of void pointer types */ + case object CVoidPtrType extends VSTType with PureType { + override def pp: String = s"void *" } - case object CUnitType extends VSTType { - override def pp: String = "tvoid" + // the following types are only used in the program analysis, so don't form part of the + // VSTType hierarchy, but can be used to track additional information + + /** encoding of void ** pointer types */ + case object CVoidPtrPtrType extends PtrType with PrettyPrinting { + override def pp: String = s"void **" + } + /** encoding of int* pointer types */ + case object CIntPtrType extends PtrType with PrettyPrinting { + override def pp: String = s"int *" + } + /** encoding of bool* pointer types */ + case object CBoolPtrType extends PtrType with PrettyPrinting { + override def pp: String = s"bool *" } + def deref_type(ptrType: PtrType) = ptrType match { + case CVoidPtrPtrType => CVoidPtrType + case CIntPtrType => CIntType + case CBoolPtrType => CBoolType + } + def ptr_type(elem_type: PureType) = elem_type match { + case CBoolType => CBoolPtrType + case CIntType => CIntPtrType + case CUnitType => CVoidPtrType + case CVoidPtrType => CVoidPtrPtrType + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala index a81b8aff0..835bc0ce9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala @@ -1,11 +1,17 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +/** + * package encodes a wrapper around VST terms */ package object language { /** Typing context - maps variables to types */ type CGamma = Map[CVar, VSTType] /** Formal parameter specification - types and names of parameters */ type CFormals = Seq[(VSTType, CVar)] + + /** type of mapping in context */ + type CtxMapping = (CExpr, VSTType) + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala new file mode 100644 index 000000000..bb7e1cb46 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -0,0 +1,59 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.language.CFormals +import org.tygus.suslik.certification.targets.vst.logic.Proof.VSTPredicate +import org.tygus.suslik.language.Expressions.Expr +import org.tygus.suslik.language.PrettyPrinting + +import scala.collection.SortedSet + +object Formulae { + + /** abstract type containing all logical formulae */ + sealed trait VSTFormula + + /** abstract type categorizing all spatial formulae */ + sealed trait VSTHeaplet + + + /** Spatial formulae + * =========================================== + * */ + + /** spatial formuala indicating points to - loc + offset :-> value */ + case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends VSTHeaplet { } + + /** spatial formula indicating block allocation [loc; sz] */ + case class CBlock(loc: CExpr, sz: Int) extends VSTHeaplet {} + + /** predicate application + * + * @param pred predicate name + * @param args arguments + * @param card cardinality of call + **/ + case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends VSTHeaplet {} + + + /** Spatial Formula + * @param apps applications in the spatial formula + * */ + case class CSFormula(apps: Seq[CSApp], ptss: Seq[CPointsTo], blocks: Seq[CBlock]) extends VSTFormula { + override def productIterator: Iterator[VSTHeaplet] = (apps.iterator ++ ptss.iterator ++ blocks.iterator) + } + + case class CInductivePredicate(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { + } + + case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { + } + + case class CFunSpec( + name: String, rType: VSTType, + params: CFormals, ghostParams: CFormals, + pre: CAssertion, post: CAssertion) extends PrettyPrinting { + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index ca4d6a366..3115db7a8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -2,9 +2,23 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.ProofStep +import org.tygus.suslik.language.Ident object Proof { + + case class VSTClause() { + + } + case class VSTPredicate( + predicate_name: Ident, + params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.language.Types.VSTType)], + + ) { + + } + case class Proof(root: ProofStep, params: Seq[CVar]) { } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala deleted file mode 100644 index b336a5084..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Sentences.scala +++ /dev/null @@ -1,27 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.logic - -import org.tygus.suslik.certification.targets.vst.language.Types.VSTType -import org.tygus.suslik.certification.targets.vst.language.Expressions.{ -CExpr, CSFormula -} -import org.tygus.suslik.certification.targets.vst.language.CFormals -import org.tygus.suslik.language.PrettyPrinting - -object Sentences { - case class CInductivePredicate( - pred: String, idx: Int, selector: CExpr, - asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { - - } - - case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { - - } - - case class CFunSpec( - name: String, rType: VSTType, - params: CFormals, ghostParams: CFormals, - pre: CAssertion, post: CAssertion) extends PrettyPrinting { - } - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala deleted file mode 100644 index 6eda32d0d..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/program/Statements.scala +++ /dev/null @@ -1,17 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.program - -import org.tygus.suslik.certification.targets.vst.language.Types.VSTType -import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar -import org.tygus.suslik.language.PrettyPrinting - -object Statements { - - sealed abstract class CStatement extends PrettyPrinting { - } - - case class CProcedure( - name: String, tp: VSTType, formals: Seq[(VSTType, CVar)], body: CStatement) extends - CStatement { - } - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 52294ec12..fcbf5764b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -1,19 +1,385 @@ package org.tygus.suslik.certification.targets.vst.translation - import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.language.Statements.Procedure -import org.tygus.suslik.logic.Environment -import org.tygus.suslik.certification.targets.vst.logic.Sentences.CInductivePredicate -import org.tygus.suslik.certification.targets.vst.logic.Sentences.CFunSpec +import org.tygus.suslik.certification.targets.vst.language.{Expressions, PrettyPrinting, Types} +import org.tygus.suslik.certification.targets.vst.language.Expressions.{CBinOp, CBinaryExpr, CBoolConst, CExpr, CIfThenElse, CNatConst, COpAnd, COpBoolEq, COpDiff, COpEq, COpImplication, COpIn, COpIntersect, COpLeq, COpLt, COpMinus, COpMultiply, COpNot, COpOr, COpPlus, COpSetEq, COpSubset, COpUnaryMinus, COpUnion, CSetLiteral, CUnOp, CUnaryExpr, CVar} +import org.tygus.suslik.certification.targets.vst.language.Types.{AnalysisVSTTypes, CBoolType, CIntType, CUnitType, CVoidPtrPtrType, CVoidPtrType, PtrType, PureType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof -import org.tygus.suslik.certification.targets.vst.program.Statements.CProcedure +import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CFunSpec, CInductivePredicate} +import org.tygus.suslik.certification.targets.vst.program.Statements.{CCall, CError, CFree, CGuarded, CHole, CIf, CLoad, CMalloc, CProcedureDefinition, CSeqComp, CSkip, CStatement, CStore} +import org.tygus.suslik.language.Expressions.{BinOp, BinaryExpr, BoolConst, Expr, IfThenElse, IntConst, OpAnd, OpBoolEq, OpDiff, OpEq, OpGeq, OpGt, OpImplication, OpIn, OpIntersect, OpLeq, OpLt, OpMinus, OpMultiply, OpNot, OpNotEqual, OpOr, OpOverloadedEq, OpOverloadedLeq, OpOverloadedMinus, OpOverloadedPlus, OpOverloadedStar, OpPlus, OpSetEq, OpSubset, OpUnaryMinus, OpUnion, OverloadedBinOp, OverloadedBinaryExpr, SetLiteral, UnOp, UnaryExpr, Var} +import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntType, LocType, SSLType, Statements, VoidType} +import org.tygus.suslik.language.Statements.{Procedure, Statement} +import org.tygus.suslik.logic.Specifications.Assertion +import org.tygus.suslik.logic.{Environment, FunSpec, InductiveClause, InductivePredicate, PFormula, PredicateEnv, SFormula} + +import scala.collection.immutable object Translation { - def translate(root:CertTree.Node, proc: Procedure, env: Environment) : - (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedure) = { + case class TranslationException(msg: String) extends Exception(msg) + + + def pp_tuple(value: (PrettyPrinting, PrettyPrinting)) = + value match {case (v1:PrettyPrinting,v2:PrettyPrinting) => s"${v1.pp} :-> ${v2.pp}"} + + def translate_unary_op(op: UnOp) = op match { + case OpNot => COpNot + case OpUnaryMinus => COpUnaryMinus + } + + def translate_unary_expr(e1: UnaryExpr): CExpr = + CUnaryExpr(translate_unary_op(e1.op), translate_expression(e1.arg)) + + + /** translates a variable to C Expression type */ + def translate_variable(param_name: Var) = param_name match { + case Var(name) => CVar(name) + } + + def translate_binary_op(op: BinOp): CBinOp = op match { + case OpImplication => COpImplication + case OpPlus => COpPlus + case OpMinus => COpMinus + case OpMultiply => COpMultiply + case OpEq => COpEq + case OpBoolEq => COpBoolEq + case OpLeq => COpLeq + case OpLt => COpLt + case OpAnd => COpAnd + case OpOr => COpOr + case OpUnion => COpUnion + case OpDiff => COpDiff + case OpIn => COpIn + case OpSetEq => COpSetEq + case OpSubset => COpSubset + case OpIntersect => COpIntersect + } + + def translate_binary_expr(op: BinOp, e1: Expr, e2: Expr): CExpr = { + CBinaryExpr(translate_binary_op(op), translate_expression(e1), translate_expression(e2)) + } + + + def translate_overloaded_binary_expr(expr: OverloadedBinaryExpr): CExpr = expr match { + case OverloadedBinaryExpr(overloaded_op, left, right) => + val tleft = translate_expression(left) + val tright = translate_expression(right) + overloaded_op match { + case op: BinOp => CBinaryExpr(translate_binary_op(op), tleft, tright) + case OpOverloadedEq => CBinaryExpr(COpEq, tleft, tright) + case OpNotEqual => + CUnaryExpr(COpNot, CBinaryExpr(COpEq, tleft, tright)) + case OpGt => + CUnaryExpr(COpNot, CBinaryExpr(COpLeq, tleft, tright)) + case OpGeq => + CUnaryExpr(COpNot, CBinaryExpr(COpLt, tleft, tright)) + case OpOverloadedPlus => + CBinaryExpr(COpPlus, tleft, tright) + case OpOverloadedMinus => + CBinaryExpr(COpMinus, tleft, tright) + case OpOverloadedLeq => + CBinaryExpr(COpLeq, tleft, tright) + case OpOverloadedStar => ??? + } + } + + /** translate a Suslik expression into VST specific encoding */ + def translate_expression(expr: Expr): CExpr = expr match { + case Var(name) => CVar(name) + case BoolConst(value) => CBoolConst(value) + case IntConst(value) => CNatConst(value) + case e1@UnaryExpr(_, _) => translate_unary_expr(e1) + case BinaryExpr(op, e1, e2) => translate_binary_expr(op, e1, e2) + case SetLiteral(elems) => CSetLiteral(elems.map(e => translate_expression(e))) + case IfThenElse(c, t, e) => CIfThenElse(translate_expression(c), translate_expression(t), translate_expression(e)) + case expr@OverloadedBinaryExpr(_, _, _) => + translate_overloaded_binary_expr(expr) + case v => throw TranslationException(s"Operation ${v.toString} not supported") + } + + + /** translate type to C Type */ + def translate_type(lType: SSLType): VSTType = { + lType match { + case IntType => CIntType + case BoolType => CBoolType + case LocType => CVoidPtrType + case VoidType => CUnitType + case v => throw TranslationException(s"translating ${v} not implemeneted") + } + + } + + def translate_clause(clause: InductiveClause) = clause match { + case InductiveClause(selector: Expr, asn@Assertion(phi: PFormula, sigma: SFormula)) => { + val c_selector = translate_expression(selector) + + ??? + } + } + + def translate_predicate(predicate: InductivePredicate) = predicate match { + case (predicate@InductivePredicate(predicate_name, predicate_params, predicate_clauses: Seq[InductiveClause])) => + val params = predicate_params map { case (raw_variable, ty) => + val variable = translate_variable(raw_variable) + val tty = translate_type(ty) + val clauses = predicate_clauses map translate_clause + (variable, tty) + } + + val clauses = predicate_clauses map { case InductiveClause( + selector: Expr, asn@Assertion(phi: PFormula, sigma: SFormula) + ) => + ??? + } + (predicate_name, params, clauses) + } + + + def print_as_c_program(f: FunSpec, body: CStatement, ctx: Map[CVar, AnalysisVSTTypes]): String = { + + val c_prelude ="#include \n\nextern void free(void *p);\n\n" + + val return_type = translate_type(f.rType) + val body_string = body.ppIndent(1) + val function_def = + s"${c_prelude}${return_type.pp} ${f.name}(${ + f.params.map({case (variable_name, _) => + s"${ctx(translate_variable(variable_name)).downcast.pp} ${translate_variable(variable_name).pp}" + }).mkString(", ") + }) {\n${body_string}\n}\n" + function_def + } + def translate_procedure(f: FunSpec, body: CStatement, ctx: Map[CVar, AnalysisVSTTypes]): + CProcedureDefinition = { + val rt = translate_type(f.rType) + val params = f.params.map({case (variable_name, _) => + (translate_variable(variable_name), ctx(translate_variable(variable_name)).downcast) + }) + val name = f.name + CProcedureDefinition(name, rt, params, body) + } + + + def translate(root: CertTree.Node, proc: Procedure, env: Environment): + (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedureDefinition) = { + // translate body into VST types, and build context of variables + var (body, ctx) = translate_body(proc.f, proc.body) + // use this to build a C-encoding of the synthesized function + val function_definition = translate_procedure(proc.f, body, ctx) + + // translate predicate to C format + + + var body_string = print_as_c_program(proc.f, body, ctx) + + print(body_string) + + + // translate predicates + // translate proof ??? } + + def translate_body(funspec: FunSpec, stmt: Statement) = { + // build an initial context from the parameters + var context = funspec.params.map({ + case (variable, ssl_type) => (translate_variable(variable), translate_type(ssl_type).asInstanceOf[AnalysisVSTTypes]) + }).toMap + + // returns the most precise type of the two provided types + def unify(ty1: AnalysisVSTTypes, ty2: AnalysisVSTTypes): AnalysisVSTTypes = (ty1, ty2) match { + case (_, CUnitType) => CUnitType + case (CUnitType, _) => CUnitType + case (_, CBoolType) => CBoolType + case (CBoolType, _) => CBoolType + case (_, CIntType) => CIntType + case (CIntType, _) => CIntType + case (ot, CVoidPtrType) => ot + case (CVoidPtrType, ot) => ot + case (CVoidPtrPtrType, ot:PtrType) => ot + case (ot:PtrType, CVoidPtrPtrType) => ot + case (v1, v2) => + if (v1 == v2) + v1 + else + throw TranslationException(s"Can not unify type (${v1.pp}) with (${v2.pp})") + } + + // retrieves the VST Type of the current expression + def type_expression(expr: CExpr): AnalysisVSTTypes = expr match { + case value@CVar(_) => context(value) + case CBoolConst(value) => CBoolType + case CNatConst(value) => CIntType + case CIfThenElse(cond, left, right) => + val left_ty = type_expression(left) + val right_ty = type_expression(right) + unify(left_ty, right_ty) + case CBinaryExpr(op, left, right) => + op match { + case COpImplication => CBoolType + case COpPlus => CIntType + case COpMinus => CIntType + case COpMultiply => CIntType + case COpEq => CBoolType + case COpBoolEq => CBoolType + case COpLeq => CBoolType + case COpLt => CBoolType + case COpAnd => CBoolType + case COpOr => CBoolType + case COpIn => CBoolType + case _ => throw TranslationException("unsupported binary operand in function body") + } + case CUnaryExpr(op, e) => op match { + case COpNot => CBoolType + case COpUnaryMinus => CIntType + } + case CSetLiteral(elems) => throw TranslationException("unsupported expression in function body") + } + + def debug() = { + print("context = {\n"+context.map({case (variable, ty) => s"\t${variable.pp}: ${ty.pp}\n"}) + "}\n") + } + // a very light abstract interpretation of the suslik program to convert types to + // C-like types + // should reach fixpoint in two iterations + def eval_statement(stmt: Statement): Unit = + stmt match { + // T to = malloc(sz * sizeof(tpe)); + case Statements.Malloc(to, tpe, sz) => + val to_variable = translate_variable(to) + val old_ty = context.getOrElse(to_variable, CVoidPtrType) + context = context.updated(to_variable, unify(old_ty, translate_type(tpe))) + // println(s"after Malloc(${to.pp}, ${tpe.pp}, ${sz.toString})") + // debug() + + // T to = *(from + offset); + case Statements.Load(to, _, from, offset) => + val to_variable = translate_variable(to) + val from_variable = translate_variable(from) + + // use the type of variable being stored into to infer type of the from variable + context.get(to_variable) match { + case Some(value : PureType) => + val from_type = Types.ptr_type(value) + val old_from_type = context.getOrElse(from_variable, from_type) + context = context.updated(from_variable, unify(from_type, old_from_type)) + case _ => () + } + // use the type of the variable being stored to infer type of the variable being stored into + context.get(from_variable) match { + case Some(value : PtrType) => + val to_type = Types.deref_type(value) + val old_to_type = context.getOrElse(to_variable, to_type) + context = context.updated(to_variable, unify(to_type, old_to_type)) + case Some(CVoidPtrType) => + /* in this case, we're de-referencing a void pointer, which is clearly + * impossible - hence we'll state that from_variable is a void ** type and to + * is void * type */ + context = context.updated(from_variable, CVoidPtrPtrType) + context = context.updated(to_variable, CVoidPtrType) + case _ => () + } + case Statements.Store(to, offset, e) => + // when storing, we can be assured that the + // type of the to element must be *(type of assignment expression) + val to_variable = translate_variable(to) + // type of expression being assigned + val expr = translate_expression(e) + val expr_ty = type_expression(expr) + + // we expect the type of the storage location to be + // a pointer to elements of the type of the stored + // expression + expr_ty match { + case pureType: PureType => + val to_type = Types.ptr_type(pureType) + // retrieve the old type + var old_to_type : AnalysisVSTTypes = context.getOrElse(to_variable, to_type) + + // unify the expected type vs the stored type + context = context.updated(to_variable, unify(to_type,old_to_type)) + case v => () + } + + // additional case for the situation in which our expression is just another pointer + // i.e *to = from + // then we can use properties about to to infer the type of from + expr match { + case from_variable@CVar(_) => + context.get(to_variable) match { + case Some (ptrType: PtrType) => + val from_type = Types.deref_type(ptrType) + var old_from_type : AnalysisVSTTypes = context.getOrElse(from_variable, from_type) + context = context.updated(from_variable, unify(from_type,old_from_type)) + } + case _ => () + } + case Statements.SeqComp(s1, s2) => + eval_statement(s1) + eval_statement(s2) + case Statements.If(cond, tb, eb) => + cond match { + case variable@Var(_) => context = context.updated(translate_variable(variable), CBoolType) + case _ => () + } + eval_statement(tb) + eval_statement(eb) + case Statements.Guarded(cond, body, els, branchPoint) => + cond match { + case variable@Var(_) => context = context.updated(translate_variable(variable), CBoolType) + case _ => () + } + eval_statement(body) + eval_statement(els) + // case Statements.Call(fun, args, companion) => () // TODO: Should probably also handle this, but for now it works + + // everything else, we ignore + case _ => () + } + + // assuming a completed context, converts suslik statement to VST C statements + def translate_statement(stmt: Statement): CStatement = + stmt match { + case Statements.Skip => CSkip + case Statements.Hole => CHole + case Statements.Error => CError + case Statements.Malloc(to, _, sz) => + CMalloc(translate_variable(to), context(translate_variable(to)).downcast, sz) + case Statements.Free(v) => + CFree(translate_variable(v)) + case Statements.Load(to, _, from, offset) => + val to_var = translate_variable(to) + val from_var = translate_variable(from) + CLoad(to_var, context(to_var).downcast, from_var, offset) + case Statements.Store(to, offset, e) => + val to_variable = translate_variable(to) + val expr = translate_expression(e) + CStore(to_variable, type_expression(expr).downcast, offset, expr) + case Statements.Call(fun, args, companion) => + CCall(translate_variable(fun), args map translate_expression, companion) + case Statements.SeqComp(s1, s2) => + CSeqComp(translate_statement(s1), translate_statement(s2)) + case Statements.If(cond, tb, eb) => + CIf(translate_expression(cond), translate_statement(tb), translate_statement(eb)) + case Statements.Guarded(cond, body, els, branchPoint) => + CGuarded(translate_expression(cond), translate_statement(body), translate_statement(els), branchPoint) + } + + // first, run program analysis + var prev_context = context + do { + prev_context = context + eval_statement(stmt) + } while (prev_context != context) + + // then translate the body + val body: CStatement = translate_statement(stmt) + + // QED + (body, context) + } + } From 96623fab56c0d443876975f78e266e7bc0fe6a95 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 13 Aug 2020 16:31:37 +0800 Subject: [PATCH 027/211] Small fixes --- .../certification/targets/htt/logic/ProofSteps.scala | 2 +- .../targets/htt/translation/ProofTranslation.scala | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index a6f241463..70de36508 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -125,7 +125,7 @@ object ProofSteps { } // Solve everything except the predicate applications - builder.append("ssl_emp_post.\n") + builder.append("sslauto.\n") // For each predicate application, unfold the correct constructor and recursively solve its expanded form for { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 50d3bb8a8..bdba8e50b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -60,11 +60,9 @@ object ProofTranslation { case PartiallyAppliedProducer(p, _) => translateProducer(p, cenv) case SubstProducer(subst) => - if (item.node.goal.callGoal.isEmpty) { - val csub = subst.map { case (v, e) => translateVar(v) -> translateExpr(e).subst(cenv.ghostSubst) } - val cenv1 = cenv.copy(subst = cenv.subst ++ csub) - (IdProofProducer, cenv1) - } else (IdProofProducer, cenv) + val csub = subst.map { case (v, e) => translateVar(v) -> translateExpr(e).subst(cenv.ghostSubst) } + val cenv1 = cenv.copy(subst = cenv.subst ++ csub) + (IdProofProducer, cenv1) case GhostSubstProducer(ghostSubst) => val newGhostSubst = ghostSubst.map { case (v, e) => translateVar(v) -> translateVar(e) } val newSubst = cenv.subst.map { case (v, e) => v.substVar(newGhostSubst) -> e.subst(newGhostSubst)} From a72ea8517fdf89025ee746bb20b588da461dcb7b Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 15 Aug 2020 12:58:03 +0100 Subject: [PATCH 028/211] Implemented translation function using Proof context instead of performing program analysis --- .../language/{Types.scala => CTypes.scala} | 28 +- .../targets/vst/language/Expressions.scala | 75 +--- .../targets/vst/language/Statements.scala | 20 +- .../targets/vst/language/package.scala | 9 +- .../targets/vst/logic/Formulae.scala | 4 +- .../targets/vst/logic/Proof.scala | 171 +++++++- .../targets/vst/logic/ProofTypes.scala | 32 ++ .../vst/translation/CTranslation.scala | 217 ++++++++++ .../vst/translation/ProofTranslation.scala | 6 + .../targets/vst/translation/Translation.scala | 374 +----------------- 10 files changed, 475 insertions(+), 461 deletions(-) rename src/main/scala/org/tygus/suslik/certification/targets/vst/language/{Types.scala => CTypes.scala} (64%) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala similarity index 64% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala index 5beff8f01..b88e75137 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst.language -object Types { +object CTypes { sealed trait AnalysisVSTTypes extends PrettyPrinting { @@ -8,32 +8,28 @@ object Types { /** the Analysis VST type encodes more precise information about pointers * than is stored in a VST proof - after the analysis, we can drop this * additional information */ - def downcast : VSTType = this match { - case tType: VSTType => tType + def downcast : VSTCType = this match { + case tType: VSTCType => tType case _: PtrType => CVoidPtrType - case pureType: PureType =>pureType.asInstanceOf[VSTType] + case pureType: PureType =>pureType.asInstanceOf[VSTCType] } } - /** types used in a VST program */ - sealed abstract class VSTType extends PrettyPrinting with AnalysisVSTTypes + /** types used in a C program */ + sealed abstract class VSTCType extends PrettyPrinting with AnalysisVSTTypes sealed trait PtrType extends AnalysisVSTTypes sealed trait PureType extends AnalysisVSTTypes - case object CBoolType extends VSTType with PureType { - override def pp: String = "bool" - } - - case object CIntType extends VSTType with PureType { + case object CIntType extends VSTCType with PureType { override def pp: String = "int" } - case object CUnitType extends VSTType with PureType { + case object CUnitType extends VSTCType with PureType { override def pp: String = "void" } /** encoding of void pointer types */ - case object CVoidPtrType extends VSTType with PureType { + case object CVoidPtrType extends VSTCType with PureType { override def pp: String = s"void *" } @@ -48,19 +44,13 @@ object Types { case object CIntPtrType extends PtrType with PrettyPrinting { override def pp: String = s"int *" } - /** encoding of bool* pointer types */ - case object CBoolPtrType extends PtrType with PrettyPrinting { - override def pp: String = s"bool *" - } def deref_type(ptrType: PtrType) = ptrType match { case CVoidPtrPtrType => CVoidPtrType case CIntPtrType => CIntType - case CBoolPtrType => CBoolType } def ptr_type(elem_type: PureType) = elem_type match { - case CBoolType => CBoolPtrType case CIntType => CIntPtrType case CUnitType => CVoidPtrType case CVoidPtrType => CVoidPtrPtrType diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala index 8408607f2..3f18e32b1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst.language -import org.tygus.suslik.certification.targets.vst.language.Types.{CBoolType, CIntType, VSTType} +import org.tygus.suslik.certification.targets.vst.language.CTypes.{CBoolType, CIntType, VSTCType} import org.tygus.suslik.language.Expressions.SubstVar /** Encodes VST specific C expressions */ @@ -21,9 +21,6 @@ object Expressions { override def pp: String = value.toString } - case class CSetLiteral(elems: List[CExpr]) extends CExpr { - } - case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { override def pp: String = s"(${cond.pp} ? ${left.pp} : ${right.pp})" @@ -32,7 +29,6 @@ object Expressions { case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { override def pp: String = op match { - case COpBoolEq => s"(${left.pp} == ${right.pp})" case COpEq => s"(${left.pp} == ${right.pp})" case COpLt => s"(${left.pp} < ${right.pp})" case COpLeq => s"(${left.pp} <= ${right.pp})" @@ -52,74 +48,27 @@ object Expressions { } } - sealed abstract class CUnOp extends PrettyPrinting - - object COpNot extends CUnOp { - override def pp: String = "~~" - } + sealed abstract class CUnOp + object COpNot extends CUnOp object COpUnaryMinus extends CUnOp - sealed abstract class CBinOp extends PrettyPrinting - - object COpImplication extends CBinOp { - override def pp: String = "->" - } - - object COpPlus extends CBinOp { - override def pp: String = "+" - } - - object COpMinus extends CBinOp { - override def pp: String = "-" - } - - object COpMultiply extends CBinOp { - override def pp: String = "*" - } - - object COpEq extends CBinOp { - override def pp: String = "==" - } + sealed abstract class CBinOp - object COpBoolEq extends CBinOp { - override def pp: String = "=" - } + object COpPlus extends CBinOp - object COpLeq extends CBinOp { - override def pp: String = "<=" - } + object COpMinus extends CBinOp - object COpLt extends CBinOp { - override def pp: String = "<" - } + object COpMultiply extends CBinOp - object COpAnd extends CBinOp { - override def pp: String = "/\\" - } + object COpEq extends CBinOp - object COpOr extends CBinOp { - override def pp: String = "\\/" - } + object COpLeq extends CBinOp - object COpUnion extends CBinOp { - override def pp: String = "++" - } + object COpLt extends CBinOp - object COpDiff extends CBinOp { - override def pp: String = "--" - } - - object COpIn extends CBinOp - - object COpSetEq extends CBinOp { - override def pp: String = "=" - } - - object COpSubset extends CBinOp { - override def pp: String = "<=" - } + object COpAnd extends CBinOp - object COpIntersect extends CBinOp + object COpOr extends CBinOp } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala index 55377bd84..95bbf7b29 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala @@ -1,10 +1,10 @@ -package org.tygus.suslik.certification.targets.vst.program +package org.tygus.suslik.certification.targets.vst.language -import org.tygus.suslik.certification.targets.vst.language.Types.{VSTType} +import org.tygus.suslik.certification.targets.vst.language.CTypes.{VSTCType} import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.{PrettyPrinting, Types} import org.tygus.suslik.logic.Specifications.GoalLabel +/** Encoding of C statements */ object Statements { /** pretty printing a VST C Statement returns a C embedding */ @@ -28,7 +28,7 @@ object Statements { } // let to = malloc(n) - case class CMalloc(to: CVar, tpe: VSTType, sz: Int = 1) extends CStatement { + case class CMalloc(to: CVar, tpe: VSTCType, sz: Int = 1) extends CStatement { override def pp: String = s"${tpe.pp} ${to.pp} = (${tpe.pp})malloc(${sz.toString} * sizeof(${tpe.pp}));" } @@ -41,8 +41,8 @@ object Statements { /** encoding of a load operation * let to = *from.offset * */ - case class CLoad(to: CVar, elem_ty: VSTType, from: CVar, - offset: Int = 0) extends CStatement { + case class CLoad(to: CVar, elem_ty: VSTCType, from: CVar, + offset: Int = 0) extends CStatement { override def pp: String = if (offset == 0) { s"${elem_ty.pp} ${to.pp} = *(${elem_ty.pp}*)${from.pp};" @@ -53,7 +53,7 @@ object Statements { /** Encoding of a store operation * *to.offset = e */ - case class CStore(to: CVar, elem_ty: VSTType, offset: Int, e: CExpr) extends CStatement { + case class CStore(to: CVar, elem_ty: VSTCType, offset: Int, e: CExpr) extends CStatement { override def pp: String = { val cast = s"(${elem_ty.pp})" val ptr = s"(${elem_ty.pp}*)${to.pp}" @@ -97,7 +97,11 @@ object Statements { /** Definition of a CProcedure */ case class CProcedureDefinition( - name: String, rt: VSTType, params: Seq[(CVar, VSTType)], body: CStatement) extends PrettyPrinting { + name: String, + rt: VSTCType, + params: Seq[(CVar, VSTCType)], + body: CStatement + ) extends PrettyPrinting { override def pp: String = { val body_string = body.ppIndent(1) val function_def = diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala index 835bc0ce9..fb8a32ce1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala @@ -1,17 +1,18 @@ package org.tygus.suslik.certification.targets.vst import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType + /** * package encodes a wrapper around VST terms */ package object language { /** Typing context - maps variables to types */ - type CGamma = Map[CVar, VSTType] + type CGamma = Map[CVar, VSTCType] /** Formal parameter specification - types and names of parameters */ - type CFormals = Seq[(VSTType, CVar)] + type CFormals = Seq[(VSTCType, CVar)] /** type of mapping in context */ - type CtxMapping = (CExpr, VSTType) + type CtxMapping = (CExpr, VSTCType) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index bb7e1cb46..c41f0af4a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.language.Types.VSTType +import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} import org.tygus.suslik.certification.targets.vst.language.CFormals import org.tygus.suslik.certification.targets.vst.logic.Proof.VSTPredicate @@ -51,7 +51,7 @@ object Formulae { } case class CFunSpec( - name: String, rType: VSTType, + name: String, rType: VSTCType, params: CFormals, ghostParams: CFormals, pre: CAssertion, post: CAssertion) extends PrettyPrinting { } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 3115db7a8..103bc5e35 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,18 +1,185 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.language.Expressions.CVar +import com.sun.imageio.plugins.bmp.BMPCompressionTypes +import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.language.{CTypes, Expressions, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.ProofStep import org.tygus.suslik.language.Ident object Proof { + case class FormalParamType() extends PrettyPrinting + + sealed abstract class PureFormula extends PrettyPrinting + + /** predicate encoding that C-parameter (of type val) is a valid pointer */ + case class IsValidPointerOrNull(name: CVar) extends PureFormula { + override def pp: String = + s"is_valid_pointer_or_null(${name.pp})" + } + + /** predicate encoding that C-parameter (of type val) is a valid int */ + case class IsValidInt(name: CVar) extends PureFormula { + override def pp:String = + s"ssl_is_valid_int(s${name})" + } + + /** Redefinition of expressions for use in VST proofs + * */ + object Expressions { + sealed abstract class ProofCExpr extends PrettyPrinting { } + + /** a variable in a VST proof */ + case class ProofCVar(name: String) extends ProofCExpr { + override def pp: String = s"(force_signed_int ${name})" + } + + /** boolean constant in a VST proof */ + case class ProofCBoolConst(value: Boolean) extends ProofCExpr { + override def pp: String = value.toString + } + + /** integer constant in a VST proof */ + case class ProofCNatConst(value: Int) extends ProofCExpr { + override def pp: String = value.toString + } + + /** set literal (encoded as set) in a VST proof */ + case class ProofCSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { + override def pp: String = + s"[${elems.map(_.pp).mkString("; ")}]" + } + + /** encodes a ternary expression in a VST proof */ + case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = + s"(if ${cond.pp} then ${left.pp} else ${right.pp})" + } + + case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpLt => s"(${left.pp} < ${right.pp})" + case ProofCOpLeq => s"(${left.pp} <= ${right.pp})" + case ProofCOpOr => s"(orb ${left.pp} ${right.pp})" + case ProofCOpAnd => s"(andb ${left.pp} ${right.pp})" + case ProofCOpPlus => s"(${left.pp} + ${right.pp})" + case ProofCOpMinus => s"(${left.pp} - ${right.pp})" + case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" + + // no real good way of doing equality + case ProofCOpEq => ??? + } + } + + case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpNot$ => s"(negb ${e.pp})" + case ProofCOpUnaryMinus$ => s"(-(${e.pp}))" + } + } + + sealed abstract class ProofCUnOp + + object ProofCOpNot$ extends ProofCUnOp + + object ProofCOpUnaryMinus$ extends ProofCUnOp + + sealed abstract class ProofCBinOp + + object ProofCOpImplication extends ProofCBinOp + object ProofCOpPlus extends ProofCBinOp + + object ProofCOpMinus extends ProofCBinOp + + object ProofCOpMultiply extends ProofCBinOp + + object ProofCOpEq extends ProofCBinOp + + object ProofCOpLeq extends ProofCBinOp + + object ProofCOpLt extends ProofCBinOp + + object ProofCOpAnd extends ProofCBinOp + + object ProofCOpOr extends ProofCBinOp + } + + /** predicate encoding that a given boolean expression is true + * c_params are the list of parameter variables + * */ + case class IsTrue(expr: Expressions.ProofCExpr) extends PureFormula { + override def pp: String = { + s"(is_true ${expr.pp})" + } + } + + sealed case class SpatialFormula() extends PrettyPrinting + + sealed case class FormalCondition( + pure_constraints: List[PureFormula], + spatial_constraints: List[SpatialFormula] + ) + + /** + * Type encoding VST-compliant formal specifications of a C Function + * @param name name of the function + * @param c_params parameters of the function + * @param formal_params parameters of the specification + * @param existensial_params existential params of the function + * @param precondition precondtion for the function + * @param postcondition post condition of the function + * @param return_type return type of the function + */ + case class FormalSpecification( + name: Ident, + c_params: List[(Ident,VSTCType)], + formal_params: List[(Ident,FormalParamType)], + existensial_params: List[(Ident,FormalParamType)], + precondition: FormalCondition, + postcondition: FormalCondition, + return_type: VSTCType + ) extends PrettyPrinting{ + + def as_vst_type(var_type: VSTCType) = var_type match { + case CTypes.CIntType => "tint" + case CTypes.CUnitType => "tvoid" + case CTypes.CVoidPtrType => "(tptr tvoid)" + } + + override def pp: String = { + val formal_args = formal_params.map({case (var_name, var_type) => s"${var_name}: ${var_type.pp}"}) + val c_args = c_params.map({case (var_name, _) => s"${var_name}: val"}) + val FormalCondition(pre_pure_constraints, pre_spatial_constraints) = precondition + val FormalCondition(post_pure_constraints, post_spatial_constraints) = postcondition + s"""Definition ${name}_spec := + | DECLARE _${name} + | WITH ${(c_args ++ formal_args).mkString(", ")} + | PRE [ ${c_params.map({case (_, var_type) => s"${as_vst_type(var_type)}"}).mkString(", ") } ] + | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) + | PARAMS(${c_params.map({case (var_name, _) => var_name}).mkString("; ")}) + | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) + | POST[ ${as_vst_type(return_type)} ]|${existensial_params match { + case Nil => "" + case _ => + existensial_params.map({case (param_name, param_type) => s" EX ${param_name}: ${param_type.pp}"}).mkString("\n") + } + } + | PROP( ${post_pure_constraints.map(_.pp).mkString("; ")} ) + | LOCAL() + | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). + |""".stripMargin + } + } case class VSTClause() { } case class VSTPredicate( predicate_name: Ident, - params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.language.Types.VSTType)], + params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType)], ) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala new file mode 100644 index 000000000..ed4ea5e57 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala @@ -0,0 +1,32 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.language.{CTypes, PrettyPrinting} + + +/** Encapsulates all types used in proofs - i.e if these types are pretty printed, then they will be valid Coq terms */ +object ProofTypes { + + def proof_type_of_c_type(vst: VSTCType) : VSTProofType = + vst match { + case CTypes.CIntType => CoqNatType + case CTypes.CVoidPtrType => CoqPtrType + case CTypes.CUnitType => ??? + } + + /** proof types */ + sealed abstract class VSTProofType extends PrettyPrinting + + case object CoqPtrType extends VSTProofType { + override def pp: String = "val" + } + + case object CoqNatType extends VSTProofType { + override def pp:String = "Z" + } + + sealed case class CoqListType(elem: VSTProofType) extends VSTProofType { + override def pp: String = s"(list ${elem.pp})" + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala new file mode 100644 index 000000000..30a210421 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -0,0 +1,217 @@ +package org.tygus.suslik.certification.targets.vst.translation + +import org.tygus.suslik.certification.targets.vst.language.CTypes +import org.tygus.suslik.certification.targets.vst.language.CTypes._ +import org.tygus.suslik.certification.targets.vst.language.Expressions._ +import org.tygus.suslik.certification.targets.vst.language.Statements._ +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.language.Expressions._ +import org.tygus.suslik.language.Statements.{Procedure, Statement} +import org.tygus.suslik.language._ +import org.tygus.suslik.logic.Gamma + +/** encapsulates all functions for translating suslik terms to a C encoding */ +object CTranslation { + + def translate_unary_expr(e1: UnaryExpr): CExpr = { + def translate_unary_op : UnOp => CUnOp = { + case OpNot => COpNot + case OpUnaryMinus => COpUnaryMinus + } + CUnaryExpr(translate_unary_op(e1.op), translate_expression(e1.arg)) + } + + /** translates a variable to C Expression type */ + def translate_variable : Var => CVar = { case Var(name) => CVar(name) } + + + /* translates a unary operation to a C expression */ + def translate_binary_op: BinOp => CBinOp = { + case OpPlus => COpPlus + case OpMinus => COpMinus + case OpMultiply => COpMultiply + case OpEq => COpEq + case OpLeq => COpLeq + case OpLt => COpLt + case OpAnd => COpAnd + case OpOr => COpOr + case v => throw TranslationException(s"translation to C program failed - use of unsupported binary operation ${v.pp}") + } + + def translate_binary_expr(op: BinOp, e1: Expr, e2: Expr): CExpr = { + CBinaryExpr(translate_binary_op(op), translate_expression(e1), translate_expression(e2)) + } + + + def translate_overloaded_binary_expr(expr: OverloadedBinaryExpr): CExpr = expr match { + case OverloadedBinaryExpr(overloaded_op, left, right) => + val tleft = translate_expression(left) + val tright = translate_expression(right) + overloaded_op match { + case op: BinOp => CBinaryExpr(translate_binary_op(op), tleft, tright) + case OpOverloadedEq => CBinaryExpr(COpEq, tleft, tright) + case OpNotEqual => + CUnaryExpr(COpNot, CBinaryExpr(COpEq, tleft, tright)) + case OpGt => + CUnaryExpr(COpNot, CBinaryExpr(COpLeq, tleft, tright)) + case OpGeq => + CUnaryExpr(COpNot, CBinaryExpr(COpLt, tleft, tright)) + case OpOverloadedPlus => + CBinaryExpr(COpPlus, tleft, tright) + case OpOverloadedMinus => + CBinaryExpr(COpMinus, tleft, tright) + case OpOverloadedLeq => + CBinaryExpr(COpLeq, tleft, tright) + case OpOverloadedStar => ??? + } + } + + /** translate a Suslik expression into VST specific encoding */ + def translate_expression(expr: Expr): CExpr = expr match { + case Var(name) => CVar(name) + case BoolConst(value) => CBoolConst(value) + case IntConst(value) => CNatConst(value) + case e1@UnaryExpr(_, _) => translate_unary_expr(e1) + case BinaryExpr(op, e1, e2) => translate_binary_expr(op, e1, e2) + case IfThenElse(c, t, e) => CIfThenElse(translate_expression(c), translate_expression(t), translate_expression(e)) + case expr@OverloadedBinaryExpr(_, _, _) => + translate_overloaded_binary_expr(expr) + case v => throw TranslationException(s"Operation ${v.toString} not supported") + } + + + /** translate type to C Type */ + def translate_type(lType: SSLType): VSTCType = lType match { + case IntType => CIntType + case LocType => CVoidPtrType + case VoidType => CUnitType + case v => throw TranslationException(s"translating ${v} not implemeneted") + } + + def is_supported_c_type: SSLType => Boolean = { + case IntType => true + case LocType => true + case _ => false + } + + + /** translates a suslik encoding of a procedure to a C one */ + def translate_function(proc:Procedure, gamma: Gamma) = { + val ctx: Map[CVar, AnalysisVSTTypes] = + gamma + .filter({ case (_, lType) => is_supported_c_type(lType)}) + .map({ + case (variable, ty) => + (translate_variable(variable), translate_type(ty).asInstanceOf[AnalysisVSTTypes])}) + def type_expr (ctx: Map[CVar, AnalysisVSTTypes]) (expr: Expr): Option[AnalysisVSTTypes] = { + def translate_op : BinOp => Option[AnalysisVSTTypes] = { + case op: RelOp => op match { + case Expressions.OpEq => Some(CIntType) + case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + } + case Expressions.OpPlus => Some(CIntType) + case Expressions.OpMinus => Some(CIntType) + case Expressions.OpMultiply => Some(CIntType) + case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + } + expr match { + case Var(name) => ctx.get(CVar(name)) + case const: Const => const match { + case IntConst(value) => Some (CIntType) + case BoolConst(value) => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + } + case BinaryExpr(op, left, right) => translate_op(op) + case OverloadedBinaryExpr(overloaded_op, left, right) => + overloaded_op match { + case op: BinOp => translate_op(op) + case Expressions.OpOverloadedPlus => Some(CIntType) + case Expressions.OpOverloadedMinus => Some(CIntType) + case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + } + case UnaryExpr(op, arg) => op match { + case Expressions.OpUnaryMinus => Some(CIntType) + case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + } + case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") + case IfThenElse(cond, left, right) => type_expr(ctx)(left).orElse( type_expr(ctx)(right)) + } + } + + def translate_body(ctx: Map[CVar, AnalysisVSTTypes], body: Statement) : (Map[CVar, AnalysisVSTTypes], CStatement) = + body match { + case Statements.Skip => (ctx, CSkip) + case Statements.Malloc(to, tpe, sz) => + (ctx, CMalloc(translate_variable(to), translate_type(tpe), sz)) + case Statements.Free(v) => (ctx, CFree(translate_variable(v))) + case Statements.Load(to, tpe, from, offset) => + val (new_ctx, elem_typ) = ctx.get(translate_variable(to)) match { + case Some(value) => (ctx, value.downcast) + case None => ctx.get(translate_variable(from)) match { + case Some(CIntPtrType) => (ctx.updated(translate_variable(to), CIntType), CIntType) + case Some(CVoidPtrPtrType) => + (ctx.updated(translate_variable(to), CVoidPtrType), CVoidPtrType) + // TODO: Handle errors + case Some(CVoidPtrType) => + val new_ctx = + ctx.updated(translate_variable(to), CVoidPtrType) + .updated(translate_variable(from), CVoidPtrPtrType) + (new_ctx, CVoidPtrType) + case None => ??? + } + } + (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from))) + case Statements.Store(to, offset, e) => + val (new_ctx, elem_ty) = + type_expr(ctx)(e) match { + case Some(value) => (ctx.updated(translate_variable(to), value match { + case cType: VSTCType => cType match { + case CTypes.CIntType => CIntPtrType + case CTypes.CVoidPtrType => CVoidPtrPtrType + case CTypes.CUnitType => ??? + } + case ptrType: PtrType => ptrType match { + case CTypes.CVoidPtrPtrType => CVoidPtrPtrType + case CTypes.CIntPtrType => CVoidPtrPtrType + } + }), value) + case None => ctx.get(translate_variable(to)) match { + case Some(value) => value match { + case ptrType: PtrType => ptrType match { + case CTypes.CVoidPtrPtrType => (ctx, CVoidPtrType) + case CTypes.CIntPtrType => (ctx, CIntType) + } + case _ => ??? + } + case None => ??? + } + + } + (new_ctx, CStore(translate_variable(to), elem_ty.downcast, offset, translate_expression(e))) + case Statements.Call(fun, args, companion) => + (ctx, CCall(translate_variable(fun), args.map(translate_expression(_)), companion)) + case Statements.SeqComp(s1, s2) => + val (new_ctx, s1_up) = translate_body(ctx, s1) + val (new_ctx2, s2_up) = translate_body(new_ctx, s2) + (new_ctx2, CSeqComp(s1_up, s2_up)) + case Statements.If(cond, s1, s2) => + val (new_ctx, s1_up) = translate_body(ctx, s1) + val (new_ctx2, s2_up) = translate_body(new_ctx, s2) + (new_ctx2, CIf(translate_expression(cond), s1_up, s2_up)) + case Statements.Guarded(cond, s1, s2, branchPoint) => + val (new_ctx, s1_up) = translate_body(ctx, s1) + val (new_ctx2, s2_up) = translate_body(new_ctx, s2) + (new_ctx2, CIf(translate_expression(cond), s1_up, s2_up)) + case Statements.Hole => throw TranslationException(s"Unsupported value ${body.pp} found during translation") + case Statements.Error => throw TranslationException(s"Unsupported value ${body.pp} found during translation") + } + val (_, body) = translate_body(ctx, proc.body) + CProcedureDefinition( + proc.f.name, + translate_type(proc.f.rType), + proc.f.params.map({ case (variable, rtype) => (translate_variable(variable), translate_type(rtype)) }), + body + ) + } + + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala new file mode 100644 index 000000000..84d663d09 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -0,0 +1,6 @@ +package org.tygus.suslik.certification.targets.vst.translation + +/** translates suslik terms to VST compatible terms */ +class ProofTranslation { + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index fcbf5764b..24b2964fd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -2,384 +2,32 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.vst.language.{Expressions, PrettyPrinting, Types} -import org.tygus.suslik.certification.targets.vst.language.Expressions.{CBinOp, CBinaryExpr, CBoolConst, CExpr, CIfThenElse, CNatConst, COpAnd, COpBoolEq, COpDiff, COpEq, COpImplication, COpIn, COpIntersect, COpLeq, COpLt, COpMinus, COpMultiply, COpNot, COpOr, COpPlus, COpSetEq, COpSubset, COpUnaryMinus, COpUnion, CSetLiteral, CUnOp, CUnaryExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.Types.{AnalysisVSTTypes, CBoolType, CIntType, CUnitType, CVoidPtrPtrType, CVoidPtrType, PtrType, PureType, VSTType} -import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof +import org.tygus.suslik.certification.targets.vst.language.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CFunSpec, CInductivePredicate} -import org.tygus.suslik.certification.targets.vst.program.Statements.{CCall, CError, CFree, CGuarded, CHole, CIf, CLoad, CMalloc, CProcedureDefinition, CSeqComp, CSkip, CStatement, CStore} -import org.tygus.suslik.language.Expressions.{BinOp, BinaryExpr, BoolConst, Expr, IfThenElse, IntConst, OpAnd, OpBoolEq, OpDiff, OpEq, OpGeq, OpGt, OpImplication, OpIn, OpIntersect, OpLeq, OpLt, OpMinus, OpMultiply, OpNot, OpNotEqual, OpOr, OpOverloadedEq, OpOverloadedLeq, OpOverloadedMinus, OpOverloadedPlus, OpOverloadedStar, OpPlus, OpSetEq, OpSubset, OpUnaryMinus, OpUnion, OverloadedBinOp, OverloadedBinaryExpr, SetLiteral, UnOp, UnaryExpr, Var} -import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntType, LocType, SSLType, Statements, VoidType} -import org.tygus.suslik.language.Statements.{Procedure, Statement} -import org.tygus.suslik.logic.Specifications.Assertion -import org.tygus.suslik.logic.{Environment, FunSpec, InductiveClause, InductivePredicate, PFormula, PredicateEnv, SFormula} - -import scala.collection.immutable +import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof +import org.tygus.suslik.certification.targets.vst.language.Statements.CProcedureDefinition +import org.tygus.suslik.language.Statements.Procedure +import org.tygus.suslik.logic.Environment object Translation { case class TranslationException(msg: String) extends Exception(msg) - def pp_tuple(value: (PrettyPrinting, PrettyPrinting)) = - value match {case (v1:PrettyPrinting,v2:PrettyPrinting) => s"${v1.pp} :-> ${v2.pp}"} - - def translate_unary_op(op: UnOp) = op match { - case OpNot => COpNot - case OpUnaryMinus => COpUnaryMinus - } - - def translate_unary_expr(e1: UnaryExpr): CExpr = - CUnaryExpr(translate_unary_op(e1.op), translate_expression(e1.arg)) - - - /** translates a variable to C Expression type */ - def translate_variable(param_name: Var) = param_name match { - case Var(name) => CVar(name) - } - - def translate_binary_op(op: BinOp): CBinOp = op match { - case OpImplication => COpImplication - case OpPlus => COpPlus - case OpMinus => COpMinus - case OpMultiply => COpMultiply - case OpEq => COpEq - case OpBoolEq => COpBoolEq - case OpLeq => COpLeq - case OpLt => COpLt - case OpAnd => COpAnd - case OpOr => COpOr - case OpUnion => COpUnion - case OpDiff => COpDiff - case OpIn => COpIn - case OpSetEq => COpSetEq - case OpSubset => COpSubset - case OpIntersect => COpIntersect - } - - def translate_binary_expr(op: BinOp, e1: Expr, e2: Expr): CExpr = { - CBinaryExpr(translate_binary_op(op), translate_expression(e1), translate_expression(e2)) - } - - - def translate_overloaded_binary_expr(expr: OverloadedBinaryExpr): CExpr = expr match { - case OverloadedBinaryExpr(overloaded_op, left, right) => - val tleft = translate_expression(left) - val tright = translate_expression(right) - overloaded_op match { - case op: BinOp => CBinaryExpr(translate_binary_op(op), tleft, tright) - case OpOverloadedEq => CBinaryExpr(COpEq, tleft, tright) - case OpNotEqual => - CUnaryExpr(COpNot, CBinaryExpr(COpEq, tleft, tright)) - case OpGt => - CUnaryExpr(COpNot, CBinaryExpr(COpLeq, tleft, tright)) - case OpGeq => - CUnaryExpr(COpNot, CBinaryExpr(COpLt, tleft, tright)) - case OpOverloadedPlus => - CBinaryExpr(COpPlus, tleft, tright) - case OpOverloadedMinus => - CBinaryExpr(COpMinus, tleft, tright) - case OpOverloadedLeq => - CBinaryExpr(COpLeq, tleft, tright) - case OpOverloadedStar => ??? - } - } - - /** translate a Suslik expression into VST specific encoding */ - def translate_expression(expr: Expr): CExpr = expr match { - case Var(name) => CVar(name) - case BoolConst(value) => CBoolConst(value) - case IntConst(value) => CNatConst(value) - case e1@UnaryExpr(_, _) => translate_unary_expr(e1) - case BinaryExpr(op, e1, e2) => translate_binary_expr(op, e1, e2) - case SetLiteral(elems) => CSetLiteral(elems.map(e => translate_expression(e))) - case IfThenElse(c, t, e) => CIfThenElse(translate_expression(c), translate_expression(t), translate_expression(e)) - case expr@OverloadedBinaryExpr(_, _, _) => - translate_overloaded_binary_expr(expr) - case v => throw TranslationException(s"Operation ${v.toString} not supported") - } - - - /** translate type to C Type */ - def translate_type(lType: SSLType): VSTType = { - lType match { - case IntType => CIntType - case BoolType => CBoolType - case LocType => CVoidPtrType - case VoidType => CUnitType - case v => throw TranslationException(s"translating ${v} not implemeneted") - } - - } - - def translate_clause(clause: InductiveClause) = clause match { - case InductiveClause(selector: Expr, asn@Assertion(phi: PFormula, sigma: SFormula)) => { - val c_selector = translate_expression(selector) - - ??? - } - } - - def translate_predicate(predicate: InductivePredicate) = predicate match { - case (predicate@InductivePredicate(predicate_name, predicate_params, predicate_clauses: Seq[InductiveClause])) => - val params = predicate_params map { case (raw_variable, ty) => - val variable = translate_variable(raw_variable) - val tty = translate_type(ty) - val clauses = predicate_clauses map translate_clause - (variable, tty) - } - - val clauses = predicate_clauses map { case InductiveClause( - selector: Expr, asn@Assertion(phi: PFormula, sigma: SFormula) - ) => - ??? - } - (predicate_name, params, clauses) - } - - - def print_as_c_program(f: FunSpec, body: CStatement, ctx: Map[CVar, AnalysisVSTTypes]): String = { - - val c_prelude ="#include \n\nextern void free(void *p);\n\n" - - val return_type = translate_type(f.rType) - val body_string = body.ppIndent(1) - val function_def = - s"${c_prelude}${return_type.pp} ${f.name}(${ - f.params.map({case (variable_name, _) => - s"${ctx(translate_variable(variable_name)).downcast.pp} ${translate_variable(variable_name).pp}" - }).mkString(", ") - }) {\n${body_string}\n}\n" - function_def - } - def translate_procedure(f: FunSpec, body: CStatement, ctx: Map[CVar, AnalysisVSTTypes]): - CProcedureDefinition = { - val rt = translate_type(f.rType) - val params = f.params.map({case (variable_name, _) => - (translate_variable(variable_name), ctx(translate_variable(variable_name)).downcast) - }) - val name = f.name - CProcedureDefinition(name, rt, params, body) - } - - def translate(root: CertTree.Node, proc: Procedure, env: Environment): (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedureDefinition) = { + val procedure = CTranslation.translate_function(proc, root.goal.gamma) + println(procedure.pp) + print(root.goal.gamma) // translate body into VST types, and build context of variables - var (body, ctx) = translate_body(proc.f, proc.body) + // var (body, ctx) = CTranslation.translate_body(proc.f, proc.body, root.goal.gamma) // use this to build a C-encoding of the synthesized function - val function_definition = translate_procedure(proc.f, body, ctx) - - // translate predicate to C format - - - var body_string = print_as_c_program(proc.f, body, ctx) - - print(body_string) + // var body_string = print_as_c_program(proc.f, body, ctx) + // print(body_string) // translate predicates // translate proof ??? } - - def translate_body(funspec: FunSpec, stmt: Statement) = { - // build an initial context from the parameters - var context = funspec.params.map({ - case (variable, ssl_type) => (translate_variable(variable), translate_type(ssl_type).asInstanceOf[AnalysisVSTTypes]) - }).toMap - - // returns the most precise type of the two provided types - def unify(ty1: AnalysisVSTTypes, ty2: AnalysisVSTTypes): AnalysisVSTTypes = (ty1, ty2) match { - case (_, CUnitType) => CUnitType - case (CUnitType, _) => CUnitType - case (_, CBoolType) => CBoolType - case (CBoolType, _) => CBoolType - case (_, CIntType) => CIntType - case (CIntType, _) => CIntType - case (ot, CVoidPtrType) => ot - case (CVoidPtrType, ot) => ot - case (CVoidPtrPtrType, ot:PtrType) => ot - case (ot:PtrType, CVoidPtrPtrType) => ot - case (v1, v2) => - if (v1 == v2) - v1 - else - throw TranslationException(s"Can not unify type (${v1.pp}) with (${v2.pp})") - } - - // retrieves the VST Type of the current expression - def type_expression(expr: CExpr): AnalysisVSTTypes = expr match { - case value@CVar(_) => context(value) - case CBoolConst(value) => CBoolType - case CNatConst(value) => CIntType - case CIfThenElse(cond, left, right) => - val left_ty = type_expression(left) - val right_ty = type_expression(right) - unify(left_ty, right_ty) - case CBinaryExpr(op, left, right) => - op match { - case COpImplication => CBoolType - case COpPlus => CIntType - case COpMinus => CIntType - case COpMultiply => CIntType - case COpEq => CBoolType - case COpBoolEq => CBoolType - case COpLeq => CBoolType - case COpLt => CBoolType - case COpAnd => CBoolType - case COpOr => CBoolType - case COpIn => CBoolType - case _ => throw TranslationException("unsupported binary operand in function body") - } - case CUnaryExpr(op, e) => op match { - case COpNot => CBoolType - case COpUnaryMinus => CIntType - } - case CSetLiteral(elems) => throw TranslationException("unsupported expression in function body") - } - - def debug() = { - print("context = {\n"+context.map({case (variable, ty) => s"\t${variable.pp}: ${ty.pp}\n"}) + "}\n") - } - // a very light abstract interpretation of the suslik program to convert types to - // C-like types - // should reach fixpoint in two iterations - def eval_statement(stmt: Statement): Unit = - stmt match { - // T to = malloc(sz * sizeof(tpe)); - case Statements.Malloc(to, tpe, sz) => - val to_variable = translate_variable(to) - val old_ty = context.getOrElse(to_variable, CVoidPtrType) - context = context.updated(to_variable, unify(old_ty, translate_type(tpe))) - // println(s"after Malloc(${to.pp}, ${tpe.pp}, ${sz.toString})") - // debug() - - // T to = *(from + offset); - case Statements.Load(to, _, from, offset) => - val to_variable = translate_variable(to) - val from_variable = translate_variable(from) - - // use the type of variable being stored into to infer type of the from variable - context.get(to_variable) match { - case Some(value : PureType) => - val from_type = Types.ptr_type(value) - val old_from_type = context.getOrElse(from_variable, from_type) - context = context.updated(from_variable, unify(from_type, old_from_type)) - case _ => () - } - // use the type of the variable being stored to infer type of the variable being stored into - context.get(from_variable) match { - case Some(value : PtrType) => - val to_type = Types.deref_type(value) - val old_to_type = context.getOrElse(to_variable, to_type) - context = context.updated(to_variable, unify(to_type, old_to_type)) - case Some(CVoidPtrType) => - /* in this case, we're de-referencing a void pointer, which is clearly - * impossible - hence we'll state that from_variable is a void ** type and to - * is void * type */ - context = context.updated(from_variable, CVoidPtrPtrType) - context = context.updated(to_variable, CVoidPtrType) - case _ => () - } - case Statements.Store(to, offset, e) => - // when storing, we can be assured that the - // type of the to element must be *(type of assignment expression) - val to_variable = translate_variable(to) - // type of expression being assigned - val expr = translate_expression(e) - val expr_ty = type_expression(expr) - - // we expect the type of the storage location to be - // a pointer to elements of the type of the stored - // expression - expr_ty match { - case pureType: PureType => - val to_type = Types.ptr_type(pureType) - // retrieve the old type - var old_to_type : AnalysisVSTTypes = context.getOrElse(to_variable, to_type) - - // unify the expected type vs the stored type - context = context.updated(to_variable, unify(to_type,old_to_type)) - case v => () - } - - // additional case for the situation in which our expression is just another pointer - // i.e *to = from - // then we can use properties about to to infer the type of from - expr match { - case from_variable@CVar(_) => - context.get(to_variable) match { - case Some (ptrType: PtrType) => - val from_type = Types.deref_type(ptrType) - var old_from_type : AnalysisVSTTypes = context.getOrElse(from_variable, from_type) - context = context.updated(from_variable, unify(from_type,old_from_type)) - } - case _ => () - } - case Statements.SeqComp(s1, s2) => - eval_statement(s1) - eval_statement(s2) - case Statements.If(cond, tb, eb) => - cond match { - case variable@Var(_) => context = context.updated(translate_variable(variable), CBoolType) - case _ => () - } - eval_statement(tb) - eval_statement(eb) - case Statements.Guarded(cond, body, els, branchPoint) => - cond match { - case variable@Var(_) => context = context.updated(translate_variable(variable), CBoolType) - case _ => () - } - eval_statement(body) - eval_statement(els) - // case Statements.Call(fun, args, companion) => () // TODO: Should probably also handle this, but for now it works - - // everything else, we ignore - case _ => () - } - - // assuming a completed context, converts suslik statement to VST C statements - def translate_statement(stmt: Statement): CStatement = - stmt match { - case Statements.Skip => CSkip - case Statements.Hole => CHole - case Statements.Error => CError - case Statements.Malloc(to, _, sz) => - CMalloc(translate_variable(to), context(translate_variable(to)).downcast, sz) - case Statements.Free(v) => - CFree(translate_variable(v)) - case Statements.Load(to, _, from, offset) => - val to_var = translate_variable(to) - val from_var = translate_variable(from) - CLoad(to_var, context(to_var).downcast, from_var, offset) - case Statements.Store(to, offset, e) => - val to_variable = translate_variable(to) - val expr = translate_expression(e) - CStore(to_variable, type_expression(expr).downcast, offset, expr) - case Statements.Call(fun, args, companion) => - CCall(translate_variable(fun), args map translate_expression, companion) - case Statements.SeqComp(s1, s2) => - CSeqComp(translate_statement(s1), translate_statement(s2)) - case Statements.If(cond, tb, eb) => - CIf(translate_expression(cond), translate_statement(tb), translate_statement(eb)) - case Statements.Guarded(cond, body, els, branchPoint) => - CGuarded(translate_expression(cond), translate_statement(body), translate_statement(els), branchPoint) - } - - // first, run program analysis - var prev_context = context - do { - prev_context = context - eval_statement(stmt) - } while (prev_context != context) - - // then translate the body - val body: CStatement = translate_statement(stmt) - - // QED - (body, context) - } - } From edefff0da40b81a271be402f64943b6d2d9b73ad Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 19 Aug 2020 04:01:08 +0100 Subject: [PATCH 029/211] Completed translation of Suslik specifications to VST - now for predicates --- .../certification/targets/vst/VST.scala | 2 +- .../vst/{language => clang}/CTypes.scala | 2 +- .../vst/{language => clang}/Expressions.scala | 5 +- .../{language => clang}/PrettyPrinting.scala | 2 +- .../vst/{language => clang}/Statements.scala | 6 +- .../targets/vst/clang/package.scala | 16 ++ .../targets/vst/language/package.scala | 18 -- .../targets/vst/logic/Formulae.scala | 49 ++-- .../targets/vst/logic/Proof.scala | 125 ++++++-- .../targets/vst/logic/ProofSteps.scala | 9 - .../targets/vst/logic/ProofTypes.scala | 36 ++- .../vst/translation/CTranslation.scala | 12 +- .../vst/translation/ProofTranslation.scala | 270 +++++++++++++++++- .../targets/vst/translation/Translation.scala | 13 +- .../org/tygus/suslik/logic/Declarations.scala | 8 +- 15 files changed, 454 insertions(+), 119 deletions(-) rename src/main/scala/org/tygus/suslik/certification/targets/vst/{language => clang}/CTypes.scala (96%) rename src/main/scala/org/tygus/suslik/certification/targets/vst/{language => clang}/Expressions.scala (89%) rename src/main/scala/org/tygus/suslik/certification/targets/vst/{language => clang}/PrettyPrinting.scala (77%) rename src/main/scala/org/tygus/suslik/certification/targets/vst/{language => clang}/Statements.scala (94%) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index 6b4f4f6ba..7970aea1e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -40,7 +40,7 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. val c_prelude ="""#include \n\nextern void free(void *p);\n\n""" - val (preds, spec, proof, cproc) = Translation.translate(root, proc, env) + val x = Translation.translate(root, proc, env) ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala similarity index 96% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala index b88e75137..a434ae393 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/CTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.vst.language +package org.tygus.suslik.certification.targets.vst.clang object CTypes { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala similarity index 89% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala index 3f18e32b1..07103cc8a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala @@ -1,7 +1,4 @@ -package org.tygus.suslik.certification.targets.vst.language - -import org.tygus.suslik.certification.targets.vst.language.CTypes.{CBoolType, CIntType, VSTCType} -import org.tygus.suslik.language.Expressions.SubstVar +package org.tygus.suslik.certification.targets.vst.clang /** Encodes VST specific C expressions */ object Expressions { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala similarity index 77% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala index e0e884849..25f4f7576 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/PrettyPrinting.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.certification.targets.vst.language +package org.tygus.suslik.certification.targets.vst.clang trait PrettyPrinting { def pp: String = toString diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala similarity index 94% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index 95bbf7b29..6b1c704b9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -1,7 +1,7 @@ -package org.tygus.suslik.certification.targets.vst.language +package org.tygus.suslik.certification.targets.vst.clang -import org.tygus.suslik.certification.targets.vst.language.CTypes.{VSTCType} -import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.{VSTCType} +import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} import org.tygus.suslik.logic.Specifications.GoalLabel /** Encoding of C statements */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala new file mode 100644 index 000000000..a8a6cd628 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala @@ -0,0 +1,16 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType + + +/** + * package contains encoding of suslik terms in C format */ +package object clang { + /** Typing context - maps variables to types */ + type CGamma = Map[CVar, VSTCType] + /** Formal parameter specification - types and names of parameters */ + type CFormals = Seq[(VSTCType, CVar)] + + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala deleted file mode 100644 index fb8a32ce1..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/language/package.scala +++ /dev/null @@ -1,18 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType - - -/** - * package encodes a wrapper around VST terms */ -package object language { - /** Typing context - maps variables to types */ - type CGamma = Map[CVar, VSTCType] - /** Formal parameter specification - types and names of parameters */ - type CFormals = Seq[(VSTCType, CVar)] - - /** type of mapping in context */ - type CtxMapping = (CExpr, VSTCType) - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index c41f0af4a..c659823b1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,21 +1,17 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.CFormals -import org.tygus.suslik.certification.targets.vst.logic.Proof.VSTPredicate -import org.tygus.suslik.language.Expressions.Expr +import org.tygus.suslik.certification.targets.vst.clang.CFormals +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.clang.Expressions.CExpr +import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions +import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, VSTProofType} import org.tygus.suslik.language.PrettyPrinting -import scala.collection.SortedSet - object Formulae { - /** abstract type containing all logical formulae */ - sealed trait VSTFormula - /** abstract type categorizing all spatial formulae */ - sealed trait VSTHeaplet + sealed trait VSTHeaplet extends PrettyPrinting /** Spatial formulae @@ -23,10 +19,11 @@ object Formulae { * */ /** spatial formuala indicating points to - loc + offset :-> value */ - case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends VSTHeaplet { } - - /** spatial formula indicating block allocation [loc; sz] */ - case class CBlock(loc: CExpr, sz: Int) extends VSTHeaplet {} + case class CDataAt(loc: ProofCExpr, elem_typ: VSTProofType, count: Int, elem: ProofCExpr) extends VSTHeaplet { + // returns the type of a proof expression + override def pp: String = + s"(data_at Tsh ${elem_typ.pp_as_ctype} ${elem.pp_as_c_value} ${loc.pp})" + } /** predicate application * @@ -34,26 +31,18 @@ object Formulae { * @param args arguments * @param card cardinality of call **/ - case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends VSTHeaplet {} + case class CSApp(pred: String, var args: Seq[ProofCExpr], card: ProofCExpr) extends VSTHeaplet { + override def pp: String = + s"(${pred} ${(args ++ List(card)).map(_.pp).mkString(" ")})" + } /** Spatial Formula * @param apps applications in the spatial formula * */ - case class CSFormula(apps: Seq[CSApp], ptss: Seq[CPointsTo], blocks: Seq[CBlock]) extends VSTFormula { - override def productIterator: Iterator[VSTHeaplet] = (apps.iterator ++ ptss.iterator ++ blocks.iterator) - } + case class VSTSFormula(apps: Seq[CSApp], data_at: Seq[CDataAt]) { + override def productIterator: Iterator[VSTHeaplet] = (apps.iterator ++ data_at.iterator) + } - case class CInductivePredicate(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { - } - - case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { - } - - case class CFunSpec( - name: String, rType: VSTCType, - params: CFormals, ghostParams: CFormals, - pre: CAssertion, post: CAssertion) extends PrettyPrinting { - } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 103bc5e35..54b9737c3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,16 +1,15 @@ package org.tygus.suslik.certification.targets.vst.logic import com.sun.imageio.plugins.bmp.BMPCompressionTypes -import org.tygus.suslik.certification.targets.vst.language.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.language.{CTypes, Expressions, PrettyPrinting} -import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.ProofStep +import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.clang.{CTypes, Expressions, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType import org.tygus.suslik.language.Ident object Proof { - case class FormalParamType() extends PrettyPrinting - sealed abstract class PureFormula extends PrettyPrinting /** predicate encoding that C-parameter (of type val) is a valid pointer */ @@ -28,11 +27,58 @@ object Proof { /** Redefinition of expressions for use in VST proofs * */ object Expressions { - sealed abstract class ProofCExpr extends PrettyPrinting { } + sealed abstract class ProofCExpr extends PrettyPrinting { + /** prints the expression as though it were an element + * of type val (vst's encoding of C-values) + */ + def pp_as_c_value : String = this match { + case ProofCVar(name, typ) => typ match { + case ProofTypes.CoqParamType(ty) => name + case ProofTypes.CoqPtrType => name + case ProofTypes.CoqIntType => s"(Vint (Int.repr ${name}))" + case ProofTypes.CoqNatType => s"(Vint (Int.repr (Z.of_nat ${name})))" + case ProofTypes.CoqListType(elem, length) => name + } + case ProofCIntConst(value) => s"(Vint (Int.repr ${value.toString}))" + case ProofCSetLiteral(elems) => + s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" + case value@ProofCBinaryExpr(op, _, _) => + val is_int = op match { + case ProofCOpPlus => true + case ProofCOpMinus =>true + case ProofCOpMultiply =>true + case _ => false + } + if (is_int) { + s"(Vint (Int.repr ${value.pp}))" + } else { + value.pp + } + case value@ProofCUnaryExpr(op, _) => op match { + case ProofCOpNot => value.pp + case ProofCOpUnaryMinus => s"(Vint (Int.repr ${value.pp}))" + } + case v => v.pp + } + } /** a variable in a VST proof */ - case class ProofCVar(name: String) extends ProofCExpr { - override def pp: String = s"(force_signed_int ${name})" + case class ProofCVar(name: String, typ: VSTProofType) extends ProofCExpr { + override def pp: String = typ match { + case ProofTypes.CoqPtrType => name + case ProofTypes.CoqIntType => name + case ProofTypes.CoqNatType => name + case ProofTypes.CoqParamType(ty) => + // if the variable has a param type then + // its actually of type val, and we need to + // extract it's contained value + ty match { + case CTypes.CIntType => s"(force_signed_int ${name})" + case CTypes.CVoidPtrType => s"${name}" + case CTypes.CUnitType => ??? /// Unimplemented as we should never be dealing with unit types + } + case ProofTypes.CoqListType(elem, _) => name + } } /** boolean constant in a VST proof */ @@ -41,7 +87,7 @@ object Proof { } /** integer constant in a VST proof */ - case class ProofCNatConst(value: Int) extends ProofCExpr { + case class ProofCIntConst(value: Int) extends ProofCExpr { override def pp: String = value.toString } @@ -67,25 +113,26 @@ object Proof { case ProofCOpPlus => s"(${left.pp} + ${right.pp})" case ProofCOpMinus => s"(${left.pp} - ${right.pp})" case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" + case ProofCOpIntEq => s"(Zeq_bool ${left.pp} ${right.pp})" + case ProofCOpBoolEq => s"(eqb ${left.pp} ${right.pp})" - // no real good way of doing equality - case ProofCOpEq => ??? + // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" } } case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { override def pp: String = op match { - case ProofCOpNot$ => s"(negb ${e.pp})" - case ProofCOpUnaryMinus$ => s"(-(${e.pp}))" + case ProofCOpNot => s"(negb ${e.pp})" + case ProofCOpUnaryMinus => s"(-(${e.pp}))" } } sealed abstract class ProofCUnOp - object ProofCOpNot$ extends ProofCUnOp + object ProofCOpNot extends ProofCUnOp - object ProofCOpUnaryMinus$ extends ProofCUnOp + object ProofCOpUnaryMinus extends ProofCUnOp sealed abstract class ProofCBinOp @@ -96,7 +143,11 @@ object Proof { object ProofCOpMultiply extends ProofCBinOp - object ProofCOpEq extends ProofCBinOp + object ProofCOpIntEq extends ProofCBinOp + + object ProofCOpBoolEq extends ProofCBinOp + + object ProofCOpSetEq extends ProofCBinOp object ProofCOpLeq extends ProofCBinOp @@ -105,22 +156,36 @@ object Proof { object ProofCOpAnd extends ProofCBinOp object ProofCOpOr extends ProofCBinOp + + object ProofCOpIn extends ProofCBinOp + + object ProofCOpSubset extends ProofCBinOp + + object ProofCOpUnion extends ProofCBinOp + object ProofCOpDiff extends ProofCBinOp + object ProofCOpIntersect extends ProofCBinOp + } - /** predicate encoding that a given boolean expression is true - * c_params are the list of parameter variables + /** prop predicate encoding that a given propositional expression is true * */ + case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { + override def pp: String = { + s"${expr.pp}" + } + } + + /** prop predicate encoding that a given boolean expression is true */ case class IsTrue(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { s"(is_true ${expr.pp})" } } - sealed case class SpatialFormula() extends PrettyPrinting sealed case class FormalCondition( pure_constraints: List[PureFormula], - spatial_constraints: List[SpatialFormula] + spatial_constraints: List[VSTHeaplet] ) /** @@ -135,9 +200,9 @@ object Proof { */ case class FormalSpecification( name: Ident, - c_params: List[(Ident,VSTCType)], - formal_params: List[(Ident,FormalParamType)], - existensial_params: List[(Ident,FormalParamType)], + c_params: Seq[(Ident,VSTCType)], + formal_params: Seq[(Ident,VSTProofType)], + existensial_params: Seq[(Ident,VSTProofType)], precondition: FormalCondition, postcondition: FormalCondition, return_type: VSTCType @@ -161,12 +226,12 @@ object Proof { | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) | PARAMS(${c_params.map({case (var_name, _) => var_name}).mkString("; ")}) | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) - | POST[ ${as_vst_type(return_type)} ]|${existensial_params match { + | POST[ ${as_vst_type(return_type)} ]${existensial_params match { case Nil => "" case _ => - existensial_params.map({case (param_name, param_type) => s" EX ${param_name}: ${param_type.pp}"}).mkString("\n") - } - } + "\n" ++ + existensial_params.map({case (param_name, param_type) => s"| EX ${param_name}: ${param_type.pp},"}).mkString("\n") + }} | PROP( ${post_pure_constraints.map(_.pp).mkString("; ")} ) | LOCAL() | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). @@ -179,13 +244,13 @@ object Proof { } case class VSTPredicate( predicate_name: Ident, - params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType)], + params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType)], ) { } - case class Proof(root: ProofStep, params: Seq[CVar]) { + case class Proof(params: Seq[CVar]) { } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala deleted file mode 100644 index 064a987ce..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala +++ /dev/null @@ -1,9 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.logic - -object ProofSteps { - - sealed abstract class ProofStep { - def pp: String = "" - } - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala index ed4ea5e57..a003bd271 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.language.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.language.{CTypes, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} /** Encapsulates all types used in proofs - i.e if these types are pretty printed, then they will be valid Coq terms */ @@ -9,24 +9,46 @@ object ProofTypes { def proof_type_of_c_type(vst: VSTCType) : VSTProofType = vst match { - case CTypes.CIntType => CoqNatType - case CTypes.CVoidPtrType => CoqPtrType + case CTypes.CIntType => CoqParamType(vst) + case CTypes.CVoidPtrType => CoqParamType(vst) case CTypes.CUnitType => ??? } /** proof types */ - sealed abstract class VSTProofType extends PrettyPrinting + sealed abstract class VSTProofType extends PrettyPrinting { + def pp_as_ctype: String = + this match { + case CoqParamType(ty) => ty match { + case CTypes.CIntType => "tint" + case CTypes.CVoidPtrType => "(tptr tvoid)" + } + case CoqPtrType => "(tptr tvoid)" + case CoqIntType => "tint" + case CoqListType(elem, length) => s"(tarray ${elem.pp_as_ctype} ${length.get})" + } + } + + case class CoqParamType(ty: VSTCType) extends VSTProofType { + override def pp : String = "val" + } case object CoqPtrType extends VSTProofType { override def pp: String = "val" } - case object CoqNatType extends VSTProofType { + /** type of integers (used to represent the values of variables in a C program) */ + case object CoqIntType extends VSTProofType { override def pp:String = "Z" } - sealed case class CoqListType(elem: VSTProofType) extends VSTProofType { + /** type of natural numbers (used to type metavariables in Coq proofs) */ + case object CoqNatType extends VSTProofType { + override def pp:String = "nat" + } + + sealed case class CoqListType(elem: VSTProofType, length: Option[Int]) extends VSTProofType { override def pp: String = s"(list ${elem.pp})" } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 30a210421..1c9835d7e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -1,9 +1,9 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.targets.vst.language.CTypes -import org.tygus.suslik.certification.targets.vst.language.CTypes._ -import org.tygus.suslik.certification.targets.vst.language.Expressions._ -import org.tygus.suslik.certification.targets.vst.language.Statements._ +import org.tygus.suslik.certification.targets.vst.clang.CTypes +import org.tygus.suslik.certification.targets.vst.clang.CTypes._ +import org.tygus.suslik.certification.targets.vst.clang.Expressions._ +import org.tygus.suslik.certification.targets.vst.clang.Statements._ import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Procedure, Statement} @@ -25,7 +25,7 @@ object CTranslation { def translate_variable : Var => CVar = { case Var(name) => CVar(name) } - /* translates a unary operation to a C expression */ + /** translates a unary operation to a C expression */ def translate_binary_op: BinOp => CBinOp = { case OpPlus => COpPlus case OpMinus => COpMinus @@ -95,7 +95,7 @@ object CTranslation { } - /** translates a suslik encoding of a procedure to a C one */ + /** translates a suslik encoding of a procedure to a VST C one */ def translate_function(proc:Procedure, gamma: Gamma) = { val ctx: Map[CVar, AnalysisVSTTypes] = gamma diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 84d663d09..06a2f023d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -1,6 +1,272 @@ package org.tygus.suslik.certification.targets.vst.translation -/** translates suslik terms to VST compatible terms */ -class ProofTranslation { +import org.tygus.suslik.certification.targets.htt.language.Expressions.CPointsTo +import org.tygus.suslik.certification.targets.vst.clang.CTypes +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} +import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTypes} +import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.Proof.{FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, CoqNatType, CoqPtrType, VSTProofType} +import org.tygus.suslik.language.Expressions.Var +import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} +import org.tygus.suslik.logic.{Block, FunSpec, Gamma, Heaplet, PointsTo, SApp} +import org.tygus.suslik.logic.Specifications.{Assertion, Goal} +import scala.collection.immutable + +/** translates suslik proof terms to VST compatible proof terms */ +object ProofTranslation { + + def translate_type(lType: SSLType): VSTProofType = + lType match { + case IntType => CoqIntType + case LocType => CoqPtrType + case CardType => CoqNatType + + // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types + // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible + // but doesn't mesh well with C in which an allocated array must have all elements of the same type + // otherwise a separate struct definition would be needed + case IntSetType => CoqListType(CoqPtrType, None) + } + + + def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): Proof.Expressions.ProofCExpr = { + def type_expr(left_1: ProofCExpr): VSTProofType = ??? + + def translate_binop(op: Expressions.BinOp): ProofCBinOp = { + op match { + case op: Expressions.RelOp => op match { + case Expressions.OpEq => ProofCOpIntEq + case Expressions.OpBoolEq => ProofCOpBoolEq + case Expressions.OpLeq => ProofCOpLeq + case Expressions.OpLt => ProofCOpLt + case Expressions.OpIn => ProofCOpIn + case Expressions.OpSetEq => ProofCOpSetEq + case Expressions.OpSubset => ProofCOpSubset + } + case op: Expressions.LogicOp => op match { + case Expressions.OpAnd => ProofCOpAnd + case Expressions.OpOr => ProofCOpOr + } + case Expressions.OpImplication => ProofCOpImplication + case Expressions.OpPlus => ProofCOpPlus + case Expressions.OpMinus => ProofCOpMinus + case Expressions.OpMultiply => ProofCOpMultiply + case Expressions.OpUnion => ProofCOpUnion + case Expressions.OpDiff => ProofCOpDiff + case Expressions.OpIntersect => ProofCOpIntersect + } + } + + expr match { + case const: Expressions.Const => const match { + case Expressions.IntConst(value) => ProofCIntConst(value) + case Expressions.BoolConst(value) => ProofCBoolConst(value) + } + case Var(name) => ProofCVar(name, context(name)) + case Expressions.SetLiteral(elems) => { + ProofCSetLiteral(elems.map(translate_expression(context))) + } + case Expressions.UnaryExpr(op, arg) => + val top: ProofCUnOp = op match { + case Expressions.OpNot => ProofCOpNot + case Expressions.OpUnaryMinus => ProofCOpUnaryMinus + } + ProofCUnaryExpr(top, translate_expression(context)(arg)) + case Expressions.BinaryExpr(op, left, right) => + val top: ProofCBinOp = translate_binop(op) + ProofCBinaryExpr(top, translate_expression(context)(left), translate_expression(context)(right)) + case Expressions.IfThenElse(cond, left, right) => + ProofCIfThenElse( + translate_expression(context)(cond), + translate_expression(context)(left), + translate_expression(context)(right) + ) + case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => + val left_1 = translate_expression(context)(left) + val right_1 = translate_expression(context)(right) + overloaded_op match { + case op: Expressions.BinOp => + ProofCBinaryExpr(translate_binop(op), left_1, right_1) + case Expressions.OpOverloadedEq => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1) + case CoqListType(_, _) => + ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1) + case ProofTypes.CoqPtrType => ??? /// TODO: Handle pointer equality? or fail? + } + case Expressions.OpNotEqual => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqIntType => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1)) + case CoqListType(elem, _) => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1)) + case ProofTypes.CoqPtrType => ??? // TODO: Handle pointer equality? or fail? + } + case Expressions.OpGt => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLeq, left_1, right_1)) + case Expressions.OpGeq => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLt, left_1, right_1)) + case Expressions.OpOverloadedPlus => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpPlus, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpUnion, left_1, right_1) + } + case Expressions.OpOverloadedMinus => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpMinus, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpDiff, left_1, right_1) + } + case Expressions.OpOverloadedLeq => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpLeq, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpSubset, left_1, right_1) + } + case Expressions.OpOverloadedStar => ??? // TODO: Handle star operation + } + + } + } + + + def type_expr(context: Map[Ident, VSTProofType]) (cvalue: Proof.Expressions.ProofCExpr) : VSTProofType = + cvalue match { + case ProofCVar(name, typ) => typ + case ProofCIntConst(value) => CoqIntType + case ProofCSetLiteral(elems) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) + case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) + case ProofCBinaryExpr(op, left, right) => op match { + case ProofCOpPlus => CoqIntType + case ProofCOpMinus => CoqIntType + case ProofCOpMultiply => CoqIntType + } + case ProofCUnaryExpr(op, e) => op match { + case ProofCOpUnaryMinus => CoqIntType + } + } + + def translate_heaplets(context: Map[Ident, VSTProofType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { + var initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty + + var (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = + heaplets.foldLeft((initial_map, List(): List[CSApp]))({ + case ((map, acc), ty: Heaplet) => + ty match { + case ty@PointsTo(loc@Var(name), offset, value) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(ty), None)) + case Some((points_to_acc: List[_], block_acc)) => + map.updated(name, (List(ty) ++ points_to_acc, block_acc)) + } + (updated_map, acc: List[CSApp]) + case ty@Block(loc@Var(name), sz) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(), Some(ty))) + case Some((points_to_acc, None)) => map.updated(name, (points_to_acc, Some(ty))) + } + (updated_map, acc: List[CSApp]) + case SApp(pred, args, tag, card) => + (map, (List(CSApp(pred, args.map(translate_expression((context))), translate_expression(context)(card))) ++ acc) + ) + } + }) + val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => + o_block match { + case Some((_@Block(loc,sz))) => + val loc_pos = translate_expression(context)(loc) + val o_array : Array[Option[ProofCExpr]] = Array.fill(sz)(None) + points_to.foreach({case PointsTo(_, offset, value) => + o_array.update(offset, Some(translate_expression(context)(value))) + }) + val elems = o_array.map(_.get).toList + val elem_type = type_expr(context)(elems.head) + CDataAt(loc_pos, CoqListType(elem_type, Some(sz)), sz, ProofCSetLiteral(elems)) + case None => + assert( + points_to.length == 1, + "found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block" + ) + (points_to.head : PointsTo) match { + case PointsTo(loc, 0, value) => + val c_value = translate_expression(context)(value) + CDataAt(translate_expression(context)(loc), type_expr(context)(c_value), 0, c_value) + case PointsTo(_, _, _) => + assert(false, "found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") + ??? + } + } + }).toList + + blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) + } + + /** translates a Suslik function specification into a proof */ + def translate_conditions(proc: CProcedureDefinition)(goal: Goal): FormalSpecification = { + + val name: Ident = proc.name + val c_params: Seq[(Ident, VSTCType)] = proc.params.map({ case (CVar(name), cType) => (name, cType) }) + + val formal_params: List[(Ident, VSTProofType)] = { + val c_param_set = c_params.map(_._1).toSet + goal.universals.map({ case variable@Var(name) => (name, translate_type(goal.gamma(variable))) }) + .filterNot({case (name, _) => c_param_set.contains(name)}).toList + } + val existential_params: List[(Ident, VSTProofType)] = + goal.existentials.map({ case variable@Var(name) => (name, translate_type(goal.gamma(variable))) }).toList + + val return_type: VSTCType = proc.rt + + val context = ( + formal_params ++ existential_params ++ + c_params.map({ case (ident, cType) => (ident, ProofTypes.proof_type_of_c_type(cType)) }) + ).toMap + + val precondition: FormalCondition = { + val pure_conditions = + goal.pre.phi.conjuncts.map(translate_expression(context)) + .map(IsTrueProp).toList ++ c_params.flatMap({ case (ident, cType) => + cType match { + case CTypes.CIntType => Some(IsValidInt(CVar(ident))) + case CTypes.CUnitType => None + case CTypes.CVoidPtrType => Some(IsValidPointerOrNull(CVar(ident))) + } + }) + val spatial_conditions: List[VSTHeaplet] = + translate_heaplets(context)(goal.pre.sigma.chunks) + + FormalCondition(pure_conditions, spatial_conditions) + } + val postcondition: FormalCondition = { + val pure_conditions = + goal.post.phi.conjuncts.map(translate_expression(context)) + .map(IsTrueProp).toList + val spatial_conditions = + translate_heaplets(context)(goal.post.sigma.chunks) + // goal.post.sigma.chunks.map(translate_heaplet(context)).toList + FormalCondition(pure_conditions, spatial_conditions) + } + + FormalSpecification( + name, c_params, formal_params, existential_params, precondition, postcondition, return_type + ) + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 24b2964fd..b869b5483 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -2,10 +2,9 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.vst.language.Statements.CProcedureDefinition -import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CFunSpec, CInductivePredicate} +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof -import org.tygus.suslik.certification.targets.vst.language.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -14,11 +13,13 @@ object Translation { case class TranslationException(msg: String) extends Exception(msg) - def translate(root: CertTree.Node, proc: Procedure, env: Environment): - (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedureDefinition) = { + def translate(root: CertTree.Node, proc: Procedure, env: Environment): Nothing = { val procedure = CTranslation.translate_function(proc, root.goal.gamma) + val spec = ProofTranslation.translate_conditions(procedure)(root.goal) println(procedure.pp) - print(root.goal.gamma) + println(spec.pp) + println(root.goal.gamma) + // translate body into VST types, and build context of variables // var (body, ctx) = CTranslation.translate_body(proc.f, proc.body, root.goal.gamma) // use this to build a C-encoding of the synthesized function diff --git a/src/main/scala/org/tygus/suslik/logic/Declarations.scala b/src/main/scala/org/tygus/suslik/logic/Declarations.scala index 3b88e5976..c85ca822c 100644 --- a/src/main/scala/org/tygus/suslik/logic/Declarations.scala +++ b/src/main/scala/org/tygus/suslik/logic/Declarations.scala @@ -22,9 +22,14 @@ sealed abstract class TopLevelDeclaration extends PrettyPrinting with PureLogicU * * @param name function name * @param rType function return type + * @param params function parameters names and types + * @param pre precondition + * @param post post-condition + * @param var_decl local variable of function? */ case class FunSpec(name: Ident, rType: SSLType, params: Formals, - pre: Assertion, post: Assertion, var_decl: Formals = Nil) extends TopLevelDeclaration { + pre: Assertion, post: Assertion, + var_decl: Formals = Nil) extends TopLevelDeclaration { def resolveOverloading(env: Environment): FunSpec = { val gamma0 = params.toMap // initial environment: derived from the formals @@ -198,5 +203,6 @@ case class Environment(predicates: PredicateEnv, functions: FunctionEnv, this.copy(predicates = predicates.map{case (k,v) => (k, v.resolveOverloading(this))}, functions=functions.map{case (k,v) => (k, v.resolveOverloading(this))}) } + } From 04a651c9b9795b59005e1f1426a24dba7e7c6564 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 19 Aug 2020 19:22:58 +0100 Subject: [PATCH 030/211] Implemented translation of predicates (mostly except for small expression issues). --- .../targets/vst/logic/Proof.scala | 163 +++++++++++++++++- .../vst/translation/ProofTranslation.scala | 131 +++++++++++++- .../targets/vst/translation/Translation.scala | 16 +- 3 files changed, 297 insertions(+), 13 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 54b9737c3..40bdfed1d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar import org.tygus.suslik.certification.targets.vst.clang.{CTypes, Expressions, PrettyPrinting} import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet +import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType import org.tygus.suslik.language.Ident @@ -115,7 +116,9 @@ object Proof { case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" case ProofCOpIntEq => s"(Zeq_bool ${left.pp} ${right.pp})" case ProofCOpBoolEq => s"(eqb ${left.pp} ${right.pp})" - + case ProofCOpPtrEq => s"(${left.pp} = ${right.pp})" + case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" + case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" } } @@ -149,6 +152,8 @@ object Proof { object ProofCOpSetEq extends ProofCBinOp + object ProofCOpPtrEq extends ProofCBinOp + object ProofCOpLeq extends ProofCBinOp object ProofCOpLt extends ProofCBinOp @@ -239,17 +244,163 @@ object Proof { } } - case class VSTClause() { + /** + * Abstract constructors mapping cardinality constraints to + * termination measures in Coq + */ + sealed abstract class CardConstructor extends PrettyPrinting + /** + * Null constructor of 0 cardinality + */ + case object CardNull extends CardConstructor {} + /** Cardinality constructor of multiple components + * @param args the variables produced by unwrwapping this element + */ + case class CardOf(args: List[Ident]) extends CardConstructor {} - } + + /** represents a clause of the VST predicate, + * @param pure are the pure assertions + * @param spatial are the spatial assertions + * @param sub_constructor are the subconstructors + * */ + case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String,CardConstructor]) + /** + * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker + * + * The idea is to conver the cardinality constraints produced by suslik into an inductive datatype + * with the expectation that each clause of the suslik predicate maps to a unique constructor + * + * Constructors are identified by their cardinality, and each suslik predicate maps to a unique cardinality datatype + * + * @param name is the name of the predicate + * @param params is the list of arguments to the predicate + * @param clauses is the mapping from cardinality constructors to clauses + * */ case class VSTPredicate( - predicate_name: Ident, - params: Seq[(CVar, org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType)], + name: Ident, params: List[(String,VSTProofType)], + existentials: List[(String, VSTProofType)], + clauses: Map[CardConstructor, VSTPredicateClause]) + extends PrettyPrinting { + + def constructors: List[CardConstructor] = + clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => + constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor}) + }).toList + + def base_constructors: List[CardConstructor] = + clauses.map({ case (constructor, _) => + constructor + }).toList + + def constructor_args (constructor: CardConstructor) = + constructor match { + case CardNull => Nil + case CardOf(args) => args + } - ) { + def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { + val param_map = params.toMap + val exist_map : Map[String, VSTProofType] = existentials.toMap + val card_map = constructor_args(cons) + pclause match { + case VSTPredicateClause(pure, spatial, sub_clauses) => + val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => constructor_args(cons)})).toSet + def to_variables(exp: ProofCExpr) : List[String] = exp match { + case Expressions.ProofCVar(name, typ) => + param_map.get(name) match { + case None if !clause_card_map.contains(name) => List(name) + case _ => List() + } + case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) + case Expressions.ProofCIfThenElse(cond, left, right) => + to_variables(cond) ++ to_variables(left) ++ to_variables(right) + case Expressions.ProofCBinaryExpr(op, left, right) => + to_variables(left) ++ to_variables(right) + case Expressions.ProofCUnaryExpr(op, e) => + to_variables(e) + case _ => List() + } + def to_variables_heap(heap: VSTHeaplet) : List[String] = heap match { + case Formulae.CDataAt(loc, elem_typ, count, elem) => + to_variables(loc) ++ to_variables(elem) + case Formulae.CSApp(pred, args, card) => + args.flatMap(to_variables).toList + } + (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap) : List[String]) + .toSet + .map((v: String) => (v, exist_map(v))).toList + } + } + override def pp: String = { + val constructor_map = base_constructors.map({ + case CardNull => (0, CardNull ) + case v@CardOf(args) => (args.length, v) + }).toMap + + val inductive_name = s"${name}_card" + + def constructor_name (constructor: CardConstructor) = { + val count = constructor match { + case CardNull => 0 + case CardOf(args) => args.length + } + s"${inductive_name}_${count}" + } + def pp_constructor (constructor: CardConstructor) = { + constructor match { + case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" + case CardOf(args) => + s"${constructor_name(constructor)} : ${(args ++ List(inductive_name)).map(_ => inductive_name).mkString(" -> ")}" + } + } + + val inductive_definition = { + s"""Inductive ${inductive_name} : Set := + ${constructor_map.map({ case (_, constructor) => + s"| | ${pp_constructor(constructor)}" + }).mkString("\n")}. + | + |""".stripMargin + } + + def expand_args(sub_constructor: Map[String, CardConstructor]) (idents: List[Ident]) : String = { + idents.map(arg => + sub_constructor.get(arg) match { + case Some(constructor) => + s"(${constructor_name(constructor)} ${expand_args(sub_constructor)(constructor_args(constructor))} as ${arg})" + case None => arg + } + ).mkString(" ") + } + val predicate_definition = + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})"}).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with + ${clauses.map({case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => + s"| | ${constructor_name(constructor)} ${ + expand_args(sub_constructor)(constructor_args(constructor)) + } => ${ + val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) + val str = clause_existentials.map({case (name, ty) => s"| EX ${name} : ${ty.pp},"}).mkString("\n") + clause_existentials match { + case Nil => "" + case ::(_, _) => "\n" + str + "\n" + } + } ${ + (pure.map(v => s"!!${v.pp}") + ++ + List((spatial match { case Nil => List("emp") case v => v.map(_.pp)}).mkString(" * "))).mkString(" && ") + }" + }).mkString("\n")} + |end. + |""".stripMargin + /// IMPLEMENT THIS + s"${inductive_definition}${predicate_definition}" + } } + + case class Proof(params: Seq[CVar]) { } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 06a2f023d..a6f442500 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -1,18 +1,20 @@ package org.tygus.suslik.certification.targets.vst.translation +import java.io + import org.tygus.suslik.certification.targets.htt.language.Expressions.CPointsTo -import org.tygus.suslik.certification.targets.vst.clang.CTypes +import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTypes} -import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.Proof.{FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull} +import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.Proof.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, CoqNatType, CoqPtrType, VSTProofType} import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} -import org.tygus.suslik.logic.{Block, FunSpec, Gamma, Heaplet, PointsTo, SApp} +import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import scala.collection.immutable @@ -35,7 +37,23 @@ object ProofTranslation { def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): Proof.Expressions.ProofCExpr = { - def type_expr(left_1: ProofCExpr): VSTProofType = ??? + def type_expr(left_1: ProofCExpr): VSTProofType = + left_1 match { + case ProofCVar(name, typ) => typ + case ProofCIntConst(value) => CoqIntType + case ProofCSetLiteral(elems) => CoqListType(CoqPtrType, Some(elems.length)) + case ProofCIfThenElse(cond, left, right) => type_expr(left) + case ProofCBinaryExpr(op, left, right) => + op match { + case ProofCOpPlus => CoqIntType + case ProofCOpMinus => CoqIntType + case ProofCOpMultiply => CoqIntType + case ProofCOpUnion => CoqListType(CoqPtrType, None) + } + case ProofCUnaryExpr(op, e) => op match { + case ProofCOpUnaryMinus => CoqIntType + } + } def translate_binop(op: Expressions.BinOp): ProofCBinOp = { op match { @@ -99,7 +117,8 @@ object ProofTranslation { ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1) case CoqListType(_, _) => ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1) - case ProofTypes.CoqPtrType => ??? /// TODO: Handle pointer equality? or fail? + case ProofTypes.CoqPtrType => + ProofCBinaryExpr(ProofCOpPtrEq, left_1, right_1) } case Expressions.OpNotEqual => val l1_ty: VSTProofType = type_expr(left_1) @@ -269,4 +288,104 @@ object ProofTranslation { name, c_params, formal_params, existential_params, precondition, postcondition, return_type ) } + + + /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map + * from cardinality name to constructors */ + def build_card_cons(card_conds: List[(String, String)]): Map[String, CardOf] = { + var child_map : Map[String, List[String]] = Map.empty + card_conds.foreach({case (child, parent) => + child_map.get(parent) match { + case None => child_map = child_map.updated(parent, List(child)) + case Some(children) => child_map = child_map.updated(parent, child :: children) + }}) + child_map.map({ case (str, strings) => (str, CardOf(strings))}) + } + + + def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { + + + def is_card (s: String) : Boolean = s.startsWith("_") || s.contentEquals("self_card") + def extract_card_constructor(expr: Expressions.Expr) : Option[(String, String)] = { + expr match { + case Expressions.BinaryExpr(op, Var(left), Var(parent)) + if is_card(left) && is_card(parent) => + op match { + case op: Expressions.RelOp => op match { + case Expressions.OpLt => + Some ((left, parent)) + case _ => None + } + case _ => None + } + case Expressions.OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) + if is_card(left) && is_card(parent) => + overloaded_op match { + case op: Expressions.BinOp => op match { + case op: Expressions.RelOp => op match { + case Expressions.OpLt => Some ((left, parent)) + case _ => None + } + case _ => None + } + case Expressions.OpGt =>Some ((parent, left)) + case _ => None + } + case _ => None + } + + } + + val base_context : List[(Ident, VSTProofType)] = { + var gamma: Gamma = Map.empty + predicate match { + case InductivePredicate(name, params, clauses) => + clauses.foreach({case InductiveClause(selector, assn) => + selector.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) + assn.phi.conjuncts.foreach(expr => + expr.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) + ) + assn.sigma.resolve(gamma, env).foreach(v => gamma = v) + }) + } + gamma.map({case (Var(name), ty) => (name, translate_type(ty))}).toList + } + + predicate match { + case InductivePredicate(name, raw_params, raw_clauses) => { + + val params: List[(String, VSTProofType)] = + raw_params.map({case (Var(name), sType) => (name, translate_type(sType))}) + val context: Map[Ident, VSTProofType] = (base_context ++ params).toMap + + + // separate clauses by cardinality constructors + // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a + // unique cardinality constraint + val clauses: Map[CardConstructor, VSTPredicateClause] = raw_clauses.map({ + case InductiveClause(selector, asn) => + var (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => + extract_card_constructor(expr) match { + case value@Some(_) => (None, value) + case None => (Some(expr), None) + } + ).toList.unzip + + val select = translate_expression(context)(selector) + val conds = r_conds.flatMap({ case v => v }).map(translate_expression(context)).toList + val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) + val card_conds = r_card_conds.flatMap({ case v => v }) + + card_conds match { + case card_conds@(::(_, _)) => + val card_cons : Map[String, CardConstructor] = build_card_cons(card_conds) + (card_cons("self_card"), VSTPredicateClause(select :: conds, spat_conds, card_cons)) + case Nil => (CardNull, VSTPredicateClause(select :: conds, spat_conds, Map.empty)) + } + }).toMap + VSTPredicate(name, params, base_context, clauses) + } + } + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index b869b5483..e5d37b967 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -3,11 +3,14 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition -import org.tygus.suslik.certification.targets.vst.logic.Proof.Proof +import org.tygus.suslik.certification.targets.vst.logic.Proof.{Proof, VSTPredicate} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import scala.collection.immutable + object Translation { case class TranslationException(msg: String) extends Exception(msg) @@ -18,8 +21,19 @@ object Translation { val spec = ProofTranslation.translate_conditions(procedure)(root.goal) println(procedure.pp) println(spec.pp) + println( + env.predicates.head._2.pp + + ) + val predicates: List[VSTPredicate] = env.predicates.map({ case (_, predicate) => + ProofTranslation.translate_predicate(env)(predicate) + }).toList + + predicates.foreach(v => println(v.pp)) println(root.goal.gamma) + + // translate body into VST types, and build context of variables // var (body, ctx) = CTranslation.translate_body(proc.f, proc.body, root.goal.gamma) // use this to build a C-encoding of the synthesized function From 6201fdbb895f46db141a0ba2d19061ffe2dc8027 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 20 Aug 2020 03:03:23 +0100 Subject: [PATCH 031/211] Added better documentation for proof translation and proof sections. --- .../targets/vst/logic/Proof.scala | 55 +++++++++--- .../vst/translation/ProofTranslation.scala | 90 +++++++++++++++++-- 2 files changed, 129 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 40bdfed1d..06564e4f8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -283,11 +283,29 @@ object Proof { clauses: Map[CardConstructor, VSTPredicateClause]) extends PrettyPrinting { + /** returns all instances of constructors and the bindings they expose */ def constructors: List[CardConstructor] = clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor}) }).toList + /** returns all the constructors used in the base match, assumed to be a superset + * of the constructors used elsewhere + * + * Note: to see how there might be constructors elsewhere, suppose we had a cardinality + * constraint of the form: + * + * - `a < self_card` + * + * - `b < a` + * + * Then the corresponding match case would look like: + * + * `pred_1 ((pred_1 b) as a) => ... ` + * + * I don't this this actually happens in practice, so this is probably just a bit of + * over-engineering + * */ def base_constructors: List[CardConstructor] = clauses.map({ case (constructor, _) => constructor @@ -299,6 +317,13 @@ object Proof { case CardOf(args) => args } + /** + * For a given clause of the predicate and its associated constructor, + * return the list of existential variables used in the body of the clause + * @param cons a constructor matching some clause of the predicate + * @param pclause the corresponding clause of the predicate + * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause + * */ def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { val param_map = params.toMap val exist_map : Map[String, VSTProofType] = existentials.toMap @@ -333,21 +358,27 @@ object Proof { } } + /** returns the name of the associated cardinality datatype + * for this predicate */ + def inductive_name : String = s"${name}_card" + + /** Given a cardinality constructor, return the Coq name of the + * associated cardinality constructor */ + def constructor_name (constructor: CardConstructor) : String = { + val count = constructor match { + case CardNull => 0 + case CardOf(args) => args.length + } + s"${inductive_name}_${count}" + } + + /** pretty print the constructor */ override def pp: String = { val constructor_map = base_constructors.map({ case CardNull => (0, CardNull ) case v@CardOf(args) => (args.length, v) }).toMap - val inductive_name = s"${name}_card" - - def constructor_name (constructor: CardConstructor) = { - val count = constructor match { - case CardNull => 0 - case CardOf(args) => args.length - } - s"${inductive_name}_${count}" - } def pp_constructor (constructor: CardConstructor) = { constructor match { case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" @@ -365,6 +396,10 @@ object Proof { |""".stripMargin } + // This function expands the arguments of a constructor and + // creates recursive pattern matches if necassary - i.e + // + // S ((S b) as a) => ..... def expand_args(sub_constructor: Map[String, CardConstructor]) (idents: List[Ident]) : String = { idents.map(arg => sub_constructor.get(arg) match { @@ -374,6 +409,7 @@ object Proof { } ).mkString(" ") } + val predicate_definition = s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})"}).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with ${clauses.map({case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => @@ -394,7 +430,6 @@ object Proof { }).mkString("\n")} |end. |""".stripMargin - /// IMPLEMENT THIS s"${inductive_definition}${predicate_definition}" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index a6f442500..743cb3946 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -22,6 +22,7 @@ import scala.collection.immutable /** translates suslik proof terms to VST compatible proof terms */ object ProofTranslation { + /** translate a suslik type into a VST proof type */ def translate_type(lType: SSLType): VSTProofType = lType match { case IntType => CoqIntType @@ -36,6 +37,7 @@ object ProofTranslation { } + /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) */ def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): Proof.Expressions.ProofCExpr = { def type_expr(left_1: ProofCExpr): VSTProofType = left_1 match { @@ -167,6 +169,9 @@ object ProofTranslation { } + /** given a VST proof expression and a typing context, + * this function will type the expression and return + * a type */ def type_expr(context: Map[Ident, VSTProofType]) (cvalue: Proof.Expressions.ProofCExpr) : VSTProofType = cvalue match { case ProofCVar(name, typ) => typ @@ -183,10 +188,32 @@ object ProofTranslation { } } + /** + * Translate a list of suslik heaplets into a form accepted by VST + * @param context the typing context + * @param heaplets a list of suslik heaplets + * @return a VST encoding of these heaplets + * + * Note: Suslik encodes blocks of pointers slightly differently to + * VST - when dealing with a block of contiguous pointers in memory, + * Suslik first uses a block declaration to specify the size of the + * contiguous block, and then has a number of subsequent heaplets + * that assign values to each element of this block. + * + * VST combines these two declarations into one: `data_at` - a `data_at` declaration + * states what a given pointer points to - in the case of contiguous memory, + * we must list out the corresponding values in order - just as they would be encoded in memory + * + * This function performs the translation between suslik's encoding and VST's encoding + */ def translate_heaplets(context: Map[Ident, VSTProofType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { - var initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty + val initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty - var (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = + // we first build up a mapping from pointer variables + // to the declarations that relate to them + // predicate applications are separated out unchanged + // as these translate directly to vst + val (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = heaplets.foldLeft((initial_map, List(): List[CSApp]))({ case ((map, acc), ty: Heaplet) => ty match { @@ -208,6 +235,9 @@ object ProofTranslation { ) } }) + + // having built the mapping, we then translate each (k,v) pair in this + // mapping into a VST Data at declaration val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => o_block match { case Some((_@Block(loc,sz))) => @@ -235,6 +265,7 @@ object ProofTranslation { } }).toList + // return the blocks and the applications blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) } @@ -251,7 +282,6 @@ object ProofTranslation { } val existential_params: List[(Ident, VSTProofType)] = goal.existentials.map({ case variable@Var(name) => (name, translate_type(goal.gamma(variable))) }).toList - val return_type: VSTCType = proc.rt val context = ( @@ -293,20 +323,60 @@ object ProofTranslation { /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map * from cardinality name to constructors */ def build_card_cons(card_conds: List[(String, String)]): Map[String, CardOf] = { + // to perform this translation, we first construct a mapping of relations + // where for every constraint of the form (a < b), we set map(b) = a :: map(b), + // thus the mapping for a variable contains the list of other variables that are + // constrainted to be immediately smaller than it var child_map : Map[String, List[String]] = Map.empty card_conds.foreach({case (child, parent) => child_map.get(parent) match { case None => child_map = child_map.updated(parent, List(child)) case Some(children) => child_map = child_map.updated(parent, child :: children) }}) + // the keys of the map now become variables that are destructured + // in the match case to produce the variables immediately below it child_map.map({ case (str, strings) => (str, CardOf(strings))}) } + /** translates suslik's inductive predicate into a format that is + * accepted by VST + * + * In order to do this, we make use of the cardinality constraints that are + * associated with each clause, and use this to construct an inductive data + * type that encodes the proof of termination + * + * For example, consider the lseg predicate + * + * lseg(x, s) { + * + * x == 0 ==> ... (no cardinality constraints) + * + * x <> 0 ==> a < self_card ... lseg(x',s') + * + * } + * + * Then we'd create a cardinality datatype as: + * + * Inductive lseg_card : Set := + * + * | lseg_card_0 : lseg_card + * + * | lseg_card_1 : lseg_card -> lseg_card. + * + * And then implement lseg as taking in a third parameter being its cardinality, + * and matching on this - taking the first clause if the input is `lseg_card0` and the + * second clause if the input is `lseg_card1 a` (and recursing on `a` + * + * */ def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { + // Determines whether a given variable is a cardinality constraint + // TODO: I've definitely seen some function elsewhere that already does this def is_card (s: String) : Boolean = s.startsWith("_") || s.contentEquals("self_card") + + // extracts a cardinality relation from an expression if it exists def extract_card_constructor(expr: Expressions.Expr) : Option[(String, String)] = { expr match { case Expressions.BinaryExpr(op, Var(left), Var(parent)) @@ -337,6 +407,8 @@ object ProofTranslation { } + // base context contains type information for every variable used in the + // predicate (even if it occurs at a top level or not) val base_context : List[(Ident, VSTProofType)] = { var gamma: Gamma = Map.empty predicate match { @@ -365,18 +437,24 @@ object ProofTranslation { // unique cardinality constraint val clauses: Map[CardConstructor, VSTPredicateClause] = raw_clauses.map({ case InductiveClause(selector, asn) => - var (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => + // first, split the pure conditions in the predicate between those that + // encode cardinality constraints and those that don't + val (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => extract_card_constructor(expr) match { case value@Some(_) => (None, value) case None => (Some(expr), None) } ).toList.unzip + // translate the pure conditions into VST format val select = translate_expression(context)(selector) - val conds = r_conds.flatMap({ case v => v }).map(translate_expression(context)).toList + val conds = r_conds.flatten.map(translate_expression(context)).toList + + // translate the spatial constraints val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) - val card_conds = r_card_conds.flatMap({ case v => v }) + // Convert the cardinality constraints into an associated constructor + val card_conds = r_card_conds.flatten card_conds match { case card_conds@(::(_, _)) => val card_cons : Map[String, CardConstructor] = build_card_cons(card_conds) From dccdf6c0a31b7ea682a2b3431eea06e8df652a96 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 21 Aug 2020 10:12:09 +0800 Subject: [PATCH 032/211] Prepare CertTree data using existing ProofTrace accumulation mechanism --- .../tygus/suslik/certification/CertTree.scala | 77 +++++++++--------- .../org/tygus/suslik/report/ProofTrace.scala | 78 +++++++++++++++++-- .../tygus/suslik/synthesis/SearchTree.scala | 6 +- .../tygus/suslik/synthesis/Synthesis.scala | 26 +------ .../synthesis/SynthesisRunnerUtil.scala | 32 ++++++-- 5 files changed, 141 insertions(+), 78 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index e4ed3be5b..c6da069a5 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -1,9 +1,10 @@ package org.tygus.suslik.certification import org.tygus.suslik.logic.Specifications.{Footprint, Goal} -import org.tygus.suslik.synthesis.SearchTree.{AndNode, NodeId, OrNode} +import org.tygus.suslik.report.ProofTraceCert +import org.tygus.suslik.synthesis.SearchTree.{NodeId, OrNode} import org.tygus.suslik.synthesis.{StmtProducer, SynthesisException} -import org.tygus.suslik.synthesis.rules.Rules.{RuleResult, SynthesisRule} +import org.tygus.suslik.synthesis.rules.Rules.SynthesisRule import scala.collection.mutable @@ -22,7 +23,7 @@ object CertTree { lazy val footprint: Footprint = goal.toFootprint val id: Int = hashCode() - def children: List[Node] = childrenMap.getOrElse(this, List.empty).reverse + def children: Seq[Node] = childrenMap.getOrElse(this, Seq.empty) def parent: Option[Node] = parentMap.get(this) override def equals(obj: Any): Boolean = obj.isInstanceOf[Node] && (obj.asInstanceOf[Node].nodeId == this.nodeId) @@ -33,15 +34,10 @@ object CertTree { val builder = new StringBuilder() builder.append(s"Node <$id>\n") - builder.append(s"Goal to solve:\n") - builder.append(s"${goal.pp}\n") - builder.append(s"Rule applied: $rule\n") - builder.append(s"Footprint: $showChildren\n") - builder.append(s"Parent node: ${parent.map(_.id).getOrElse("none")}\n") builder.append(s"Child nodes: [${cs.map(_.id).mkString(", ")}]\n") @@ -59,45 +55,42 @@ object CertTree { } } - // [Certify]: Maintain a certification tree as a pair of bidirectional hash maps - private val childrenMap: mutable.Map[Node, List[Node]] = mutable.Map.empty - private val parentMap: mutable.Map[Node, Node] = mutable.Map.empty - def root: Option[Node] = get(Vector()) - /** - * [Certify]: Adds a successful terminal or-node and its ancestors - * from the synthesis search tree to this certification tree - * @param terminal a terminal or-node in the search tree - * @param e a successful rule expansion for the terminal or-node + * [Certify] Use the accumulated non-backtracked derivations to populate the CertTree + * @param trace the trace + * @return the root CertTree node */ - def addSuccessfulPath(terminal: OrNode, e: RuleResult): Unit = { - traverse(terminal, e.producer, e.rule, _ => ()) - } - - def addSuccessFromCache(node: OrNode): Unit = { - for { - existingNode <- findByGoal(node.goal) - } yield traverse(node, existingNode.kont, existingNode.rule, _ => ()) - } - - @scala.annotation.tailrec - private def traverse(on: OrNode, stmtProducer: StmtProducer, rule: SynthesisRule, kont: Node => Unit): Unit = { - def mkKont(n: Node, prevKont: Node => Unit): Node => Unit = parent => { - prevKont(n) // execute the rest of the continuation - parentMap(n) = parent // add link: child -> parent - childrenMap(parent) = // add link: parent -> child - n :: childrenMap.getOrElse(parent, List.empty) - } - - val n = Node(on.id, on.goal, stmtProducer, rule) - on.parent match { - case Some(parentAn) if !parentMap.contains(n) => // only continue if parent hasn't been added before - traverse(parentAn.parent, parentAn.kont, parentAn.rule, mkKont(n, kont)) - case _ => - kont(n) + def fromTrace(trace: ProofTraceCert): Node = { + def traverse(on: OrNode): Node = { + val ans = trace.childAnds(on) + if (ans.isEmpty) { + // Cached result was used; search for the same goal in previously encountered nodes + val n = findByGoal(on.goal).get + Node(on.id, n.goal, n.kont, n.rule) + } else { + // Candidate derivations exist; find and process the correct one + val n = for { + an <- ans + childOrs <- trace.childOrs(an) + } yield { + val node = Node(on.id, on.goal, an.kont, an.rule) + val children = childOrs.map(traverse) + parentMap ++= children.map(_ -> node) + childrenMap(node) = children + node + } + assert(n.nonEmpty, s"No successful derivations found for goal ${on.id}") + n.head + } } + traverse(trace.root) } + // [Certify]: Maintain a certification tree as a pair of bidirectional hash maps + private val childrenMap: mutable.Map[Node, Seq[Node]] = mutable.Map.empty + private val parentMap: mutable.Map[Node, Node] = mutable.Map.empty + def root: Option[Node] = get(Vector()) + private def findByGoal(goal: Goal): Option[Node] = parentMap.keySet.find(n => n.goal.pre == goal.pre && n.goal.post == goal.post) diff --git a/src/main/scala/org/tygus/suslik/report/ProofTrace.scala b/src/main/scala/org/tygus/suslik/report/ProofTrace.scala index 293f9ff3f..0641df98c 100644 --- a/src/main/scala/org/tygus/suslik/report/ProofTrace.scala +++ b/src/main/scala/org/tygus/suslik/report/ProofTrace.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.language.Expressions import org.tygus.suslik.logic.Specifications.Goal import org.tygus.suslik.synthesis.Memoization import org.tygus.suslik.synthesis.Memoization.GoalStatus -import org.tygus.suslik.synthesis.SearchTree.{AndNode, NodeId, OrNode} +import org.tygus.suslik.synthesis.SearchTree.{AndNode, OrNode, SearchNode} import org.tygus.suslik.synthesis.rules.Rules import upickle.default.{macroRW, ReadWriter => RW} @@ -15,7 +15,7 @@ sealed abstract class ProofTrace { import ProofTrace._ def add(node: OrNode) { } def add(node: AndNode, nChildren: Int) { } - def add(at: NodeId, status: GoalStatus, from: Option[String] = None) { } + def add(node: SearchNode, status: GoalStatus, from: Option[String] = None) { } def add(result: Rules.RuleResult, parent: OrNode) { } def add(backlink: BackLink) { } } @@ -48,13 +48,13 @@ class ProofTraceJson(val outputFile: File) extends ProofTrace { override def add(node: AndNode, nChildren: Int): Unit = writeObject(NodeEntry(node.id, "AndNode", node.pp(), null, nChildren, -1)) - override def add(at: NodeId, status: GoalStatus, from: Option[String] = None): Unit = { + override def add(node: SearchNode, status: GoalStatus, from: Option[String] = None): Unit = { val st = status match { case Memoization.Succeeded(_) => Succeeded case Memoization.Failed => Failed case _ => throw new RuntimeException(s"cannot serialize $status") } - writeObject(StatusEntry(at, st.copy(from = from))) + writeObject(StatusEntry(node.id, st.copy(from = from))) } override def add(result: Rules.RuleResult, parent: OrNode) { @@ -62,8 +62,8 @@ class ProofTraceJson(val outputFile: File) extends ProofTrace { val resolution = AndNode(-1 +: parent.id, parent, result) val status = Memoization.Succeeded(null) // ignoring solution, sry add(resolution, 0) - add(resolution.id, status) - add(parent.id, status) + add(resolution, status) + add(parent, status) } } @@ -124,3 +124,69 @@ object ProofTraceJson { implicit val rw: RW[BackLinkEntry] = macroRW } } + +// [Certify] Collects non-backtracked SearchTree nodes (and their ancestors), used to populate the CertTree +class ProofTraceCert extends ProofTrace { + import scala.collection.mutable + + // The candidate derivations encountered for each OrNode + private val derivations: mutable.Map[OrNode, Seq[AndNode]] = mutable.Map.empty + // The solved subgoals for each AndNode + private val fulfilledSubgoals: mutable.Map[AndNode, Seq[OrNode]] = mutable.Map.empty + // The number of subgoals that need to be solved for each AndNode + private val numSubgoals: mutable.Map[AndNode, Int] = mutable.Map.empty + + var root: OrNode = _ + + override def add(node: AndNode, nChildren: Int): Unit = numSubgoals(node) = nChildren + + override def add(node: SearchNode, status: GoalStatus, from: Option[String] = None): Unit = node match { + case node: OrNode => if (status.isInstanceOf[Memoization.Succeeded]) addOr(node) + case _ => + } + + override def add(result: Rules.RuleResult, parent: OrNode): Unit = { + val an = AndNode(-1 +: parent.id, parent, result) + addAnd(an) + } + + private def addAnd(node: AndNode): Unit = { + val parentOr = node.parent + derivations.get(parentOr) match { + case Some(ans) => derivations(parentOr) = ans :+ node + case None => derivations(parentOr) = Seq(node) + } + addOr(parentOr) // Continue adding ancestors + } + + private def addOr(node: OrNode): Unit = node.parent match { + case Some(parentAn) => + fulfilledSubgoals.get(parentAn) match { + case Some(ons) => + if (!ons.contains(node)) { + // Make sure new goal respects ordering of subgoals by goal id + val idx = scala.math.min(node.id.head, ons.length) + val (fst, snd) = ons.splitAt(idx) + fulfilledSubgoals(parentAn) = fst ++ Seq(node) ++ snd + } // Terminate (node already encountered) + case None => + fulfilledSubgoals(parentAn) = Seq(node) + addAnd(parentAn) // Continue adding ancestors + } + case None => + root = node // Terminate (node is root) + } + + // Retrieve candidate derivations for a goal + def childAnds(node: OrNode): Seq[AndNode] = derivations.getOrElse(node, Seq.empty) + + // Retrieve subgoals for a derivation if it succeeded + def childOrs(node: AndNode): Option[Seq[OrNode]] = fulfilledSubgoals.get(node) match { + // All subgoals were solved, so this derivation was successful + case Some(ors) if ors.length == numSubgoals(node) => Some(ors) + // This is a terminal node + case None => Some(Seq.empty) + // This is a non-terminal node, but not all subgoals were solved + case _ => None + } +} diff --git a/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala b/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala index 814bb43ab..7b6e39ad2 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala @@ -18,6 +18,8 @@ object SearchTree { type Worklist = List[OrNode] + trait SearchNode { val id: NodeId } + // List of nodes to process var worklist: Worklist = List() @@ -36,7 +38,7 @@ object SearchTree { * represents a synthesis goal to solve. * For this node to succeed, one of its children has to succeed. */ - case class OrNode(id: NodeId, goal: Goal, parent: Option[AndNode], extraCost: Int = 0) { + case class OrNode(id: NodeId, goal: Goal, parent: Option[AndNode], extraCost: Int = 0) extends SearchNode { // My index among the children of parent def childIndex: Int = id.headOption.getOrElse(0).max(0) @@ -200,7 +202,7 @@ object SearchTree { * represents a set of premises of a rule application, whose result should be combined with kont. * For this node to succeed, all of its children (premises, subgoals) have to succeed. */ - case class AndNode(id: NodeId, parent: OrNode, kont: StmtProducer, rule: SynthesisRule, transitions: Seq[Transition]) { + case class AndNode(id: NodeId, parent: OrNode, kont: StmtProducer, rule: SynthesisRule, transitions: Seq[Transition]) extends SearchNode { // Does this node have an ancestor with label l? def hasAncestor(l: NodeId): Boolean = if (id == l) true diff --git a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala index 57c0b1895..9d65f0d7e 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/Synthesis.scala @@ -1,7 +1,5 @@ package org.tygus.suslik.synthesis -import java.io.PrintWriter -import org.tygus.suslik.certification.CertTree import org.tygus.suslik.language.Statements.{Solution, _} import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic._ @@ -47,7 +45,6 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof try { synthesize(goal)(stats = stats) match { case Some((body, helpers)) => - printTree log.print(List((s"Succeeded leaves (${successLeaves.length}): ${successLeaves.map(n => s"${n.pp()}").mkString(" ")}", Console.YELLOW))) val main = Procedure(funGoal, body) (main :: helpers, stats) @@ -99,7 +96,7 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof val res = memo.lookup(goal) match { case Some(Failed) => { // Same goal has failed before: record as failed log.print(List((s"Recalled FAIL", RED))) - trace.add(node.id, Failed, Some("cache")) + trace.add(node, Failed, Some("cache")) worklist = addNewNodes(Nil) node.fail None @@ -107,9 +104,8 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof case Some(Succeeded(sol)) => { // Same goal has succeeded before: return the same solution log.print(List((s"Recalled solution ${sol._1.pp}", RED))) - trace.add(node.id, Succeeded(sol), Some("cache")) + trace.add(node, Succeeded(sol), Some("cache")) worklist = addNewNodes(Nil) - CertTree.addSuccessFromCache(node) node.succeed(sol) } case Some(Expanded) => { // Same goal has been expanded before: wait until it's fully explored @@ -158,10 +154,6 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof // Check if any of the expansions is a terminal expansions.find(_.subgoals.isEmpty) match { case Some(e) => - if (config.certTarget != null || config.printTree) { - // [Certify]: Add a terminal node and its ancestors to the certification tree - CertTree.addSuccessfulPath(node, e) - } trace.add(e, node) successLeaves = node :: successLeaves worklist = addNewNodes(Nil) @@ -194,7 +186,7 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof if (newNodes.isEmpty) { // This is a dead-end: prune worklist and try something else log.print(List((s"Cannot expand goal: BACKTRACK", Console.RED))) - trace.add(node.id, Failed) + trace.add(node, Failed) node.fail } else { stats.addGeneratedGoals(newNodes.size) @@ -252,16 +244,4 @@ class Synthesis(tactic: Tactic, implicit val log: Log, implicit val trace: Proof print(s"$RESET") } } - - protected def printTree(implicit config: SynConfig): Unit = { - if (config.printTree) { - val tree = CertTree.pp() - println() - if (config.treeDest == null) println(tree) else { - new PrintWriter(config.treeDest) { write(tree); close() } - val msg = s"Successful derivations saved to ${config.treeDest.getCanonicalPath}" - println(s"$MAGENTA$msg") - } - } - } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index c248fed00..e32433639 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -4,11 +4,12 @@ import java.io.{File, PrintWriter} import java.nio.file.Paths import org.tygus.suslik.LanguageUtils +import org.tygus.suslik.certification.CertTree import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor._ import org.tygus.suslik.logic.smt.SMTSolving import org.tygus.suslik.parsing.SSLParser -import org.tygus.suslik.report.{Log, ProofTrace, ProofTraceJson, ProofTraceNone, StopWatch} +import org.tygus.suslik.report.{Log, ProofTrace, ProofTraceCert, ProofTraceJson, ProofTraceNone, StopWatch} import org.tygus.suslik.synthesis.SearchTree.AndNode import org.tygus.suslik.synthesis.tactics._ import org.tygus.suslik.util._ @@ -109,9 +110,11 @@ trait SynthesisRunnerUtil { new ReplaySynthesis(env.config) else new PhasedSynthesis(env.config) - val trace : ProofTrace = env.config.traceToJsonFile match { - case None => ProofTraceNone - case Some(file) => new ProofTraceJson(file) + val trace : ProofTrace = if (env.config.certTarget != null) new ProofTraceCert() else { + env.config.traceToJsonFile match { + case None => ProofTraceNone + case Some(file) => new ProofTraceJson(file) + } } new Synthesis(tactic, log, trace) } @@ -172,6 +175,21 @@ trait SynthesisRunnerUtil { testPrintln(StopWatch.summary.toString) } + def initCertTree(trace: ProofTrace): Unit = trace match { + case trace: ProofTraceCert => CertTree.fromTrace(trace) + case _ => + } + + def printCertTree(): Unit = if (params.printTree) { + val tree = CertTree.pp() + println() + if (params.treeDest == null) println(tree) else { + new PrintWriter(params.treeDest) { write(tree); close() } + val msg = s"Successful derivations saved to ${params.treeDest.getCanonicalPath}" + testPrintln(msg, Console.MAGENTA) + } + } + sresult._1 match { case Nil => printStats(sresult._2) @@ -185,7 +203,11 @@ trait SynthesisRunnerUtil { } else { procs.map(_.pp.trim).mkString("\n\n") } - + + // [Certify] initialize and print cert tree + initCertTree(synthesizer.trace) + printCertTree() + if (params.printStats) { testPrintln(s"\n[$testName]:", Console.MAGENTA) testPrintln(params.pp) From 4bdfef800700a32828324fe107c3753b337252e7 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 28 Aug 2020 04:23:34 +0100 Subject: [PATCH 033/211] Fixed bugs in pretty printing now all terms are valid --- .../targets/vst/logic/Proof.scala | 50 ++++++++++++++----- .../targets/vst/logic/ProofTypes.scala | 5 +- .../vst/translation/ProofTranslation.scala | 31 ++++++++++-- .../targets/vst/translation/Translation.scala | 4 -- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 06564e4f8..55f5159fa 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -7,6 +7,7 @@ import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Ident object Proof { @@ -16,7 +17,7 @@ object Proof { /** predicate encoding that C-parameter (of type val) is a valid pointer */ case class IsValidPointerOrNull(name: CVar) extends PureFormula { override def pp: String = - s"is_valid_pointer_or_null(${name.pp})" + s"is_pointer_or_null(${name.pp})" } /** predicate encoding that C-parameter (of type val) is a valid int */ @@ -28,6 +29,11 @@ object Proof { /** Redefinition of expressions for use in VST proofs * */ object Expressions { + + /** encoding of expressions in VST proof script + * By default any boolean value will print to prop + * use pp_as_bool_value to print as a boolean + */ sealed abstract class ProofCExpr extends PrettyPrinting { /** prints the expression as though it were an element * of type val (vst's encoding of C-values) @@ -37,8 +43,8 @@ object Proof { case ProofTypes.CoqParamType(ty) => name case ProofTypes.CoqPtrType => name case ProofTypes.CoqIntType => s"(Vint (Int.repr ${name}))" - case ProofTypes.CoqNatType => s"(Vint (Int.repr (Z.of_nat ${name})))" case ProofTypes.CoqListType(elem, length) => name + case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") } case ProofCIntConst(value) => s"(Vint (Int.repr ${value.toString}))" case ProofCSetLiteral(elems) => @@ -61,6 +67,19 @@ object Proof { } case v => v.pp } + + /** print as a pointer value + * @throws NotImplementedError if expression is not a variable or 0-int */ + def pp_as_ptr_value : String = this match { + case ProofCVar(name, typ) => name + case ProofCBoolConst(value) => ??? + case ProofCIntConst(value) => if (value == 0) { "nullval" } else { ??? } + case ProofCSetLiteral(elems) => ??? + case ProofCIfThenElse(cond, left, right) => ??? + case ProofCBinaryExpr(op, left, right) => ??? + case ProofCUnaryExpr(op, e) => ??? + } + } /** a variable in a VST proof */ @@ -68,7 +87,7 @@ object Proof { override def pp: String = typ match { case ProofTypes.CoqPtrType => name case ProofTypes.CoqIntType => name - case ProofTypes.CoqNatType => name + case ProofTypes.CoqCardType(_) => name case ProofTypes.CoqParamType(ty) => // if the variable has a param type then // its actually of type val, and we need to @@ -95,13 +114,18 @@ object Proof { /** set literal (encoded as set) in a VST proof */ case class ProofCSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { override def pp: String = - s"[${elems.map(_.pp).mkString("; ")}]" + s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" } /** encodes a ternary expression in a VST proof */ case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = - s"(if ${cond.pp} then ${left.pp} else ${right.pp})" + override def pp: String = ??? + // TODO: if statements don't seem to be used inside suslik proofs, so implementing this would be pointless + // if this assumption changes, then the correct implementation will look something like: + // + // s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" + // + // where pp_as_bool_value should be a method that prints expressions in boolean form } case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { @@ -109,14 +133,14 @@ object Proof { op match { case ProofCOpLt => s"(${left.pp} < ${right.pp})" case ProofCOpLeq => s"(${left.pp} <= ${right.pp})" - case ProofCOpOr => s"(orb ${left.pp} ${right.pp})" - case ProofCOpAnd => s"(andb ${left.pp} ${right.pp})" + case ProofCOpOr => s"(${left.pp} \/ ${right.pp})" + case ProofCOpAnd => s"(${left.pp} /\ ${right.pp})" case ProofCOpPlus => s"(${left.pp} + ${right.pp})" case ProofCOpMinus => s"(${left.pp} - ${right.pp})" case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" - case ProofCOpIntEq => s"(Zeq_bool ${left.pp} ${right.pp})" - case ProofCOpBoolEq => s"(eqb ${left.pp} ${right.pp})" - case ProofCOpPtrEq => s"(${left.pp} = ${right.pp})" + case ProofCOpIntEq => s"(${left.pp} = ${right.pp})" + case ProofCOpBoolEq => s"(${left.pp} = ${right.pp})" + case ProofCOpPtrEq => s"(${left.pp_as_ptr_value} = ${right.pp_as_ptr_value})" case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" @@ -126,7 +150,7 @@ object Proof { case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { override def pp: String = op match { - case ProofCOpNot => s"(negb ${e.pp})" + case ProofCOpNot => s"(~ ${e.pp})" case ProofCOpUnaryMinus => s"(-(${e.pp}))" } } @@ -176,7 +200,7 @@ object Proof { * */ case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { - s"${expr.pp}" + s"${expr.pp_as_c_value}" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala index a003bd271..1cc3fa6a6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala @@ -28,6 +28,7 @@ object ProofTypes { } } + /** represents a val type in vst that maps to a parameter to the function of type ty */ case class CoqParamType(ty: VSTCType) extends VSTProofType { override def pp : String = "val" } @@ -42,8 +43,8 @@ object ProofTypes { } /** type of natural numbers (used to type metavariables in Coq proofs) */ - case object CoqNatType extends VSTProofType { - override def pp:String = "nat" + case class CoqCardType(pred_type: String) extends VSTProofType { + override def pp:String = s"${pred_type}_card" } sealed case class CoqListType(elem: VSTProofType, length: Option[Int]) extends VSTProofType { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 743cb3946..a010e459f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -11,7 +11,8 @@ import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTypes} import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.Proof.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, CoqNatType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqIntType, CoqListType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} @@ -27,7 +28,7 @@ object ProofTranslation { lType match { case IntType => CoqIntType case LocType => CoqPtrType - case CardType => CoqNatType + case CardType => CoqCardType("") // TODO: add a safe version of this (only used when initializing base context) // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible @@ -275,13 +276,35 @@ object ProofTranslation { val name: Ident = proc.name val c_params: Seq[(Ident, VSTCType)] = proc.params.map({ case (CVar(name), cType) => (name, cType) }) + + // collect all cardinality_params and their associated types + val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ + case PointsTo(loc, offset, value) => None + case Block(loc, sz) => None + case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) + case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") + }).toMap + val formal_params: List[(Ident, VSTProofType)] = { val c_param_set = c_params.map(_._1).toSet - goal.universals.map({ case variable@Var(name) => (name, translate_type(goal.gamma(variable))) }) + goal.universals + .map({ case variable@Var(name) => + if (cardinality_params.contains(name)) { + (name, cardinality_params(name)) + } else { + (name, translate_type(goal.gamma(variable))) + }}) .filterNot({case (name, _) => c_param_set.contains(name)}).toList } + val existential_params: List[(Ident, VSTProofType)] = - goal.existentials.map({ case variable@Var(name) => (name, translate_type(goal.gamma(variable))) }).toList + goal.existentials.map({ case variable@Var(name) => + if (cardinality_params.contains(name)) { + (name, cardinality_params(name)) + } else { + (name, translate_type(goal.gamma(variable))) + } + }).toList val return_type: VSTCType = proc.rt val context = ( diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index e5d37b967..15757ff78 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -21,10 +21,6 @@ object Translation { val spec = ProofTranslation.translate_conditions(procedure)(root.goal) println(procedure.pp) println(spec.pp) - println( - env.predicates.head._2.pp - - ) val predicates: List[VSTPredicate] = env.predicates.map({ case (_, predicate) => ProofTranslation.translate_predicate(env)(predicate) }).toList From 8b48b2df25e823fa5dda51ed1a496e2c35d27def Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 4 Sep 2020 07:40:46 +0100 Subject: [PATCH 034/211] Updated vst certification output to use unions to encode suslik semantics in C --- .../certification/targets/vst/VST.scala | 18 ++++++- .../targets/vst/clang/CTypes.scala | 2 +- .../targets/vst/clang/Statements.scala | 23 ++++----- .../targets/vst/logic/Formulae.scala | 15 ++++-- .../targets/vst/logic/Proof.scala | 35 ++++++++++++- .../targets/vst/logic/ProofTypes.scala | 4 +- .../vst/translation/ProofTranslation.scala | 8 ++- .../targets/vst/translation/Translation.scala | 51 ++++++++++++++++++- 8 files changed, 133 insertions(+), 23 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index 7970aea1e..5c9f30ca1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -38,7 +38,23 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. builder.append(coq_prelude(fun_name)) - val c_prelude ="""#include \n\nextern void free(void *p);\n\n""" + val c_prelude = + """ + |#include + | + |extern void free(void *p); + |extern void *malloc(size_t size); + | + |typedef union sslval { + | int ssl_int; + | void *ssl_ptr; + |} *loc; + |#define READ_LOC(x,y) (*(x+y)).ssl_ptr + |#define READ_INT(x,y) (*(x+y)).ssl_int + |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z + |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z + | + |""".stripMargin val x = Translation.translate(root, proc, env) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala index a434ae393..6d6622ad5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala @@ -30,7 +30,7 @@ object CTypes { /** encoding of void pointer types */ case object CVoidPtrType extends VSTCType with PureType { - override def pp: String = s"void *" + override def pp: String = s"loc" } // the following types are only used in the program analysis, so don't form part of the diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index 6b1c704b9..d07d05560 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -1,7 +1,8 @@ package org.tygus.suslik.certification.targets.vst.clang -import org.tygus.suslik.certification.targets.vst.clang.CTypes.{VSTCType} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.translation.Translation.{TranslationException, fail_with} import org.tygus.suslik.logic.Specifications.GoalLabel /** Encoding of C statements */ @@ -44,10 +45,10 @@ object Statements { case class CLoad(to: CVar, elem_ty: VSTCType, from: CVar, offset: Int = 0) extends CStatement { override def pp: String = - if (offset == 0) { - s"${elem_ty.pp} ${to.pp} = *(${elem_ty.pp}*)${from.pp};" - }else { - s"${elem_ty.pp} ${to.pp} = *((${elem_ty.pp}*)${from.pp} + ${offset.toString});" + elem_ty match { + case CTypes.CIntType => s"${elem_ty.pp} ${to.pp} = READ_INT(${from.pp}, ${offset});" + case CTypes.CVoidPtrType => s"${elem_ty.pp} ${to.pp} = READ_LOC(${from.pp}, ${offset});" + case CTypes.CUnitType => fail_with("inconsistent program - loading into a void variable") } } @@ -55,12 +56,10 @@ object Statements { * *to.offset = e */ case class CStore(to: CVar, elem_ty: VSTCType, offset: Int, e: CExpr) extends CStatement { override def pp: String = { - val cast = s"(${elem_ty.pp})" - val ptr = s"(${elem_ty.pp}*)${to.pp}" - if (offset == 0) { - s"*${ptr} = ${cast} ${e.pp};" - } else { - s"*(${ptr} + ${offset.toString}) = ${cast} ${e.pp};" + elem_ty match { + case CTypes.CIntType => s"WRITE_INT(${to.pp}, ${offset}, ${e.pp});" + case CTypes.CVoidPtrType => s"WRITE_LOC(${to.pp}, ${offset}, ${e.pp});" + case CTypes.CUnitType => fail_with("inconsistent program - writing into a void variable") } } } @@ -83,7 +82,7 @@ object Statements { * */ case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement { override def ppIndent(depth: Int): String = - s"${getIndent(depth)}if(${cond.pp}) {\n${tb.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${eb.ppIndent(depth+1)}\n${getIndent(depth)}}\n" + s"${getIndent(depth)}if(${cond.pp}) {\n${tb.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${eb.ppIndent(depth+1)}\n${getIndent(depth)}}" } /** diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index c659823b1..d308d22e0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.clang.CFormals +import org.tygus.suslik.certification.targets.vst.clang.{CFormals, CTypes} import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.CExpr import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions @@ -21,8 +21,17 @@ object Formulae { /** spatial formuala indicating points to - loc + offset :-> value */ case class CDataAt(loc: ProofCExpr, elem_typ: VSTProofType, count: Int, elem: ProofCExpr) extends VSTHeaplet { // returns the type of a proof expression - override def pp: String = - s"(data_at Tsh ${elem_typ.pp_as_ctype} ${elem.pp_as_c_value} ${loc.pp})" + override def pp: String = { +// s"(data_at Tsh ${elem_typ.pp_as_ctype} ${elem.pp_as_c_value} ${loc.pp})" + elem_typ match { + case ProofTypes.CoqParamType(_) => s"(data_at Tsh (tarray ${elem_typ.pp_as_ctype} 1) [${elem.pp_as_ssl_union_value}] ${loc.pp})" + case ProofTypes.CoqPtrType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inr ${elem.pp_as_c_value}] ${loc.pp})" + case ProofTypes.CoqIntType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inl ${elem.pp_as_c_value}] ${loc.pp})" + case ProofTypes.CoqCardType(pred_type) => assert(false, "data at pointing to meta variable"); ??? + case CoqListType(_, length) => + s"(data_at Tsh (tarray (Tunion _sslval noattr) ${length}) ${elem.pp_as_ssl_union_value} ${loc.pp})" + } + } } /** predicate application diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 55f5159fa..cfa50c535 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -23,7 +23,7 @@ object Proof { /** predicate encoding that C-parameter (of type val) is a valid int */ case class IsValidInt(name: CVar) extends PureFormula { override def pp:String = - s"ssl_is_valid_int(s${name})" + s"ssl_is_valid_int(${name.pp})" } /** Redefinition of expressions for use in VST proofs @@ -68,6 +68,37 @@ object Proof { case v => v.pp } + def pp_as_ssl_union_value : String = this match { + case ProofCVar(name, typ) => typ match { + case ProofTypes.CoqParamType(ty) => ty match { + case CTypes.CIntType => s"inl ${name}" + case CTypes.CVoidPtrType =>s"inl ${name}" + } + case ProofTypes.CoqPtrType => s"inr ${name}" + case ProofTypes.CoqIntType => s"inl (Vint (Int.repr ${name}))" + case ProofTypes.CoqListType(elem, length) => name + case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") + } + case ProofCIntConst(value) => s"inl (Vint (Int.repr ${value.toString}))" + case ProofCSetLiteral(elems) => + s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" + case value@ProofCBinaryExpr(op, _, _) => + val is_int = op match { + case ProofCOpPlus => true + case ProofCOpMinus =>true + case ProofCOpMultiply =>true + case _ => false + } + if (is_int) { + s"inl (Vint (Int.repr ${value.pp}))" + } else { + ??? + } + case value@ProofCUnaryExpr(op, _) => op match { case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" } + case v => v.pp + } + + /** print as a pointer value * @throws NotImplementedError if expression is not a variable or 0-int */ def pp_as_ptr_value : String = this match { @@ -240,7 +271,7 @@ object Proof { def as_vst_type(var_type: VSTCType) = var_type match { case CTypes.CIntType => "tint" case CTypes.CUnitType => "tvoid" - case CTypes.CVoidPtrType => "(tptr tvoid)" + case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" } override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala index 1cc3fa6a6..ef52b6b0d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala @@ -16,11 +16,13 @@ object ProofTypes { /** proof types */ sealed abstract class VSTProofType extends PrettyPrinting { + + /** prints the type as a term of type type (VST's definition) in Coq */ def pp_as_ctype: String = this match { case CoqParamType(ty) => ty match { case CTypes.CIntType => "tint" - case CTypes.CVoidPtrType => "(tptr tvoid)" + case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" } case CoqPtrType => "(tptr tvoid)" case CoqIntType => "tint" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index a010e459f..a3be33e9f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -315,13 +315,17 @@ object ProofTranslation { val precondition: FormalCondition = { val pure_conditions = goal.pre.phi.conjuncts.map(translate_expression(context)) - .map(IsTrueProp).toList ++ c_params.flatMap({ case (ident, cType) => + .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => cType match { case CTypes.CIntType => Some(IsValidInt(CVar(ident))) case CTypes.CUnitType => None case CTypes.CVoidPtrType => Some(IsValidPointerOrNull(CVar(ident))) } - }) + }) ++ formal_params.flatMap({ case (ident, ty) => ty match { + case ProofTypes.CoqPtrType =>Some(IsValidPointerOrNull(CVar(ident))) + case ProofTypes.CoqIntType => Some(IsValidInt(CVar(ident))) + case _ => None + }}) val spatial_conditions: List[VSTHeaplet] = translate_heaplets(context)(goal.pre.sigma.chunks) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 15757ff78..8cc36db04 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -8,13 +8,62 @@ import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDef import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import org.tygus.suslik.synthesis.IdProducer.ruleAssert +import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} +import scala.annotation.tailrec import scala.collection.immutable object Translation { case class TranslationException(msg: String) extends Exception(msg) + def fail_with(msg: String) = throw TranslationException(msg) + + + /** Implementation of a state monad where the state is a stack of elements */ + case class State[S,A](f: List[S] => (A, List[S])) { + + /** map = fmap */ + def map[B](g: A => B): State[S, B] = + State(s => { + val (a, t) = f(s) + (g(a), t) + }) + + /** flatMap = >>= */ + def flatMap[B](g: A => State[S, B]): State[S, B] = + State(s => { + val (a, t) = f(s) + g(a).f(t) + }) + + + /** push an element onto the state */ + def push (s:S) : State[S, Unit] = { + State(ss => ((), s +: ss)) + } + + /** push multiple values onto the state, in such a way that is equivalent to iterating through the list and pushing them on individually */ + def push_multi (s:List[S]) : State[S, Unit] = { + @tailrec + def rev_cat[C](ls: List[C])(acc: List[C]) : List[C] = ls match { + case Nil => acc + case ::(head,tail) => rev_cat(tail)(head +: acc) + } + State(ss => ((), rev_cat(s)(ss))) + } + + /** pop a value off the stack */ + def pop : State[S, S] = State { + case Nil => fail_with("State monad ran out of state") + case ::(head, tl) => (head, tl) + } + + /** run the state monad to get a value */ + def eval(s: List[S]): A = f(s)._1 + } + def translate(root: CertTree.Node, proc: Procedure, env: Environment): Nothing = { val procedure = CTranslation.translate_function(proc, root.goal.gamma) @@ -28,7 +77,7 @@ object Translation { predicates.foreach(v => println(v.pp)) println(root.goal.gamma) - + //translate_cont(root.children, root.kont) // translate body into VST types, and build context of variables // var (body, ctx) = CTranslation.translate_body(proc.f, proc.body, root.goal.gamma) From 3bdd4d5f7b5bd2e8335d26e0bb17672b2a8fa669 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 9 Sep 2020 11:18:30 +0800 Subject: [PATCH 035/211] Add more benchmark tests --- .../targets/htt/language/Expressions.scala | 10 +++--- .../targets/htt/logic/ProofSteps.scala | 8 ++--- .../targets/htt/logic/Sentences.scala | 16 ++++++---- .../targets/htt/translation/Translation.scala | 2 +- .../synthesis/certification/dll/common.def | 10 ++++++ .../certification/dll/dll-dupleton.syn | 22 +++++++++++++ .../certification/dll/dll-singleton.syn | 18 +++++++++++ .../certification/sll/sll-append.syn | 20 ++++++++++++ .../synthesis/certification/sll/sll-copy.syn | 26 ++++++++++++++++ .../certification/sll/sll-dupleton.syn | 17 ++++++++++ .../certification/tree/tree-copy.syn | 31 +++++++++++++++++++ .../tree/{treefree.syn => tree-free.syn} | 8 ++--- .../targets/htt/HTTCertificationTests.scala | 1 + 13 files changed, 170 insertions(+), 19 deletions(-) create mode 100644 src/test/resources/synthesis/certification/dll/common.def create mode 100644 src/test/resources/synthesis/certification/dll/dll-dupleton.syn create mode 100644 src/test/resources/synthesis/certification/dll/dll-singleton.syn create mode 100644 src/test/resources/synthesis/certification/sll/sll-append.syn create mode 100644 src/test/resources/synthesis/certification/sll/sll-copy.syn create mode 100644 src/test/resources/synthesis/certification/sll/sll-dupleton.syn create mode 100644 src/test/resources/synthesis/certification/tree/tree-copy.syn rename src/test/resources/synthesis/certification/tree/{treefree.syn => tree-free.syn} (66%) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 9a642313d..f34508c4f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -33,6 +33,7 @@ object Expressions { collector(acc3)(r) case a@CSApp(_, args, card) => val acc1 = if (p(a)) acc :+ a.asInstanceOf[R] else acc + args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) val acc2 = args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) collector(acc2)(card) case CPointsTo(loc, _, value) => @@ -84,6 +85,8 @@ object Expressions { } def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]) + + def cardVars: Seq[CVar] = collect(_.isInstanceOf[CSApp]).flatMap {v: CSApp => v.card.vars} } case class CVar(name: String) extends CExpr { @@ -114,6 +117,7 @@ object Expressions { case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { override def pp: String = op match { case COpSubset => s"{subset ${left.pp} ${op.pp} ${right.pp}}" + case COpSetEq => s"perm_eq (${left.pp}) (${right.pp})" case _ => s"${left.pp} ${op.pp} ${right.pp}" } } @@ -135,7 +139,7 @@ object Expressions { override def subst(sigma: Subst): CSApp = CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) - val uniqueName: String = s"$pred${card.pp}" + val uniqueName: String = s"${pred}_${args.flatMap(_.vars).map(_.pp).mkString("")}_${card.pp}" val heapName: String = s"h_$uniqueName" val hypName: String = s"H_$uniqueName" } @@ -232,9 +236,7 @@ object Expressions { object COpIn extends CBinOp - object COpSetEq extends CBinOp { - override def pp: String = "=" - } + object COpSetEq extends CBinOp object COpSubset extends CBinOp { override def pp: String = "<=" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 70de36508..b5e84d9f3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -52,7 +52,7 @@ object ProofSteps { if (nested) { builder.append(s"move=>${nestedDestructL(ex)}.\n") } else { - builder.append(s"move=>${ex.map(v => s"[${v.pp}]").mkString(" ")}.\n") + builder.append(s"ex_elim ${ex.map(_.pp).mkString(" ")}.\n") } } } @@ -76,7 +76,7 @@ object ProofSteps { // move spatial part to context, and then substitute where appropriate builder.append(s"move=>[$sigmaName].\n") - builder.append(s"rewrite->$sigmaName in *.\n") + builder.append(s"subst.\n") // move predicate apps to context, if any if (sigma.apps.nonEmpty) { @@ -150,7 +150,7 @@ object ProofSteps { val acc1 = collector(acc)(s1) collector(acc1)(s2) case CallStep(goal, _) => - acc ++ goal.programVars ++ goal.universalGhosts ++ goal.existentials + acc ++ goal.programVars case _ => acc } @@ -202,7 +202,7 @@ object ProofSteps { override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" - val writeStep = "ssl_write.\n" + val writeStep = s"ssl_write $ptr.\n" // SSL's `Write` rule does an implicit frame under normal circumstances, but not during a call synthesis val writePostStep = if (frame) s"ssl_write_post $ptr.\n" else "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 921997fd4..78b4adc9d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -32,22 +32,26 @@ object Sentences { CAssertion(phi.subst(sub), sigma.subst(sub)) val valueVars: Seq[CVar] = - (phi.vars ++ sigma.vars).distinct.filterNot(_.isCard) + (phi.vars ++ sigma.vars).distinct.diff(phi.cardVars ++ sigma.cardVars) val heapVars: Seq[CVar] = sigma.heapVars val inferredTypes: Map[CVar, HTTType] = { - @scala.annotation.tailrec def collectPhi(el: CExpr, m: Map[CVar, HTTType]): Map[CVar, HTTType] = el match { - case CBinaryExpr(COpSetEq, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) - case CBinaryExpr(COpSetEq, left: CVar, right) => collectPhi(right, m ++ Map(left -> CNatSeqType)) - case CBinaryExpr(COpSetEq, left, right: CVar) => collectPhi(left, m ++ Map(right -> CNatSeqType)) + case CSetLiteral(elems) => m ++ elems.filter(_.isInstanceOf[CVar]).map { case v: CVar => v -> CNatType } + case CBinaryExpr(COpSetEq | COpUnion, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) + case CBinaryExpr(COpSetEq | COpUnion, left: CVar, right) => collectPhi(right, m + (left -> CNatSeqType)) + case CBinaryExpr(COpSetEq | COpUnion, left, right: CVar) => collectPhi(left, m + (right -> CNatSeqType)) + case CBinaryExpr(COpUnion, left, right) => collectPhi(right, collectPhi(left, m)) case _ => m } def collectSigma: Map[CVar, HTTType] = { val ptss = sigma.ptss - ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ case (acc, CPointsTo(loc, _, _)) => acc ++ Map(loc.asInstanceOf[CVar] -> CPtrType)} + ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ + case (acc, CPointsTo(loc: CVar, _, _)) => acc ++ Map(loc -> CPtrType) + case (acc, _) => acc + } } val mPhi = collectPhi(phi, Map.empty) val mSigma = collectSigma diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 00c869bd2..fa4e0d58b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -62,7 +62,7 @@ object Translation { val post = translateAsn(goal.post) val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } val programVars = goal.programVars.map(translateVar) - val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(_.isCard) + val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(v => gamma(v) == CCardType) CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) } diff --git a/src/test/resources/synthesis/certification/dll/common.def b/src/test/resources/synthesis/certification/dll/common.def new file mode 100644 index 000000000..9f35a5a8c --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/common.def @@ -0,0 +1,10 @@ +predicate dll(loc x, loc z, set s) { +| x == 0 => { s =i {} ; emp } +| not (x == 0) => + { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> w ** (x + 2) :-> z ** dll(w, x, s1) } +} + +predicate sll(loc x, set s) { +| x == 0 => { s =i {} ; emp } +| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} diff --git a/src/test/resources/synthesis/certification/dll/dll-dupleton.syn b/src/test/resources/synthesis/certification/dll/dll-dupleton.syn new file mode 100644 index 000000000..02b6eb978 --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/dll-dupleton.syn @@ -0,0 +1,22 @@ +# -c 3 +doubly-linked list: construct a list with two elements + +##### + +{ r :-> a } +void dll_dupleton (int x, int y, loc r) +{ elems =i {x, y} ; r :-> z ** dll(z, 0, elems) } + +##### + +void dll_dupleton (int x, int y, loc r) { + let z = malloc(3); + let w = malloc(3); + *r = z; + *(z + 1) = w; + *(z + 2) = 0; + *(w + 1) = 0; + *(w + 2) = z; + *z = y; + *w = x; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/dll/dll-singleton.syn b/src/test/resources/synthesis/certification/dll/dll-singleton.syn new file mode 100644 index 000000000..8b9396e62 --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/dll-singleton.syn @@ -0,0 +1,18 @@ +#. -c 2 +doubly-linked list: construct a list with one element + +##### + +{ r :-> a } +void dll_singleton (int x, loc r) +{ elems =i {x} ; r :-> y ** dll(y, 0, elems) } + +##### + +void dll_singleton (int x, loc r) { + let y = malloc(3); + *r = y; + *(y + 1) = 0; + *(y + 2) = 0; + *y = x; +} diff --git a/src/test/resources/synthesis/certification/sll/sll-append.syn b/src/test/resources/synthesis/certification/sll/sll-append.syn new file mode 100644 index 000000000..f11e5b9ad --- /dev/null +++ b/src/test/resources/synthesis/certification/sll/sll-append.syn @@ -0,0 +1,20 @@ +singly-linked list: append + +##### + +{true ; sll(x1, s1) ** sll(x2, s2) ** r :-> x2} +void sll_append (loc x1, loc r) +{s =i s1 ++ s2 ; sll(y, s) ** r :-> y } + +##### + +void sll_append (loc x1, loc r) { + if (x1 == 0) { + } else { + let n = *(x1 + 1); + sll_append(n, r); + let y = *r; + *(x1 + 1) = y; + *r = x1; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/sll/sll-copy.syn b/src/test/resources/synthesis/certification/sll/sll-copy.syn new file mode 100644 index 000000000..76503a7a3 --- /dev/null +++ b/src/test/resources/synthesis/certification/sll/sll-copy.syn @@ -0,0 +1,26 @@ +should be able to synthesize list copy + +##### + +{true ; r :-> x ** sll(x, s)} +void sll_copy(loc r) +{true ; r :-> y ** sll(x, s) ** sll(y, s) } + +##### + +void sll_copy (loc r) { + let x = *r; + if (x == 0) { + } else { + let v = *x; + let n = *(x + 1); + *x = n; + sll_copy(x); + let yx = *x; + let y = malloc(2); + *r = y; + *(y + 1) = yx; + *x = v; + *y = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/sll/sll-dupleton.syn b/src/test/resources/synthesis/certification/sll/sll-dupleton.syn new file mode 100644 index 000000000..cbf9bb219 --- /dev/null +++ b/src/test/resources/synthesis/certification/sll/sll-dupleton.syn @@ -0,0 +1,17 @@ +# -c 3 +singly-linked list: construct a list with two elements + +##### + +{ true ; r :-> a } +void sll_dupleton (int x, int y, loc r) +{ elems =i {x, y} ; r :-> z ** sll(z, elems) } + +##### + +void sll_dupleton (int x, loc r) { + let y2 = malloc(2); + *y2 = x; + *(y2 + 1) = 0; + *r = y2; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/tree/tree-copy.syn b/src/test/resources/synthesis/certification/tree/tree-copy.syn new file mode 100644 index 000000000..89d7690f1 --- /dev/null +++ b/src/test/resources/synthesis/certification/tree/tree-copy.syn @@ -0,0 +1,31 @@ +should be able to synthesize a tree copy (with elements) + +##### + +{true ; r :-> x ** tree(x, s)} +void tree_copy(loc r) +{true ; r :-> y ** tree(x, s) ** tree(y, s) } + +##### + +void tree_copy (loc r) { + let x = *r; + if (x == 0) { + } else { + let v = *x; + let l = *(x + 1); + let rx = *(x + 2); + *x = l; + tree_copy(x); + let yx = *x; + *r = rx; + tree_copy(r); + let yr = *r; + let y = malloc(3); + *r = y; + *(y + 1) = yx; + *(y + 2) = yr; + *x = v; + *y = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/tree/treefree.syn b/src/test/resources/synthesis/certification/tree/tree-free.syn similarity index 66% rename from src/test/resources/synthesis/certification/tree/treefree.syn rename to src/test/resources/synthesis/certification/tree/tree-free.syn index 7497c9596..a290788aa 100644 --- a/src/test/resources/synthesis/certification/tree/treefree.syn +++ b/src/test/resources/synthesis/certification/tree/tree-free.syn @@ -3,18 +3,18 @@ should be able to free a tree #### {true ; tree(x, s) } - void treefree(loc x) + void tree_free(loc x) {true ; emp } #### -void treefree (loc x) { +void tree_free (loc x) { if (x == 0) { } else { let l2 = *(x + 1); let r2 = *(x + 2); - treefree(l2); - treefree(r2); + tree_free(l2); + tree_free(r2); free(x); } } \ No newline at end of file diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index 6004d2096..0ce291fc7 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -38,6 +38,7 @@ class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUt runAllTestsFromDir("certification/list") runAllTestsFromDir("certification/tree") runAllTestsFromDir("certification/sll") + runAllTestsFromDir("certification/dll") } } \ No newline at end of file From f2dde14e53ecd4fbf8bb73b0289aa5021e8016c1 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sun, 13 Sep 2020 06:51:29 +0100 Subject: [PATCH 036/211] synthesizing listfree now works --- build.sbt | 4 +- .../htt/translation/ProgramTranslation.scala | 2 +- .../certification/targets/vst/Debug.scala | 205 ++++ .../certification/targets/vst/State.scala | 63 + .../targets/vst/logic/Formulae.scala | 6 +- .../targets/vst/logic/Proof.scala | 513 +------- .../targets/vst/logic/ProofSteps.scala | 121 ++ .../targets/vst/logic/ProofTerms.scala | 562 +++++++++ .../targets/vst/logic/ProofTypes.scala | 4 + .../translation/ProofSpecTranslation.scala | 516 ++++++++ .../vst/translation/ProofTranslation.scala | 1044 ++++++++++------- .../targets/vst/translation/Translation.scala | 59 +- .../tygus/suslik/synthesis/StmtProducer.scala | 2 +- .../rules/SymbolicExecutionRules.scala | 4 +- .../synthesis/rules/UnfoldingRules.scala | 12 +- 15 files changed, 2111 insertions(+), 1006 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala diff --git a/build.sbt b/build.sbt index 412c8f594..e50135806 100644 --- a/build.sbt +++ b/build.sbt @@ -5,7 +5,7 @@ name := "suslik" version := "0.1.0" -scalaVersion := "2.12.7" +scalaVersion := "2.12.12" javacOptions ++= Seq("-source", "1.8", "-target", "1.8", "-Xlint") scalacOptions += "-target:jvm-1.8" @@ -52,4 +52,4 @@ assemblyJarName in assembly := "suslik.jar" assemblyMergeStrategy in assembly := { case PathList("META-INF", xs @ _*) => MergeStrategy.discard case x => MergeStrategy.first -} \ No newline at end of file +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index cd40b62ac..599ec9f08 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -46,7 +46,7 @@ object ProgramTranslation { case PrependProducer(s) => val stmt = translateOperation(s) PrependCStmtProducer(stmt) - case BranchProducer(selectors) => + case BranchProducer(_, selectors) => BranchCStmtProducer(selectors.map(translateExpr)) case GuardedProducer(cond, _) => GuardedCStmtProducer(translateExpr(cond)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala new file mode 100644 index 000000000..37c18b749 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala @@ -0,0 +1,205 @@ +package org.tygus.suslik.certification.targets.vst + +import java.io.{BufferedOutputStream, BufferedReader, ByteArrayInputStream, ByteArrayOutputStream, File, FileOutputStream, FileWriter, InputStreamReader, StringWriter} + +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} +import scalaz.Scalaz.{ToTraverseOps, ToTraverseOpsUnapply} + +import scala.io._ +import scala.sys.process._ +import scala.sys.process.ProcessLogger + +object Debug { + + + def dotbin(format: String)(dot: String): Array[Byte] = { + val process = "dot -Gdpi=300 -T" + format + val bos = new ByteArrayOutputStream() + val exitCode = process #< new ByteArrayInputStream(dot.getBytes) #> bos !< ProcessLogger(s => ()) + if (exitCode == 0) { + bos.toByteArray() + } + else { + throw new RuntimeException("Nonzero exit value (" + exitCode + ") for '" + process + "' with: " + dot) + } + } + + def stmt_producer_node_to_text(stmtProducer: StmtProducer): String = stmtProducer match { + case ChainedProducer(p1, p2) => s"ChainedProducer [${stmtProducer.arity}]" + case PartiallyAppliedProducer(p, s) => s"PartiallyAppliedProducer [${stmtProducer.arity}] (${s.toString()})" + case IdProducer => s"IdProducer[${stmtProducer.arity}]" + case ConstProducer(s) => s"ConstProducer[${stmtProducer.arity}](${s.pp})" + case PrependProducer(s) => s"PrependProducer[${stmtProducer.arity}](${s.pp})" + case PrependFromSketchProducer(s) => s"PrependFromSketchProducer[${stmtProducer.arity}](${s.pp})" + case AppendProducer(s) => s"AppendProducer[${stmtProducer.arity}](${s.pp})" + case SeqCompProducer => "SeqCompProducer[${stmtProducer.arity}]" + case ExtractHelper(goal) => s"ExtractHelper[${stmtProducer.arity}](${""})" + case HandleGuard(goal) => s"HandleGuard[${stmtProducer.arity}](${""})" + case BranchProducer(_, selectors) => s"BranchProducer[${stmtProducer.arity}] {\n${selectors.map(_.pp).mkString("\n")}\n}" + case GuardedProducer(cond, goal) => + s"GuardedProducer[${stmtProducer.arity}] {\n cond=${cond.pp}\n goal=${goal.pp}\n}" + case SubstProducer(subst) => s"SubstProducer[${stmtProducer.arity}](${subst.toString})" + case GhostSubstProducer(subst) => s"GhostSubstProducer[${stmtProducer.arity}](${subst.toString})" + case UnfoldProducer(app, selector, substPred, substEx, substArgs) => + s"UnfoldProducer[${stmtProducer.arity}] {\n app=${app.pp}\n selector=${selector.pp}\n substPred=${substPred.toString}\n substEx=${substEx.toString}\n substArgs=${substArgs.map(_.toString()).mkString("; ")}\n}" + } + + + def visualize_proof_tree(stmtProducer: StmtProducer): Unit = { + var nodes: List[(String, String)] = Nil + var edges: List[(String, String)] = Nil + var same_ranks: List[List[String]] = Nil + var count = 0 + + def next_id(x: Unit): String = { + count = count + 1 + s"node_${count}" + } + + def process_producer(root_node: Option[String])(stmtProducer: StmtProducer): String = { + val node_id = next_id() + val node_text = stmt_producer_node_to_text(stmtProducer) + nodes = (node_id, node_text) :: nodes + root_node match { + case Some(value) => edges = (value, node_id) :: edges + case None => () + } + stmtProducer match { + case ChainedProducer(p1, p2) => + val child1 = process_producer(Some(node_id))(p1) + val child2 = process_producer(Some(node_id))(p2) + same_ranks = List(child1, child2) :: same_ranks + case PartiallyAppliedProducer(p, s) => + process_producer(Some(node_id))(p) + case _ => () + } + node_id + } + + process_producer(None)(stmtProducer) + + val graph_dot_spec = + s""" + |digraph proof_tree { + | size="10,10"; + ${nodes.map({ case (node_id, node_text) => s" | ${node_id} [label=${"\"" ++ node_text ++ "\""}]" }).mkString("\n")} + | + ${edges.map({ case (fro_n, to_n) => s" | ${fro_n} -> ${to_n}" }).mkString("\n")} + | + ${same_ranks.map((ls => s" | { rank=same; ${ls.mkString(" ")} }")).mkString("\n")} + |} + |""".stripMargin + + visualize_dot_viz_spec(graph_dot_spec) + + } + + + def visualize_ctree(root: CertTree.Node): Unit = { + var nodes: List[(String, String)] = Nil + var edges: List[(String, String)] = Nil + var same_ranks: List[List[String]] = Nil + var count = 0 + + def next_id(x: Unit): String = { + count = count + 1 + s"node_${count}" + } + + def process_producer(root_node: Option[String])(stmtProducer: StmtProducer): State[CertTree.Node, String] = { + val node_id = next_id() + val node_text = stmt_producer_node_to_text(stmtProducer) + nodes = (node_id, node_text) :: nodes + root_node match { + case Some(value) => edges = (value, node_id) :: edges + case None => () + } + for { + _ <- (stmtProducer match { + case ChainedProducer(p1, p2) => + for { + child1 <- process_producer(Some(node_id))(p1) + child2 <- process_producer(Some(node_id))(p2) + _ <- State.ret(same_ranks = List(child1, child2) :: same_ranks) + } yield () + case PartiallyAppliedProducer(p, s) => for { + _ <- process_producer(Some(node_id)) (p) + } yield () + case BranchProducer(_,selectors) =>for { + _ <- State.mapM(selectors.toList)(_ => for { + child <- State.pop[CertTree.Node] + result <- process_root(Some(node_id))(child: CertTree.Node) + } yield result) + } yield () + + case GuardedProducer(cond, _) => for { + if_true <- State.pop[CertTree.Node] + if_false <- State.pop[CertTree.Node] + _ <- process_root(Some(node_id))(if_true) + _ <- process_root(Some(node_id))(if_false) + } yield () + case HandleGuard(_) => State.ret(()) + case ConstProducer(_) => State.ret(()) + case ExtractHelper(_) => State.ret(()) + case IdProducer => for { + child <- State.pop[CertTree.Node] + result <- process_root(Some(node_id))(child) + } yield () + + + case PrependProducer(_) => for { + child <- State.pop[CertTree.Node] + result <- process_root(Some(node_id))(child) + } yield () + + case v => for { + _ <- State.mapM(List.range(1, v.arity))(_ => for { + child <- State.pop[CertTree.Node] + result <- process_root(Some(node_id))(child: CertTree.Node) + } yield result) + } yield () + }) : State[CertTree.Node, Unit] + result <- State.ret(node_id) + } yield result + } + def process_root(parent:Option[String])(node: CertTree.Node) : State[CertTree.Node, String] = { + for { + _ <- State.push_multi(node.children) + result <- process_producer(parent)(node.kont) + } yield result + } + + process_root(None)(root).eval(Nil) + + val graph_dot_spec = + s""" + |digraph proof_tree { + | size="10,10"; + ${nodes.map({ case (node_id, node_text) => s" | ${node_id} [label=${"\"" ++ node_text ++ "\""}]" }).mkString("\n")} + | + ${edges.map({ case (fro_n, to_n) => s" | ${fro_n} -> ${to_n}" }).mkString("\n")} + | + ${same_ranks.map((ls => s" | { rank=same; ${ls.mkString(" ")} }")).mkString("\n")} + |} + |""".stripMargin + + visualize_dot_viz_spec(graph_dot_spec) + + } + + + private def visualize_dot_viz_spec(graph_dot_spec: String) = { + val dot_bin: Array[Byte] = dotbin("png")(graph_dot_spec) + + val image_file: File = { + val file = File.createTempFile("suslik_visualizer", ".png") + val out = new BufferedOutputStream(new FileOutputStream(file)) + out.write(dot_bin) + out.close() + file + } + val cmd = "feh " ++ image_file.toPath.toAbsolutePath.toString + cmd !! + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala new file mode 100644 index 000000000..65947ec32 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala @@ -0,0 +1,63 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with + +import scala.annotation.tailrec + +/** Implementation of a state monad where the state is a stack of elements */ +case class State[S, A](f: List[S] => (A, List[S])) { + + /** map = fmap */ + def map[B](g: A => B): State[S, B] = + State(s => { + val (a, t) = f(s) + (g(a), t) + }) + + /** flatMap = >>= */ + def flatMap[B](g: A => State[S, B]): State[S, B] = + State(s => { + val (a, t) = f(s) + g(a).f(t) + }) + + + /** run the state monad to get a value */ + def eval(s: List[S]): A = f(s)._1 +} + +object State { + /** return value */ + def ret[S, A] (v: A ) : State[S, A] = State(ls => (v ,ls)) + + /** push an element onto the state */ + def push[S] (s :S) : State[S, Unit] = { + State(ss => ((), s +: ss)) + } + + /** push multiple values onto the state, in such a way that is equivalent to iterating through the list and pushing them on individually */ + def push_multi[S] (s :List[S]) : State[S, Unit] = { + @tailrec + def rev_cat[C](ls: List[C])(acc: List[C]) : List[C] = ls match { + case Nil => acc + case ::(head ,tail) => rev_cat(tail)(head +: acc) + } + State(ss => ((), rev_cat(s)(ss))) + } + + /** pop a value off the stack */ + def pop[S] : State[S, S] = State { + case Nil => fail_with("State monad ran out of state") + case ::(head, tl) => (head, tl) + } + + def mapM[B,S,A] (ls: List[B]) (f: B => State[S,A]) : State[S,List[A]] = + ls match { + case Nil => State.ret(Nil) + case ::(head0, tail0) => for { + head <- f(head0) + tail <- mapM(tail0)(f) + } yield (head :: tail) + } + +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index d308d22e0..2bfaf4db0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -3,15 +3,15 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.clang.{CFormals, CTypes} import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.CExpr -import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions -import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.ProofCExpr +import ProofTerms.Expressions +import ProofTerms.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, VSTProofType} import org.tygus.suslik.language.PrettyPrinting object Formulae { /** abstract type categorizing all spatial formulae */ - sealed trait VSTHeaplet extends PrettyPrinting + sealed abstract class VSTHeaplet extends PrettyPrinting /** Spatial formulae diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index cfa50c535..065fd8c61 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,497 +1,24 @@ package org.tygus.suslik.certification.targets.vst.logic -import com.sun.imageio.plugins.bmp.BMPCompressionTypes -import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.clang.{CTypes, Expressions, PrettyPrinting} -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet -import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.ProofCExpr -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType -import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.language.Ident - -object Proof { - - sealed abstract class PureFormula extends PrettyPrinting - - /** predicate encoding that C-parameter (of type val) is a valid pointer */ - case class IsValidPointerOrNull(name: CVar) extends PureFormula { - override def pp: String = - s"is_pointer_or_null(${name.pp})" - } - - /** predicate encoding that C-parameter (of type val) is a valid int */ - case class IsValidInt(name: CVar) extends PureFormula { - override def pp:String = - s"ssl_is_valid_int(${name.pp})" - } - - /** Redefinition of expressions for use in VST proofs - * */ - object Expressions { - - /** encoding of expressions in VST proof script - * By default any boolean value will print to prop - * use pp_as_bool_value to print as a boolean - */ - sealed abstract class ProofCExpr extends PrettyPrinting { - /** prints the expression as though it were an element - * of type val (vst's encoding of C-values) - */ - def pp_as_c_value : String = this match { - case ProofCVar(name, typ) => typ match { - case ProofTypes.CoqParamType(ty) => name - case ProofTypes.CoqPtrType => name - case ProofTypes.CoqIntType => s"(Vint (Int.repr ${name}))" - case ProofTypes.CoqListType(elem, length) => name - case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") - } - case ProofCIntConst(value) => s"(Vint (Int.repr ${value.toString}))" - case ProofCSetLiteral(elems) => - s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" - case value@ProofCBinaryExpr(op, _, _) => - val is_int = op match { - case ProofCOpPlus => true - case ProofCOpMinus =>true - case ProofCOpMultiply =>true - case _ => false - } - if (is_int) { - s"(Vint (Int.repr ${value.pp}))" - } else { - value.pp - } - case value@ProofCUnaryExpr(op, _) => op match { - case ProofCOpNot => value.pp - case ProofCOpUnaryMinus => s"(Vint (Int.repr ${value.pp}))" - } - case v => v.pp - } - - def pp_as_ssl_union_value : String = this match { - case ProofCVar(name, typ) => typ match { - case ProofTypes.CoqParamType(ty) => ty match { - case CTypes.CIntType => s"inl ${name}" - case CTypes.CVoidPtrType =>s"inl ${name}" - } - case ProofTypes.CoqPtrType => s"inr ${name}" - case ProofTypes.CoqIntType => s"inl (Vint (Int.repr ${name}))" - case ProofTypes.CoqListType(elem, length) => name - case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") - } - case ProofCIntConst(value) => s"inl (Vint (Int.repr ${value.toString}))" - case ProofCSetLiteral(elems) => - s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" - case value@ProofCBinaryExpr(op, _, _) => - val is_int = op match { - case ProofCOpPlus => true - case ProofCOpMinus =>true - case ProofCOpMultiply =>true - case _ => false - } - if (is_int) { - s"inl (Vint (Int.repr ${value.pp}))" - } else { - ??? - } - case value@ProofCUnaryExpr(op, _) => op match { case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" } - case v => v.pp - } - - - /** print as a pointer value - * @throws NotImplementedError if expression is not a variable or 0-int */ - def pp_as_ptr_value : String = this match { - case ProofCVar(name, typ) => name - case ProofCBoolConst(value) => ??? - case ProofCIntConst(value) => if (value == 0) { "nullval" } else { ??? } - case ProofCSetLiteral(elems) => ??? - case ProofCIfThenElse(cond, left, right) => ??? - case ProofCBinaryExpr(op, left, right) => ??? - case ProofCUnaryExpr(op, e) => ??? - } - - } - - /** a variable in a VST proof */ - case class ProofCVar(name: String, typ: VSTProofType) extends ProofCExpr { - override def pp: String = typ match { - case ProofTypes.CoqPtrType => name - case ProofTypes.CoqIntType => name - case ProofTypes.CoqCardType(_) => name - case ProofTypes.CoqParamType(ty) => - // if the variable has a param type then - // its actually of type val, and we need to - // extract it's contained value - ty match { - case CTypes.CIntType => s"(force_signed_int ${name})" - case CTypes.CVoidPtrType => s"${name}" - case CTypes.CUnitType => ??? /// Unimplemented as we should never be dealing with unit types - } - case ProofTypes.CoqListType(elem, _) => name - } - } - - /** boolean constant in a VST proof */ - case class ProofCBoolConst(value: Boolean) extends ProofCExpr { - override def pp: String = value.toString - } - - /** integer constant in a VST proof */ - case class ProofCIntConst(value: Int) extends ProofCExpr { - override def pp: String = value.toString - } - - /** set literal (encoded as set) in a VST proof */ - case class ProofCSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { - override def pp: String = - s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" - } - - /** encodes a ternary expression in a VST proof */ - case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = ??? - // TODO: if statements don't seem to be used inside suslik proofs, so implementing this would be pointless - // if this assumption changes, then the correct implementation will look something like: - // - // s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" - // - // where pp_as_bool_value should be a method that prints expressions in boolean form - } - - case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = - op match { - case ProofCOpLt => s"(${left.pp} < ${right.pp})" - case ProofCOpLeq => s"(${left.pp} <= ${right.pp})" - case ProofCOpOr => s"(${left.pp} \/ ${right.pp})" - case ProofCOpAnd => s"(${left.pp} /\ ${right.pp})" - case ProofCOpPlus => s"(${left.pp} + ${right.pp})" - case ProofCOpMinus => s"(${left.pp} - ${right.pp})" - case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" - case ProofCOpIntEq => s"(${left.pp} = ${right.pp})" - case ProofCOpBoolEq => s"(${left.pp} = ${right.pp})" - case ProofCOpPtrEq => s"(${left.pp_as_ptr_value} = ${right.pp_as_ptr_value})" - case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" - case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" - // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" - } - } - - case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { - override def pp: String = - op match { - case ProofCOpNot => s"(~ ${e.pp})" - case ProofCOpUnaryMinus => s"(-(${e.pp}))" - } - } - - sealed abstract class ProofCUnOp - - object ProofCOpNot extends ProofCUnOp - - object ProofCOpUnaryMinus extends ProofCUnOp - - sealed abstract class ProofCBinOp - - object ProofCOpImplication extends ProofCBinOp - object ProofCOpPlus extends ProofCBinOp - - object ProofCOpMinus extends ProofCBinOp - - object ProofCOpMultiply extends ProofCBinOp - - object ProofCOpIntEq extends ProofCBinOp - - object ProofCOpBoolEq extends ProofCBinOp - - object ProofCOpSetEq extends ProofCBinOp - - object ProofCOpPtrEq extends ProofCBinOp - - object ProofCOpLeq extends ProofCBinOp - - object ProofCOpLt extends ProofCBinOp - - object ProofCOpAnd extends ProofCBinOp - - object ProofCOpOr extends ProofCBinOp - - object ProofCOpIn extends ProofCBinOp - - object ProofCOpSubset extends ProofCBinOp - - object ProofCOpUnion extends ProofCBinOp - object ProofCOpDiff extends ProofCBinOp - object ProofCOpIntersect extends ProofCBinOp - - } - - /** prop predicate encoding that a given propositional expression is true - * */ - case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { - override def pp: String = { - s"${expr.pp_as_c_value}" - } - } - - /** prop predicate encoding that a given boolean expression is true */ - case class IsTrue(expr: Expressions.ProofCExpr) extends PureFormula { - override def pp: String = { - s"(is_true ${expr.pp})" - } - } - - - sealed case class FormalCondition( - pure_constraints: List[PureFormula], - spatial_constraints: List[VSTHeaplet] - ) - - /** - * Type encoding VST-compliant formal specifications of a C Function - * @param name name of the function - * @param c_params parameters of the function - * @param formal_params parameters of the specification - * @param existensial_params existential params of the function - * @param precondition precondtion for the function - * @param postcondition post condition of the function - * @param return_type return type of the function - */ - case class FormalSpecification( - name: Ident, - c_params: Seq[(Ident,VSTCType)], - formal_params: Seq[(Ident,VSTProofType)], - existensial_params: Seq[(Ident,VSTProofType)], - precondition: FormalCondition, - postcondition: FormalCondition, - return_type: VSTCType - ) extends PrettyPrinting{ - - def as_vst_type(var_type: VSTCType) = var_type match { - case CTypes.CIntType => "tint" - case CTypes.CUnitType => "tvoid" - case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" - } - - override def pp: String = { - val formal_args = formal_params.map({case (var_name, var_type) => s"${var_name}: ${var_type.pp}"}) - val c_args = c_params.map({case (var_name, _) => s"${var_name}: val"}) - val FormalCondition(pre_pure_constraints, pre_spatial_constraints) = precondition - val FormalCondition(post_pure_constraints, post_spatial_constraints) = postcondition - s"""Definition ${name}_spec := - | DECLARE _${name} - | WITH ${(c_args ++ formal_args).mkString(", ")} - | PRE [ ${c_params.map({case (_, var_type) => s"${as_vst_type(var_type)}"}).mkString(", ") } ] - | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) - | PARAMS(${c_params.map({case (var_name, _) => var_name}).mkString("; ")}) - | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) - | POST[ ${as_vst_type(return_type)} ]${existensial_params match { - case Nil => "" - case _ => - "\n" ++ - existensial_params.map({case (param_name, param_type) => s"| EX ${param_name}: ${param_type.pp},"}).mkString("\n") - }} - | PROP( ${post_pure_constraints.map(_.pp).mkString("; ")} ) - | LOCAL() - | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). - |""".stripMargin - } - } - - /** - * Abstract constructors mapping cardinality constraints to - * termination measures in Coq - */ - sealed abstract class CardConstructor extends PrettyPrinting - /** - * Null constructor of 0 cardinality - */ - case object CardNull extends CardConstructor {} - /** Cardinality constructor of multiple components - * @param args the variables produced by unwrwapping this element - */ - case class CardOf(args: List[Ident]) extends CardConstructor {} - - - /** represents a clause of the VST predicate, - * @param pure are the pure assertions - * @param spatial are the spatial assertions - * @param sub_constructor are the subconstructors - * */ - case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String,CardConstructor]) - /** - * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker - * - * The idea is to conver the cardinality constraints produced by suslik into an inductive datatype - * with the expectation that each clause of the suslik predicate maps to a unique constructor - * - * Constructors are identified by their cardinality, and each suslik predicate maps to a unique cardinality datatype - * - * @param name is the name of the predicate - * @param params is the list of arguments to the predicate - * @param clauses is the mapping from cardinality constructors to clauses - * */ - case class VSTPredicate( - name: Ident, params: List[(String,VSTProofType)], - existentials: List[(String, VSTProofType)], - clauses: Map[CardConstructor, VSTPredicateClause]) - extends PrettyPrinting { - - /** returns all instances of constructors and the bindings they expose */ - def constructors: List[CardConstructor] = - clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => - constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor}) - }).toList - - /** returns all the constructors used in the base match, assumed to be a superset - * of the constructors used elsewhere - * - * Note: to see how there might be constructors elsewhere, suppose we had a cardinality - * constraint of the form: - * - * - `a < self_card` - * - * - `b < a` - * - * Then the corresponding match case would look like: - * - * `pred_1 ((pred_1 b) as a) => ... ` - * - * I don't this this actually happens in practice, so this is probably just a bit of - * over-engineering - * */ - def base_constructors: List[CardConstructor] = - clauses.map({ case (constructor, _) => - constructor - }).toList - - def constructor_args (constructor: CardConstructor) = - constructor match { - case CardNull => Nil - case CardOf(args) => args - } - - /** - * For a given clause of the predicate and its associated constructor, - * return the list of existential variables used in the body of the clause - * @param cons a constructor matching some clause of the predicate - * @param pclause the corresponding clause of the predicate - * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause - * */ - def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { - val param_map = params.toMap - val exist_map : Map[String, VSTProofType] = existentials.toMap - val card_map = constructor_args(cons) - pclause match { - case VSTPredicateClause(pure, spatial, sub_clauses) => - val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => constructor_args(cons)})).toSet - def to_variables(exp: ProofCExpr) : List[String] = exp match { - case Expressions.ProofCVar(name, typ) => - param_map.get(name) match { - case None if !clause_card_map.contains(name) => List(name) - case _ => List() - } - case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) - case Expressions.ProofCIfThenElse(cond, left, right) => - to_variables(cond) ++ to_variables(left) ++ to_variables(right) - case Expressions.ProofCBinaryExpr(op, left, right) => - to_variables(left) ++ to_variables(right) - case Expressions.ProofCUnaryExpr(op, e) => - to_variables(e) - case _ => List() - } - def to_variables_heap(heap: VSTHeaplet) : List[String] = heap match { - case Formulae.CDataAt(loc, elem_typ, count, elem) => - to_variables(loc) ++ to_variables(elem) - case Formulae.CSApp(pred, args, card) => - args.flatMap(to_variables).toList - } - (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap) : List[String]) - .toSet - .map((v: String) => (v, exist_map(v))).toList - } - } - - /** returns the name of the associated cardinality datatype - * for this predicate */ - def inductive_name : String = s"${name}_card" - - /** Given a cardinality constructor, return the Coq name of the - * associated cardinality constructor */ - def constructor_name (constructor: CardConstructor) : String = { - val count = constructor match { - case CardNull => 0 - case CardOf(args) => args.length - } - s"${inductive_name}_${count}" - } - - /** pretty print the constructor */ - override def pp: String = { - val constructor_map = base_constructors.map({ - case CardNull => (0, CardNull ) - case v@CardOf(args) => (args.length, v) - }).toMap - - def pp_constructor (constructor: CardConstructor) = { - constructor match { - case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" - case CardOf(args) => - s"${constructor_name(constructor)} : ${(args ++ List(inductive_name)).map(_ => inductive_name).mkString(" -> ")}" - } - } - - val inductive_definition = { - s"""Inductive ${inductive_name} : Set := - ${constructor_map.map({ case (_, constructor) => - s"| | ${pp_constructor(constructor)}" - }).mkString("\n")}. - | - |""".stripMargin - } - - // This function expands the arguments of a constructor and - // creates recursive pattern matches if necassary - i.e - // - // S ((S b) as a) => ..... - def expand_args(sub_constructor: Map[String, CardConstructor]) (idents: List[Ident]) : String = { - idents.map(arg => - sub_constructor.get(arg) match { - case Some(constructor) => - s"(${constructor_name(constructor)} ${expand_args(sub_constructor)(constructor_args(constructor))} as ${arg})" - case None => arg - } - ).mkString(" ") - } - - val predicate_definition = - s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})"}).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with - ${clauses.map({case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => - s"| | ${constructor_name(constructor)} ${ - expand_args(sub_constructor)(constructor_args(constructor)) - } => ${ - val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) - val str = clause_existentials.map({case (name, ty) => s"| EX ${name} : ${ty.pp},"}).mkString("\n") - clause_existentials match { - case Nil => "" - case ::(_, _) => "\n" + str + "\n" - } - } ${ - (pure.map(v => s"!!${v.pp}") - ++ - List((spatial match { case Nil => List("emp") case v => v.map(_.pp)}).mkString(" * "))).mkString(" && ") - }" - }).mkString("\n")} - |end. - |""".stripMargin - s"${inductive_definition}${predicate_definition}" - } - } - - +import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar +import org.tygus.suslik.language.Expressions.Expr +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.vst.{Debug, State} +import org.tygus.suslik.certification.targets.vst.logic.Proof +import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with +import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} +import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.language.Statements.{Skip, Store} +import org.tygus.suslik.logic.{PFormula, PointsTo, SFormula} +import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnificationRules} +import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} + + +case class Proof(steps: List[ProofSteps]) extends PrettyPrinting { + override def pp: String = + "start_function.\n" + + "ssl_open_context.\n" + + steps.map(_.pp).mkString("\n") +} - case class Proof(params: Seq[CVar]) { - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala new file mode 100644 index 000000000..4f55e4f1f --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala @@ -0,0 +1,121 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting +import ProofTerms.CardConstructor +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType +import org.tygus.suslik.language.Ident + +sealed abstract class ProofSteps extends PrettyPrinting { + + def to_validity_assertion(var_name: Ident, var_type: VSTProofType) : Option[ProofSteps] = { + var_type match { + case ProofTypes.CoqParamType(ty) => None + case ProofTypes.CoqPtrType => None + case ProofTypes.CoqIntType => None + case ProofTypes.CoqCardType(pred_type) => None + case ProofTypes.CoqListType(elem, length) => None + } + } + +} + +object ProofSteps { + case class Entailer(next: ProofSteps) extends ProofSteps { + override def pp: String = s"entailer!.\n${next.pp}" + } + + case class ForwardIfConstructor( + card_variable: String, + predicate_name: String, + branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String], ProofSteps)] + ) extends ProofSteps { + override def pp: String = { + def constructor_prop (cons_name: Ident, cons: CardConstructor) : String = cons match { + case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" + case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + } + + val branch_strings = + branches match { + case Nil => "" + case _ => + "\n" ++ branches.map( + { case ((cons_name, cons, cons_args), expr, args, ls) => + " - {\n" ++ + s"assert_PROP (${constructor_prop(cons_name, cons)}) as ssl_card_assert. { entailer!; ssl_dispatch_card. }\n" ++ + s"ssl_card ${predicate_name} ssl_card_assert ${cons_args.mkString(" ")}.\n" ++ + s"assert_PROP (${expr.pp}). { entailer!. }\n" ++ + (args match { + case Nil => "" + case args => s"Intros ${args.mkString(" ")}.\n" + }) ++ + ls.pp ++ + "\n}" + } + ).mkString("\n") + } + "forward_if." ++ branch_strings + } + } + + case class ForwardIf(branches: List[ProofSteps]) extends ProofSteps { + override def pp: String = { + val branch_strings = + branches match { + case Nil => "" + case _ => "\n" ++ branches.map(ls => " - {\n" ++ ls.pp ++ "\n}").mkString("\n") + } + "forward_if." ++ branch_strings + } + } + + case class Forward(next:ProofSteps) extends ProofSteps { + override def pp: String = s"forward.\n${next.pp}" + } + + case class Intros(variables: List[(Ident, VSTProofType)], next: ProofSteps) extends ProofSteps { + override def pp: String = { + val extra_assertions = + variables.flatMap({ case (var_name, var_type) => + to_validity_assertion(var_name, var_type) + }) match { + case Nil => "" + case ls => "\n" ++ ls.map(_.pp).mkString(".\n") + } + s"Intros ${variables.map(_._1).mkString(" ")}." ++ extra_assertions ++ s"\n${next.pp}" + } + } + + case class ValidPointerOrNull(variable: Ident,next: ProofSteps) extends ProofSteps { + override def pp: String = s"assert_PROP (is_pointer_or_null ${variable}). { entailer!. }\n${next.pp}" + } + + case class ValidPointer(variable: Ident,next: ProofSteps) extends ProofSteps { + override def pp: String = s"assert_PROP (isptr ${variable}). { entailer!. }\n${next.pp}" + } + + case class ForwardCall(args: List[Ident], next: ProofSteps) extends ProofSteps { + override def pp: String = s"forward_call (${args.mkString(", ")}).\n${next.pp}" + } + + case class Rename(old_name: Ident, new_name: Ident, next: ProofSteps) extends ProofSteps { + override def pp: String = s"try rename ${old_name} into ${new_name}.\n${next.pp}" + } + + case class Exists(variable: Ident, next: ProofSteps) extends ProofSteps { + override def pp: String = s"Exists ${variable}.\n${next.pp}" + } + + case class Free(variable: Ident, sz: Int, next: ProofSteps) extends ProofSteps { + override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${sz}, ${variable}).\n${next.pp}" + } + + case class AssertPropSubst(variable: Ident, expr: ProofCExpr, next: ProofSteps) extends ProofSteps { + override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp_as_ptr_value}) as ssl_var; try rewrite ssl_var. { entailer!. }\n${next.pp}" + } + + case object Qed extends ProofSteps { + override def pp: String = "" + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala new file mode 100644 index 000000000..a2ee5bc1a --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -0,0 +1,562 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqBoolType, CoqIntType, CoqListType, CoqParamType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.language.Ident + +object ProofTerms { + + sealed abstract class PureFormula extends PrettyPrinting + + /** predicate encoding that C-parameter (of type val) is a valid pointer */ + case class IsValidPointerOrNull(name: CVar) extends PureFormula { + override def pp: String = + s"is_pointer_or_null(${name.pp})" + } + + /** predicate encoding that C-parameter (of type val) is a valid int */ + case class IsValidInt(name: CVar) extends PureFormula { + override def pp:String = + s"ssl_is_valid_int(${name.pp})" + } + + /** Redefinition of expressions for use in VST proofs + * */ + object Expressions { + + /** encoding of expressions in VST proof script + * By default any boolean value will print to prop + * use pp_as_bool_value to print as a boolean + */ + sealed abstract class ProofCExpr extends PrettyPrinting { + + def type_expr: VSTProofType = this match { + case ProofCVar(name, typ) => typ + case ProofCBoolConst(value) => CoqBoolType + case ProofCIntConst(value) => CoqIntType + case ProofCSetLiteral(elems) => CoqListType(elems.head.type_expr, Some(elems.length)) + case ProofCIfThenElse(cond, left, right) => left.type_expr + case ProofCBinaryExpr(op, left, right) => op match { + case ProofCOpPlus => CoqIntType + case ProofCOpMinus => CoqIntType + case ProofCOpMultiply => CoqIntType + case _ => CoqBoolType + } + case ProofCUnaryExpr(op, e) => e.type_expr + } + + /** prints the expression as though it were an element + * of type val (vst's encoding of C-values) + */ + def pp_as_c_value : String = this match { + case ProofCVar(name, typ) => typ match { + case ProofTypes.CoqParamType(ty) => name + case ProofTypes.CoqPtrType => name + case ProofTypes.CoqIntType => s"(Vint (Int.repr ${name}))" + case ProofTypes.CoqListType(elem, length) => name + case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") + } + case ProofCIntConst(value) => s"(Vint (Int.repr ${value.toString}))" + case ProofCSetLiteral(elems) => + s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" + case value@ProofCBinaryExpr(op, _, _) => + val is_int = op match { + case ProofCOpPlus => true + case ProofCOpMinus =>true + case ProofCOpMultiply =>true + case _ => false + } + if (is_int) { + s"(Vint (Int.repr ${value.pp}))" + } else { + value.pp + } + case value@ProofCUnaryExpr(op, _) => op match { + case ProofCOpNot => value.pp + case ProofCOpUnaryMinus => s"(Vint (Int.repr ${value.pp}))" + } + case v => v.pp + } + + def pp_as_ssl_union_value : String = this match { + case ProofCVar(name, typ) => typ match { + case ProofTypes.CoqParamType(ty) => ty match { + case CTypes.CIntType => s"inl ${name}" + case CTypes.CVoidPtrType =>s"inl ${name}" + } + case ProofTypes.CoqPtrType => s"inr ${name}" + case ProofTypes.CoqIntType => s"inl (Vint (Int.repr ${name}))" + case ProofTypes.CoqListType(elem, length) => name + case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") + } + case ProofCIntConst(value) => s"inl (Vint (Int.repr ${value.toString}))" + case ProofCSetLiteral(elems) => + s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" + case value@ProofCBinaryExpr(op, _, _) => + val is_int = op match { + case ProofCOpPlus => true + case ProofCOpMinus =>true + case ProofCOpMultiply =>true + case _ => false + } + if (is_int) { + s"inl (Vint (Int.repr ${value.pp}))" + } else { + ??? + } + case value@ProofCUnaryExpr(op, _) => op match { case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" } + case v => v.pp + } + + + /** print as a pointer value + * @throws NotImplementedError if expression is not a variable or 0-int */ + def pp_as_ptr_value : String = this match { + case ProofCVar(name, typ) => name + case ProofCBoolConst(value) => this.pp + case ProofCIntConst(value) => if (value == 0) { "nullval" } else { this.pp } + case ProofCSetLiteral(elems) => this.pp + case ProofCIfThenElse(cond, left, right) => this.pp + case ProofCBinaryExpr(op, left, right) => this.pp + case ProofCUnaryExpr(op, e) => this.pp + } + + } + + /** a variable in a VST proof */ + case class ProofCVar(name: String, typ: VSTProofType) extends ProofCExpr { + override def pp: String = typ match { + case ProofTypes.CoqPtrType => name + case ProofTypes.CoqIntType => name + case ProofTypes.CoqCardType(_) => name + case ProofTypes.CoqParamType(ty) => + // if the variable has a param type then + // its actually of type val, and we need to + // extract it's contained value + ty match { + case CTypes.CIntType => s"(force_signed_int ${name})" + case CTypes.CVoidPtrType => s"${name}" + case CTypes.CUnitType => ??? /// Unimplemented as we should never be dealing with unit types + } + case ProofTypes.CoqListType(elem, _) => name + } + } + + /** boolean constant in a VST proof */ + case class ProofCBoolConst(value: Boolean) extends ProofCExpr { + override def pp: String = value.toString + } + + /** integer constant in a VST proof */ + case class ProofCIntConst(value: Int) extends ProofCExpr { + override def pp: String = value.toString + } + + /** set literal (encoded as set) in a VST proof */ + case class ProofCSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { + override def pp: String = + s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" + } + + /** encodes a ternary expression in a VST proof */ + case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = ??? + // TODO: if statements don't seem to be used inside suslik proofs, so implementing this would be pointless + // if this assumption changes, then the correct implementation will look something like: + // + // s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" + // + // where pp_as_bool_value should be a method that prints expressions in boolean form + } + + case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpLt => s"(${left.pp} < ${right.pp})" + case ProofCOpLeq => s"(${left.pp} <= ${right.pp})" + case ProofCOpOr => s"(${left.pp} \/ ${right.pp})" + case ProofCOpAnd => s"(${left.pp} /\ ${right.pp})" + case ProofCOpPlus => s"(${left.pp} + ${right.pp})" + case ProofCOpMinus => s"(${left.pp} - ${right.pp})" + case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" + case ProofCOpIntEq => s"(${left.pp} = ${right.pp})" + case ProofCOpBoolEq => s"(${left.pp} = ${right.pp})" + case ProofCOpPtrEq => s"(${left.pp_as_ptr_value} = ${right.pp_as_ptr_value})" + case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" + case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" + // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" + } + } + + case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpNot => s"(~ ${e.pp})" + case ProofCOpUnaryMinus => s"(-(${e.pp}))" + } + } + + sealed abstract class ProofCUnOp + + object ProofCOpNot extends ProofCUnOp + + object ProofCOpUnaryMinus extends ProofCUnOp + + sealed abstract class ProofCBinOp + + object ProofCOpImplication extends ProofCBinOp + object ProofCOpPlus extends ProofCBinOp + + object ProofCOpMinus extends ProofCBinOp + + object ProofCOpMultiply extends ProofCBinOp + + object ProofCOpIntEq extends ProofCBinOp + + object ProofCOpBoolEq extends ProofCBinOp + + object ProofCOpSetEq extends ProofCBinOp + + object ProofCOpPtrEq extends ProofCBinOp + + object ProofCOpLeq extends ProofCBinOp + + object ProofCOpLt extends ProofCBinOp + + object ProofCOpAnd extends ProofCBinOp + + object ProofCOpOr extends ProofCBinOp + + object ProofCOpIn extends ProofCBinOp + + object ProofCOpSubset extends ProofCBinOp + + object ProofCOpUnion extends ProofCBinOp + object ProofCOpDiff extends ProofCBinOp + object ProofCOpIntersect extends ProofCBinOp + + } + + /** prop predicate encoding that a given propositional expression is true + * */ + case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { + override def pp: String = { + s"${expr.pp_as_c_value}" + } + } + + /** prop predicate encoding that a given boolean expression is true */ + case class IsTrue(expr: Expressions.ProofCExpr) extends PureFormula { + override def pp: String = { + s"(is_true ${expr.pp})" + } + } + + + sealed case class FormalCondition( + pure_constraints: List[PureFormula], + spatial_constraints: List[VSTHeaplet] + ) + + /** + * Type encoding VST-compliant formal specifications of a C Function + * @param name name of the function + * @param c_params parameters of the function + * @param formal_params parameters of the specification + * @param existensial_params existential params of the function + * @param precondition precondtion for the function + * @param postcondition post condition of the function + * @param return_type return type of the function + */ + case class FormalSpecification( + name: Ident, + c_params: Seq[(Ident,VSTCType)], + formal_params: Seq[(Ident,VSTProofType)], + existensial_params: Seq[(Ident,VSTProofType)], + precondition: FormalCondition, + postcondition: FormalCondition, + return_type: VSTCType + ) extends PrettyPrinting{ + + def as_vst_type(var_type: VSTCType) = var_type match { + case CTypes.CIntType => "tint" + case CTypes.CUnitType => "tvoid" + case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" + } + + def params : List[(Ident, VSTProofType)] = + (c_params.map({case (name,ty) => (name, CoqParamType(ty))}) ++ formal_params).toList + + override def pp: String = { + val formal_args = formal_params.map({case (var_name, var_type) => s"${var_name}: ${var_type.pp}"}) + val c_args = c_params.map({case (var_name, _) => s"${var_name}: val"}) + val FormalCondition(pre_pure_constraints, pre_spatial_constraints) = precondition + val FormalCondition(post_pure_constraints, post_spatial_constraints) = postcondition + s"""Definition ${name}_spec := + | DECLARE _${name} + | WITH ${(c_args ++ formal_args).mkString(", ")} + | PRE [ ${c_params.map({case (_, var_type) => s"${as_vst_type(var_type)}"}).mkString(", ") } ] + | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) + | PARAMS(${c_params.map({case (var_name, _) => var_name}).mkString("; ")}) + | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) + | POST[ ${as_vst_type(return_type)} ]${existensial_params match { + case Nil => "" + case _ => + "\n" ++ + existensial_params.map({case (param_name, param_type) => s"| EX ${param_name}: ${param_type.pp},"}).mkString("\n") + }} + | PROP( ${post_pure_constraints.map(_.pp).mkString("; ")} ) + | LOCAL() + | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). + |""".stripMargin + } + } + + /** + * Abstract constructors mapping cardinality constraints to + * termination measures in Coq + */ + sealed abstract class CardConstructor extends PrettyPrinting { + def constructor_args : List[Ident] = + this match { + case CardNull => Nil + case CardOf(args) => args + } + } + /** + * Null constructor of 0 cardinality + */ + case object CardNull extends CardConstructor {} + /** Cardinality constructor of multiple components + * @param args the variables produced by unwrwapping this element + */ + case class CardOf(args: List[Ident]) extends CardConstructor {} + + + /** represents a clause of the VST predicate, + * @param pure are the pure assertions + * @param spatial are the spatial assertions + * @param sub_constructor are the subconstructors + * */ + case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String,CardConstructor]) { + + /** finds existential variables in the expression using args */ + def find_existentials_args (args: Set[String]): List[(Ident,VSTProofType)] = { + def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { + case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil + case Expressions.ProofCBoolConst(value) => Nil + case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) + case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) + case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) + case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) + } + def spatial_expr_existential: VSTHeaplet => List[(Ident,VSTProofType)] = { + case Formulae.CSApp(pred, args, card) => + expr_existential(card) ::: args.flatMap(expr_existential).toList + } + pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) + } + + /** finds existential variables in the expression using args */ + def find_existentials (existentials: Map[Ident, VSTProofType]): List[(Ident,VSTProofType)] = { + def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { + case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList + case Expressions.ProofCBoolConst(value) => Nil + case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) + case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) + case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) + case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) + } + def spatial_expr_existential: VSTHeaplet => List[(Ident,VSTProofType)] = { + case Formulae.CSApp(pred, args, card) => + expr_existential(card) ::: args.flatMap(expr_existential).toList + case Formulae.CDataAt(loc, elem_typ, count, elem) => + (expr_existential(loc) ++ expr_existential(elem)) + } + pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) + } + + } + /** + * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker + * + * The idea is to conver the cardinality constraints produced by suslik into an inductive datatype + * with the expectation that each clause of the suslik predicate maps to a unique constructor + * + * Constructors are identified by their cardinality, and each suslik predicate maps to a unique cardinality datatype + * + * @param name is the name of the predicate + * @param params is the list of arguments to the predicate + * @param clauses is the mapping from cardinality constructors to clauses + * */ + case class VSTPredicate( + name: Ident, params: List[(String,VSTProofType)], + existentials: List[(String, VSTProofType)], + clauses: Map[CardConstructor, VSTPredicateClause]) + extends PrettyPrinting { + + /** returns the existential variables introduced by a constructor invokation */ + def constructor_existentials (constructor: CardConstructor) : List[(Ident,VSTProofType)] = { + val param_map = params.toMap + val existential_map = existentials.toMap.filterKeys(key => !param_map.contains(key)) + clauses(constructor).find_existentials(existential_map) + } + + /** returns all instances of constructors and the bindings they expose */ + def constructors: List[CardConstructor] = + clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => + constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor}) + }).toList + + /** returns all the constructors used in the base match, assumed to be a superset + * of the constructors used elsewhere + * + * Note: to see how there might be constructors elsewhere, suppose we had a cardinality + * constraint of the form: + * + * - `a < self_card` + * + * - `b < a` + * + * Then the corresponding match case would look like: + * + * `pred_1 ((pred_1 b) as a) => ... ` + * + * I don't this this actually happens in practice, so this is probably just a bit of + * over-engineering + * */ + def base_constructors: List[CardConstructor] = + clauses.map({ case (constructor, _) => + constructor + }).toList + + /** + * For a given clause of the predicate and its associated constructor, + * return the list of existential variables used in the body of the clause + * @param cons a constructor matching some clause of the predicate + * @param pclause the corresponding clause of the predicate + * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause + * */ + def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { + val param_map = params.toMap + val exist_map : Map[String, VSTProofType] = existentials.toMap + val card_map = cons.constructor_args + pclause match { + case VSTPredicateClause(pure, spatial, sub_clauses) => + val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructor_args})).toSet + def to_variables(exp: ProofCExpr) : List[String] = exp match { + case Expressions.ProofCVar(name, typ) => + param_map.get(name) match { + case None if !clause_card_map.contains(name) => List(name) + case _ => List() + } + case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) + case Expressions.ProofCIfThenElse(cond, left, right) => + to_variables(cond) ++ to_variables(left) ++ to_variables(right) + case Expressions.ProofCBinaryExpr(op, left, right) => + to_variables(left) ++ to_variables(right) + case Expressions.ProofCUnaryExpr(op, e) => + to_variables(e) + case _ => List() + } + def to_variables_heap(heap: VSTHeaplet) : List[String] = heap match { + case Formulae.CDataAt(loc, elem_typ, count, elem) => + to_variables(loc) ++ to_variables(elem) + case Formulae.CSApp(pred, args, card) => + args.flatMap(to_variables).toList + } + (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap) : List[String]) + .toSet + .map((v: String) => (v, exist_map(v))).toList + } + } + + /** returns the name of the associated cardinality datatype + * for this predicate */ + def inductive_name : String = s"${name}_card" + + /** Given a cardinality constructor, return the Coq name of the + * associated cardinality constructor */ + def constructor_name (constructor: CardConstructor) : String = { + val count = constructor match { + case CardNull => 0 + case CardOf(args) => args.length + } + s"${inductive_name}_${count}" + } + + /** pretty print the constructor */ + override def pp: String = { + val constructor_map = base_constructors.map({ + case CardNull => (0, CardNull ) + case v@CardOf(args) => (args.length, v) + }).toMap + + def pp_constructor (constructor: CardConstructor) = { + constructor match { + case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" + case CardOf(args) => + s"${constructor_name(constructor)} : ${(args ++ List(inductive_name)).map(_ => inductive_name).mkString(" -> ")}" + } + } + + val inductive_definition = { + s"""Inductive ${inductive_name} : Set := + ${constructor_map.map({ case (_, constructor) => + s"| | ${pp_constructor(constructor)}" + }).mkString("\n")}. + | + |""".stripMargin + } + + // This function expands the arguments of a constructor and + // creates recursive pattern matches if necassary - i.e + // + // S ((S b) as a) => ..... + def expand_args(sub_constructor: Map[String, CardConstructor]) (idents: List[Ident]) : String = { + idents.map(arg => + sub_constructor.get(arg) match { + case Some(constructor) => + s"(${constructor_name(constructor)} ${expand_args(sub_constructor)(constructor.constructor_args)} as ${arg})" + case None => arg + } + ).mkString(" ") + } + + val predicate_definition = + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})"}).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with + ${clauses.map({case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => + s"| | ${constructor_name(constructor)} ${ + expand_args(sub_constructor)(constructor.constructor_args) + } => ${ + val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) + val str = clause_existentials.map({case (name, ty) => s"| EX ${name} : ${ty.pp},"}).mkString("\n") + clause_existentials match { + case Nil => "" + case ::(_, _) => "\n" + str + "\n" + } + } ${ + (pure.map(v => s"!!${v.pp}") + ++ + List((spatial match { case Nil => List("emp") case v => v.map(_.pp)}).mkString(" * "))).mkString(" && ") + }" + }).mkString("\n")} + |end. + |""".stripMargin + s"${inductive_definition}${predicate_definition}" + } + } + + + + +} + + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala index ef52b6b0d..351380e1c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala @@ -39,6 +39,10 @@ object ProofTypes { override def pp: String = "val" } + case object CoqBoolType extends VSTProofType { + override def pp: String = ??? + } + /** type of integers (used to represent the values of variables in a C program) */ case object CoqIntType extends VSTProofType { override def pp:String = "Z" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala new file mode 100644 index 000000000..bc923658e --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -0,0 +1,516 @@ +package org.tygus.suslik.certification.targets.vst.translation + +import java.io + +import org.tygus.suslik.certification.targets.htt.language.Expressions.CPointsTo +import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.clang.CTypes.{CIntType, CVoidPtrType, VSTCType} +import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} +import org.tygus.suslik.certification.targets.vst.logic.{ProofTerms, ProofTypes} +import ProofTerms.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} +import ProofTerms.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqIntType, CoqListType, CoqParamType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.language.Expressions.Var +import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} +import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} +import org.tygus.suslik.logic.Specifications.{Assertion, Goal} + +import scala.collection.immutable + +/** translates suslik proof terms to VST compatible proof terms */ +object ProofSpecTranslation { + + /** translate a suslik type into a VST proof type */ + def translate_type(lType: SSLType): VSTProofType = + lType match { + case IntType => CoqIntType + case LocType => CoqPtrType + case CardType => CoqCardType("") // TODO: add a safe version of this (only used when initializing base context) + + // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types + // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible + // but doesn't mesh well with C in which an allocated array must have all elements of the same type + // otherwise a separate struct definition would be needed + case IntSetType => CoqListType(CoqPtrType, None) + } + + + /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) */ + def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): ProofTerms.Expressions.ProofCExpr = { + def type_expr(left_1: ProofCExpr): VSTProofType = + left_1 match { + case ProofCVar(name, typ) => typ + case ProofCIntConst(value) => CoqIntType + case ProofCSetLiteral(elems) => CoqListType(CoqPtrType, Some(elems.length)) + case ProofCIfThenElse(cond, left, right) => type_expr(left) + case ProofCBinaryExpr(op, left, right) => + op match { + case ProofCOpPlus => CoqIntType + case ProofCOpMinus => CoqIntType + case ProofCOpMultiply => CoqIntType + case ProofCOpUnion => CoqListType(CoqPtrType, None) + } + case ProofCUnaryExpr(op, e) => op match { + case ProofCOpUnaryMinus => CoqIntType + } + } + + def translate_binop(op: Expressions.BinOp)(ty: VSTProofType): ProofCBinOp = { + op match { + case op: Expressions.RelOp => (op, ty) match { + case (Expressions.OpEq, CoqIntType) => ProofCOpIntEq + case (Expressions.OpEq, CoqParamType(CIntType)) => ProofCOpIntEq + case (Expressions.OpEq, CoqPtrType) => ProofCOpPtrEq + case (Expressions.OpEq, CoqParamType(CVoidPtrType)) => ProofCOpPtrEq + case (Expressions.OpEq, _) => ProofCOpIntEq + case (Expressions.OpBoolEq, _) => ProofCOpBoolEq + case (Expressions.OpLeq, _) => ProofCOpLeq + case (Expressions.OpLt, _) => ProofCOpLt + case (Expressions.OpIn, _) => ProofCOpIn + case (Expressions.OpSetEq,_) => ProofCOpSetEq + case (Expressions.OpSubset, _) => ProofCOpSubset + } + case op: Expressions.LogicOp => op match { + case Expressions.OpAnd => ProofCOpAnd + case Expressions.OpOr => ProofCOpOr + } + case Expressions.OpImplication => ProofCOpImplication + case Expressions.OpPlus => ProofCOpPlus + case Expressions.OpMinus => ProofCOpMinus + case Expressions.OpMultiply => ProofCOpMultiply + case Expressions.OpUnion => ProofCOpUnion + case Expressions.OpDiff => ProofCOpDiff + case Expressions.OpIntersect => ProofCOpIntersect + } + } + + expr match { + case const: Expressions.Const => const match { + case Expressions.IntConst(value) => ProofCIntConst(value) + case Expressions.BoolConst(value) => ProofCBoolConst(value) + } + case Var(name) => ProofCVar(name, context(name)) + case Expressions.SetLiteral(elems) => { + ProofCSetLiteral(elems.map(translate_expression(context))) + } + case Expressions.UnaryExpr(op, arg) => + val top: ProofCUnOp = op match { + case Expressions.OpNot => ProofCOpNot + case Expressions.OpUnaryMinus => ProofCOpUnaryMinus + } + ProofCUnaryExpr(top, translate_expression(context)(arg)) + case Expressions.BinaryExpr(op, left, right) => + val left_expr = translate_expression(context)(left) + val type_of_expr = type_expr(left_expr) + val top: ProofCBinOp = translate_binop(op)(type_of_expr) + ProofCBinaryExpr(top, left_expr, translate_expression(context)(right)) + case Expressions.IfThenElse(cond, left, right) => + ProofCIfThenElse( + translate_expression(context)(cond), + translate_expression(context)(left), + translate_expression(context)(right) + ) + case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => + val left_1 = translate_expression(context)(left) + val right_1 = translate_expression(context)(right) + overloaded_op match { + case op: Expressions.BinOp => + val type_of_expr = type_expr(left_1) + ProofCBinaryExpr(translate_binop(op)(type_of_expr), left_1, right_1) + case Expressions.OpOverloadedEq => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1) + case CoqListType(_, _) => + ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1) + case ProofTypes.CoqPtrType => + ProofCBinaryExpr(ProofCOpPtrEq, left_1, right_1) + } + case Expressions.OpNotEqual => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqIntType => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1)) + case CoqListType(elem, _) => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1)) + case ProofTypes.CoqPtrType => ??? // TODO: Handle pointer equality? or fail? + } + case Expressions.OpGt => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLeq, left_1, right_1)) + case Expressions.OpGeq => + ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLt, left_1, right_1)) + case Expressions.OpOverloadedPlus => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpPlus, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpUnion, left_1, right_1) + } + case Expressions.OpOverloadedMinus => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpMinus, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpDiff, left_1, right_1) + } + case Expressions.OpOverloadedLeq => + val l1_ty: VSTProofType = type_expr(left_1) + l1_ty match { + case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? + case ProofTypes.CoqIntType => + ProofCBinaryExpr(ProofCOpLeq, left_1, right_1) + case CoqListType(elem, _) => + ProofCBinaryExpr(ProofCOpSubset, left_1, right_1) + } + case Expressions.OpOverloadedStar => ??? // TODO: Handle star operation + } + + } + } + + + /** given a VST proof expression and a typing context, + * this function will type the expression and return + * a type */ + def type_expr(context: Map[Ident, VSTProofType]) (cvalue: ProofTerms.Expressions.ProofCExpr) : VSTProofType = + cvalue match { + case ProofCVar(name, typ) => typ + case ProofCIntConst(value) => CoqIntType + case ProofCSetLiteral(elems) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) + case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) + case ProofCBinaryExpr(op, left, right) => op match { + case ProofCOpPlus => CoqIntType + case ProofCOpMinus => CoqIntType + case ProofCOpMultiply => CoqIntType + } + case ProofCUnaryExpr(op, e) => op match { + case ProofCOpUnaryMinus => CoqIntType + } + } + + /** + * Translate a list of suslik heaplets into a form accepted by VST + * @param context the typing context + * @param heaplets a list of suslik heaplets + * @return a VST encoding of these heaplets + * + * Note: Suslik encodes blocks of pointers slightly differently to + * VST - when dealing with a block of contiguous pointers in memory, + * Suslik first uses a block declaration to specify the size of the + * contiguous block, and then has a number of subsequent heaplets + * that assign values to each element of this block. + * + * VST combines these two declarations into one: `data_at` - a `data_at` declaration + * states what a given pointer points to - in the case of contiguous memory, + * we must list out the corresponding values in order - just as they would be encoded in memory + * + * This function performs the translation between suslik's encoding and VST's encoding + */ + def translate_heaplets(context: Map[Ident, VSTProofType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { + val initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty + + // we first build up a mapping from pointer variables + // to the declarations that relate to them + // predicate applications are separated out unchanged + // as these translate directly to vst + val (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = + heaplets.foldLeft((initial_map, List(): List[CSApp]))({ + case ((map, acc), ty: Heaplet) => + ty match { + case ty@PointsTo(loc@Var(name), offset, value) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(ty), None)) + case Some((points_to_acc: List[_], block_acc)) => + map.updated(name, (List(ty) ++ points_to_acc, block_acc)) + } + (updated_map, acc: List[CSApp]) + case ty@Block(loc@Var(name), sz) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(), Some(ty))) + case Some((points_to_acc, None)) => map.updated(name, (points_to_acc, Some(ty))) + } + (updated_map, acc: List[CSApp]) + case SApp(pred, args, tag, card) => + (map, (List(CSApp(pred, args.map(translate_expression((context))), translate_expression(context)(card))) ++ acc) + ) + } + }) + + // having built the mapping, we then translate each (k,v) pair in this + // mapping into a VST Data at declaration + val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => + o_block match { + case Some((_@Block(loc,sz))) => + val loc_pos = translate_expression(context)(loc) + val o_array : Array[Option[ProofCExpr]] = Array.fill(sz)(None) + points_to.foreach({case PointsTo(_, offset, value) => + o_array.update(offset, Some(translate_expression(context)(value))) + }) + val elems = o_array.map(_.get).toList + val elem_type = type_expr(context)(elems.head) + CDataAt(loc_pos, CoqListType(elem_type, Some(sz)), sz, ProofCSetLiteral(elems)) + case None => + assert( + points_to.length == 1, + "found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block" + ) + (points_to.head : PointsTo) match { + case PointsTo(loc, 0, value) => + val c_value = translate_expression(context)(value) + CDataAt(translate_expression(context)(loc), type_expr(context)(c_value), 0, c_value) + case PointsTo(_, _, _) => + assert(false, "found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") + ??? + } + } + }).toList + + // return the blocks and the applications + blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) + } + def translate_assertion (context: Map[Ident, VSTProofType]) (assertion: Assertion): FormalCondition = assertion match { + case Assertion(phi, sigma) => + { + val pure_conditions = + phi.conjuncts.map(translate_expression(context)) + .map(IsTrueProp).toList + + val spatial_conditions: List[VSTHeaplet] = + translate_heaplets(context)(sigma.chunks) + + FormalCondition(pure_conditions, spatial_conditions) + } + } + + /** translates a Suslik function specification into a proof */ + def translate_conditions(proc: CProcedureDefinition)(goal: Goal): (FormalSpecification, Map[Ident, VSTProofType]) = { + + val name: Ident = proc.name + val c_params: Seq[(Ident, VSTCType)] = proc.params.map({ case (CVar(name), cType) => (name, cType) }) + + + // collect all cardinality_params and their associated types + val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ + case PointsTo(loc, offset, value) => None + case Block(loc, sz) => None + case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) + case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") + }).toMap + + val formal_params: List[(Ident, VSTProofType)] = { + val c_param_set = c_params.map(_._1).toSet + goal.universals + .map({ case variable@Var(name) => + if (cardinality_params.contains(name)) { + (name, cardinality_params(name)) + } else { + (name, translate_type(goal.gamma(variable))) + }}) + .filterNot({case (name, _) => c_param_set.contains(name)}).toList + } + + val existential_params: List[(Ident, VSTProofType)] = + goal.existentials.map({ case variable@Var(name) => + if (cardinality_params.contains(name)) { + (name, cardinality_params(name)) + } else { + (name, translate_type(goal.gamma(variable))) + } + }).toList + val return_type: VSTCType = proc.rt + + val context = ( + formal_params ++ existential_params ++ + c_params.map({ case (ident, cType) => (ident, ProofTypes.proof_type_of_c_type(cType)) }) + ).toMap + + val precondition: FormalCondition = { + val pure_conditions = + goal.pre.phi.conjuncts.map(translate_expression(context)) + .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => + cType match { + case CTypes.CIntType => Some(IsValidInt(CVar(ident))) + case CTypes.CUnitType => None + case CTypes.CVoidPtrType => Some(IsValidPointerOrNull(CVar(ident))) + } + }) ++ formal_params.flatMap({ case (ident, ty) => ty match { + case ProofTypes.CoqPtrType =>Some(IsValidPointerOrNull(CVar(ident))) + case ProofTypes.CoqIntType => Some(IsValidInt(CVar(ident))) + case _ => None + }}) + val spatial_conditions: List[VSTHeaplet] = + translate_heaplets(context)(goal.pre.sigma.chunks) + + FormalCondition(pure_conditions, spatial_conditions) + } + val postcondition: FormalCondition = { + val pure_conditions = + goal.post.phi.conjuncts.map(translate_expression(context)) + .map(IsTrueProp).toList + val spatial_conditions = + translate_heaplets(context)(goal.post.sigma.chunks) + // goal.post.sigma.chunks.map(translate_heaplet(context)).toList + FormalCondition(pure_conditions, spatial_conditions) + } + + (FormalSpecification( + name, c_params, formal_params, existential_params, precondition, postcondition, return_type + ), context) + } + + + /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map + * from cardinality name to constructors */ + def build_card_cons(card_conds: List[(String, String)]): Map[String, CardOf] = { + // to perform this translation, we first construct a mapping of relations + // where for every constraint of the form (a < b), we set map(b) = a :: map(b), + // thus the mapping for a variable contains the list of other variables that are + // constrainted to be immediately smaller than it + var child_map : Map[String, List[String]] = Map.empty + card_conds.foreach({case (child, parent) => + child_map.get(parent) match { + case None => child_map = child_map.updated(parent, List(child)) + case Some(children) => child_map = child_map.updated(parent, child :: children) + }}) + // the keys of the map now become variables that are destructured + // in the match case to produce the variables immediately below it + child_map.map({ case (str, strings) => (str, CardOf(strings))}) + } + + + /** translates suslik's inductive predicate into a format that is + * accepted by VST + * + * In order to do this, we make use of the cardinality constraints that are + * associated with each clause, and use this to construct an inductive data + * type that encodes the proof of termination + * + * For example, consider the lseg predicate + * + * lseg(x, s) { + * + * x == 0 ==> ... (no cardinality constraints) + * + * x <> 0 ==> a < self_card ... lseg(x',s') + * + * } + * + * Then we'd create a cardinality datatype as: + * + * Inductive lseg_card : Set := + * + * | lseg_card_0 : lseg_card + * + * | lseg_card_1 : lseg_card -> lseg_card. + * + * And then implement lseg as taking in a third parameter being its cardinality, + * and matching on this - taking the first clause if the input is `lseg_card0` and the + * second clause if the input is `lseg_card1 a` (and recursing on `a` + * + * */ + def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { + + + // Determines whether a given variable is a cardinality constraint + // TODO: I've definitely seen some function elsewhere that already does this + def is_card (s: String) : Boolean = s.startsWith("_") || s.contentEquals("self_card") + + // extracts a cardinality relation from an expression if it exists + def extract_card_constructor(expr: Expressions.Expr) : Option[(String, String)] = { + expr match { + case Expressions.BinaryExpr(op, Var(left), Var(parent)) + if is_card(left) && is_card(parent) => + op match { + case op: Expressions.RelOp => op match { + case Expressions.OpLt => + Some ((left, parent)) + case _ => None + } + case _ => None + } + case Expressions.OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) + if is_card(left) && is_card(parent) => + overloaded_op match { + case op: Expressions.BinOp => op match { + case op: Expressions.RelOp => op match { + case Expressions.OpLt => Some ((left, parent)) + case _ => None + } + case _ => None + } + case Expressions.OpGt =>Some ((parent, left)) + case _ => None + } + case _ => None + } + + } + + // base context contains type information for every variable used in the + // predicate (even if it occurs at a top level or not) + val base_context : List[(Ident, VSTProofType)] = { + var gamma: Gamma = Map.empty + predicate match { + case InductivePredicate(name, params, clauses) => + clauses.foreach({case InductiveClause(selector, assn) => + selector.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) + assn.phi.conjuncts.foreach(expr => + expr.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) + ) + assn.sigma.resolve(gamma, env).foreach(v => gamma = v) + }) + } + gamma.map({case (Var(name), ty) => (name, translate_type(ty))}).toList + } + + predicate match { + case InductivePredicate(name, raw_params, raw_clauses) => { + + val params: List[(String, VSTProofType)] = + raw_params.map({case (Var(name), sType) => (name, translate_type(sType))}) + val context: Map[Ident, VSTProofType] = (base_context ++ params).toMap + + + // separate clauses by cardinality constructors + // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a + // unique cardinality constraint + val clauses: Map[CardConstructor, VSTPredicateClause] = raw_clauses.map({ + case InductiveClause(selector, asn) => + // first, split the pure conditions in the predicate between those that + // encode cardinality constraints and those that don't + val (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => + extract_card_constructor(expr) match { + case value@Some(_) => (None, value) + case None => (Some(expr), None) + } + ).toList.unzip + + // translate the pure conditions into VST format + val select = translate_expression(context)(selector) + val conds = r_conds.flatten.map(translate_expression(context)).toList + + // translate the spatial constraints + val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) + + // Convert the cardinality constraints into an associated constructor + val card_conds = r_card_conds.flatten + card_conds match { + case card_conds@(::(_, _)) => + val card_cons : Map[String, CardConstructor] = build_card_cons(card_conds) + (card_cons("self_card"), VSTPredicateClause(select :: conds, spat_conds, card_cons)) + case Nil => (CardNull, VSTPredicateClause(select :: conds, spat_conds, Map.empty)) + } + }).toMap + VSTPredicate(name, params, base_context, clauses) + } + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index a3be33e9f..5f66e84ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -1,496 +1,642 @@ package org.tygus.suslik.certification.targets.vst.translation -import java.io - -import org.tygus.suslik.certification.targets.htt.language.Expressions.CPointsTo -import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar -import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} -import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTypes} -import org.tygus.suslik.certification.targets.vst.logic.Proof.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.Proof.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqIntType, CoqListType, CoqPtrType, VSTProofType} -import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.language.Expressions.Var -import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} -import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} -import org.tygus.suslik.logic.Specifications.{Assertion, Goal} - -import scala.collection.immutable - -/** translates suslik proof terms to VST compatible proof terms */ +import org.tygus.suslik.certification.targets.vst.logic.ProofRule.EmpRule +import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.AssertPropSubst +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{VSTPredicate, VSTPredicateClause} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqParamType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.{Debug, State} +import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofRule, ProofSteps, ProofTerms} +import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with +import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} +import org.tygus.suslik.language.{Expressions, Ident, PrettyPrinting, Statements} +import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Skip, Store} +import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} +import org.tygus.suslik.logic.Specifications.SuspendedCallGoal +import org.tygus.suslik.logic.{Block, Heaplet, PFormula, PointsTo, SApp, SFormula} +import org.tygus.suslik.synthesis.rules.LogicalRules.FrameBlock.profilesMatch +import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure +import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnfoldingRules, UnificationRules} +import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} + +import scala.annotation.tailrec +import scala.collection.immutable.Map + + + object ProofTranslation { - /** translate a suslik type into a VST proof type */ - def translate_type(lType: SSLType): VSTProofType = - lType match { - case IntType => CoqIntType - case LocType => CoqPtrType - case CardType => CoqCardType("") // TODO: add a safe version of this (only used when initializing base context) - - // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types - // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible - // but doesn't mesh well with C in which an allocated array must have all elements of the same type - // otherwise a separate struct definition would be needed - case IntSetType => CoqListType(CoqPtrType, None) + case class ProofRuleTranslationException(msg: String) extends Exception { + override def toString: String = s"ProofRuleTranslationException(${msg})" + } + + /** converts a single proof node into a compressed rule */ + def proof_rule_of_proof_node(node: CertTree.Node): ProofRule = { + def fail_with_bad_proof_structure(): Nothing = { + throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") } + def fail_with_bad_children(ls: List[CertTree.Node], count: Int): Nothing = { + throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != ${count}") + } - /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) */ - def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): Proof.Expressions.ProofCExpr = { - def type_expr(left_1: ProofCExpr): VSTProofType = - left_1 match { - case ProofCVar(name, typ) => typ - case ProofCIntConst(value) => CoqIntType - case ProofCSetLiteral(elems) => CoqListType(CoqPtrType, Some(elems.length)) - case ProofCIfThenElse(cond, left, right) => type_expr(left) - case ProofCBinaryExpr(op, left, right) => - op match { - case ProofCOpPlus => CoqIntType - case ProofCOpMinus => CoqIntType - case ProofCOpMultiply => CoqIntType - case ProofCOpUnion => CoqListType(CoqPtrType, None) - } - case ProofCUnaryExpr(op, e) => op match { - case ProofCOpUnaryMinus => CoqIntType - } - } + node.rule match { + case LogicalRules.NilNotLval => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => { + // find all pointers that are not yet known to be non-null + def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { + // All pointers + val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet + allPointers.filter( + x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) + ) + } + + val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList - def translate_binop(op: Expressions.BinOp): ProofCBinOp = { - op match { - case op: Expressions.RelOp => op match { - case Expressions.OpEq => ProofCOpIntEq - case Expressions.OpBoolEq => ProofCOpBoolEq - case Expressions.OpLeq => ProofCOpLeq - case Expressions.OpLt => ProofCOpLt - case Expressions.OpIn => ProofCOpIn - case Expressions.OpSetEq => ProofCOpSetEq - case Expressions.OpSubset => ProofCOpSubset + node.children match { + case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } } - case op: Expressions.LogicOp => op match { - case Expressions.OpAnd => ProofCOpAnd - case Expressions.OpOr => ProofCOpOr + case v => fail_with_bad_proof_structure() + } + case FailRules.CheckPost => node.kont match { + case IdProducer => node.children match { + case ::(head, Nil) => ProofRule.CheckPost(proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) } - case Expressions.OpImplication => ProofCOpImplication - case Expressions.OpPlus => ProofCOpPlus - case Expressions.OpMinus => ProofCOpMinus - case Expressions.OpMultiply => ProofCOpMultiply - case Expressions.OpUnion => ProofCOpUnion - case Expressions.OpDiff => ProofCOpDiff - case Expressions.OpIntersect => ProofCOpIntersect + case _ => fail_with_bad_proof_structure() } - } - - expr match { - case const: Expressions.Const => const match { - case Expressions.IntConst(value) => ProofCIntConst(value) - case Expressions.BoolConst(value) => ProofCBoolConst(value) + case UnificationRules.Pick => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Pick(map, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - case Var(name) => ProofCVar(name, context(name)) - case Expressions.SetLiteral(elems) => { - ProofCSetLiteral(elems.map(translate_expression(context))) + case FailRules.AbduceBranch => node.kont match { + case GuardedProducer(cond, _) => + node.children match { + case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, proof_rule_of_proof_node(if_true), proof_rule_of_proof_node(if_false)) + case ls => fail_with_bad_children(ls, 2) + } + case _ => fail_with_bad_proof_structure() } - case Expressions.UnaryExpr(op, arg) => - val top: ProofCUnOp = op match { - case Expressions.OpNot => ProofCOpNot - case Expressions.OpUnaryMinus => ProofCOpUnaryMinus - } - ProofCUnaryExpr(top, translate_expression(context)(arg)) - case Expressions.BinaryExpr(op, left, right) => - val top: ProofCBinOp = translate_binop(op) - ProofCBinaryExpr(top, translate_expression(context)(left), translate_expression(context)(right)) - case Expressions.IfThenElse(cond, left, right) => - ProofCIfThenElse( - translate_expression(context)(cond), - translate_expression(context)(left), - translate_expression(context)(right) - ) - case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => - val left_1 = translate_expression(context)(left) - val right_1 = translate_expression(context)(right) - overloaded_op match { - case op: Expressions.BinOp => - ProofCBinaryExpr(translate_binop(op), left_1, right_1) - case Expressions.OpOverloadedEq => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1) - case CoqListType(_, _) => - ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1) - case ProofTypes.CoqPtrType => - ProofCBinaryExpr(ProofCOpPtrEq, left_1, right_1) - } - case Expressions.OpNotEqual => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqIntType => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1)) - case CoqListType(elem, _) => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1)) - case ProofTypes.CoqPtrType => ??? // TODO: Handle pointer equality? or fail? - } - case Expressions.OpGt => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLeq, left_1, right_1)) - case Expressions.OpGeq => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLt, left_1, right_1)) - case Expressions.OpOverloadedPlus => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpPlus, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpUnion, left_1, right_1) - } - case Expressions.OpOverloadedMinus => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpMinus, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpDiff, left_1, right_1) - } - case Expressions.OpOverloadedLeq => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpLeq, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpSubset, left_1, right_1) - } - case Expressions.OpOverloadedStar => ??? // TODO: Handle star operation - } - - } - } - - - /** given a VST proof expression and a typing context, - * this function will type the expression and return - * a type */ - def type_expr(context: Map[Ident, VSTProofType]) (cvalue: Proof.Expressions.ProofCExpr) : VSTProofType = - cvalue match { - case ProofCVar(name, typ) => typ - case ProofCIntConst(value) => CoqIntType - case ProofCSetLiteral(elems) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) - case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) - case ProofCBinaryExpr(op, left, right) => op match { - case ProofCOpPlus => CoqIntType - case ProofCOpMinus => CoqIntType - case ProofCOpMultiply => CoqIntType + case OperationalRules.WriteRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Write(stmt, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - case ProofCUnaryExpr(op, e) => op match { - case ProofCOpUnaryMinus => CoqIntType + case LogicalRules.WeakenPre => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) + node.children match { + case ::(head, Nil) => ProofRule.WeakenPre(unused, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - } - - /** - * Translate a list of suslik heaplets into a form accepted by VST - * @param context the typing context - * @param heaplets a list of suslik heaplets - * @return a VST encoding of these heaplets - * - * Note: Suslik encodes blocks of pointers slightly differently to - * VST - when dealing with a block of contiguous pointers in memory, - * Suslik first uses a block declaration to specify the size of the - * contiguous block, and then has a number of subsequent heaplets - * that assign values to each element of this block. - * - * VST combines these two declarations into one: `data_at` - a `data_at` declaration - * states what a given pointer points to - in the case of contiguous memory, - * we must list out the corresponding values in order - just as they would be encoded in memory - * - * This function performs the translation between suslik's encoding and VST's encoding - */ - def translate_heaplets(context: Map[Ident, VSTProofType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { - val initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty - - // we first build up a mapping from pointer variables - // to the declarations that relate to them - // predicate applications are separated out unchanged - // as these translate directly to vst - val (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = - heaplets.foldLeft((initial_map, List(): List[CSApp]))({ - case ((map, acc), ty: Heaplet) => - ty match { - case ty@PointsTo(loc@Var(name), offset, value) => - val updated_map = map.get(name) match { - case None => map.updated(name, (List(ty), None)) - case Some((points_to_acc: List[_], block_acc)) => - map.updated(name, (List(ty) ++ points_to_acc, block_acc)) + case LogicalRules.EmpRule => node.kont match { + case ConstProducer(Skip) => + node.children match { + case Nil => ProofRule.EmpRule + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + ProofRule.PureSynthesis(true, assignments, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Open => node.kont match { + case ChainedProducer(ChainedProducer(BranchProducer(Some((heaplet, fresh_vars)), selectors), HandleGuard(_)), ExtractHelper(_)) => + ProofRule.Open(heaplet, fresh_vars, selectors.zip(node.children).map({ case (expr, node) => (expr, proof_rule_of_proof_node(node)) }).toList) + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.SubstLeft => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.SubstL(map, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.SubstRight => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.SubstR(map, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.ReadRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Read(map, stmt, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.AbduceCall => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + + // find out which new variables were added to the context + val new_vars = + head.goal.gamma.filterKeys(key => !(node.goal.gamma.contains(key))) + var f_pre = head.goal.post + + var SuspendedCallGoal(_, _, callePost, call, freshSub, _) = head.goal.callGoal.get + ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPure => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPointer => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + val prePtss = pre.sigma.ptss + val postPtss = post.sigma.ptss + + def lcpLen(s1: String, s2: String): Int = s1.zip(s2).takeWhile(Function.tupled(_ == _)).length + + val alternatives = for { + PointsTo(y, oy, _) <- postPtss + if y.vars.exists(goal.isExistential) + t@PointsTo(x, ox, _) <- prePtss + if ox == oy + if !postPtss.exists(sameLhs(t)) + } yield (y -> x) + alternatives.minBy { case (e1, e2) => -lcpLen(e1.pp, e2.pp) } match { + case (y: Var, x) => ProofRule.HeapUnifyPointer(Map(y -> x), proof_rule_of_proof_node(head)) } - (updated_map, acc: List[CSApp]) - case ty@Block(loc@Var(name), sz) => - val updated_map = map.get(name) match { - case None => map.updated(name, (List(), Some(ty))) - case Some((points_to_acc, None)) => map.updated(name, (points_to_acc, Some(ty))) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) } - (updated_map, acc: List[CSApp]) - case SApp(pred, args, tag, card) => - (map, (List(CSApp(pred, args.map(translate_expression((context))), translate_expression(context)(card))) ++ acc) - ) + case ls => fail_with_bad_children(ls, 1) } - }) - - // having built the mapping, we then translate each (k,v) pair in this - // mapping into a VST Data at declaration - val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => - o_block match { - case Some((_@Block(loc,sz))) => - val loc_pos = translate_expression(context)(loc) - val o_array : Array[Option[ProofCExpr]] = Array.fill(sz)(None) - points_to.foreach({case PointsTo(_, offset, value) => - o_array.update(offset, Some(translate_expression(context)(value))) - }) - val elems = o_array.map(_.get).toList - val elem_type = type_expr(context)(elems.head) - CDataAt(loc_pos, CoqListType(elem_type, Some(sz)), sz, ProofCSetLiteral(elems)) - case None => - assert( - points_to.length == 1, - "found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block" - ) - (points_to.head : PointsTo) match { - case PointsTo(loc, 0, value) => - val c_value = translate_expression(context)(value) - CDataAt(translate_expression(context)(loc), type_expr(context)(c_value), 0, c_value) - case PointsTo(_, _, _) => - assert(false, "found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") - ??? + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfoldingFinal => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) + } + case ls => fail_with_bad_children(ls, 1) } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameFlat => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.CallRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(call: Call), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Call(call, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.FreeRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => + val size : Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { + case Some(value) => value + case None => 1 + } + node.children match { + case ::(head, Nil) => ProofRule.Free(stmt, size, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.AllocRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + ProofRule. + Malloc(map, stmt, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Close => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, pred_subst, fresh_exist, subst_args), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + } + case LogicalRules.StarPartial => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) + val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) + + node.children match { + case ::(head, Nil) => + ProofRule.StarPartial(new_pre_phi, new_post_phi, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - }).toList - - // return the blocks and the applications - blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) - } - /** translates a Suslik function specification into a proof */ - def translate_conditions(proc: CProcedureDefinition)(goal: Goal): FormalSpecification = { - - val name: Ident = proc.name - val c_params: Seq[(Ident, VSTCType)] = proc.params.map({ case (CVar(name), cType) => (name, cType) }) - - - // collect all cardinality_params and their associated types - val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ - case PointsTo(loc, offset, value) => None - case Block(loc, sz) => None - case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) - case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") - }).toMap - - val formal_params: List[(Ident, VSTProofType)] = { - val c_param_set = c_params.map(_._1).toSet - goal.universals - .map({ case variable@Var(name) => - if (cardinality_params.contains(name)) { - (name, cardinality_params(name)) - } else { - (name, translate_type(goal.gamma(variable))) - }}) - .filterNot({case (name, _) => c_param_set.contains(name)}).toList - } + case UnificationRules.PickCard => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => ProofRule.PickCard(proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + } + case UnificationRules.PickArg => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => ProofRule.PickArg(map, proof_rule_of_proof_node(head)) + case ls => fail_with_bad_children(ls, 1) + } + } - val existential_params: List[(Ident, VSTProofType)] = - goal.existentials.map({ case variable@Var(name) => - if (cardinality_params.contains(name)) { - (name, cardinality_params(name)) - } else { - (name, translate_type(goal.gamma(variable))) - } - }).toList - val return_type: VSTCType = proc.rt - - val context = ( - formal_params ++ existential_params ++ - c_params.map({ case (ident, cType) => (ident, ProofTypes.proof_type_of_c_type(cType)) }) - ).toMap - - val precondition: FormalCondition = { - val pure_conditions = - goal.pre.phi.conjuncts.map(translate_expression(context)) - .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => - cType match { - case CTypes.CIntType => Some(IsValidInt(CVar(ident))) - case CTypes.CUnitType => None - case CTypes.CVoidPtrType => Some(IsValidPointerOrNull(CVar(ident))) - } - }) ++ formal_params.flatMap({ case (ident, ty) => ty match { - case ProofTypes.CoqPtrType =>Some(IsValidPointerOrNull(CVar(ident))) - case ProofTypes.CoqIntType => Some(IsValidInt(CVar(ident))) - case _ => None - }}) - val spatial_conditions: List[VSTHeaplet] = - translate_heaplets(context)(goal.pre.sigma.chunks) - - FormalCondition(pure_conditions, spatial_conditions) - } - val postcondition: FormalCondition = { - val pure_conditions = - goal.post.phi.conjuncts.map(translate_expression(context)) - .map(IsTrueProp).toList - val spatial_conditions = - translate_heaplets(context)(goal.post.sigma.chunks) - // goal.post.sigma.chunks.map(translate_heaplet(context)).toList - FormalCondition(pure_conditions, spatial_conditions) } - FormalSpecification( - name, c_params, formal_params, existential_params, precondition, postcondition, return_type - ) } - /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map - * from cardinality name to constructors */ - def build_card_cons(card_conds: List[(String, String)]): Map[String, CardOf] = { - // to perform this translation, we first construct a mapping of relations - // where for every constraint of the form (a < b), we set map(b) = a :: map(b), - // thus the mapping for a variable contains the list of other variables that are - // constrainted to be immediately smaller than it - var child_map : Map[String, List[String]] = Map.empty - card_conds.foreach({case (child, parent) => - child_map.get(parent) match { - case None => child_map = child_map.updated(parent, List(child)) - case Some(children) => child_map = child_map.updated(parent, child :: children) - }}) - // the keys of the map now become variables that are destructured - // in the match case to produce the variables immediately below it - child_map.map({ case (str, strings) => (str, CardOf(strings))}) + def generate_args(new_variables: List[(String, VSTProofType)]) (card_args: List[String]) = { + var seen_variables_set : Set[String] = Set() + var contructor_map : Map[Ident, Ident] = Map() + val args = new_variables.flatMap({ + case (variable_name, ty) => + if (!seen_variables_set.contains(variable_name)){ + seen_variables_set = seen_variables_set + variable_name + (card_args.find(name => variable_name.startsWith(name))) match { + case Some(name) => + contructor_map = contructor_map + (name -> variable_name) + None + case None => + Some(variable_name) + } + } else { + None + } + }) + (args, card_args.map(arg => contructor_map(arg))) } + def translate_proof(predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, root: CertTree.Node, pre_cond: ProofTerms.FormalCondition): Proof = { + val pred_map = predicates.map(v => (v.name, v)).toMap + + type FunSpec = (Ident, List[Ident]) + type Context = (Map[Ident, VSTProofType], List[(Ident, List[Ident])]) + + + def unify_expr (context: Map[Ident,Ident]) (pure:ProofCExpr) (call: ProofCExpr) : Map[Ident,Ident] = + (pure, call) match { + case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) + case (ProofCBoolConst(_), ProofCBoolConst(_)) => context + case (ProofCIntConst(_), ProofCIntConst(_)) => context + case (ProofCSetLiteral(elems), ProofCSetLiteral(call_elems)) => + elems.zip(call_elems).foldLeft(context)({case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr)}) + case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => + var new_context = unify_expr(context)(cond)(call_cond) + new_context = unify_expr(new_context)(left)(call_left) + unify_expr(new_context)(right)(call_right) + case (ProofCBinaryExpr(_, left, right),ProofCBinaryExpr(_, call_left, call_right)) => + val new_context = unify_expr(context)(left)(call_left) + unify_expr(new_context)(right)(call_right) + case (ProofCUnaryExpr(_, e),ProofCUnaryExpr(_, call_e)) => + unify_expr(context)(e)(call_e) + } - /** translates suslik's inductive predicate into a format that is - * accepted by VST - * - * In order to do this, we make use of the cardinality constraints that are - * associated with each clause, and use this to construct an inductive data - * type that encodes the proof of termination - * - * For example, consider the lseg predicate - * - * lseg(x, s) { - * - * x == 0 ==> ... (no cardinality constraints) - * - * x <> 0 ==> a < self_card ... lseg(x',s') - * - * } - * - * Then we'd create a cardinality datatype as: - * - * Inductive lseg_card : Set := - * - * | lseg_card_0 : lseg_card - * - * | lseg_card_1 : lseg_card -> lseg_card. - * - * And then implement lseg as taking in a third parameter being its cardinality, - * and matching on this - taking the first clause if the input is `lseg_card0` and the - * second clause if the input is `lseg_card1 a` (and recursing on `a` - * - * */ - def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { - - - // Determines whether a given variable is a cardinality constraint - // TODO: I've definitely seen some function elsewhere that already does this - def is_card (s: String) : Boolean = s.startsWith("_") || s.contentEquals("self_card") - - // extracts a cardinality relation from an expression if it exists - def extract_card_constructor(expr: Expressions.Expr) : Option[(String, String)] = { - expr match { - case Expressions.BinaryExpr(op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => - op match { - case op: Expressions.RelOp => op match { - case Expressions.OpLt => - Some ((left, parent)) - case _ => None + def unify_call_params (call_pre: ProofTerms.FormalCondition) : List[(Ident, VSTProofType)] = { + (pre_cond,call_pre) match { + case (ProofTerms.FormalCondition(pure, spatial),ProofTerms.FormalCondition(call_pure, call_spatial)) => + def unify_pure(context: Map[Ident, Ident])(pure: ProofTerms.PureFormula) (call: ProofTerms.PureFormula) : Map[Ident,Ident] = { + (pure, call) match { + case (ProofTerms.IsValidPointerOrNull(CVar(name)),ProofTerms.IsValidPointerOrNull(CVar(name1))) => + context + (name -> name1) + case (ProofTerms.IsValidInt(CVar(name)), ProofTerms.IsValidInt(CVar(name1))) => + context + (name -> name1) + case (ProofTerms.IsTrueProp(expr), ProofTerms.IsTrueProp(expr1)) => + unify_expr(context)(expr)(expr1) + case (ProofTerms.IsTrue(expr), ProofTerms.IsTrue(expr1)) => + unify_expr(context)(expr)(expr1) } - case _ => None - } - case Expressions.OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => - overloaded_op match { - case op: Expressions.BinOp => op match { - case op: Expressions.RelOp => op match { - case Expressions.OpLt => Some ((left, parent)) - case _ => None - } - case _ => None + } + def unify_spatial(context: Map[Ident, Ident])(pure: VSTHeaplet)(call: VSTHeaplet) : Map[Ident,Ident] = { + (pure,call) match { + case (CDataAt(loc, elem_ty, count, elem), CDataAt(call_loc, call_elem_ty, call_count, call_elem)) => + unify_expr(unify_expr(context)(loc)(call_loc))(elem)(call_elem) + case (CSApp(pred, args, card), CSApp(call_pred, call_args, call_card)) => + assert(pred == call_pred) + unify_expr(args.zip(call_args).foldRight(context)({case ((exp, call_exp), context) => + unify_expr(context)(exp)(call_exp) + }))(card)(call_card) } - case Expressions.OpGt =>Some ((parent, left)) - case _ => None } - case _ => None + var context = pure.zip(call_pure).foldLeft(Map[Ident,Ident]())({case (context, (pure, call_pure)) => unify_pure(context)(pure)(call_pure)}) + context = spatial.zip(call_spatial).foldLeft(context)({case (context, (pure, call_pure)) => unify_spatial(context)(pure)(call_pure)}) + spec.params.map({case (name, ty) => (context(name), ty)}) } + } + + def initial_context: Context = + ((spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ + spec.formal_params).toMap, Nil) + def retrieve_typing_context: Context => Map[Ident, VSTProofType] = { + case (gamma, _) => gamma } - // base context contains type information for every variable used in the - // predicate (even if it occurs at a top level or not) - val base_context : List[(Ident, VSTProofType)] = { - var gamma: Gamma = Map.empty - predicate match { - case InductivePredicate(name, params, clauses) => - clauses.foreach({case InductiveClause(selector, assn) => - selector.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) - assn.phi.conjuncts.foreach(expr => - expr.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) - ) - assn.sigma.resolve(gamma, env).foreach(v => gamma = v) - }) + def add_new_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { + case (old_params, funs) => (old_params ++ new_params, funs) + } + + def pop_function(context: Context): (FunSpec, Context) = context match { + case (old_params, fun :: funs) => (fun, (old_params, funs)) + } + + def push_function(fun_spec: FunSpec)(context: Context): Context = context match { + case (old_params, old_funs) => (old_params, fun_spec :: old_funs) + } + + def record_variable_mapping(mapping: Map[Var, Expr])(context: Context): Context = { + val variable_mapping = mapping.flatMap({ case (Var(old_name), Var(new_name)) => Some((old_name, new_name)) case _ => None }) + context match { + case (old_params, funs) => + val new_params = old_params.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) + val new_funs = funs.map({ case (fun_name, args) => (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg))) }) + (new_params, new_funs) } - gamma.map({case (Var(name), ty) => (name, translate_type(ty))}).toList } - predicate match { - case InductivePredicate(name, raw_params, raw_clauses) => { - - val params: List[(String, VSTProofType)] = - raw_params.map({case (Var(name), sType) => (name, translate_type(sType))}) - val context: Map[Ident, VSTProofType] = (base_context ++ params).toMap - - - // separate clauses by cardinality constructors - // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a - // unique cardinality constraint - val clauses: Map[CardConstructor, VSTPredicateClause] = raw_clauses.map({ - case InductiveClause(selector, asn) => - // first, split the pure conditions in the predicate between those that - // encode cardinality constraints and those that don't - val (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => - extract_card_constructor(expr) match { - case value@Some(_) => (None, value) - case None => (Some(expr), None) + + def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { + rule match { + case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, cases) => + val arg_set = args.toSet + val pred = pred_map(predicate_name) + ProofSteps.ForwardIfConstructor( + card_variable, + predicate_name, + pred.clauses.zip(cases).map({ + case ((constructor, clause), (expr, rule)) => + val new_variables = pred.constructor_existentials(constructor).map({ + case (variable, ty) => + fresh_vars.get(Var(variable)).map({case Var(new_name) => (new_name, ty)}).getOrElse((variable,ty)) + }) + val variable_map = new_variables.toMap + val new_context = add_new_variables(variable_map)(context) + + val (args, constructor_args) = generate_args(new_variables)(constructor.constructor_args) + + ((pred.constructor_name(constructor), constructor, constructor_args), + ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), + args, + translate_proof_rules(rule)(new_context)) + }).toList + ) + case ProofRule.NilNotLval(vars, next) => ??? + case ProofRule.CheckPost(next) => ??? + case ProofRule.Pick(subst, next) => ??? + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => ??? + case ProofRule.Write(stmt, next) => ??? + case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) + case ProofRule.EmpRule => ProofSteps.Forward(ProofSteps.Qed) + case ProofRule.PureSynthesis(is_final, assignments, next) => ??? + case ProofRule.SubstL(map, next) => + map.toList.foldRight(translate_proof_rules(next)(context))({ + case ((Var(name), expr), next) => + AssertPropSubst( + name, + ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr), + next) + }) + case ProofRule.SubstR(map, next) => + map.toList match { + case ::((Var(old_name), Var(new_name)), _) => + val new_context = record_variable_mapping(map)(context) + ProofSteps.Rename(old_name, new_name, + translate_proof_rules(next)(new_context) + ) + } + case ProofRule.Read(subst, operation, next) => + subst.toList match { + case ::((Var(old_var), Var(new_var)), tl) => + def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { + case Var(name) => (name == variable) + case const: Expressions.Const => false + case Expressions.BinaryExpr(op, left, right) => is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => + is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + case Expressions.UnaryExpr(op, arg) => is_variable_used_in_exp(variable)(arg) + case Expressions.SetLiteral(elems) => elems.exists(is_variable_used_in_exp(variable)) + case Expressions.IfThenElse(cond, left, right) => + is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) } - ).toList.unzip - - // translate the pure conditions into VST format - val select = translate_expression(context)(selector) - val conds = r_conds.flatten.map(translate_expression(context)).toList - - // translate the spatial constraints - val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) - - // Convert the cardinality constraints into an associated constructor - val card_conds = r_card_conds.flatten - card_conds match { - case card_conds@(::(_, _)) => - val card_cons : Map[String, CardConstructor] = build_card_cons(card_conds) - (card_cons("self_card"), VSTPredicateClause(select :: conds, spat_conds, card_cons)) - case Nil => (CardNull, VSTPredicateClause(select :: conds, spat_conds, Map.empty)) - } - }).toMap - VSTPredicate(name, params, base_context, clauses) + def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { + def map_varaible(map: Map[Var, Expr]): Ident = + map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) + + rule match { + case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.CheckPost(next) => is_variable_used_in_proof(variable)(next) + case ProofRule.PickCard(next) => is_variable_used_in_proof(variable)(next) + case ProofRule.PickArg(subst, next) => + val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) + case ProofRule.Pick(subst, next) => + val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + is_variable_used_in_exp(variable)(cond) || + is_variable_used_in_proof(variable)(ifTrue) || + is_variable_used_in_proof(variable)(ifFalse) + case ProofRule.Write(Statements.Store(Var(tov), offset, e), next) => + (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) + case ProofRule.WeakenPre(unused, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.EmpRule => false + case ProofRule.PureSynthesis(is_final, assignments, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.Open(pred, heaplet, cases) => + cases.exists({ case (expr, rule) => + is_variable_used_in_exp(variable)(expr) || + is_variable_used_in_proof(variable)(rule) + }) + case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.HeapUnify(next) => is_variable_used_in_proof(variable)(next) + case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset), next) => + (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) + case ProofRule.Call(Call(_, args, _), next) => + args.exists(is_variable_used_in_exp(variable)) || + is_variable_used_in_proof(variable)(next) + case ProofRule.Free(Free(Var(v)), _, next) => + (v == variable) || is_variable_used_in_proof(variable)(next) + case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz), next) => + (toe != variable) && is_variable_used_in_proof(variable)(next) + } + } + val new_context = record_variable_mapping(subst)(context) + val rest = (retrieve_typing_context(context).get(old_var)) match { + case Some(CoqPtrType) => + ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) + case _ => translate_proof_rules(next)(new_context) + } + + if (is_variable_used_in_proof(new_var)(next)) { + ProofSteps.Rename(old_var, new_var, + ProofSteps.Forward( + rest + ) + ) + } else { + ProofSteps.Rename(old_var, new_var, + rest + ) + } + } + case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, next) => + var typing_context = retrieve_typing_context(context) + f_pre.vars.foreach({case Var(name) => if (!typing_context.contains(name)) { + typing_context = typing_context + (name -> CoqPtrType) + } }) + val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) + val call_args = unify_call_params(call_precond).map({case (name, _) => name}) + var new_context = push_function((fun, call_args))(context) + translate_proof_rules(next)(new_context) + case ProofRule.HeapUnify(next) => translate_proof_rules(next)(context) + case ProofRule.HeapUnifyPointer(map, next) => translate_proof_rules(next)(context) + case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) + case ProofRule.Call(call, next) => + val ((fun, args),new_context) = pop_function(context) + ProofSteps.ForwardCall(args, translate_proof_rules(next)(new_context)) + case ProofRule.Free(Free(Var(name)), size, next) => + ProofSteps.Free(name, size, translate_proof_rules(next)(context)) + case ProofRule.Malloc(map, stmt, next) => ??? + case ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, next) => ??? + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => ??? + case ProofRule.PickCard(next) => ??? + case ProofRule.PickArg(map, next) => ??? } } + + val simplified = proof_rule_of_proof_node(root) + println(s"Converted proof:\n ${simplified.pp}") + + val vst_proof: ProofSteps = translate_proof_rules(simplified)(initial_context) + + println("Converted proof:") + println(vst_proof.pp) + + //Debug.visualize_ctree(root) + //Debug.visualize_proof_tree(root.kont) + + ??? } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 8cc36db04..b1b46f894 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -2,10 +2,11 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition -import org.tygus.suslik.certification.targets.vst.logic.Proof.{Proof, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition -import org.tygus.suslik.certification.targets.vst.logic.Proof +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.synthesis.IdProducer.ruleAssert @@ -21,61 +22,21 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - /** Implementation of a state monad where the state is a stack of elements */ - case class State[S,A](f: List[S] => (A, List[S])) { - - /** map = fmap */ - def map[B](g: A => B): State[S, B] = - State(s => { - val (a, t) = f(s) - (g(a), t) - }) - - /** flatMap = >>= */ - def flatMap[B](g: A => State[S, B]): State[S, B] = - State(s => { - val (a, t) = f(s) - g(a).f(t) - }) - - - /** push an element onto the state */ - def push (s:S) : State[S, Unit] = { - State(ss => ((), s +: ss)) - } - - /** push multiple values onto the state, in such a way that is equivalent to iterating through the list and pushing them on individually */ - def push_multi (s:List[S]) : State[S, Unit] = { - @tailrec - def rev_cat[C](ls: List[C])(acc: List[C]) : List[C] = ls match { - case Nil => acc - case ::(head,tail) => rev_cat(tail)(head +: acc) - } - State(ss => ((), rev_cat(s)(ss))) - } - - /** pop a value off the stack */ - def pop : State[S, S] = State { - case Nil => fail_with("State monad ran out of state") - case ::(head, tl) => (head, tl) - } - - /** run the state monad to get a value */ - def eval(s: List[S]): A = f(s)._1 - } - - def translate(root: CertTree.Node, proc: Procedure, env: Environment): Nothing = { val procedure = CTranslation.translate_function(proc, root.goal.gamma) - val spec = ProofTranslation.translate_conditions(procedure)(root.goal) + val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) + val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) println(procedure.pp) println(spec.pp) val predicates: List[VSTPredicate] = env.predicates.map({ case (_, predicate) => - ProofTranslation.translate_predicate(env)(predicate) + ProofSpecTranslation.translate_predicate(env)(predicate) }).toList predicates.foreach(v => println(v.pp)) - println(root.goal.gamma) + + val proof = ProofTranslation.translate_proof(predicates, spec, root, pre_cond) + + println(proof.pp) //translate_cont(root.children, root.kont) diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 8122d08d4..c3d76a360 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -145,7 +145,7 @@ case class HandleGuard(goal: Goal) extends StmtProducer { } // Produces a conditional that branches on the selectors -case class BranchProducer(selectors: Seq[Expr]) extends StmtProducer { +case class BranchProducer(pred: Option[(SApp, SubstVar)], selectors: Seq[Expr]) extends StmtProducer { val arity: Int = selectors.length val fn: Kont = liftToSolutions(stmts => { if (stmts.length == 1) stmts.head else { diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala index 22ffbd5b5..bc4208600 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala @@ -249,7 +249,7 @@ object SymbolicExecutionRules extends SepLogicUtils with RuleUtils { val pre = goal.pre val thenGoal = goal.spawnChild(Assertion(pre.phi && cond, pre.sigma), sketch = tb) val elseGoal = goal.spawnChild(Assertion(pre.phi && cond.not, pre.sigma), sketch = eb) - List(RuleResult(List(thenGoal, elseGoal), BranchProducer(List(cond, cond.not)), this, goal)) + List(RuleResult(List(thenGoal, elseGoal), BranchProducer(None, List(cond, cond.not)), this, goal)) } case (If(_, _, _), _) => { throw SynthesisException("Found conditional in the middle of the program. Conditionals only allowed at the end.") @@ -268,7 +268,7 @@ object SymbolicExecutionRules extends SepLogicUtils with RuleUtils { def apply(goal: Goal): Seq[RuleResult] = { for { h <- goal.pre.sigma.chunks - (selGoals, _) <- UnfoldingRules.Open.mkInductiveSubGoals(goal, h).toList + (selGoals, _,_) <- UnfoldingRules.Open.mkInductiveSubGoals(goal, h).toList (selector, subGoal) <- selGoals if SMTSolving.valid(goal.pre.phi ==> selector) } yield RuleResult(List(subGoal), IdProducer, this, goal) diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index ed535fde2..7f06219ad 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -24,15 +24,15 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { override def toString: Ident = "Open" - def mkInductiveSubGoals(goal: Goal, h: Heaplet): Option[(Seq[(Expr, Goal)], Heaplet)] = { + def mkInductiveSubGoals(goal: Goal, h: Heaplet): Option[(Seq[(Expr, Goal)], SApp, SubstVar)] = { val pre = goal.pre val env = goal.env h match { - case SApp(pred, args, PTag(cls, unf), card) if unf < env.config.maxOpenDepth => + case h@SApp(pred, args, PTag(cls, unf), card) if unf < env.config.maxOpenDepth => ruleAssert(env.predicates.contains(pred), s"Open rule encountered undefined predicate: $pred") val freshSuffix = args.take(1).map(_.pp).mkString("_") - val (InductivePredicate(_, params, clauses), _) = env.predicates(pred).refreshExistentials(goal.vars, freshSuffix) + val (InductivePredicate(_, params, clauses), fresh_sbst) = env.predicates(pred).refreshExistentials(goal.vars, freshSuffix) // [Cardinality] adjust cardinality of sub-clauses val sbst = params.map(_._1).zip(args).toMap + (selfCardVar -> card) val remainingSigma = pre.sigma - h @@ -52,7 +52,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { // We can make the conditional without additional reading // TODO: Generalise this in the future val noGhosts = newGoals.forall { case (sel, _) => sel.vars.subsetOf(goal.programVars.toSet) } - if (noGhosts) Some((newGoals, h)) else None + if (noGhosts) Some((newGoals, h, fresh_sbst)) else None case _ => None } } @@ -62,9 +62,9 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { heaplet <- goal.pre.sigma.chunks s <- mkInductiveSubGoals(goal, heaplet) match { case None => None - case Some((selGoals, heaplet)) => + case Some((selGoals, heaplet, fresh_subst)) => val (selectors, subGoals) = selGoals.unzip - val kont = BranchProducer(selectors) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = BranchProducer(Some (heaplet, fresh_subst), selectors) >> HandleGuard(goal) >> ExtractHelper(goal) Some(RuleResult(subGoals, kont, this, goal)) } } yield s From b4864ade765172711d32766f0054769862993b19 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 18 Sep 2020 11:12:55 +0800 Subject: [PATCH 037/211] Process nested conditionals --- .../targets/htt/logic/Proof.scala | 4 +- .../targets/htt/logic/ProofProducers.scala | 23 ++++++++--- .../targets/htt/logic/ProofSteps.scala | 4 +- .../targets/htt/logic/Sentences.scala | 38 +++++------------ .../htt/program/StatementProducers.scala | 23 ++++++++--- .../targets/htt/program/Statements.scala | 2 +- .../htt/translation/ProofTranslation.scala | 3 +- .../targets/htt/translation/Translation.scala | 11 +++-- .../certification/dll/sll-to-dll.syn | 41 +++++++++++++++++++ .../synthesis/certification/tree/common.def | 6 +++ .../certification/tree/tree-size.syn | 24 +++++++++++ 11 files changed, 131 insertions(+), 48 deletions(-) create mode 100644 src/test/resources/synthesis/certification/dll/sll-to-dll.syn create mode 100644 src/test/resources/synthesis/certification/tree/tree-size.syn diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 668e0dc31..9f9361a30 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.htt.logic +import org.tygus.suslik.certification.targets.htt.language.CGamma import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{ProofStep, nestedDestructL} @@ -12,7 +13,6 @@ object Proof { type Unfoldings = Map[CSApp, CInductiveClause] type Subst = Map[CVar, CExpr] type SubstVar = Map[CVar, CVar] - type Gamma = Map[CVar, HTTType] type PredicateEnv = Map[String, CInductivePredicate] case class Proof(root: ProofStep, params: Seq[CVar]) { @@ -27,7 +27,7 @@ object Proof { case class CGoal(pre: CAssertion, post: CAssertion, - gamma: Gamma, + gamma: CGamma, programVars: Seq[CVar], universalGhosts: Seq[CVar], fname: String) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala index 83d8bc011..9bf91d159 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala @@ -132,15 +132,26 @@ object ProofProducers { } // Find the BranchProducer in the `bp`, and then partially apply `step` - def update(curr: ProofProducer): ProofProducer = curr match { - case FoldProofProducer(op, item, bp) => FoldProofProducer(op, item, update(bp)) - case ChainedProofProducer(p1, p2) => ChainedProofProducer(update(p1), update(p2)) - case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => curr.partApply(step) - case _ => curr + def update(curr: ProofProducer): (ProofProducer, Boolean) = curr match { + case FoldProofProducer(op, item, bp) => + val (bp1, modified) = update(bp) + (FoldProofProducer(op, item, bp1), modified) + case ChainedProofProducer(p1, p2) => + val (p11, modified1) = update(p1) + if (modified1) { + (ChainedProofProducer(p11, p2), modified1) + } else { + val (p21, modified2) = update(p2) + (ChainedProofProducer(p11, p21), modified2) + } + case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => + (curr.partApply(step), true) + case _ => + (curr, false) } // Update the `bp` with the new result and step into the next branch - op(item, update(bp)) + op(item, update(bp)._1) } } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index b5e84d9f3..b48435572 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -240,10 +240,10 @@ object ProofSteps { * Free a memory block of size `sz` * @param size the size of the block to free */ - case class FreeStep(size: Int) extends ProofStep { + case class FreeStep(v: CVar, size: Int) extends ProofStep { override def pp: String = { // In HTT, each location offset needs to be freed individually - val deallocStmts = (1 to size).map(_ => "ssl_dealloc.") + val deallocStmts = (0 until size).map(i => s"ssl_dealloc (${v.pp}${if (i == 0) "" else s" .+ $i"}).") s"${deallocStmts.mkString("\n")}\n" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 78b4adc9d..5982ca8b4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.language.{CFormals, PrettyPrinting} +import org.tygus.suslik.certification.targets.htt.language.{CFormals, CGamma, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.logic.Proof.Subst @@ -11,13 +11,13 @@ object Sentences { def existentials(quantifiedVars: Seq[CVar]): Seq[CVar] = valueVars.diff(quantifiedVars) - def ppQuantified(quantifiedVars: Seq[CVar], depth: Int = 0): String = { + def ppQuantified(quantifiedVars: Seq[CVar], depth: Int = 0, gamma: CGamma = Map.empty): String = { val builder = new StringBuilder() val valueEx = existentials(quantifiedVars) val heapEx = heapVars if (valueEx.nonEmpty) { - builder.append(s"exists ${valueEx.map(v => inferredTypes.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") + builder.append(s"exists ${valueEx.map(v => gamma.get(v).map(t => s"(${v.pp} : ${t.pp})").getOrElse(v.pp)).mkString(" ")},\n") } if (heapEx.nonEmpty) { builder.append(s"exists ${heapEx.map(_.pp).mkString(" ")},\n") @@ -36,27 +36,6 @@ object Sentences { val heapVars: Seq[CVar] = sigma.heapVars - - val inferredTypes: Map[CVar, HTTType] = { - def collectPhi(el: CExpr, m: Map[CVar, HTTType]): Map[CVar, HTTType] = el match { - case CSetLiteral(elems) => m ++ elems.filter(_.isInstanceOf[CVar]).map { case v: CVar => v -> CNatType } - case CBinaryExpr(COpSetEq | COpUnion, left: CVar, right: CVar) => m ++ Map(left -> CNatSeqType, right -> CNatSeqType) - case CBinaryExpr(COpSetEq | COpUnion, left: CVar, right) => collectPhi(right, m + (left -> CNatSeqType)) - case CBinaryExpr(COpSetEq | COpUnion, left, right: CVar) => collectPhi(left, m + (right -> CNatSeqType)) - case CBinaryExpr(COpUnion, left, right) => collectPhi(right, collectPhi(left, m)) - case _ => m - } - def collectSigma: Map[CVar, HTTType] = { - val ptss = sigma.ptss - ptss.foldLeft[Map[CVar, HTTType]](Map.empty){ - case (acc, CPointsTo(loc: CVar, _, _)) => acc ++ Map(loc -> CPtrType) - case (acc, _) => acc - } - } - val mPhi = collectPhi(phi, Map.empty) - val mSigma = collectSigma - mPhi ++ mSigma - } } case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { @@ -64,11 +43,16 @@ object Sentences { CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) } - case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause]) extends PrettyPrinting { + case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends PrettyPrinting { val paramVars: Seq[CVar] = params.map(_._2) def subst(sub: Subst): CInductivePredicate = - CInductivePredicate(name, params.map(p => (p._1, p._2.substVar(sub))), clauses.map(_.subst(sub))) + CInductivePredicate( + name, + params.map(p => (p._1, p._2.substVar(sub))), + clauses.map(_.subst(sub)), + gamma.map(g => (g._1.substVar(sub), g._2)) + ) override def pp: String = { val builder = new StringBuilder() @@ -77,7 +61,7 @@ object Sentences { // clauses val clausesStr = for { CInductiveClause(pred, idx, selector, asn, _) <- clauses - } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1)}" + } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1, gamma)}" builder.append(s"${clausesStr.mkString("\n")}.\n") builder.toString() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala index 140089a44..4c6af3a83 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala @@ -95,13 +95,24 @@ object StatementProducers { case _: Branching => true case _ => false } - def update(curr: CStmtProducer): CStmtProducer = curr match { - case FoldCStmtProducer(op, item, bp) => FoldCStmtProducer(op, item, update(bp)) - case ChainedCStmtProducer(p1, p2) => ChainedCStmtProducer(update(p1), update(p2)) - case _: PartiallyAppliedCStmtProducer | _: Branching if isBase(curr) => curr.partApply(steps.head) - case _ => curr + def update(curr: CStmtProducer): (CStmtProducer, Boolean) = curr match { + case FoldCStmtProducer(op, item, bp) => + val (bp1, modified) = update(bp) + (FoldCStmtProducer(op, item, bp1), modified) + case ChainedCStmtProducer(p1, p2) => + val (p11, modified1) = update(p1) + if (modified1) { + (ChainedCStmtProducer(p11, p2), modified1) + } else { + val (p21, modified2) = update(p2) + (ChainedCStmtProducer(p11, p21), modified2) + } + case _: PartiallyAppliedCStmtProducer | _: Branching if isBase(curr) => + (curr.partApply(steps.head), true) + case _ => + (curr, false) } - op(item, update(bp)) + op(item, update(bp)._1) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 19b630e76..99e642c43 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -60,7 +60,7 @@ object Statements { s"dealloc ${v.pp}" } } - builder.append(s"$indent${deallocs.mkString(s";;\n${mkSpaces(depth)}")}") + builder.append(s"$indent${deallocs.mkString(s";;\n$indent")}") case CStore(to, off, e) => val t = if (off <= 0) to.pp else s"(${to.pp} .+ $off)" val v = if (e == CNatConst(0)) "null" else e.pp diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index bdba8e50b..fb13b93b3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ @@ -37,7 +38,7 @@ object ProofTranslation { case Free(v) => val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) assert(block.nonEmpty) - (FreeStep(block.get.sz), cenv) + (FreeStep(CVar(v.name), block.get.sz), cenv) case Call(_, _, _) => assert(item.node.goal.callGoal.isDefined) val callGoal = item.node.goal.callGoal.get diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index fa4e0d58b..2327a8c8a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -24,7 +24,11 @@ object Translation { */ def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedure) = { - val cpreds = env.predicates.mapValues(p => translateInductivePredicate(p.resolveOverloading(env))) + val cpreds = env.predicates.mapValues(p => { + val gamma = p.resolve(p.params.toMap, env).get + val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) + translateInductivePredicate(p1, gamma) + }) val goal = translateGoal(node.goal) val initialCEnv = CEnvironment(goal, cpreds) val proof = ProofTranslation.translate(node, initialCEnv) @@ -32,8 +36,9 @@ object Translation { (cpreds, goal.toFunspec, proof, cproc) } - private def translateInductivePredicate(el: InductivePredicate): CInductivePredicate = { + private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { val cParams = el.params.map(translateParam) :+ (CHeapType, CVar("h")) + val cGamma = gamma.map { case (v, t) => (CVar(v.name), translateType(t))} val cClauses = el.clauses.zipWithIndex.map { case (c, idx) => val selector = translateExpr(c.selector) @@ -42,7 +47,7 @@ object Translation { // Include the clause number so that we can use Coq's `constructor n` tactic CInductiveClause(el.name, idx + 1, selector, asn, asn.existentials(cParams.map(_._2))) } - CInductivePredicate(el.name, cParams, cClauses) + CInductivePredicate(el.name, cParams, cClauses, cGamma) } def translateParam(el: (Var, SSLType)): (HTTType, CVar) = diff --git a/src/test/resources/synthesis/certification/dll/sll-to-dll.syn b/src/test/resources/synthesis/certification/dll/sll-to-dll.syn new file mode 100644 index 000000000..7e017cfc2 --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/sll-to-dll.syn @@ -0,0 +1,41 @@ +# -c 2 + +should be able to convert a singly-linked list to a double-linked list + +####### + +{ f :-> x ** sll(x, s)} +void sll_to_dll(loc f) +{ f :-> i ** dll(i, 0, s)} + +####### + +void sll_to_dll (loc f) { + let x = *f; + if (x == 0) { + *f = 0; + } else { + let v = *x; + let n = *(x + 1); + *f = n; + sll_to_dll(f); + let i1 = *f; + if (i1 == 0) { + let i = malloc(3); + free(x); + *f = i; + *(i + 1) = 0; + *i = v; + *(i + 2) = 0; + } else { + let i = malloc(3); + free(x); + *(i1 + 2) = i; + *f = i; + *(i + 1) = i1; + *i = v; + *(i + 2) = 0; + } + } +} + diff --git a/src/test/resources/synthesis/certification/tree/common.def b/src/test/resources/synthesis/certification/tree/common.def index f8a093e68..085c6c802 100644 --- a/src/test/resources/synthesis/certification/tree/common.def +++ b/src/test/resources/synthesis/certification/tree/common.def @@ -2,3 +2,9 @@ predicate tree(loc x, set s) { | x == 0 => {s =i {}; emp} | not (x == 0) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} } + +predicate treeN(loc x, int n) { +| x == 0 => { n == 0 ; emp } +| not (x == 0) => { n == 1 + n1 + n2 /\ 0 <= n1 /\ 0 <= n2 ; + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** treeN(l, n1) ** treeN(r, n2)} +} diff --git a/src/test/resources/synthesis/certification/tree/tree-size.syn b/src/test/resources/synthesis/certification/tree/tree-size.syn new file mode 100644 index 000000000..40224c02e --- /dev/null +++ b/src/test/resources/synthesis/certification/tree/tree-size.syn @@ -0,0 +1,24 @@ +should be able to synthesize tree size + +##### + + +{0 <= n ; r :-> 0 ** treeN(x, n) } +void tree_size(loc x, loc r) +{true ; r :-> n ** treeN(x, n) } + +##### + +void tree_size (loc x, loc r) { + if (x == 0) { + } else { + let l = *(x + 1); + let rx = *(x + 2); + tree_size(l, r); + let n1 = *r; + *r = 0; + tree_size(rx, r); + let n = *r; + *r = 1 + n1 + n; + } +} \ No newline at end of file From 4d54e2ca63f849b116e8dae1a450225cd9f3ed5b Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 22 Sep 2020 06:29:14 +0100 Subject: [PATCH 038/211] addded proof rules --- .../targets/vst/logic/ProofRule.scala | 156 ++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala new file mode 100644 index 000000000..b13389113 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala @@ -0,0 +1,156 @@ +package org.tygus.suslik.certification.targets.vst.logic +import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} +import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} +import org.tygus.suslik.language.Statements.{Load, Skip, Store} +import org.tygus.suslik.logic.{Heaplet, PFormula, PointsTo, SApp, SFormula, Specifications} +import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnificationRules} +import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} + + + +/** compressed form of suslik rules */ +sealed abstract class ProofRule extends PrettyPrinting { + +} + +object ProofRule { + var indent : Int = 0 + + def ind : String = " " * indent + + def sanitize (str: String) = str.replace(";\n","") + + def with_scope[A] (f: Unit => A) : A = { + val old_indent_value = indent + indent = indent + 4 + val result = f(()) + indent = old_indent_value + result + } + + + /** corresponds to asserting all the variables in vars are not null */ + case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" + } + + /** check post doesn't provide any information useful for certification, so just included as empty rule */ + case class CheckPost(next:ProofRule) extends ProofRule { + override def pp: String = s"${ind}CheckPost;\n${next.pp}" + } + + /** picks an arbitrary instantiation of the proof rules */ + case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" + } + + /** abduces a condition for the proof */ + case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { + override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + } + + /** write a value */ + case class Write(stmt: Store, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" + } + + /** weaken the precondition by removing unused formulae */ + case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" + } + + /** empty rule */ + case object EmpRule extends ProofRule { + override def pp: String = s"${ind}EmpRule;" + } + + /** pure synthesis rules */ + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" + } + + /** open constructor cases */ + case class Open(pred: SApp, fresh_vars: SubstVar, cases: List[(Expr, ProofRule)]) extends ProofRule { + override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + } + + /** subst L */ + case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" + } + + /** subst R */ + case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" + } + + + /** read rule */ + case class Read(map: Map[Var,Expr], operation: Load, next:ProofRule) extends ProofRule { + override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" + } + +// /** abduce a call */ +case class AbduceCall( + new_vars: Map[Var, SSLType], + f_pre: Specifications.Assertion, + callePost: Specifications.Assertion, + call: Statements.Call, + freshSub: SubstVar, + next: ProofRule + ) extends ProofRule { + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" +} + + + /** unification of heap (ignores block/pure distinction) */ + case class HeapUnify(next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnify;\n${next.pp}" + } + + /** unification of pointers */ + case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" + } + + /** unfolds frame */ + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" + } + + /** call operation */ + case class Call(call: Statements.Call, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Call(${sanitize(call.pp)});\n${next.pp}" + } + + /** free operation */ + case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" + } + + /** malloc rule */ + case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" + } + + /** close rule */ + case class Close(app: SApp, selector: Expr, pred_subst: SubstVar, fresh_exist: SubstVar, subst_args: Subst, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, {${pred_subst.mkString(",")}}, {${fresh_exist.mkString(",")}, {${subst_args.mkString(",")});\n${next.pp}" + } + + /** star partial */ + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" + } + + case class PickCard(next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickCard;\n${next.pp}" + } + + + case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" + } + + +} \ No newline at end of file From 50dbc7b699cdb7234270498d44b1294dd37ade8c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 17 Nov 2020 16:05:08 +0800 Subject: [PATCH 039/211] List -> Seq --- .../certification/targets/vst/State.scala | 24 +++++++++---------- .../vst/translation/ProofTranslation.scala | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala index 65947ec32..afb81013a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_w import scala.annotation.tailrec /** Implementation of a state monad where the state is a stack of elements */ -case class State[S, A](f: List[S] => (A, List[S])) { +case class State[S, A](f: Seq[S] => (A, Seq[S])) { /** map = fmap */ def map[B](g: A => B): State[S, B] = @@ -23,7 +23,7 @@ case class State[S, A](f: List[S] => (A, List[S])) { /** run the state monad to get a value */ - def eval(s: List[S]): A = f(s)._1 + def eval(s: Seq[S]): A = f(s)._1 } object State { @@ -36,28 +36,28 @@ object State { } /** push multiple values onto the state, in such a way that is equivalent to iterating through the list and pushing them on individually */ - def push_multi[S] (s :List[S]) : State[S, Unit] = { + def push_multi[S] (s :Seq[S]) : State[S, Unit] = { @tailrec - def rev_cat[C](ls: List[C])(acc: List[C]) : List[C] = ls match { - case Nil => acc - case ::(head ,tail) => rev_cat(tail)(head +: acc) + def rev_cat[C](ls: Seq[C])(acc: Seq[C]) : Seq[C] = ls match { + case Seq(head, tail@_*) => rev_cat(tail)(head +: acc) + case _ => acc } State(ss => ((), rev_cat(s)(ss))) } /** pop a value off the stack */ def pop[S] : State[S, S] = State { - case Nil => fail_with("State monad ran out of state") - case ::(head, tl) => (head, tl) + case Seq(head, tl@_*) => (head, tl) + case _ => fail_with("State monad ran out of state") } - def mapM[B,S,A] (ls: List[B]) (f: B => State[S,A]) : State[S,List[A]] = + def mapM[B,S,A] (ls: Seq[B]) (f: B => State[S,A]) : State[S,Seq[A]] = ls match { - case Nil => State.ret(Nil) - case ::(head0, tail0) => for { + case Seq(head0, tail0@_*) => for { head <- f(head0) tail <- mapM(tail0)(f) - } yield (head :: tail) + } yield head +: tail + case _ => State.ret(Seq.empty) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 5f66e84ea..a8fe2205f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -39,7 +39,7 @@ object ProofTranslation { throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") } - def fail_with_bad_children(ls: List[CertTree.Node], count: Int): Nothing = { + def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = { throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != ${count}") } From 860fe276778653c216f2d77444ea27fe859a7a79 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 17 Nov 2020 22:18:45 +0800 Subject: [PATCH 040/211] Fix substitution bug --- .../htt/translation/ProofTranslation.scala | 15 +++++++++------ .../suslik/certification/targets/vst/Debug.scala | 4 ++-- .../targets/vst/logic/ProofRule.scala | 5 +++-- .../vst/translation/ProofTranslation.scala | 8 ++++---- .../org/tygus/suslik/synthesis/StmtProducer.scala | 2 +- .../suslik/synthesis/rules/UnfoldingRules.scala | 2 +- 6 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index fb13b93b3..991c2755b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -5,9 +5,12 @@ import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar import org.tygus.suslik.certification.targets.htt.logic.Proof._ import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductiveClause import org.tygus.suslik.certification.targets.htt.translation.Translation._ import org.tygus.suslik.language.Statements._ +import org.tygus.suslik.logic.InductiveClause import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.rules.LogicalRules.Inconsistency object ProofTranslation { private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) @@ -70,17 +73,17 @@ object ProofTranslation { val newUnfoldings = cenv.unfoldings.map { case (app, e) => app.subst(newGhostSubst) -> e.subst(newGhostSubst) } val cenv1 = cenv.copy(subst = newSubst, ghostSubst = cenv.ghostSubst ++ newGhostSubst, unfoldings = newUnfoldings) (IdProofProducer, cenv1) - case UnfoldProducer(app, selector, substPred, substEx, substArgs) => + case UnfoldProducer(app, selector, asn, substEx) => val cselector = translateExpr(selector) val capp = translateSApp(app) - val csubPred = substPred.map { case (src, dst) => translateVar(src) -> translateVar(dst) } - val csubEx = substEx.map { case (src, dst) => translateVar(src) -> translateVar(dst) } - val csubArgs = substArgs.map { case (src, dst) => translateVar(src) -> translateExpr(dst) } + val casn = translateAsn(asn).subst(cenv.ghostSubst) // get clause with substitutions val predicate = cenv.predicates(app.pred) val cclause = predicate.clauses.find(_.selector == cselector).get - val actualClause = cclause.subst(csubPred).subst(csubEx).subst(csubArgs).subst(cenv.ghostSubst) + val csub = substEx.map { case (k, v) => CVar(k.name) -> translateExpr(v)} + val ex = cclause.existentials.map(_.subst(csub)) + val actualClause = CInductiveClause(app.pred, cclause.idx, cselector, casn, ex) val cenv1 = cenv.copy(unfoldings = cenv.unfoldings ++ Map(capp -> actualClause)) (IdProofProducer, cenv1) case ConstProducer(s) => @@ -89,7 +92,7 @@ object ProofTranslation { case PrependProducer(s) => val (step, cenv1) = translateOperation(s, cenv) (PrependProofProducer(step), cenv1) - case BranchProducer(_) => + case BranchProducer(_, _) => val sapp = translateSApp(item.node.footprint.pre.sigma.apps.head) val pred = cenv.predicates(sapp.pred) val subgoals = item.node.children.map(n => translateGoal(n.goal)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala index 37c18b749..a24f40e11 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala @@ -41,8 +41,8 @@ object Debug { s"GuardedProducer[${stmtProducer.arity}] {\n cond=${cond.pp}\n goal=${goal.pp}\n}" case SubstProducer(subst) => s"SubstProducer[${stmtProducer.arity}](${subst.toString})" case GhostSubstProducer(subst) => s"GhostSubstProducer[${stmtProducer.arity}](${subst.toString})" - case UnfoldProducer(app, selector, substPred, substEx, substArgs) => - s"UnfoldProducer[${stmtProducer.arity}] {\n app=${app.pp}\n selector=${selector.pp}\n substPred=${substPred.toString}\n substEx=${substEx.toString}\n substArgs=${substArgs.map(_.toString()).mkString("; ")}\n}" + case UnfoldProducer(app, selector, asn, substEx) => + s"UnfoldProducer[${stmtProducer.arity}] {\n app=${app.pp}\n selector=${selector.pp}\n asn=${asn.toString}\n substEx=${substEx.toString}\n}" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala index b13389113..4ea1da030 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} import org.tygus.suslik.language.Statements.{Load, Skip, Store} +import org.tygus.suslik.logic.Specifications.Assertion import org.tygus.suslik.logic.{Heaplet, PFormula, PointsTo, SApp, SFormula, Specifications} import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnificationRules} import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} @@ -134,8 +135,8 @@ case class AbduceCall( } /** close rule */ - case class Close(app: SApp, selector: Expr, pred_subst: SubstVar, fresh_exist: SubstVar, subst_args: Subst, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, {${pred_subst.mkString(",")}}, {${fresh_exist.mkString(",")}, {${subst_args.mkString(",")});\n${next.pp}" + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" } /** star partial */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index a8fe2205f..bc3127824 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -318,10 +318,10 @@ object ProofTranslation { case _ => fail_with_bad_proof_structure() } case UnfoldingRules.Close => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, pred_subst, fresh_exist, subst_args), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, proof_rule_of_proof_node(head)) + ProofRule.Close(app, selector, asn, fresh_exist, proof_rule_of_proof_node(head)) case ls => fail_with_bad_children(ls, 1) } } @@ -566,7 +566,7 @@ object ProofTranslation { case ProofRule.HeapUnify(next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, next) => + case ProofRule.Close(app, selector, asn, fresh_exist, next) => is_variable_used_in_proof(variable)(next) case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => is_variable_used_in_proof(variable)(next) @@ -618,7 +618,7 @@ object ProofTranslation { case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) case ProofRule.Malloc(map, stmt, next) => ??? - case ProofRule.Close(app, selector, pred_subst, fresh_exist, subst_args, next) => ??? + case ProofRule.Close(app, selector, asn, fresh_exist, next) => ??? case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => ??? case ProofRule.PickCard(next) => ??? case ProofRule.PickArg(map, next) => ??? diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index c3d76a360..c8bb788e6 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -174,4 +174,4 @@ case class SubstProducer(subst: Subst) extends StmtProducer with Noop case class GhostSubstProducer(subst: SubstVar) extends StmtProducer with Noop // Captures an unfolded predicate application -case class UnfoldProducer(app: SApp, selector: Expr, substPred: SubstVar, substEx: SubstVar, substArgs: Subst) extends StmtProducer with Noop +case class UnfoldProducer(app: SApp, selector: Expr, asn: Assertion, substEx: SubstVar) extends StmtProducer with Noop diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index 7f06219ad..b7dda66ad 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -209,7 +209,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPhi = post.phi && actualConstraints && actualSelector val newPost = Assertion(newPhi, goal.post.sigma ** actualBody - h) - val kont = UnfoldProducer(a, selector, predSbst, freshExistentialsSubst, substArgs) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnfoldProducer(a, selector, Assertion(actualConstraints, actualBody), predSbst ++ freshExistentialsSubst) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(goal.spawnChild(post = newPost)), kont, this, goal) } From a724dc9335ae74fddbb50d52910780ccbc69940f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 18 Nov 2020 20:23:38 +0800 Subject: [PATCH 041/211] Add BST tests (WIP) --- .../targets/htt/logic/ProofSteps.scala | 4 +++ .../targets/htt/program/Statements.scala | 2 ++ .../htt/translation/ProgramTranslation.scala | 2 ++ .../htt/translation/ProofTranslation.scala | 3 ++ .../certification/bst/bst-find-smallest.syn | 30 ++++++++++++++++ .../certification/bst/bst-insert.syn | 36 +++++++++++++++++++ .../certification/bst/bst-left-rotate.syn | 19 ++++++++++ .../bst/bst-remove-root-no-left.syn | 36 +++++++++++++++++++ .../bst/bst-remove-root-no-right.syn | 36 +++++++++++++++++++ .../certification/bst/bst-right-rotate.syn | 19 ++++++++++ .../synthesis/certification/bst/common.def | 8 +++++ .../targets/htt/HTTCertificationTests.scala | 1 + 12 files changed, 196 insertions(+) create mode 100644 src/test/resources/synthesis/certification/bst/bst-find-smallest.syn create mode 100644 src/test/resources/synthesis/certification/bst/bst-insert.syn create mode 100644 src/test/resources/synthesis/certification/bst/bst-left-rotate.syn create mode 100644 src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn create mode 100644 src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn create mode 100644 src/test/resources/synthesis/certification/bst/bst-right-rotate.syn create mode 100644 src/test/resources/synthesis/certification/bst/common.def diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index b48435572..d0b44e0c6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -174,6 +174,8 @@ object ProofSteps { */ def simplify: ProofStep = { (s1, s2) match { + case (ErrorStep, _) => s2 + case (_, ErrorStep) => s1 case (ReadStep(to, _), _) => simplifyBinding(to) // case (WriteStep(to), _) => simplifyBinding(to) // case (AllocStep(to, _, _), _) => simplifyBinding(to) @@ -357,4 +359,6 @@ object ProofSteps { builder.toString() } } + + case object ErrorStep extends ProofStep } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 99e642c43..53e4a7061 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -102,6 +102,8 @@ object Statements { trait ReturnsValue + case object CError extends CStatement + case object CSkip extends CStatement case class CMalloc(to: CVar, tpe: HTTType, sz: Int = 1) extends CStatement with ReturnsValue diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 599ec9f08..21d31ee35 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -29,6 +29,8 @@ object ProgramTranslation { CFree(translateVar(v), block.get.sz) case Call(v, args, _) => CCall(translateVar(v), args.map(translateExpr)) + case Error => + CError case _ => throw TranslationException("Operation not supported") } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 991c2755b..fc3ba0d92 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -52,6 +52,8 @@ object ProofTranslation { val cgoal = cenv.initialGoal.subst(companionToFresh).subst(freshToActual) (CallStep(cgoal, freshCallId), cenv) + case Error => + (ErrorStep, cenv) case _ => throw TranslationException("Operation not supported") } @@ -131,6 +133,7 @@ object ProofTranslation { case Some(childHead) => traverseProof(childHead, nextKont) case None => + if (item.node.rule == Inconsistency) {} nextKont(Nil) } } diff --git a/src/test/resources/synthesis/certification/bst/bst-find-smallest.syn b/src/test/resources/synthesis/certification/bst/bst-find-smallest.syn new file mode 100644 index 000000000..0e0af61bc --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-find-smallest.syn @@ -0,0 +1,30 @@ +# -x true -c 2 -p true + +binary search tree: find smallest element + +##### + +{ 0 <= sz /\ 0 <= lo /\ hi <= 7; + retv :-> unused ** + bst(x, sz, lo, hi) } + +void bst_find_smallest (loc x, loc retv) + +{ retv :-> lo ** + bst(x, sz, lo, hi) } + +##### + +{0 <= lo && 0 <= sz && hi <= 7 ; retv :-> unused ** bst(x, sz, lo, hi)} +{retv :-> lo ** bst(x, sz, lo, hi)} +void bst_find_smallest (loc x, loc retv) { + if (x == 0) { + *retv = 7; + } else { + let v = *x; + let lx = *(x + 1); + bst_find_smallest(lx, retv); + let l = *retv; + *retv = v <= l ? v : l; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/bst-insert.syn b/src/test/resources/synthesis/certification/bst/bst-insert.syn new file mode 100644 index 000000000..107c540c4 --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-insert.syn @@ -0,0 +1,36 @@ +# -b true -c 2 +binary search tree: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; retv :-> k ** bst(x, n, lo, hi) } +void bst_insert (loc x, loc retv) +{b == a + 3 /\ n1 == n + 1 /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; retv :-> y ** bst(y, n1, lo1, hi1) } + +##### + +void bst_insert (loc x, loc retv) { + let k = *retv; + if (x == 0) { + let y = malloc(3); + *retv = y; + *(y + 1) = 0; + *(y + 2) = 0; + *y = k; + } else { + let v = *x; + if (v <= k) { + let r = *(x + 2); + bst_insert(r, retv); + let y = *retv; + *retv = x; + *(x + 2) = y; + } else { + let l = *(x + 1); + bst_insert(l, retv); + let y = *retv; + *(x + 1) = y; + *retv = x; + } + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn b/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn new file mode 100644 index 000000000..bfcfe0ef2 --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn @@ -0,0 +1,19 @@ +binary search tree: rotate left + +##### + +{ not (r == 0) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; + retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +void bst_left_rotate (loc x, loc retv) +{ sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; + retv :-> y ** [y, 3] ** y :-> v3 ** (y + 1) :-> x ** (y + 2) :-> r3 ** bst(x, sz3, lo3, hi3) ** bst(r3, sz4, lo4, hi4) } + +##### + +void bst_left_rotate (loc x, loc retv) { + let r = *(x + 2); + let l = *(r + 1); + *(r + 1) = x; + *retv = r; + *(x + 2) = l; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn new file mode 100644 index 000000000..e5df89c39 --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn @@ -0,0 +1,36 @@ +# -x true -c 2 -p true + +binary search tree: delete root + +Simple case: no left subtree + +##### + +{ l == 0 /\ + 0 <= sz1 /\ 0 <= sz2 /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + retv :-> unused ** + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** + bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } + +void bst_remove_root_no_left (loc x, loc retv) + +{ n1 == sz1 + sz2 /\ + lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) /\ + hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) ; + retv :-> y ** bst(y, n1, lo, hi) } + +##### + +{0 <= sz1 && 0 <= sz2 && 0 <= v && hi1 <= v && l == 0 && v <= 7 && v <= lo2 ; (x + 1) :-> l ** (x + 2) :-> r ** retv :-> unused ** x :-> v ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) ** [x, 3]} +{hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) && lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) && n1 == sz1 + sz2 ; retv :-> y ** bst(y, n1, lo, hi)} +void bst_remove_root_no_left (loc x, loc retv) { + let r = *(x + 2); + if (r == 0) { + free(x); + *retv = 0; + } else { + free(x); + *retv = r; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn new file mode 100644 index 000000000..63889c83a --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn @@ -0,0 +1,36 @@ +# -x true -c 2 -p true + +binary search tree: delete root + +Simple case: no right subtree + +##### + +{ r == 0 /\ + 0 <= sz1 /\ 0 <= sz2 /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + retv :-> unused ** + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** + bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } + +void bst_remove_root_no_right (loc x, loc retv) + +{ n1 == sz1 + sz2 /\ + lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) /\ + hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) ; + retv :-> y ** bst(y, n1, lo, hi) } + +##### + +{0 <= sz1 && 0 <= sz2 && 0 <= v && hi1 <= v && r == 0 && v <= 7 && v <= lo2 ; (x + 1) :-> l ** (x + 2) :-> r ** retv :-> unused ** x :-> v ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) ** [x, 3]} +{hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) && lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) && n1 == sz1 + sz2 ; retv :-> y ** bst(y, n1, lo, hi)} +void bst_remove_root_no_right (loc x, loc retv) { + let l = *(x + 1); + if (l == 0) { + free(x); + *retv = 0; + } else { + free(x); + *retv = l; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn new file mode 100644 index 000000000..1f1f784fa --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn @@ -0,0 +1,19 @@ +binary search tree: rotate right + +##### + +{ not (l == 0) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; + retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +void bst_right_rotate (loc x, loc retv) +{ sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; + retv :-> y ** [y, 3] ** y :-> v3 ** (y + 1) :-> l3 ** (y + 2) :-> x ** bst(l3, sz3, lo3, hi3) ** bst(x, sz4, lo4, hi4) } + +##### + +void bst_right_rotate (loc x, loc retv) { + let l = *(x + 1); + let r = *(l + 2); + *(l + 2) = x; + *retv = l; + *(x + 1) = r; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/bst/common.def b/src/test/resources/synthesis/certification/bst/common.def new file mode 100644 index 000000000..8f448305f --- /dev/null +++ b/src/test/resources/synthesis/certification/bst/common.def @@ -0,0 +1,8 @@ +predicate bst(loc x, int sz, int lo, int hi) { +| x == 0 => { sz == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == 0) => { sz == 1 + sz1 + sz2 /\ 0 <= sz1 /\ 0 <= sz2 /\ + lo == (v <= lo1 ? v : lo1) /\ + hi == (hi2 <= v ? v : hi2) /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +} \ No newline at end of file diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index 0ce291fc7..38d81517b 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -39,6 +39,7 @@ class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUt runAllTestsFromDir("certification/tree") runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/bst") } } \ No newline at end of file From 7e56f6d0335c1439ca8f819aa044fc54e6fe84c2 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 20 Nov 2020 14:18:27 +0800 Subject: [PATCH 042/211] Update documentation of CLI flags --- README.md | 45 +++++++++++++------ .../suslik/synthesis/SynthesisRunner.scala | 24 +++++++--- 2 files changed, 48 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 2d1da64ba..bb265765e 100644 --- a/README.md +++ b/README.md @@ -121,29 +121,46 @@ where the necessary arguments and options are ``` fileName a synthesis file name (the file under the specified folder, called filename.syn) - -r, --trace print the entire derivation trace; default: true - -t, --timeout timeout for the derivation; default (in milliseconds): 120000 (2 min) - -d, --depth derivation depth; default: 100 + -r, --trace print the entire derivation trace; default: false + -t, --timeout timeout for the derivation; default (in milliseconds): 300000 (5 min) -a, --assert check that the synthesized result against the expected one; default: false -c, --maxCloseDepth maximum unfolding depth in the post-condition; default: 1 -o, --maxOpenDepth maximum unfolding depth in the pre-condition; default: 1 + -f, --maxCallDepth + maximum call depth; default: 1 + -x, --auxAbduction + abduce auxiliary functions; default: false + --topLevelRecursion + allow top-level recursion; default: true -b, --branchAbduction abduce conditional branches; default: false - --commute only try commutative rule applications in one order; default: true + --maxGuardConjuncts + maximum number of conjuncts in an abduced guard; default: 2 --phased split rules into unfolding and flat phases; default: true - --fail enable early failure rules; default: true - --invert enable invertible rules; default: true - -s, --printStats print synthesis stats; default: true - -p, --printSpecs print specifications for synthesized functions; default: false + -d, --dfs depth first search; default: false + --bfs breadth first search (ignore weights); default: false + --delegate delegate pure synthesis to CVC4; default: true + -i, --interactive + interactive mode; default: false + -s, --printStats + print synthesis stats; default: false + -p, --printSpecs + print specifications for synthesized functions; default: false -e, --printEnv print synthesis context; default: false - -f, --printFail print failed rule applications; default: false - -g, --tags print predicate application tags in derivations; default: false - -l, --log log results to a csv file; default: true - -x, --auxAbduction - abduce auxiliary functions; default: false - --help prints this usage text + --printFail print failed rule applications; default: false + -l, --log log results to a csv file; default: false + -j, --traceToJsonFile + dump entire proof search trace to a json file; default: none + --memo enable memoization; default: true + --lexi use lexicographic termination metric (as opposed to total size); default: false + --printTree print tree of successful derivations to path; default: false + --treeDest write tree of successful derivations to path; default: none + --certTarget set certification target; default: none + --certDest write certificate to path; default: none + + --help prints the help reference ``` diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 7e75163ec..80d9a98fb 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -21,21 +21,31 @@ object SynthesisRunner extends SynthesisRunnerUtil { * * fileName a synthesis file name (the file under the specified folder, called filename.syn) * - * -r, --trace print the entire derivation trace; default: true + * -r, --trace print the entire derivation trace; default: false * -t, --timeout timeout for the derivation; default (in milliseconds): 300000 (5 min) - * -a, --assert check that the synthesized result against the expected one; default: true + * -a, --assert check that the synthesized result against the expected one; default: false * -c, --maxCloseDepth maximum unfolding depth in the post-condition; default: 1 * -o, --maxOpenDepth maximum unfolding depth in the pre-condition; default: 1 + * -f, --maxCallDepth maximum call depth; default: 1 * -x, --auxAbduction abduce auxiliary functions; default: false + * --topLevelRecursion allow top-level recursion; default: true * -b, --branchAbduction abduce conditional branches; default: false + * --maxGuardConjuncts maximum number of conjuncts in an abduced guard; default: 2 * --phased split rules into unfolding and flat phases; default: true - * -d, --depth depth first search; default: false + * -d, --dfs depth first search; default: false + * --bfs breadth first search (ignore weights); default: false + * --delegate delegate pure synthesis to CVC4; default: true * -i, --interactive interactive mode; default: false - * -s, --printStats print synthesis stats; default: true + * -s, --printStats print synthesis stats; default: false + * -p, --printSpecs print specifications for synthesized functions; default: false * -e, --printEnv print synthesis context; default: false - * -f, --printFail print failed rule applications; default: false - * -l, --log log results to a csv file; default: true - * --memoization enable memoization; default: true + * --printFail print failed rule applications; default: false + * -l, --log log results to a csv file; default: false + * -j, --traceToJsonFile dump entire proof search trace to a json file; default: none + * --memo enable memoization; default: true + * --lexi use lexicographic termination metric (as opposed to total size); default: false + * --printTree print tree of successful derivations to path; default: false + * --treeDest write tree of successful derivations to path; default: none * --certTarget set certification target; default: none * --certDest write certificate to path; default: none * From 5e8e01df2a6e172c19b926dc28f03adc8413db8f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 24 Nov 2020 02:42:19 +0800 Subject: [PATCH 043/211] Update bnd_read tactic; properly handle phi conjunctions in assertions --- .../targets/htt/language/Expressions.scala | 2 +- .../targets/htt/logic/ProofSteps.scala | 18 ++++++++++++------ .../targets/htt/program/Statements.scala | 2 +- .../htt/translation/ProofTranslation.scala | 4 ++-- .../targets/htt/translation/Translation.scala | 4 +++- 5 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index f34508c4f..7dea965e6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -111,7 +111,7 @@ object Expressions { } case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { - override def pp: String = s"if ${cond.pp} then ${left.pp} else ${right.pp}" + override def pp: String = s"(if ${cond.pp} then ${left.pp} else ${right.pp})" } case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index d0b44e0c6..92ba5aae4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -52,7 +52,9 @@ object ProofSteps { if (nested) { builder.append(s"move=>${nestedDestructL(ex)}.\n") } else { - builder.append(s"ex_elim ${ex.map(_.pp).mkString(" ")}.\n") + ex.map(_.pp).grouped(5).foreach { s => + builder.append(s"ex_elim ${s.mkString(" ")}.\n") + } } } } @@ -142,7 +144,7 @@ object ProofSteps { def collector(acc: Set[CVar])(st: ProofStep): Set[CVar] = st match { case WriteStep(to, _, e, _) => acc ++ to.vars ++ e.vars - case ReadStep(to, from) => + case ReadStep(to, from, _) => acc ++ to.vars ++ from.vars case AllocStep(to, _, _) => acc ++ to.vars @@ -176,7 +178,7 @@ object ProofSteps { (s1, s2) match { case (ErrorStep, _) => s2 case (_, ErrorStep) => s1 - case (ReadStep(to, _), _) => simplifyBinding(to) + case (ReadStep(to, _, _), _) => simplifyBinding(to) // case (WriteStep(to), _) => simplifyBinding(to) // case (AllocStep(to, _, _), _) => simplifyBinding(to) case _ => this @@ -217,12 +219,16 @@ object ProofSteps { * Perform a read * @param to the dst variable * @param from the src variable + * @param offset the src offset */ - case class ReadStep(to: CVar, from: CVar) extends ProofStep { + case class ReadStep(to: CVar, from: CVar, offset: Int) extends ProofStep { override def refreshGhostVars(sbst: SubstVar): ReadStep = - ReadStep(to.substVar(sbst), from.substVar(sbst)) + ReadStep(to.substVar(sbst), from.substVar(sbst), offset) - override def pp: String = "ssl_read.\n" + override def pp: String = { + val ptr = if (offset == 0) from.pp else s"(${from.pp} .+ $offset)" + s"ssl_read $ptr.\n" + } } /** diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 53e4a7061..f2aa7e987 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -48,7 +48,7 @@ object Statements { def build(s: CStatement, depth: Int = 0) : Unit = { val indent = getIndent(depth) s match { - case CSkip => + case CSkip | CError => builder.append(s"${indent}ret tt") case CMalloc(to, _, sz) => builder.append(s"$indent${to.pp} <-- allocb null $sz") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index fc3ba0d92..d31e0ad11 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -31,8 +31,8 @@ object ProofTranslation { val postEx = goal.existentials.map(_.subst(cenv.subst)) val unfoldings = cenv.unfoldings.map { case (app, c) => app.subst(cenv.subst) -> c.subst(cenv.subst) } (EmpStep(post, postEx, unfoldings), cenv) - case Load(to, _, from, _) => - (ReadStep(translateVar(to), translateVar(from)), cenv) + case Load(to, _, from, off) => + (ReadStep(translateVar(to), translateVar(from), off), cenv) case Store(to, offset, e) => val frame = item.node.goal.callGoal.isEmpty (WriteStep(translateVar(to), offset, translateExpr(e), frame), cenv) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 2327a8c8a..cc8630726 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -88,7 +88,9 @@ object Translation { def translatePointsTo(el: PointsTo): CPointsTo = CPointsTo(translateExpr(el.loc), el.offset, translateExpr(el.value)) def translateAsn(el: Assertion): CAssertion = { - val phi = translateExpr(el.phi.toExpr).simplify + val conjuncts = el.phi.conjuncts.toSeq.map(c => translateExpr(c).simplify).filterNot(_.isCard) + val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) + val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) val sigma = translateSFormula(el.sigma) CAssertion(phi, sigma) } From 0b80042723a0e65d5b5e3b2f10a04adcd4fd7e48 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 27 Nov 2020 15:00:25 +0800 Subject: [PATCH 044/211] Distinguish null pointers from integers (fixes type inference bug in bst-left-rotate.syn) --- .../targets/htt/language/Expressions.scala | 7 ++++++- .../targets/htt/program/Statements.scala | 3 +-- .../targets/htt/translation/Translation.scala | 1 + .../org/tygus/suslik/language/Expressions.scala | 17 ++++++++++------- .../org/tygus/suslik/logic/PureLogicUtils.scala | 2 ++ .../org/tygus/suslik/logic/smt/SMTSolving.scala | 1 + .../org/tygus/suslik/parsing/SSLLexical.scala | 1 + .../org/tygus/suslik/parsing/SSLParser.scala | 5 ++++- .../certification/bst/bst-left-rotate.syn | 2 +- .../bst/bst-remove-root-no-left.syn | 6 +++--- .../bst/bst-remove-root-no-right.syn | 6 +++--- .../certification/bst/bst-right-rotate.syn | 2 +- .../synthesis/certification/bst/common.def | 4 ++-- .../synthesis/certification/dll/common.def | 8 ++++---- .../certification/dll/dll-dupleton.syn | 2 +- .../certification/dll/dll-singleton.syn | 2 +- .../synthesis/certification/dll/sll-to-dll.syn | 2 +- .../synthesis/certification/ints/min2.syn | 2 +- .../synthesis/certification/list/common.def | 4 ++-- .../synthesis/certification/sll/common.def | 4 ++-- .../synthesis/certification/tree/common.def | 10 +++++----- 21 files changed, 53 insertions(+), 38 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 7dea965e6..a00907d86 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -15,6 +15,7 @@ object Expressions { def collector(acc: Seq[R])(exp: CExpr): Seq[R] = exp match { case v@CVar(_) if p(v) => acc :+ v.asInstanceOf[R] case c@CNatConst(_) if p(c) => acc :+ c.asInstanceOf[R] + case c@CPtrConst(_) if p(c) => acc :+ c.asInstanceOf[R] case c@CBoolConst(_) if p(c) => acc :+ c.asInstanceOf[R] case b@CBinaryExpr(_, l, r) => val acc1 = if (p(b)) acc :+ b.asInstanceOf[R] else acc @@ -106,6 +107,10 @@ object Expressions { override def pp: String = value.toString } + case class CPtrConst(value: Int) extends CExpr { + override def pp: String = if (value == 0) "null" else value.toString + } + case class CSetLiteral(elems: List[CExpr]) extends CExpr { override def pp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.pp).mkString("; ")}]" } @@ -128,7 +133,7 @@ object Expressions { case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" - override def pp: String = if (value == CNatConst(0)) s"$locPP :-> null" else s"$locPP :-> ${value.pp}" + override def pp: String = s"$locPP :-> ${value.pp}" override def subst(sigma: Subst): CPointsTo = CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index f2aa7e987..d5d064b1d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -63,8 +63,7 @@ object Statements { builder.append(s"$indent${deallocs.mkString(s";;\n$indent")}") case CStore(to, off, e) => val t = if (off <= 0) to.pp else s"(${to.pp} .+ $off)" - val v = if (e == CNatConst(0)) "null" else e.pp - builder.append(s"$indent$t ::= $v") + builder.append(s"$indent$t ::= ${e.pp}") case CLoad(to, tpe, from, off) => val f = if (off <= 0) from.pp else s"(${from.pp} .+ $off)" builder.append(s"$indent${to.pp} <-- @read ${tpe.pp} $f") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index cc8630726..2a89e689f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -74,6 +74,7 @@ object Translation { def translateExpr(el: Expr): CExpr = el match { case Var(name) => CVar(name) case BoolConst(value) => CBoolConst(value) + case LocConst(value) => CPtrConst(value) case IntConst(value) => CNatConst(value) case el@UnaryExpr(_, _) => translateUnaryExpr(el) case el@BinaryExpr(_, _, _) => translateBinaryExpr(el) diff --git a/src/main/scala/org/tygus/suslik/language/Expressions.scala b/src/main/scala/org/tygus/suslik/language/Expressions.scala index f8265f08e..a4258aa28 100644 --- a/src/main/scala/org/tygus/suslik/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/language/Expressions.scala @@ -56,6 +56,7 @@ object Expressions { override def pp: String = "==" override def opFromTypes: Map[(SSLType, SSLType), BinOp] = Map( (IntType, IntType) -> OpEq, + (LocType, LocType) -> OpEq, (IntSetType, IntSetType) -> OpSetEq, (BoolType, BoolType) -> OpBoolEq, ) @@ -245,6 +246,7 @@ object Expressions { def collector(acc: Set[R])(exp: Expr): Set[R] = exp match { case v@Var(_) if p(v) => acc + v.asInstanceOf[R] case c@IntConst(_) if p(c) => acc + c.asInstanceOf[R] + case c@LocConst(_) if p(c) => acc + c.asInstanceOf[R] case c@BoolConst(_) if p(c) => acc + c.asInstanceOf[R] case b@BinaryExpr(_, l, r) => val acc1 = if (p(b)) acc + b.asInstanceOf[R] else acc @@ -315,6 +317,7 @@ object Expressions { } } case BoolConst(_) => if (BoolType.conformsTo(target)) Some(gamma) else None + case LocConst(_) => if (LocType.conformsTo(target)) Some(gamma) else None case IntConst(_) => if (IntType.conformsTo(target)) Some(gamma) else None case UnaryExpr(op, e) => op match { case OpNot => if (BoolType.conformsTo(target)) e.resolve(gamma, Some(BoolType)) else None @@ -408,6 +411,7 @@ object Expressions { expr.right.resolveOverloading(gamma)) case Var(_) | BoolConst(_) + | LocConst(_) | IntConst(_) => this case UnaryExpr(op, e) => UnaryExpr(op, e.resolveOverloading(gamma)) case BinaryExpr(op, l, r) => BinaryExpr(op, l.resolveOverloading(gamma), r.resolveOverloading(gamma)) @@ -448,17 +452,16 @@ object Expressions { def subst(sigma: Subst): Expr = this } - case class IntConst(value: Integer) extends Const(value) { - /** - * Let's have this instead of the dedicated Nil constructor - */ - def isNull: Boolean = value == 0 + case class LocConst(value: Integer) extends Const(value) { + def getType(gamma: Gamma): Option[SSLType] = Some(LocType) + } + + val NilPtr = LocConst(0) + case class IntConst(value: Integer) extends Const(value) { def getType(gamma: Gamma): Option[SSLType] = Some(IntType) } - val NilPtr = IntConst(0) - case class BoolConst(value: Boolean) extends Const(value) { def getType(gamma: Gamma): Option[SSLType] = Some(BoolType) } diff --git a/src/main/scala/org/tygus/suslik/logic/PureLogicUtils.scala b/src/main/scala/org/tygus/suslik/logic/PureLogicUtils.scala index 7bbade7eb..46304b9a7 100644 --- a/src/main/scala/org/tygus/suslik/logic/PureLogicUtils.scala +++ b/src/main/scala/org/tygus/suslik/logic/PureLogicUtils.scala @@ -43,6 +43,7 @@ trait PureLogicUtils { case BinaryExpr(op, left, right) => BinaryExpr(op, propagate_not(left), propagate_not(right)) case OverloadedBinaryExpr(op, e1, e2) => OverloadedBinaryExpr(op, propagate_not(e1), propagate_not(e2)) case _:IntConst => e + case _:LocConst => e case _:BoolConst => e case _:Var => e case IfThenElse(e1,e2,e3) => IfThenElse(propagate_not(e1),propagate_not(e2), propagate_not(e3)) @@ -64,6 +65,7 @@ trait PureLogicUtils { case OverloadedBinaryExpr(op, e1, e2) => OverloadedBinaryExpr(op, desugar(e1), desugar(e2)) case UnaryExpr(op, e1) => UnaryExpr(op, desugar(e1)) case _:IntConst => e + case _:LocConst => e case _:BoolConst => e case _:Var => e case IfThenElse(e1,e2,e3) => IfThenElse(desugar(e1),desugar(e2), desugar(e3)) diff --git a/src/main/scala/org/tygus/suslik/logic/smt/SMTSolving.scala b/src/main/scala/org/tygus/suslik/logic/smt/SMTSolving.scala index 89d93f9cb..a028f7143 100644 --- a/src/main/scala/org/tygus/suslik/logic/smt/SMTSolving.scala +++ b/src/main/scala/org/tygus/suslik/logic/smt/SMTSolving.scala @@ -224,6 +224,7 @@ object SMTSolving extends Core private def convertIntExpr(e: Expr): SMTIntTerm = e match { case Var(name) => Ints(name) case IntConst(c) => Ints(c) + case LocConst(c) => Ints(c) case BinaryExpr(op, left, right) => { val l = convertIntExpr(left) val r = convertIntExpr(right) diff --git a/src/main/scala/org/tygus/suslik/parsing/SSLLexical.scala b/src/main/scala/org/tygus/suslik/parsing/SSLLexical.scala index 718d865f5..01bbb617c 100644 --- a/src/main/scala/org/tygus/suslik/parsing/SSLLexical.scala +++ b/src/main/scala/org/tygus/suslik/parsing/SSLLexical.scala @@ -11,6 +11,7 @@ class SSLLexical extends StdLexical { // Add keywords reserved += ("if", "then", "else", "true", "false", "emp", "not", "return", "predicate", "in") reserved += ("error","magic","malloc", "free", "let", "assume") + reserved += ("null") // Types reserved += ("int", "bool", "loc", "set", "void") diff --git a/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala b/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala index fdb0fae82..5e76191e6 100644 --- a/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala +++ b/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala @@ -41,6 +41,9 @@ class SSLParser extends StandardTokenParsers with SepLogicUtils { def formal: Parser[(Var, SSLType)] = typeParser ~ ident ^^ { case a ~ b => (Var(b), a) } + def locLiteral: Parser[Const] = + "null" ^^ (_ => NilPtr) + def intLiteral: Parser[Const] = numericLit ^^ (x => IntConst(Integer.parseInt(x))) @@ -85,7 +88,7 @@ class SSLParser extends StandardTokenParsers with SepLogicUtils { def atom: Parser[Expr] = ( unOpParser ~ atom ^^ { case op ~ a => UnaryExpr(op, a) } | "(" ~> expr <~ ")" - | intLiteral | boolLiteral | setLiteral + | intLiteral | boolLiteral | setLiteral | locLiteral | varParser ) diff --git a/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn b/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn index bfcfe0ef2..c235f50e3 100644 --- a/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn +++ b/src/test/resources/synthesis/certification/bst/bst-left-rotate.syn @@ -2,7 +2,7 @@ binary search tree: rotate left ##### -{ not (r == 0) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; +{ not (r == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } void bst_left_rotate (loc x, loc retv) { sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; diff --git a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn index e5df89c39..ec47e37f1 100644 --- a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn +++ b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-left.syn @@ -6,7 +6,7 @@ Simple case: no left subtree ##### -{ l == 0 /\ +{ l == null /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; retv :-> unused ** @@ -16,8 +16,8 @@ Simple case: no left subtree void bst_remove_root_no_left (loc x, loc retv) { n1 == sz1 + sz2 /\ - lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) /\ - hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) ; + lo == (l == null ? (r == null ? 7 : lo2) : lo1) /\ + hi == (r == null ? (l == null ? 0 : hi1) : hi2) ; retv :-> y ** bst(y, n1, lo, hi) } ##### diff --git a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn index 63889c83a..196b3361c 100644 --- a/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn +++ b/src/test/resources/synthesis/certification/bst/bst-remove-root-no-right.syn @@ -6,7 +6,7 @@ Simple case: no right subtree ##### -{ r == 0 /\ +{ r == null /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; retv :-> unused ** @@ -16,8 +16,8 @@ Simple case: no right subtree void bst_remove_root_no_right (loc x, loc retv) { n1 == sz1 + sz2 /\ - lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) /\ - hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) ; + lo == (l == null ? (r == null ? 7 : lo2) : lo1) /\ + hi == (r == null ? (l == null ? 0 : hi1) : hi2) ; retv :-> y ** bst(y, n1, lo, hi) } ##### diff --git a/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn index 1f1f784fa..5b9920d06 100644 --- a/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn +++ b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn @@ -2,7 +2,7 @@ binary search tree: rotate right ##### -{ not (l == 0) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; +{ not (l == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } void bst_right_rotate (loc x, loc retv) { sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; diff --git a/src/test/resources/synthesis/certification/bst/common.def b/src/test/resources/synthesis/certification/bst/common.def index 8f448305f..e6f72099e 100644 --- a/src/test/resources/synthesis/certification/bst/common.def +++ b/src/test/resources/synthesis/certification/bst/common.def @@ -1,6 +1,6 @@ predicate bst(loc x, int sz, int lo, int hi) { -| x == 0 => { sz == 0 /\ lo == 7 /\ hi == 0 ; emp } -| not (x == 0) => { sz == 1 + sz1 + sz2 /\ 0 <= sz1 /\ 0 <= sz2 /\ +| x == null => { sz == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { sz == 1 + sz1 + sz2 /\ 0 <= sz1 /\ 0 <= sz2 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi2 <= v ? v : hi2) /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; diff --git a/src/test/resources/synthesis/certification/dll/common.def b/src/test/resources/synthesis/certification/dll/common.def index 9f35a5a8c..4091d0b28 100644 --- a/src/test/resources/synthesis/certification/dll/common.def +++ b/src/test/resources/synthesis/certification/dll/common.def @@ -1,10 +1,10 @@ predicate dll(loc x, loc z, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> w ** (x + 2) :-> z ** dll(w, x, s1) } } predicate sll(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } } diff --git a/src/test/resources/synthesis/certification/dll/dll-dupleton.syn b/src/test/resources/synthesis/certification/dll/dll-dupleton.syn index 02b6eb978..c5c51a20e 100644 --- a/src/test/resources/synthesis/certification/dll/dll-dupleton.syn +++ b/src/test/resources/synthesis/certification/dll/dll-dupleton.syn @@ -5,7 +5,7 @@ doubly-linked list: construct a list with two elements { r :-> a } void dll_dupleton (int x, int y, loc r) -{ elems =i {x, y} ; r :-> z ** dll(z, 0, elems) } +{ elems =i {x, y} ; r :-> z ** dll(z, null, elems) } ##### diff --git a/src/test/resources/synthesis/certification/dll/dll-singleton.syn b/src/test/resources/synthesis/certification/dll/dll-singleton.syn index 8b9396e62..cc1900b07 100644 --- a/src/test/resources/synthesis/certification/dll/dll-singleton.syn +++ b/src/test/resources/synthesis/certification/dll/dll-singleton.syn @@ -5,7 +5,7 @@ doubly-linked list: construct a list with one element { r :-> a } void dll_singleton (int x, loc r) -{ elems =i {x} ; r :-> y ** dll(y, 0, elems) } +{ elems =i {x} ; r :-> y ** dll(y, null, elems) } ##### diff --git a/src/test/resources/synthesis/certification/dll/sll-to-dll.syn b/src/test/resources/synthesis/certification/dll/sll-to-dll.syn index 7e017cfc2..4a41ba863 100644 --- a/src/test/resources/synthesis/certification/dll/sll-to-dll.syn +++ b/src/test/resources/synthesis/certification/dll/sll-to-dll.syn @@ -6,7 +6,7 @@ should be able to convert a singly-linked list to a double-linked list { f :-> x ** sll(x, s)} void sll_to_dll(loc f) -{ f :-> i ** dll(i, 0, s)} +{ f :-> i ** dll(i, null, s)} ####### diff --git a/src/test/resources/synthesis/certification/ints/min2.syn b/src/test/resources/synthesis/certification/ints/min2.syn index aea585f56..5b72a2cd5 100644 --- a/src/test/resources/synthesis/certification/ints/min2.syn +++ b/src/test/resources/synthesis/certification/ints/min2.syn @@ -3,7 +3,7 @@ minimum of two integers ### -{ true ; r :-> 0 } +{ true ; r :-> null } void min2(loc r, int x, int y) diff --git a/src/test/resources/synthesis/certification/list/common.def b/src/test/resources/synthesis/certification/list/common.def index 3f8a4651a..6636bc06d 100644 --- a/src/test/resources/synthesis/certification/list/common.def +++ b/src/test/resources/synthesis/certification/list/common.def @@ -1,4 +1,4 @@ predicate lseg(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } } diff --git a/src/test/resources/synthesis/certification/sll/common.def b/src/test/resources/synthesis/certification/sll/common.def index 47b030acd..da2578bf1 100644 --- a/src/test/resources/synthesis/certification/sll/common.def +++ b/src/test/resources/synthesis/certification/sll/common.def @@ -1,4 +1,4 @@ predicate sll(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } } diff --git a/src/test/resources/synthesis/certification/tree/common.def b/src/test/resources/synthesis/certification/tree/common.def index 085c6c802..ef07c0105 100644 --- a/src/test/resources/synthesis/certification/tree/common.def +++ b/src/test/resources/synthesis/certification/tree/common.def @@ -1,10 +1,10 @@ predicate tree(loc x, set s) { -| x == 0 => {s =i {}; emp} -| not (x == 0) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} +| x == null => {s =i {}; emp} +| not (x == null) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} } predicate treeN(loc x, int n) { -| x == 0 => { n == 0 ; emp } -| not (x == 0) => { n == 1 + n1 + n2 /\ 0 <= n1 /\ 0 <= n2 ; - [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** treeN(l, n1) ** treeN(r, n2)} +| x == null => { n == 0 ; emp } +| not (x == null) => { n == 1 + n1 + n2 /\ 0 <= n1 /\ 0 <= n2 ; + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** treeN(l, n1) ** treeN(r, n2)} } From c0ffcfb8d30ba4a16bc4195d5edcf976b77a56fb Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 27 Nov 2020 22:11:31 +0800 Subject: [PATCH 045/211] Make tree_size pass --- .../certification/targets/htt/language/Expressions.scala | 5 +++++ .../suslik/certification/targets/htt/logic/ProofSteps.scala | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index a00907d86..0950d5df6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -88,6 +88,11 @@ object Expressions { def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]) def cardVars: Seq[CVar] = collect(_.isInstanceOf[CSApp]).flatMap {v: CSApp => v.card.vars} + + def conjuncts: Seq[CExpr] = collect({ + case CBinaryExpr(COpAnd, _, _) => true + case _ => false + }) } case class CVar(name: String) extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala index 92ba5aae4..14a4ec196 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala @@ -73,7 +73,9 @@ object ProofSteps { // move pure part to context if (!phi.isTrivial) { - builder.append(s"move=>[$phiName].\n") + val numConjuncts = phi.conjuncts.length + val hyps = if (numConjuncts == 0) s"[$phiName]" else (0 to numConjuncts).map(i => s"[$phiName$i]").mkString("") + builder.append(s"move=>$hyps.\n") } // move spatial part to context, and then substitute where appropriate From 15045d49794b6262de740f5c175fd2b114d08822 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 9 Dec 2020 20:32:34 +0800 Subject: [PATCH 046/211] Add LocConst to VST pattern matching cases --- .../certification/targets/vst/translation/CTranslation.scala | 1 + .../targets/vst/translation/ProofSpecTranslation.scala | 1 + src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 1c9835d7e..783eec764 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -71,6 +71,7 @@ object CTranslation { case Var(name) => CVar(name) case BoolConst(value) => CBoolConst(value) case IntConst(value) => CNatConst(value) + case LocConst(value) => CNatConst(value) // TODO: handle ptr type case e1@UnaryExpr(_, _) => translate_unary_expr(e1) case BinaryExpr(op, e1, e2) => translate_binary_expr(op, e1, e2) case IfThenElse(c, t, e) => CIfThenElse(translate_expression(c), translate_expression(t), translate_expression(e)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index bc923658e..2cee61ce1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -90,6 +90,7 @@ object ProofSpecTranslation { expr match { case const: Expressions.Const => const match { case Expressions.IntConst(value) => ProofCIntConst(value) + case Expressions.LocConst(value) => ProofCIntConst(value) // TODO: handle ptr type case Expressions.BoolConst(value) => ProofCBoolConst(value) } case Var(name) => ProofCVar(name, context(name)) diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 80d9a98fb..e2667d179 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -106,6 +106,7 @@ object SynthesisRunner extends SynthesisRunnerUtil { implicit val certTargetRead: scopt.Read[CertificationTarget] = scopt.Read.reads { case "htt" => htt.HTT + case "vst" => vst.VST case _ => ??? } From ea1b2df836fd99b5d76499e4104c8444d92e267f Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 15 Dec 2020 02:32:19 +0000 Subject: [PATCH 047/211] added change to nilnotlval rule --- .../targets/htt/translation/ProofTranslation.scala | 3 ++- .../targets/vst/translation/ProofTranslation.scala | 8 +++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 50d3bb8a8..d3ac798c0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -9,6 +9,7 @@ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.synthesis._ object ProofTranslation { + private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) def translate(node: CertTree.Node, cenv: CEnvironment): Proof = { @@ -90,7 +91,7 @@ object ProofTranslation { case PrependProducer(s) => val (step, cenv1) = translateOperation(s, cenv) (PrependProofProducer(step), cenv1) - case BranchProducer(_) => + case BranchProducer(_, _) => val sapp = translateSApp(item.node.footprint.pre.sigma.apps.head) val pred = cenv.predicates(sapp.pred) val subgoals = item.node.children.map(n => translateGoal(n.goal)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 5f66e84ea..ca1933fa6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -492,7 +492,13 @@ object ProofTranslation { translate_proof_rules(rule)(new_context)) }).toList ) - case ProofRule.NilNotLval(vars, next) => ??? + + /** nilnotval(r) => assert_PROP(isptr r). {entailer!.} */ + case ProofRule.NilNotLval(vars, next) => + vars.foldRight(translate_proof_rules(next)(context))({ + case (_@Var(name), rest) => ProofSteps.ValidPointer( + name, rest + )}) case ProofRule.CheckPost(next) => ??? case ProofRule.Pick(subst, next) => ??? case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => ??? From 5dd9fb304f255ecf70bbe1078c913b10acc5c7c6 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 18 Dec 2020 02:46:31 +0000 Subject: [PATCH 048/211] refactored out proof rule to toplevel and added support for multiple outputs --- .../suslik/certification/Certificate.scala | 9 +- .../suslik/certification/ProofRule.scala | 481 ++++++++++++++++++ .../targets/htt/HTTCertificate.scala | 6 +- .../certification/targets/vst/VST.scala | 47 +- .../targets/vst/VSTCertificate.scala | 15 + .../targets/vst/clang/Statements.scala | 20 +- .../targets/vst/logic/Proof.scala | 29 +- .../targets/vst/logic/ProofRule.scala | 157 ------ .../vst/translation/ProofTranslation.scala | 441 +++------------- .../targets/vst/translation/Translation.scala | 7 +- .../synthesis/SynthesisRunnerUtil.scala | 18 +- 11 files changed, 643 insertions(+), 587 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/ProofRule.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 234ef5184..5142d6098 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -1,12 +1,15 @@ package org.tygus.suslik.certification +case class CertificateOutput(filename: Option[String], name: String, body: String) /** * A generic interface for certificates, to be implemented by all * certification backends */ trait Certificate { - val body: String - val name: String + /** returns the exported filename for a string */ + def getFileName(name: String) : String = name + target.suffix + val target: CertificationTarget - def fileName: String = name + target.suffix + def outputs: List[CertificateOutput] +// def fileName: String = name + target.suffix } diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala new file mode 100644 index 000000000..9987f0536 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -0,0 +1,481 @@ +package org.tygus.suslik.certification + +import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException +import org.tygus.suslik.language.Expressions.{Expr, NilPtr, SubstVar, Var} +import org.tygus.suslik.language.Statements.{Load, Skip, Store} +import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} +import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} +import org.tygus.suslik.logic.Specifications.{Assertion, SuspendedCallGoal} +import org.tygus.suslik.logic._ +import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure +import org.tygus.suslik.synthesis.rules._ +import org.tygus.suslik.synthesis._ + +import scala.collection.immutable.Map + + + +/** compressed form of suslik rules */ +sealed abstract class ProofRule extends PrettyPrinting { + +} + +object ProofRule { + var indent : Int = 0 + + def ind : String = " " * indent + + def sanitize (str: String) = str.replace(";\n","") + + def with_scope[A] (f: Unit => A) : A = { + val old_indent_value = indent + indent = indent + 4 + val result = f(()) + indent = old_indent_value + result + } + + + /** corresponds to asserting all the variables in vars are not null */ + case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" + } + + /** check post doesn't provide any information useful for certification, so just included as empty rule */ + case class CheckPost(next:ProofRule) extends ProofRule { + override def pp: String = s"${ind}CheckPost;\n${next.pp}" + } + + /** picks an arbitrary instantiation of the proof rules */ + case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" + } + + /** abduces a condition for the proof */ + case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { + override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + } + + /** write a value */ + case class Write(stmt: Store, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" + } + + /** weaken the precondition by removing unused formulae */ + case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" + } + + /** empty rule */ + case object EmpRule extends ProofRule { + override def pp: String = s"${ind}EmpRule;" + } + + /** pure synthesis rules */ + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" + } + + /** open constructor cases */ + case class Open(pred: SApp, fresh_vars: SubstVar, cases: List[(Expr, ProofRule)]) extends ProofRule { + override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + } + + /** subst L */ + case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" + } + + /** subst R */ + case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" + } + + + /** read rule */ + case class Read(map: Map[Var,Expr], operation: Load, next:ProofRule) extends ProofRule { + override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" + } + +// /** abduce a call */ +case class AbduceCall( + new_vars: Map[Var, SSLType], + f_pre: Specifications.Assertion, + callePost: Specifications.Assertion, + call: Statements.Call, + freshSub: SubstVar, + next: ProofRule + ) extends ProofRule { + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" +} + + + /** unification of heap (ignores block/pure distinction) */ + case class HeapUnify(next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnify;\n${next.pp}" + } + + /** unification of pointers */ + case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" + } + + /** unfolds frame */ + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" + } + + /** call operation */ + case class Call(call: Statements.Call, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Call(${sanitize(call.pp)});\n${next.pp}" + } + + /** free operation */ + case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" + } + + /** malloc rule */ + case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" + } + + /** close rule */ + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" + } + + /** star partial */ + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" + } + + case class PickCard(next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickCard;\n${next.pp}" + } + + + case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" + } + + /** converts a Suslik CertTree node into the unified ProofRule structure */ + def of_certtree(node: CertTree.Node): ProofRule = { + def fail_with_bad_proof_structure(): Nothing = + throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") + def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = + throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") + + node.rule match { + case LogicalRules.NilNotLval => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + // find all pointers that are not yet known to be non-null + def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { + // All pointers + val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet + allPointers.filter( + x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) + ) + } + + val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList + + node.children match { + case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case FailRules.CheckPost => node.kont match { + case IdProducer => node.children match { + case ::(head, Nil) => ProofRule.CheckPost(of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.Pick => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Pick(map, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case FailRules.AbduceBranch => node.kont match { + case GuardedProducer(cond, _) => + node.children match { + case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, of_certtree(if_true), of_certtree(if_false)) + case ls => fail_with_bad_children(ls, 2) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.WriteRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Write(stmt, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.WeakenPre => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) + node.children match { + case ::(head, Nil) => ProofRule.WeakenPre(unused, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.EmpRule => node.kont match { + case ConstProducer(Skip) => + node.children match { + case Nil => ProofRule.EmpRule + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + ProofRule.PureSynthesis(is_final = true, assignments, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Open => node.kont match { + case ChainedProducer(ChainedProducer(BranchProducer(Some((heaplet, fresh_vars)), selectors), HandleGuard(_)), ExtractHelper(_)) => + ProofRule.Open(heaplet, fresh_vars, selectors.zip(node.children).map({ case (expr, node) => (expr, of_certtree(node)) }).toList) + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.SubstLeft => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.SubstL(map, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.SubstRight => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.SubstR(map, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.ReadRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Read(map, stmt, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.AbduceCall => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + + // find out which new variables were added to the context + val new_vars = + head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) + val f_pre = head.goal.post + + var SuspendedCallGoal(_, _, callePost, call, freshSub, _) = head.goal.callGoal.get + ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPure => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPointer => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + val prePtss = pre.sigma.ptss + val postPtss = post.sigma.ptss + + def lcpLen(s1: String, s2: String): Int = s1.zip(s2).takeWhile(Function.tupled(_ == _)).length + + val alternatives = for { + PointsTo(y, oy, _) <- postPtss + if y.vars.exists(goal.isExistential) + t@PointsTo(x, ox, _) <- prePtss + if ox == oy + if !postPtss.exists(sameLhs(t)) + } yield y -> x + alternatives.minBy { case (e1, e2) => -lcpLen(e1.pp, e2.pp) } match { + case (y: Var, x) => ProofRule.HeapUnifyPointer(Map(y -> x), of_certtree(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfoldingFinal => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameFlat => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.CallRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(call: Statements.Call), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => ProofRule.Call(call, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.FreeRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Statements.Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => + val size: Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { + case Some(value) => value + case None => 1 + } + node.children match { + case ::(head, Nil) => ProofRule.Free(stmt, size, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.AllocRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + ProofRule. + Malloc(map, stmt, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Close => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + ProofRule.Close(app, selector, asn, fresh_exist, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + } + case LogicalRules.StarPartial => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) + val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) + + node.children match { + case ::(head, Nil) => + ProofRule.StarPartial(new_pre_phi, new_post_phi, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + + case UnificationRules.PickCard => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => ProofRule.PickCard(of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + } + case UnificationRules.PickArg => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => ProofRule.PickArg(map, of_certtree(head)) + case ls => fail_with_bad_children(ls, 1) + } + } + } + } + + +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index d6e7bf1c3..67a483bab 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -1,10 +1,12 @@ package org.tygus.suslik.certification.targets.htt -import org.tygus.suslik.certification.{Certificate, CertificationTarget} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} case class HTTCertificate(body: String, name: String) extends Certificate { val target: CertificationTarget = HTT + def sanitize(txt: String): String = txt.replace('-', '_') // Replace hyphens with underscores - override def fileName: String = super.fileName.replace('-', '_') + override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), body)) + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index 5c9f30ca1..4aec9459b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -3,14 +3,13 @@ package org.tygus.suslik.certification.targets.vst import java.nio.file.Paths import java.io.{File, PrintWriter} -import org.tygus.suslik.certification.{Certificate, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, Certificate, CertificateOutput, CertificationTarget} import org.tygus.suslik.language.Statements import org.tygus.suslik.language.Statements.Statement import org.tygus.suslik.logic.{Environment, FunSpec, FunctionEnv, PredicateEnv, Preprocessor, Program} import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.synthesis.{SynConfig, SynthesisException, SynthesisRunner} import org.tygus.suslik.util.SynStats -import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.certification.targets.vst.translation.Translation @@ -18,47 +17,22 @@ object VST extends CertificationTarget { override val name: String = "VST" override val suffix: String = ".v" - /** prelude for Coq file */ - private def coq_prelude (fun_name: String) = s""" -Require Import VST.floyd.proofauto. -Require Import $fun_name. -Instance CompSpecs : compspecs. make_compspecs prog. Defined. -Definition Vprog : varspecs. mk_varspecs prog. Defined. -""" override def certify(proc: Statements.Procedure, env: Environment): Certificate = { // retrieve the search tree val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val fun_name : String = proc.f.name + val builder = new StringBuilder // append the coq prelude - builder.append(coq_prelude(fun_name)) - - - val c_prelude = - """ - |#include - | - |extern void free(void *p); - |extern void *malloc(size_t size); - | - |typedef union sslval { - | int ssl_int; - | void *ssl_ptr; - |} *loc; - |#define READ_LOC(x,y) (*(x+y)).ssl_ptr - |#define READ_INT(x,y) (*(x+y)).ssl_int - |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z - |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z - | - |""".stripMargin - - val x = Translation.translate(root, proc, env) - - ??? + val fun_name : String = proc.f.name + //builder.append(coq_prelude(fun_name)) + + + Translation.translate(root, proc, env) + } def main(args: Array[String]) : Unit = { @@ -92,7 +66,10 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. val certificate = certTarget.certify(procs.head, env) - println(s"synthesized:\n${certificate.body}") + certificate.outputs.foreach({ + case CertificateOutput(filename, name, body) => + println(s"synthesized:\n${body}") + }) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala new file mode 100644 index 000000000..d2c470796 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -0,0 +1,15 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Proof + +case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate { + override val target: CertificationTarget = VST + + override def outputs: List[CertificateOutput] = + List( + CertificateOutput(Some(name + ".c"), name, CProcedureDefinition.pp), + CertificateOutput(None, name, Proof.pp) + ) +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index d07d05560..db1440063 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -101,6 +101,24 @@ object Statements { params: Seq[(CVar, VSTCType)], body: CStatement ) extends PrettyPrinting { + val c_prelude = + """ + |#include + | + |extern void free(void *p); + |extern void *malloc(size_t size); + | + |typedef union sslval { + | int ssl_int; + | void *ssl_ptr; + |} *loc; + |#define READ_LOC(x,y) (*(x+y)).ssl_ptr + |#define READ_INT(x,y) (*(x+y)).ssl_int + |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z + |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z + | + |""".stripMargin + override def pp: String = { val body_string = body.ppIndent(1) val function_def = @@ -110,7 +128,7 @@ object Statements { }).mkString(", ") }) {\n${body_string}\n}\n" - function_def + c_prelude + function_def } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 065fd8c61..c30b9e870 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.language.Expressions.Expr import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.vst.{Debug, State} import org.tygus.suslik.certification.targets.vst.logic.Proof +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} import org.tygus.suslik.language.PrettyPrinting @@ -14,11 +15,33 @@ import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, Logic import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} -case class Proof(steps: List[ProofSteps]) extends PrettyPrinting { - override def pp: String = +case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps) extends PrettyPrinting { + + /** prelude for Coq file */ + private def coq_prelude = s""" +Require Import VST.floyd.proofauto. +Require Import $name. +From SSL_VST Require Import core. +Instance CompSpecs : compspecs. make_compspecs prog. Defined. +Definition Vprog : varspecs. mk_varspecs prog. Defined. + +""" + + private def lemma_prelude = + s"""Lemma body_$name : semax_body Vprog Gprog f_$name ${name}_spec. + |Proof. + |""".stripMargin + + override def pp: String = { + coq_prelude + + predicates.map(_.pp).mkString("\n") + + spec.pp + "\n" + + lemma_prelude + "start_function.\n" + "ssl_open_context.\n" + - steps.map(_.pp).mkString("\n") + steps.pp + "\n" + + "Qed." + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala deleted file mode 100644 index 4ea1da030..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofRule.scala +++ /dev/null @@ -1,157 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} -import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} -import org.tygus.suslik.language.Statements.{Load, Skip, Store} -import org.tygus.suslik.logic.Specifications.Assertion -import org.tygus.suslik.logic.{Heaplet, PFormula, PointsTo, SApp, SFormula, Specifications} -import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnificationRules} -import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} - - - -/** compressed form of suslik rules */ -sealed abstract class ProofRule extends PrettyPrinting { - -} - -object ProofRule { - var indent : Int = 0 - - def ind : String = " " * indent - - def sanitize (str: String) = str.replace(";\n","") - - def with_scope[A] (f: Unit => A) : A = { - val old_indent_value = indent - indent = indent + 4 - val result = f(()) - indent = old_indent_value - result - } - - - /** corresponds to asserting all the variables in vars are not null */ - case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" - } - - /** check post doesn't provide any information useful for certification, so just included as empty rule */ - case class CheckPost(next:ProofRule) extends ProofRule { - override def pp: String = s"${ind}CheckPost;\n${next.pp}" - } - - /** picks an arbitrary instantiation of the proof rules */ - case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" - } - - /** abduces a condition for the proof */ - case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { - override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" - } - - /** write a value */ - case class Write(stmt: Store, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" - } - - /** weaken the precondition by removing unused formulae */ - case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" - } - - /** empty rule */ - case object EmpRule extends ProofRule { - override def pp: String = s"${ind}EmpRule;" - } - - /** pure synthesis rules */ - case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" - } - - /** open constructor cases */ - case class Open(pred: SApp, fresh_vars: SubstVar, cases: List[(Expr, ProofRule)]) extends ProofRule { - override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" - } - - /** subst L */ - case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" - } - - /** subst R */ - case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" - } - - - /** read rule */ - case class Read(map: Map[Var,Expr], operation: Load, next:ProofRule) extends ProofRule { - override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" - } - -// /** abduce a call */ -case class AbduceCall( - new_vars: Map[Var, SSLType], - f_pre: Specifications.Assertion, - callePost: Specifications.Assertion, - call: Statements.Call, - freshSub: SubstVar, - next: ProofRule - ) extends ProofRule { - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" -} - - - /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnify;\n${next.pp}" - } - - /** unification of pointers */ - case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" - } - - /** unfolds frame */ - case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" - } - - /** call operation */ - case class Call(call: Statements.Call, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Call(${sanitize(call.pp)});\n${next.pp}" - } - - /** free operation */ - case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" - } - - /** malloc rule */ - case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" - } - - /** close rule */ - case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" - } - - /** star partial */ - case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" - } - - case class PickCard(next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickCard;\n${next.pp}" - } - - - case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" - } - - -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 20ef876bd..56f06753c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -1,15 +1,15 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.{CertTree, ProofRule} import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} -import org.tygus.suslik.certification.targets.vst.logic.ProofRule.EmpRule +import org.tygus.suslik.certification.ProofRule.EmpRule import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.AssertPropSubst import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqParamType, CoqPtrType, VSTProofType} import org.tygus.suslik.certification.targets.vst.{Debug, State} -import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofRule, ProofSteps, ProofTerms} +import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofSteps, ProofTerms} import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} import org.tygus.suslik.language.{Expressions, Ident, PrettyPrinting, Statements} @@ -26,390 +26,71 @@ import scala.annotation.tailrec import scala.collection.immutable.Map - object ProofTranslation { case class ProofRuleTranslationException(msg: String) extends Exception { override def toString: String = s"ProofRuleTranslationException(${msg})" } - /** converts a single proof node into a compressed rule */ - def proof_rule_of_proof_node(node: CertTree.Node): ProofRule = { - def fail_with_bad_proof_structure(): Nothing = { - throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") - } - - def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = { - throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != ${count}") - } - - node.rule match { - case LogicalRules.NilNotLval => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => { - // find all pointers that are not yet known to be non-null - def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { - // All pointers - val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet - allPointers.filter( - x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) - ) - } - - val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList - node.children match { - case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - } - case v => fail_with_bad_proof_structure() - } - case FailRules.CheckPost => node.kont match { - case IdProducer => node.children match { - case ::(head, Nil) => ProofRule.CheckPost(proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.Pick => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Pick(map, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case FailRules.AbduceBranch => node.kont match { - case GuardedProducer(cond, _) => - node.children match { - case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, proof_rule_of_proof_node(if_true), proof_rule_of_proof_node(if_false)) - case ls => fail_with_bad_children(ls, 2) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.WriteRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Write(stmt, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.WeakenPre => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) - node.children match { - case ::(head, Nil) => ProofRule.WeakenPre(unused, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.EmpRule => node.kont match { - case ConstProducer(Skip) => - node.children match { - case Nil => ProofRule.EmpRule - case ls => fail_with_bad_children(ls, 0) - } - case _ => fail_with_bad_proof_structure() - } - case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - ProofRule.PureSynthesis(true, assignments, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Open => node.kont match { - case ChainedProducer(ChainedProducer(BranchProducer(Some((heaplet, fresh_vars)), selectors), HandleGuard(_)), ExtractHelper(_)) => - ProofRule.Open(heaplet, fresh_vars, selectors.zip(node.children).map({ case (expr, node) => (expr, proof_rule_of_proof_node(node)) }).toList) - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.SubstLeft => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.SubstL(map, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.SubstRight => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.SubstR(map, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.ReadRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Read(map, stmt, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.AbduceCall => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - - // find out which new variables were added to the context - val new_vars = - head.goal.gamma.filterKeys(key => !(node.goal.gamma.contains(key))) - var f_pre = head.goal.post - - var SuspendedCallGoal(_, _, callePost, call, freshSub, _) = head.goal.callGoal.get - ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPointer => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - val prePtss = pre.sigma.ptss - val postPtss = post.sigma.ptss - - def lcpLen(s1: String, s2: String): Int = s1.zip(s2).takeWhile(Function.tupled(_ == _)).length - - val alternatives = for { - PointsTo(y, oy, _) <- postPtss - if y.vars.exists(goal.isExistential) - t@PointsTo(x, ox, _) <- prePtss - if ox == oy - if !postPtss.exists(sameLhs(t)) - } yield (y -> x) - alternatives.minBy { case (e1, e2) => -lcpLen(e1.pp, e2.pp) } match { - case (y: Var, x) => ProofRule.HeapUnifyPointer(Map(y -> x), proof_rule_of_proof_node(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfoldingFinal => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameBlock => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameFlat => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, proof_rule_of_proof_node(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(call: Call), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Call(call, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.FreeRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => - val size : Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { - case Some(value) => value - case None => 1 - } - node.children match { - case ::(head, Nil) => ProofRule.Free(stmt, size, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.AllocRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - ProofRule. - Malloc(map, stmt, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Close => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - ProofRule.Close(app, selector, asn, fresh_exist, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - } - case LogicalRules.StarPartial => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) - val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) - - node.children match { - case ::(head, Nil) => - ProofRule.StarPartial(new_pre_phi, new_post_phi, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - - case UnificationRules.PickCard => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => ProofRule.PickCard(proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - } - case UnificationRules.PickArg => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => ProofRule.PickArg(map, proof_rule_of_proof_node(head)) - case ls => fail_with_bad_children(ls, 1) - } - } - - } - - } - - - def generate_args(new_variables: List[(String, VSTProofType)]) (card_args: List[String]) = { - var seen_variables_set : Set[String] = Set() - var contructor_map : Map[Ident, Ident] = Map() + def generate_args(new_variables: List[(String, VSTProofType)])(card_args: List[String]) = { + var seen_variables_set: Set[String] = Set() + var contructor_map: Map[Ident, Ident] = Map() val args = new_variables.flatMap({ case (variable_name, ty) => - if (!seen_variables_set.contains(variable_name)){ - seen_variables_set = seen_variables_set + variable_name - (card_args.find(name => variable_name.startsWith(name))) match { - case Some(name) => - contructor_map = contructor_map + (name -> variable_name) - None - case None => - Some(variable_name) + if (!seen_variables_set.contains(variable_name)) { + seen_variables_set = seen_variables_set + variable_name + (card_args.find(name => variable_name.startsWith(name))) match { + case Some(name) => + contructor_map = contructor_map + (name -> variable_name) + None + case None => + Some(variable_name) + } + } else { + None } - } else { - None - } }) (args, card_args.map(arg => contructor_map(arg))) } - def translate_proof(predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, root: CertTree.Node, pre_cond: ProofTerms.FormalCondition): Proof = { + def translate_proof( + name: String, + predicates: List[VSTPredicate], + spec: ProofTerms.FormalSpecification, + root: CertTree.Node, + pre_cond: ProofTerms.FormalCondition + ): Proof = { val pred_map = predicates.map(v => (v.name, v)).toMap type FunSpec = (Ident, List[Ident]) type Context = (Map[Ident, VSTProofType], List[(Ident, List[Ident])]) - def unify_expr (context: Map[Ident,Ident]) (pure:ProofCExpr) (call: ProofCExpr) : Map[Ident,Ident] = + def unify_expr(context: Map[Ident, Ident])(pure: ProofCExpr)(call: ProofCExpr): Map[Ident, Ident] = (pure, call) match { case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) case (ProofCBoolConst(_), ProofCBoolConst(_)) => context case (ProofCIntConst(_), ProofCIntConst(_)) => context case (ProofCSetLiteral(elems), ProofCSetLiteral(call_elems)) => - elems.zip(call_elems).foldLeft(context)({case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr)}) + elems.zip(call_elems).foldLeft(context)({ case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr) }) case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => var new_context = unify_expr(context)(cond)(call_cond) new_context = unify_expr(new_context)(left)(call_left) unify_expr(new_context)(right)(call_right) - case (ProofCBinaryExpr(_, left, right),ProofCBinaryExpr(_, call_left, call_right)) => + case (ProofCBinaryExpr(_, left, right), ProofCBinaryExpr(_, call_left, call_right)) => val new_context = unify_expr(context)(left)(call_left) unify_expr(new_context)(right)(call_right) - case (ProofCUnaryExpr(_, e),ProofCUnaryExpr(_, call_e)) => + case (ProofCUnaryExpr(_, e), ProofCUnaryExpr(_, call_e)) => unify_expr(context)(e)(call_e) } - def unify_call_params (call_pre: ProofTerms.FormalCondition) : List[(Ident, VSTProofType)] = { - (pre_cond,call_pre) match { - case (ProofTerms.FormalCondition(pure, spatial),ProofTerms.FormalCondition(call_pure, call_spatial)) => - def unify_pure(context: Map[Ident, Ident])(pure: ProofTerms.PureFormula) (call: ProofTerms.PureFormula) : Map[Ident,Ident] = { + def unify_call_params(call_pre: ProofTerms.FormalCondition): List[(Ident, VSTProofType)] = { + (pre_cond, call_pre) match { + case (ProofTerms.FormalCondition(pure, spatial), ProofTerms.FormalCondition(call_pure, call_spatial)) => + def unify_pure(context: Map[Ident, Ident])(pure: ProofTerms.PureFormula)(call: ProofTerms.PureFormula): Map[Ident, Ident] = { (pure, call) match { - case (ProofTerms.IsValidPointerOrNull(CVar(name)),ProofTerms.IsValidPointerOrNull(CVar(name1))) => + case (ProofTerms.IsValidPointerOrNull(CVar(name)), ProofTerms.IsValidPointerOrNull(CVar(name1))) => context + (name -> name1) case (ProofTerms.IsValidInt(CVar(name)), ProofTerms.IsValidInt(CVar(name1))) => context + (name -> name1) @@ -419,20 +100,22 @@ object ProofTranslation { unify_expr(context)(expr)(expr1) } } - def unify_spatial(context: Map[Ident, Ident])(pure: VSTHeaplet)(call: VSTHeaplet) : Map[Ident,Ident] = { - (pure,call) match { + + def unify_spatial(context: Map[Ident, Ident])(pure: VSTHeaplet)(call: VSTHeaplet): Map[Ident, Ident] = { + (pure, call) match { case (CDataAt(loc, elem_ty, count, elem), CDataAt(call_loc, call_elem_ty, call_count, call_elem)) => unify_expr(unify_expr(context)(loc)(call_loc))(elem)(call_elem) case (CSApp(pred, args, card), CSApp(call_pred, call_args, call_card)) => assert(pred == call_pred) - unify_expr(args.zip(call_args).foldRight(context)({case ((exp, call_exp), context) => + unify_expr(args.zip(call_args).foldRight(context)({ case ((exp, call_exp), context) => unify_expr(context)(exp)(call_exp) }))(card)(call_card) } } - var context = pure.zip(call_pure).foldLeft(Map[Ident,Ident]())({case (context, (pure, call_pure)) => unify_pure(context)(pure)(call_pure)}) - context = spatial.zip(call_spatial).foldLeft(context)({case (context, (pure, call_pure)) => unify_spatial(context)(pure)(call_pure)}) - spec.params.map({case (name, ty) => (context(name), ty)}) + + var context = pure.zip(call_pure).foldLeft(Map[Ident, Ident]())({ case (context, (pure, call_pure)) => unify_pure(context)(pure)(call_pure) }) + context = spatial.zip(call_spatial).foldLeft(context)({ case (context, (pure, call_pure)) => unify_spatial(context)(pure)(call_pure) }) + spec.params.map({ case (name, ty) => (context(name), ty) }) } } @@ -466,7 +149,6 @@ object ProofTranslation { } } - def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { rule match { case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, cases) => @@ -479,7 +161,7 @@ object ProofTranslation { case ((constructor, clause), (expr, rule)) => val new_variables = pred.constructor_existentials(constructor).map({ case (variable, ty) => - fresh_vars.get(Var(variable)).map({case Var(new_name) => (new_name, ty)}).getOrElse((variable,ty)) + fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) }) val variable_map = new_variables.toMap val new_context = add_new_variables(variable_map)(context) @@ -497,8 +179,9 @@ object ProofTranslation { case ProofRule.NilNotLval(vars, next) => vars.foldRight(translate_proof_rules(next)(context))({ case (_@Var(name), rest) => ProofSteps.ValidPointer( - name, rest - )}) + name, rest + ) + }) case ProofRule.CheckPost(next) => ??? case ProofRule.Pick(subst, next) => ??? case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => ??? @@ -519,16 +202,17 @@ object ProofTranslation { case ::((Var(old_name), Var(new_name)), _) => val new_context = record_variable_mapping(map)(context) ProofSteps.Rename(old_name, new_name, - translate_proof_rules(next)(new_context) + translate_proof_rules(next)(new_context) ) } - case ProofRule.Read(subst, operation, next) => + case ProofRule.Read(subst, option, next) => subst.toList match { - case ::((Var(old_var), Var(new_var)), tl) => + case ::((Var(old_var), Var(new_var)), _) => def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { case Var(name) => (name == variable) case const: Expressions.Const => false - case Expressions.BinaryExpr(op, left, right) => is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + case Expressions.BinaryExpr(op, left, right) => + is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) case Expressions.UnaryExpr(op, arg) => is_variable_used_in_exp(variable)(arg) @@ -539,7 +223,6 @@ object ProofTranslation { def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { def map_varaible(map: Map[Var, Expr]): Ident = map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) - rule match { case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) case ProofRule.CheckPost(next) => is_variable_used_in_proof(variable)(next) @@ -587,12 +270,13 @@ object ProofTranslation { (toe != variable) && is_variable_used_in_proof(variable)(next) } } + val new_context = record_variable_mapping(subst)(context) val rest = (retrieve_typing_context(context).get(old_var)) match { - case Some(CoqPtrType) => - ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) - case _ => translate_proof_rules(next)(new_context) - } + case Some(CoqPtrType) => + ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) + case _ => translate_proof_rules(next)(new_context) + } if (is_variable_used_in_proof(new_var)(next)) { ProofSteps.Rename(old_var, new_var, @@ -608,18 +292,19 @@ object ProofTranslation { } case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, next) => var typing_context = retrieve_typing_context(context) - f_pre.vars.foreach({case Var(name) => if (!typing_context.contains(name)) { + f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { typing_context = typing_context + (name -> CoqPtrType) - } }) + } + }) val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) - val call_args = unify_call_params(call_precond).map({case (name, _) => name}) + val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) var new_context = push_function((fun, call_args))(context) translate_proof_rules(next)(new_context) case ProofRule.HeapUnify(next) => translate_proof_rules(next)(context) case ProofRule.HeapUnifyPointer(map, next) => translate_proof_rules(next)(context) case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) case ProofRule.Call(call, next) => - val ((fun, args),new_context) = pop_function(context) + val ((fun, args), new_context) = pop_function(context) ProofSteps.ForwardCall(args, translate_proof_rules(next)(new_context)) case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) @@ -631,8 +316,8 @@ object ProofTranslation { } } - val simplified = proof_rule_of_proof_node(root) - println(s"Converted proof:\n ${simplified.pp}") + val simplified = ProofRule.of_certtree(root) + println(s"Suslik Proof:\n ${simplified.pp}") val vst_proof: ProofSteps = translate_proof_rules(simplified)(initial_context) @@ -642,7 +327,7 @@ object ProofTranslation { //Debug.visualize_ctree(root) //Debug.visualize_proof_tree(root.kont) - ??? + Proof(name, predicates, spec, vst_proof) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index b1b46f894..a548e3afd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.targets.vst.VSTCertificate import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate @@ -22,7 +23,7 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - def translate(root: CertTree.Node, proc: Procedure, env: Environment): Nothing = { + def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val procedure = CTranslation.translate_function(proc, root.goal.gamma) val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) @@ -34,7 +35,7 @@ object Translation { predicates.foreach(v => println(v.pp)) - val proof = ProofTranslation.translate_proof(predicates, spec, root, pre_cond) + val proof = ProofTranslation.translate_proof(proc.f.name, predicates, spec, root, pre_cond) println(proof.pp) @@ -49,6 +50,6 @@ object Translation { // translate predicates // translate proof - ??? + VSTCertificate(proc.f.name, procedure, proof) } } diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index e32433639..cf25c3fa8 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -4,7 +4,7 @@ import java.io.{File, PrintWriter} import java.nio.file.Paths import org.tygus.suslik.LanguageUtils -import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.{CertTree, CertificateOutput} import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor._ import org.tygus.suslik.logic.smt.SMTSolving @@ -236,11 +236,19 @@ trait SynthesisRunnerUtil { val certificate = certTarget.certify(procs.head, env) if (params.certDest == null) { testPrintln(s"\n$targetName certificate:", Console.MAGENTA) - testPrintln(certificate.body) + certificate.outputs.foreach({case CertificateOutput(filename, name, body) => + val outname = filename.getOrElse(certificate.getFileName(name)) + testPrintln(s"File ${outname}:\n", Console.MAGENTA) + testPrintln(s"$body") + }) } else { - val path = Paths.get(params.certDest.getCanonicalPath, certificate.fileName).toFile - new PrintWriter(path) { write(certificate.body); close() } - testPrintln(s"\n$targetName certificate exported to $path", Console.MAGENTA) + certificate.outputs.foreach({ + case CertificateOutput(ofname, name, body) => + val filename = ofname.getOrElse(certificate.getFileName(name)) + val path = Paths.get(params.certDest.getCanonicalPath, filename).toFile + new PrintWriter(path) { write(body); close() } + testPrintln(s"\n$targetName certificate exported to $path", Console.MAGENTA) + }) } } } From 18f4d8ecd8b163ec84fe2c36983ac216077af634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Tue, 5 Jan 2021 18:39:35 +0800 Subject: [PATCH 049/211] Translate offset for VST Loads --- .../certification/targets/vst/translation/CTranslation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 783eec764..a10ab0249 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -160,7 +160,7 @@ object CTranslation { case None => ??? } } - (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from))) + (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from), offset)) case Statements.Store(to, offset, e) => val (new_ctx, elem_ty) = type_expr(ctx)(e) match { From c23f21122ff4db766d17003113624b69e71051d9 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 5 Jan 2021 10:47:33 +0000 Subject: [PATCH 050/211] bugfixes suslik VST certification exports to integrate properly with VST --- .../targets/vst/logic/Formulae.scala | 2 +- .../targets/vst/logic/Proof.scala | 28 ++++++- .../targets/vst/logic/ProofTerms.scala | 76 +++++++++++++++++++ .../vst/translation/CTranslation.scala | 2 +- .../vst/translation/ProofTranslation.scala | 55 +++++++++++++- .../targets/vst/translation/Translation.scala | 9 --- 6 files changed, 157 insertions(+), 15 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index 2bfaf4db0..619782255 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -28,7 +28,7 @@ object Formulae { case ProofTypes.CoqPtrType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inr ${elem.pp_as_c_value}] ${loc.pp})" case ProofTypes.CoqIntType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inl ${elem.pp_as_c_value}] ${loc.pp})" case ProofTypes.CoqCardType(pred_type) => assert(false, "data at pointing to meta variable"); ??? - case CoqListType(_, length) => + case CoqListType(_, Some (length)) => s"(data_at Tsh (tarray (Tunion _sslval noattr) ${length}) ${elem.pp_as_ssl_union_value} ${loc.pp})" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index c30b9e870..95d59320f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -15,7 +15,7 @@ import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, Logic import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} -case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps) extends PrettyPrinting { +case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps, uses_free: Boolean = false) extends PrettyPrinting { /** prelude for Coq file */ private def coq_prelude = s""" @@ -27,15 +27,37 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. """ - private def lemma_prelude = + /** C standard library specs */ + private def stdlib_defs : String = """Definition free_spec := + | DECLARE _free + | WITH ty: type, x: val + | PRE [ (tptr tvoid) ] + | PROP() + | PARAMS(x) + | SEP (data_at_ Tsh ty x) + | POST [ Tvoid ] + | PROP() + | LOCAL() + | SEP (emp). + |""".stripMargin + + /** prelude for the lemma */ + private def lemma_prelude : String = s"""Lemma body_$name : semax_body Vprog Gprog f_$name ${name}_spec. |Proof. |""".stripMargin + private def library_spec : String = s"""Definition Gprog : funspecs := + | ltac:(with_library prog [listfree_spec${ if (uses_free) {"; free_spec"} else {""}}]). + |""".stripMargin + override def pp: String = { coq_prelude + - predicates.map(_.pp).mkString("\n") + + stdlib_defs + "\n" + + predicates.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + + predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"+ + library_spec + "\n" + lemma_prelude + "start_function.\n" + "ssl_open_context.\n" + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index a2ee5bc1a..7d14e8032 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -337,6 +337,62 @@ object ProofTerms { */ case class CardOf(args: List[Ident]) extends CardConstructor {} + /** + * Represents helper lemmas and operations that are required to make VST handle the predicate automatically + * */ + sealed trait VSTPredicateHelper extends PrettyPrinting + object VSTPredicateHelper { + case class ValidPointer(predicate: String, args: List[String], ptr: String) extends VSTPredicateHelper { + def lemma_name: String = s"${predicate}_${ptr}_valid_pointerP" + override def pp: String = + s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. Admitted." + } + case class HintResolve(lemma_name: String, hint_db: String) extends VSTPredicateHelper { + override def pp: String = + s"Hint Resolve ${lemma_name} : ${hint_db}." + } + + case class LocalFacts(predicate: VSTPredicate) extends VSTPredicateHelper { + + def lemma_name : String = s"${predicate.name}_local_factsP" + + override def pp: String = { + + def constructor_to_equality_term (vl: String, cons: CardConstructor) = + if (cons.constructor_args.isEmpty) { + s"${vl} = ${predicate.constructor_name(cons)}" + } else { + s"exists ${cons.constructor_args.mkString(" ")}, ${vl} = ${predicate.constructor_name(cons)} ${cons.constructor_args.mkString(" ")}" + } + + /** Converts a predicate clause into a clause that mutually exclusively matches the clause + * + * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause + * */ + def predicate_to_determininant_term(clause: ProofTerms.VSTPredicateClause) : String = + clause.pure.head.pp_as_ptr_value + + /*** + * Converts a predicate clause into a corresponding fact. + * + * NOTE: !!!ASSUMPTION!!! We assume that the first clause of the vst predicate is mutually exclusive - i.e + * if the first clause holds, then no other clause can hold. + */ + def clause_fact (cardConstructor: CardConstructor, predicate: VSTPredicateClause) : String = + s"((${predicate_to_determininant_term(predicate)}) -> (${constructor_to_equality_term(predicate.cardinality_param, cardConstructor)}))" + + + + s"""Lemma ${lemma_name} ${predicate.formal_params.mkString(" ")} : + | ${predicate.name} ${predicate.formal_params.mkString(" ")} |-- !!(${ + ( + predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ + predicate.params.flatMap({case (param, ProofTypes.CoqPtrType) => Some (s"(is_pointer_or_null ${param})") case _ => None}) + ).mkString("/\\")}). Proof. Admitted.""".stripMargin + } + + } + } /** represents a clause of the VST predicate, * @param pure are the pure assertions @@ -345,6 +401,8 @@ object ProofTerms { * */ case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String,CardConstructor]) { + val cardinality_param: String = "self_card" + /** finds existential variables in the expression using args */ def find_existentials_args (args: Set[String]): List[(Ident,VSTProofType)] = { def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { @@ -402,6 +460,20 @@ object ProofTerms { clauses: Map[CardConstructor, VSTPredicateClause]) extends PrettyPrinting { + /** returns any helper lemmas that need to be constructed for the helper */ + def get_helpers : List[VSTPredicateHelper] = + params.flatMap({ + case (param, ProofTypes.CoqPtrType) => + val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) + val local_facts = VSTPredicateHelper.LocalFacts(this) + List( + valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer"), + local_facts, VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") + ) + case _ => List() + }) + + /** returns the existential variables introduced by a constructor invokation */ def constructor_existentials (constructor: CardConstructor) : List[(Ident,VSTProofType)] = { val param_map = params.toMap @@ -492,6 +564,10 @@ object ProofTerms { s"${inductive_name}_${count}" } + /** formal parameters of the predicate. + * This is the sequence of identifiers that will need to be passed to the predicate to instantiate it. */ + def formal_params : List[String] = params.map({case (a,_) => a}) ++ List("self_card") + /** pretty print the constructor */ override def pp: String = { val constructor_map = base_constructors.map({ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 783eec764..a10ab0249 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -160,7 +160,7 @@ object CTranslation { case None => ??? } } - (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from))) + (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from), offset)) case Statements.Store(to, offset, e) => val (new_ctx, elem_ty) = type_expr(ctx)(e) match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 56f06753c..2d7252823 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -327,7 +327,60 @@ object ProofTranslation { //Debug.visualize_ctree(root) //Debug.visualize_proof_tree(root.kont) - Proof(name, predicates, spec, vst_proof) + Proof(name, predicates, spec, vst_proof, contains_free(simplified)) } + def contains_free (proof: ProofRule) : Boolean = proof match { + case ProofRule.NilNotLval(vars, next) => contains_free(next) + case ProofRule.CheckPost(next) => contains_free(next) + case ProofRule.Pick(subst, next) => contains_free(next) + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) + case ProofRule.Write(stmt, next) => contains_free(next) + case ProofRule.WeakenPre(unused, next) => contains_free(next) + case ProofRule.EmpRule => false + case ProofRule.PureSynthesis(is_final, assignments, next) => contains_free(next) + case ProofRule.Open(pred, fresh_vars, cases) => cases.exists { case (_, prf) => contains_free(prf) } + case ProofRule.SubstL(map, next) => contains_free(next) + case ProofRule.SubstR(map, next) => contains_free(next) + case ProofRule.Read(map, operation, next) => contains_free(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => contains_free(next) + case ProofRule.HeapUnify(next) => contains_free(next) + case ProofRule.HeapUnifyPointer(map, next) => contains_free(next) + case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_free(next) + case ProofRule.Call(call, next) => contains_free(next) + case ProofRule.Free(stmt, size, next) => true + case ProofRule.Malloc(map, stmt, next) =>contains_free(next) + case ProofRule.Close(app, selector, asn, fresh_exist, next) =>contains_free(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) =>contains_free(next) + case ProofRule.PickCard(next) =>contains_free(next) + case ProofRule.PickArg(map, next) =>contains_free(next) + } + + def contains_malloc (proof: ProofRule) : Boolean = proof match { + case ProofRule.NilNotLval(vars, next) => contains_malloc(next) + case ProofRule.CheckPost(next) => contains_malloc(next) + case ProofRule.Pick(subst, next) => contains_malloc(next) + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) + case ProofRule.Write(stmt, next) => contains_malloc(next) + case ProofRule.WeakenPre(unused, next) => contains_malloc(next) + case ProofRule.EmpRule => false + case ProofRule.PureSynthesis(is_final, assignments, next) => contains_malloc(next) + case ProofRule.Open(pred, fresh_vars, cases) => cases.exists { case (_, prf) => contains_malloc(prf) } + case ProofRule.SubstL(map, next) => contains_malloc(next) + case ProofRule.SubstR(map, next) => contains_malloc(next) + case ProofRule.Read(map, operation, next) => contains_malloc(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => contains_malloc(next) + case ProofRule.HeapUnify(next) => contains_malloc(next) + case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) + case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) + case ProofRule.Call(call, next) => contains_malloc(next) + case ProofRule.Free(stmt, size, next) => contains_malloc(next) + case ProofRule.Malloc(map, stmt, next) => true + case ProofRule.Close(app, selector, asn, fresh_exist, next) =>contains_malloc(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) =>contains_malloc(next) + case ProofRule.PickCard(next) =>contains_malloc(next) + case ProofRule.PickArg(map, next) =>contains_malloc(next) + } + + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index a548e3afd..dac1b55a7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -39,15 +39,6 @@ object Translation { println(proof.pp) - //translate_cont(root.children, root.kont) - - // translate body into VST types, and build context of variables - // var (body, ctx) = CTranslation.translate_body(proc.f, proc.body, root.goal.gamma) - // use this to build a C-encoding of the synthesized function - // var body_string = print_as_c_program(proc.f, body, ctx) - // print(body_string) - - // translate predicates // translate proof VSTCertificate(proc.f.name, procedure, proof) From ec46a4c308d76eff120d928a308160af94198933 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 8 Jan 2021 00:11:47 +0000 Subject: [PATCH 051/211] VST synthesis of min2 proof now works --- .../targets/vst/logic/Proof.scala | 6 +- .../vst/translation/CTranslation.scala | 5 +- .../vst/translation/ProofTranslation.scala | 64 +++++++++++++------ 3 files changed, 52 insertions(+), 23 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 95d59320f..d655b47e5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -28,7 +28,7 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. """ /** C standard library specs */ - private def stdlib_defs : String = """Definition free_spec := + private def free_defs : String = """Definition free_spec := | DECLARE _free | WITH ty: type, x: val | PRE [ (tptr tvoid) ] @@ -48,12 +48,12 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. |""".stripMargin private def library_spec : String = s"""Definition Gprog : funspecs := - | ltac:(with_library prog [listfree_spec${ if (uses_free) {"; free_spec"} else {""}}]). + | ltac:(with_library prog [${name}_spec${ if (uses_free) {"; free_spec"} else {""}}]). |""".stripMargin override def pp: String = { coq_prelude + - stdlib_defs + "\n" + + (if (uses_free) { free_defs + "\n" } else { "" }) + predicates.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"+ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index a10ab0249..06bcf88b4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -153,10 +153,11 @@ object CTranslation { (ctx.updated(translate_variable(to), CVoidPtrType), CVoidPtrType) // TODO: Handle errors case Some(CVoidPtrType) => + val value_type = translate_type(tpe) val new_ctx = - ctx.updated(translate_variable(to), CVoidPtrType) + ctx.updated(translate_variable(to), value_type) .updated(translate_variable(from), CVoidPtrPtrType) - (new_ctx, CVoidPtrType) + (new_ctx, value_type) case None => ??? } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 2d7252823..05e8b1fdf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -64,7 +64,12 @@ object ProofTranslation { val pred_map = predicates.map(v => (v.name, v)).toMap type FunSpec = (Ident, List[Ident]) - type Context = (Map[Ident, VSTProofType], List[(Ident, List[Ident])]) + /** accumulating context used during proof translation + * @param gamma typing context + * @param functions stack of functions being abduced during execution + * @param existentials stack of existential variables + * */ + case class Context(gamma:Map[Ident, VSTProofType], functions: List[(Ident, List[Ident])], existentials: Option[Ident]) def unify_expr(context: Map[Ident, Ident])(pure: ProofCExpr)(call: ProofCExpr): Map[Ident, Ident] = @@ -119,33 +124,37 @@ object ProofTranslation { } } - def initial_context: Context = - ((spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ - spec.formal_params).toMap, Nil) + val initial_context: Context = + Context((spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ + spec.formal_params).toMap, Nil, None) - def retrieve_typing_context: Context => Map[Ident, VSTProofType] = { - case (gamma, _) => gamma - } + def retrieve_typing_context: Context => Map[Ident, VSTProofType] = _.gamma def add_new_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { - case (old_params, funs) => (old_params ++ new_params, funs) + case Context(old_params, funs, ex) => Context(old_params ++ new_params, funs, ex) } def pop_function(context: Context): (FunSpec, Context) = context match { - case (old_params, fun :: funs) => (fun, (old_params, funs)) + case Context(old_params, fun :: funs,ex) => (fun, Context(old_params, funs, ex)) + case _ => fail_with("Function called without prior abduce call") } def push_function(fun_spec: FunSpec)(context: Context): Context = context match { - case (old_params, old_funs) => (old_params, fun_spec :: old_funs) + case Context(old_params, old_funs,ex) => Context(old_params, fun_spec :: old_funs, ex) + } + + def push_existential (id: Ident) (context: Context) : Context = context match { + case Context(params, funs, _) => Context(params, funs, Some(id)) } def record_variable_mapping(mapping: Map[Var, Expr])(context: Context): Context = { val variable_mapping = mapping.flatMap({ case (Var(old_name), Var(new_name)) => Some((old_name, new_name)) case _ => None }) context match { - case (old_params, funs) => + case Context(old_params, funs,ex) => val new_params = old_params.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) val new_funs = funs.map({ case (fun_name, args) => (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg))) }) - (new_params, new_funs) + val new_ex = ex.map(v => variable_mapping.getOrElse(v,v)) + Context(new_params, new_funs,new_ex) } } @@ -182,13 +191,32 @@ object ProofTranslation { name, rest ) }) - case ProofRule.CheckPost(next) => ??? - case ProofRule.Pick(subst, next) => ??? - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => ??? - case ProofRule.Write(stmt, next) => ??? + case ProofRule.CheckPost(next) => + translate_proof_rules(next)(context) + case ProofRule.Pick(subst, next) => + subst.toList match { + case ::((Var(_), Var(m)), tl) => + translate_proof_rules(next)(push_existential(m)(context)) + } + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + ProofSteps.ForwardIf(List( + translate_proof_rules(ifTrue)(context), + translate_proof_rules(ifFalse)(context), + )) + case ProofRule.Write(stmt, next) => + ProofSteps.Forward(translate_proof_rules(next)(context)) case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.EmpRule => ProofSteps.Forward(ProofSteps.Qed) - case ProofRule.PureSynthesis(is_final, assignments, next) => ??? + case ProofRule.EmpRule => + ProofSteps.Forward( + context.existentials.foldRight + (ProofSteps.Qed : ProofSteps) + ((variable, next) => ProofSteps.Exists(variable, ProofSteps.Entailer(next))) + ) + case ProofRule.PureSynthesis(is_final, subst, next) => + subst.toList match { + case ::((Var (_), Var(m)), _) => + translate_proof_rules(next)(push_existential(m)(context)) + } case ProofRule.SubstL(map, next) => map.toList.foldRight(translate_proof_rules(next)(context))({ case ((Var(name), expr), next) => From ff1fb958ea09a787dd7dee92c3e271a9940791e0 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 11 Jan 2021 13:35:32 +0000 Subject: [PATCH 052/211] listcopy now works --- .../suslik/certification/ProofRule.scala | 57 +- .../targets/vst/logic/ProofSteps.scala | 58 +- .../targets/vst/logic/ProofTerms.scala | 74 +- .../translation/ProofSpecTranslation.scala | 27 +- .../vst/translation/ProofTranslation.scala | 770 +++++++++++++----- .../org/tygus/suslik/logic/Declarations.scala | 7 + .../synthesis/rules/UnfoldingRules.scala | 2 +- .../synthesis/rules/UnificationRules.scala | 9 +- 8 files changed, 722 insertions(+), 282 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 9987f0536..3796a8144 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -111,8 +111,8 @@ case class AbduceCall( /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnify;\n${next.pp}" + case class HeapUnify(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next.pp}" } /** unification of pointers */ @@ -126,8 +126,8 @@ case class AbduceCall( } /** call operation */ - case class Call(call: Statements.Call, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Call(${sanitize(call.pp)});\n${next.pp}" + case class Call(subst: Map[Var, Expr], call: Statements.Call, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next.pp}" } /** free operation */ @@ -150,8 +150,8 @@ case class AbduceCall( override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" } - case class PickCard(next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickCard;\n${next.pp}" + case class PickCard(map: Map[Var,Expr], next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next.pp}" } @@ -276,63 +276,44 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - // find out which new variables were added to the context val new_vars = head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) val f_pre = head.goal.post - - var SuspendedCallGoal(_, _, callePost, call, freshSub, _) = head.goal.callGoal.get + var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, fresh_to_actual) = head.goal.callGoal.get ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(of_certtree(head)) + case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyPointer => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - val prePtss = pre.sigma.ptss - val postPtss = post.sigma.ptss - - def lcpLen(s1: String, s2: String): Int = s1.zip(s2).takeWhile(Function.tupled(_ == _)).length - - val alternatives = for { - PointsTo(y, oy, _) <- postPtss - if y.vars.exists(goal.isExistential) - t@PointsTo(x, ox, _) <- prePtss - if ox == oy - if !postPtss.exists(sameLhs(t)) - } yield y -> x - alternatives.minBy { case (e1, e2) => -lcpLen(e1.pp, e2.pp) } match { - case (y: Var, x) => ProofRule.HeapUnifyPointer(Map(y -> x), of_certtree(head)) - } + case ::(head, Nil) => ProofRule.HeapUnifyPointer(subst, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -410,9 +391,9 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(call: Statements.Call), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.Call(call, of_certtree(head)) + case ::(head, Nil) => ProofRule.Call(subst, call, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -461,9 +442,9 @@ case class AbduceCall( } case UnificationRules.PickCard => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => ProofRule.PickCard(of_certtree(head)) + case ::(head, Nil) => ProofRule.PickCard(map, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala index 4f55e4f1f..183c03176 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala @@ -2,13 +2,13 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import ProofTerms.CardConstructor -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCCardinalityConstructor, ProofCExpr} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType import org.tygus.suslik.language.Ident sealed abstract class ProofSteps extends PrettyPrinting { - def to_validity_assertion(var_name: Ident, var_type: VSTProofType) : Option[ProofSteps] = { + def to_validity_assertion(var_name: Ident, var_type: VSTProofType): Option[ProofSteps] = { var_type match { case ProofTypes.CoqParamType(ty) => None case ProofTypes.CoqPtrType => None @@ -21,6 +21,7 @@ sealed abstract class ProofSteps extends PrettyPrinting { } object ProofSteps { + case class Entailer(next: ProofSteps) extends ProofSteps { override def pp: String = s"entailer!.\n${next.pp}" } @@ -31,7 +32,7 @@ object ProofSteps { branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String], ProofSteps)] ) extends ProofSteps { override def pp: String = { - def constructor_prop (cons_name: Ident, cons: CardConstructor) : String = cons match { + def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" } @@ -70,7 +71,7 @@ object ProofSteps { } } - case class Forward(next:ProofSteps) extends ProofSteps { + case class Forward(next: ProofSteps) extends ProofSteps { override def pp: String = s"forward.\n${next.pp}" } @@ -87,11 +88,41 @@ object ProofSteps { } } - case class ValidPointerOrNull(variable: Ident,next: ProofSteps) extends ProofSteps { + case class IntrosTuple(variables: List[(Ident, VSTProofType)], next: ProofSteps) extends ProofSteps { + override def pp: String = { + val extra_assertions: String = + variables.flatMap({ case (var_name, var_type) => + to_validity_assertion(var_name, var_type) + }) match { + case Nil => "" + case ls => "\n" ++ ls.map(_.pp).mkString(".\n") + } + + variables match { + case Nil => s"${next.pp}" + case ::((variable, _), Nil) => + s"Intros ${variable}." ++ extra_assertions ++ s"\n${next.pp}" + case _ => + def to_destruct_pattern(base: Option[String])(ls: List[(Ident, VSTProofType)]): String = { + (base, ls) match { + case (None, ::((vara, _), ::((varb, _), rest))) => to_destruct_pattern(Some(s"[${vara} ${varb}]"))(rest) + case (Some(base), ::((vara, _), rest)) => + to_destruct_pattern(Some(s"[${base} ${vara}]"))(rest) + case (Some(base), Nil) => base + } + } + + val destruct_pattern: String = to_destruct_pattern(None)(variables) + s"let ret := fresh vret in Intros ret; destruct ret as ${destruct_pattern}." ++ extra_assertions ++ s"\n${next.pp}" + } + } + } + + case class ValidPointerOrNull(variable: Ident, next: ProofSteps) extends ProofSteps { override def pp: String = s"assert_PROP (is_pointer_or_null ${variable}). { entailer!. }\n${next.pp}" } - case class ValidPointer(variable: Ident,next: ProofSteps) extends ProofSteps { + case class ValidPointer(variable: Ident, next: ProofSteps) extends ProofSteps { override def pp: String = s"assert_PROP (isptr ${variable}). { entailer!. }\n${next.pp}" } @@ -103,14 +134,18 @@ object ProofSteps { override def pp: String = s"try rename ${old_name} into ${new_name}.\n${next.pp}" } - case class Exists(variable: Ident, next: ProofSteps) extends ProofSteps { - override def pp: String = s"Exists ${variable}.\n${next.pp}" + case class Exists(variable: ProofCExpr, next: ProofSteps) extends ProofSteps { + override def pp: String = s"Exists ${variable.pp}.\n${next.pp}" } case class Free(variable: Ident, sz: Int, next: ProofSteps) extends ProofSteps { override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${sz}, ${variable}).\n${next.pp}" } + case class Malloc(size: Int, next: ProofSteps) extends ProofSteps { + override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${size}).\n${next.pp}" + } + case class AssertPropSubst(variable: Ident, expr: ProofCExpr, next: ProofSteps) extends ProofSteps { override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp_as_ptr_value}) as ssl_var; try rewrite ssl_var. { entailer!. }\n${next.pp}" } @@ -118,4 +153,11 @@ object ProofSteps { case object Qed extends ProofSteps { override def pp: String = "" } + + case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCCardinalityConstructor, next: ProofSteps) extends ProofSteps { + override def pp: String = + s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1.\n${next.pp}" + + } + } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 7d14e8032..fecc30933 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -4,10 +4,11 @@ import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.ProofCExpr -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqBoolType, CoqIntType, CoqListType, CoqParamType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqBoolType, CoqCardType, CoqIntType, CoqListType, CoqParamType, VSTProofType} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Ident +import org.tygus.suslik.logic.SApp object ProofTerms { @@ -35,10 +36,31 @@ object ProofTerms { */ sealed abstract class ProofCExpr extends PrettyPrinting { + /** Applies a substitution to an expression */ + def subst (mapping: Map[String, ProofCExpr]) : ProofCExpr = this match { + case expr@ProofCVar(name, _) => mapping.get(name) match { + case Some(value) => value.subst(mapping) + case None => expr + } + case expr@ProofCBoolConst(_) =>expr + case expr@ProofCIntConst(_, _) =>expr + case ProofCSetLiteral(elems) => + ProofCSetLiteral(elems.map(_.subst(mapping))) + case ProofCIfThenElse(cond, left, right) => + ProofCIfThenElse(cond.subst(mapping), left.subst(mapping), right.subst(mapping)) + case ProofCBinaryExpr(op, left, right) => + ProofCBinaryExpr(op, left.subst(mapping), right.subst(mapping)) + case ProofCUnaryExpr(op, e) => + ProofCUnaryExpr(op, e.subst(mapping)) + case ProofCCardinalityConstructor(pred_type, name, args) => + ProofCCardinalityConstructor(pred_type, name, args.map(_.subst(mapping))) + } + def type_expr: VSTProofType = this match { case ProofCVar(name, typ) => typ case ProofCBoolConst(value) => CoqBoolType - case ProofCIntConst(value) => CoqIntType + case ProofCIntConst(value, false) => CoqIntType + case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) case ProofCSetLiteral(elems) => CoqListType(elems.head.type_expr, Some(elems.length)) case ProofCIfThenElse(cond, left, right) => left.type_expr case ProofCBinaryExpr(op, left, right) => op match { @@ -48,6 +70,7 @@ object ProofTerms { case _ => CoqBoolType } case ProofCUnaryExpr(op, e) => e.type_expr + case ProofCCardinalityConstructor(pred_type, _, _) => CoqCardType(pred_type) } /** prints the expression as though it were an element @@ -61,7 +84,7 @@ object ProofTerms { case ProofTypes.CoqListType(elem, length) => name case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") } - case ProofCIntConst(value) => s"(Vint (Int.repr ${value.toString}))" + case ProofCIntConst(value, false) => s"(Vint (Int.repr ${value.toString}))" case ProofCSetLiteral(elems) => s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" case value@ProofCBinaryExpr(op, _, _) => @@ -94,7 +117,8 @@ object ProofTerms { case ProofTypes.CoqListType(elem, length) => name case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") } - case ProofCIntConst(value) => s"inl (Vint (Int.repr ${value.toString}))" + case ProofCIntConst(value,false) => s"inl (Vint (Int.repr ${value.toString}))" + case ProofCIntConst(0,true) => s"inr nullval" case ProofCSetLiteral(elems) => s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" case value@ProofCBinaryExpr(op, _, _) => @@ -107,7 +131,7 @@ object ProofTerms { if (is_int) { s"inl (Vint (Int.repr ${value.pp}))" } else { - ??? + throw TranslationException("Error: inconsistent assumptions, attempting to print an arithmetic operation on non-integral values as a c expression.") } case value@ProofCUnaryExpr(op, _) => op match { case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" } case v => v.pp @@ -119,7 +143,7 @@ object ProofTerms { def pp_as_ptr_value : String = this match { case ProofCVar(name, typ) => name case ProofCBoolConst(value) => this.pp - case ProofCIntConst(value) => if (value == 0) { "nullval" } else { this.pp } + case ProofCIntConst(value, true) => if (value == 0) { "nullval" } else { this.pp } case ProofCSetLiteral(elems) => this.pp case ProofCIfThenElse(cond, left, right) => this.pp case ProofCBinaryExpr(op, left, right) => this.pp @@ -128,6 +152,11 @@ object ProofTerms { } + case class ProofCCardinalityConstructor(pred_type: String, name: String, args: List[ProofCExpr]) extends ProofCExpr { + override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type})" + } + + /** a variable in a VST proof */ case class ProofCVar(name: String, typ: VSTProofType) extends ProofCExpr { override def pp: String = typ match { @@ -141,7 +170,7 @@ object ProofTerms { ty match { case CTypes.CIntType => s"(force_signed_int ${name})" case CTypes.CVoidPtrType => s"${name}" - case CTypes.CUnitType => ??? /// Unimplemented as we should never be dealing with unit types + case CTypes.CUnitType => throw new TranslationException("Error: inconsistent assumptions, attempting to create a variable of unit type") } case ProofTypes.CoqListType(elem, _) => name } @@ -153,8 +182,8 @@ object ProofTerms { } /** integer constant in a VST proof */ - case class ProofCIntConst(value: Int) extends ProofCExpr { - override def pp: String = value.toString + case class ProofCIntConst(value: Int, is_ptr: Boolean) extends ProofCExpr { + override def pp: String = if (is_ptr && value == 0) { "nullval" } else { value.toString } } /** set literal (encoded as set) in a VST proof */ @@ -403,12 +432,17 @@ object ProofTerms { val cardinality_param: String = "self_card" + /** + * @return the selector for the clause + */ + def selector: ProofCExpr = pure.head + /** finds existential variables in the expression using args */ def find_existentials_args (args: Set[String]): List[(Ident,VSTProofType)] = { def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCIntConst(value, _) => Nil case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) @@ -426,7 +460,7 @@ object ProofTerms { def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCIntConst(value, _) => Nil case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) @@ -460,6 +494,22 @@ object ProofTerms { clauses: Map[CardConstructor, VSTPredicateClause]) extends PrettyPrinting { + /** + * Returns the constructor of the predicate with n arguments + * @param n number of arguments + */ + def constructor_by_arg(n: Int): CardConstructor = + clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n}).get._1 + + + /** + * @param selector a expression corresponding to a selector of the predicate + * @return cardinality and clause matched by predicate + */ + def apply(selector: ProofCExpr) : (CardConstructor, VSTPredicateClause) = + clauses.find({case (_, clause) => clause.selector.equals(selector) }).get + + /** returns any helper lemmas that need to be constructed for the helper */ def get_helpers : List[VSTPredicateHelper] = params.flatMap({ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 2cee61ce1..bd101a7c6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -9,7 +9,7 @@ import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} import org.tygus.suslik.certification.targets.vst.logic.{ProofTerms, ProofTypes} -import ProofTerms.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} +import ProofTerms.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} import ProofTerms.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqIntType, CoqListType, CoqParamType, CoqPtrType, VSTProofType} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException @@ -23,6 +23,19 @@ import scala.collection.immutable /** translates suslik proof terms to VST compatible proof terms */ object ProofSpecTranslation { + /** Translates a cardinality to a vst expression that can be passed around */ + def translate_cardinality(predicate: VSTPredicate, cardinality: CardConstructor): ProofCExpr = { + ProofCCardinalityConstructor( + predicate.inductive_name, + predicate.constructor_name(cardinality), + cardinality match { + case ProofTerms.CardNull => List() + case CardOf(args) => args.map(arg => ProofCVar(arg, CoqCardType(predicate.inductive_name))) + } + ) + } + + /** translate a suslik type into a VST proof type */ def translate_type(lType: SSLType): VSTProofType = lType match { @@ -43,7 +56,9 @@ object ProofSpecTranslation { def type_expr(left_1: ProofCExpr): VSTProofType = left_1 match { case ProofCVar(name, typ) => typ - case ProofCIntConst(value) => CoqIntType + + case ProofCIntConst(value, false) => CoqIntType + case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) case ProofCSetLiteral(elems) => CoqListType(CoqPtrType, Some(elems.length)) case ProofCIfThenElse(cond, left, right) => type_expr(left) case ProofCBinaryExpr(op, left, right) => @@ -89,8 +104,8 @@ object ProofSpecTranslation { expr match { case const: Expressions.Const => const match { - case Expressions.IntConst(value) => ProofCIntConst(value) - case Expressions.LocConst(value) => ProofCIntConst(value) // TODO: handle ptr type + case Expressions.IntConst(value) => ProofCIntConst(value, is_ptr=false) + case Expressions.LocConst(value) => ProofCIntConst(value, is_ptr=true) case Expressions.BoolConst(value) => ProofCBoolConst(value) } case Var(name) => ProofCVar(name, context(name)) @@ -184,7 +199,8 @@ object ProofSpecTranslation { def type_expr(context: Map[Ident, VSTProofType]) (cvalue: ProofTerms.Expressions.ProofCExpr) : VSTProofType = cvalue match { case ProofCVar(name, typ) => typ - case ProofCIntConst(value) => CoqIntType + case ProofCIntConst(value, false) => CoqIntType + case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) case ProofCSetLiteral(elems) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) case ProofCBinaryExpr(op, left, right) => op match { @@ -277,6 +293,7 @@ object ProofSpecTranslation { // return the blocks and the applications blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) } + def translate_assertion (context: Map[Ident, VSTProofType]) (assertion: Assertion): FormalCondition = assertion match { case Assertion(phi, sigma) => { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 05e8b1fdf..90f326aea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -5,18 +5,18 @@ import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} import org.tygus.suslik.certification.ProofRule.EmpRule import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.AssertPropSubst -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{VSTPredicate, VSTPredicateClause} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqParamType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqParamType, CoqPtrType, VSTProofType} import org.tygus.suslik.certification.targets.vst.{Debug, State} -import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofSteps, ProofTerms} +import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofSteps, ProofTerms, ProofTypes} import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} import org.tygus.suslik.language.{Expressions, Ident, PrettyPrinting, Statements} import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Skip, Store} import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} import org.tygus.suslik.logic.Specifications.SuspendedCallGoal -import org.tygus.suslik.logic.{Block, Heaplet, PFormula, PointsTo, SApp, SFormula} +import org.tygus.suslik.logic.{Block, Heaplet, PFormula, PointsTo, SApp, SFormula, Specifications} import org.tygus.suslik.synthesis.rules.LogicalRules.FrameBlock.profilesMatch import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnfoldingRules, UnificationRules} @@ -33,14 +33,28 @@ object ProofTranslation { } - def generate_args(new_variables: List[(String, VSTProofType)])(card_args: List[String]) = { + /** + * Partitions a list of variables into those which correspond to cardinality arguments and those that do not. + * + * - first list has new variables that do not correspond to cardinality arguments + * - second list has variables that correspond to cardinality arguments + * + * Explanation: cardinality arguments typically have names like _alpha_513, + * but when introduced into the context in a suslik proof, they are given + * fresh names such as _alpha_513x2. + */ + def partition_cardinality_args(new_variables: List[(String, VSTProofType)])(card_args: List[String]) = { var seen_variables_set: Set[String] = Set() var contructor_map: Map[Ident, Ident] = Map() + + // partition variables into variables that correspond to arguments to the cardinality val args = new_variables.flatMap({ case (variable_name, ty) => if (!seen_variables_set.contains(variable_name)) { seen_variables_set = seen_variables_set + variable_name - (card_args.find(name => variable_name.startsWith(name))) match { + card_args find (name => variable_name.startsWith(name)) match { + // if the any cardinality argument names are a prefix of the variable name, then this + // variable is a fresh variable for that particular cardinality argument case Some(name) => contructor_map = contructor_map + (name -> variable_name) None @@ -63,13 +77,31 @@ object ProofTranslation { ): Proof = { val pred_map = predicates.map(v => (v.name, v)).toMap - type FunSpec = (Ident, List[Ident]) + type FunSpec = (Ident, List[Ident], List[(Ident, VSTProofType)]) + + /** + * represents a unfold operation on a predicate + */ + case class Unfold( + VSTPredicate: VSTPredicate, + cardinality: ProofTerms.CardConstructor, + args: List[(String, VSTProofType)], + existentials: List[(String,VSTProofType)] + ) + /** accumulating context used during proof translation - * @param gamma typing context - * @param functions stack of functions being abduced during execution - * @param existentials stack of existential variables + * + * @param gamma typing context + * @param functions stack of functions being abduced during execution + * @param queued_unfolds sequence of queued unfolds * */ - case class Context(gamma:Map[Ident, VSTProofType], functions: List[(Ident, List[Ident])], existentials: Option[Ident]) + case class Context( + post: List[(Ident, VSTProofType)], + gamma: Map[Ident, VSTProofType], + variable_map: Map[Ident, ProofCExpr], + functions: List[FunSpec], + queued_unfolds: List[Unfold] + ) def unify_expr(context: Map[Ident, Ident])(pure: ProofCExpr)(call: ProofCExpr): Map[Ident, Ident] = @@ -125,222 +157,533 @@ object ProofTranslation { } val initial_context: Context = - Context((spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ - spec.formal_params).toMap, Nil, None) + Context( + spec.existensial_params.toList, + ( + spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ + spec.formal_params ++ + spec.existensial_params + ).toMap, + Map(), + Nil, + Nil + ) def retrieve_typing_context: Context => Map[Ident, VSTProofType] = _.gamma def add_new_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { - case Context(old_params, funs, ex) => Context(old_params ++ new_params, funs, ex) + case Context(post, old_params, ex_map, funs, ufs) => Context(post, old_params ++ new_params, ex_map, funs, ufs) } def pop_function(context: Context): (FunSpec, Context) = context match { - case Context(old_params, fun :: funs,ex) => (fun, Context(old_params, funs, ex)) + case Context(post, old_params, ex_map, fun :: funs, ufs) => (fun, Context(post, old_params, ex_map, funs,ufs)) case _ => fail_with("Function called without prior abduce call") } def push_function(fun_spec: FunSpec)(context: Context): Context = context match { - case Context(old_params, old_funs,ex) => Context(old_params, fun_spec :: old_funs, ex) + case Context(post, old_params, ex_map, old_funs, ufs) => Context(post, old_params, ex_map, fun_spec :: old_funs, ufs) } - def push_existential (id: Ident) (context: Context) : Context = context match { - case Context(params, funs, _) => Context(params, funs, Some(id)) - } + def push_unfolding(context: Context)(unfolded_expr: Unfold, new_equalities: Map[String, ProofCExpr]): Context = + context match { + case Context(post, gamma, variable_map, functions, queued_unfolds) => + Context(post, gamma, variable_map ++ new_equalities, functions, unfolded_expr :: queued_unfolds) + } + def record_variable_assignment(name: String, expr: Expr)(context: Context): Context = + Context( + context.post, + context.gamma, + (context.variable_map ++ Map(name -> ProofSpecTranslation.translate_expression(context.gamma)(expr))), + context.functions, + context.queued_unfolds + ) + + def record_variable_assignment_card(name: String, expr: ProofCExpr)(context:Context) = + Context( + context.post, + context.gamma, + (context.variable_map ++ Map(name -> expr)), + context.functions, + context.queued_unfolds + ) + + + + /** + * Updates context to account for renaming induced by a mapping + * + * @param mapping a mapping encoding a renaming of variables (ASSUMED TO BE RENAMING VARIABLES to VARIABLES) + * @param context the context + * @return an updated context with variables renamed + */ def record_variable_mapping(mapping: Map[Var, Expr])(context: Context): Context = { val variable_mapping = mapping.flatMap({ case (Var(old_name), Var(new_name)) => Some((old_name, new_name)) case _ => None }) - context match { - case Context(old_params, funs,ex) => - val new_params = old_params.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) - val new_funs = funs.map({ case (fun_name, args) => (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg))) }) - val new_ex = ex.map(v => variable_mapping.getOrElse(v,v)) - Context(new_params, new_funs,new_ex) - } + val expr_map = variable_mapping.flatMap({ case (name, to) => context.gamma.get(name).map(ty => (name, ProofCVar(to, ty))) }) + + def update_map(map: Map[Ident, ProofCExpr]): Map[Ident, ProofCExpr] = + map.map({ case (name, expr) => (variable_mapping.getOrElse(name, name), expr.subst(expr_map)) }) + + def update_map_static(map: Map[Ident, ProofCExpr]): Map[Ident, ProofCExpr] = + map.map({ case (name, expr) => (name, expr.subst(expr_map)) }) + + val post = context.post.map({ case (ident, proofType) => (variable_mapping.getOrElse(ident, ident), proofType) }) + // Convert mapping to a Map[String,String] using assumption that all mappings are between variables + // Then rename all terms in the context + val new_params = context.gamma.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) + val new_funs = context.functions.map({ case (fun_name, args, existentials) => + (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg)), existentials) }) + val new_variable_map = update_map(context.variable_map) + val new_unfolds = context.queued_unfolds.map({ + case Unfold(predicate, cardinality, args, existentials) => + val new_cardinality = cardinality match { + case ProofTerms.CardNull => ProofTerms.CardNull + case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) + } + val new_args = args.map({case (name,ty) => (variable_mapping.getOrElse(name,name), ty)}) + val new_existentials = existentials.map({case (name, ty) => (variable_mapping.getOrElse(name,name), ty)}) + Unfold(predicate, new_cardinality, new_args, new_existentials) + }) + Context(post, new_params, new_variable_map, new_funs, new_unfolds) } - def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { - rule match { - case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, cases) => - val arg_set = args.toSet - val pred = pred_map(predicate_name) - ProofSteps.ForwardIfConstructor( - card_variable, - predicate_name, - pred.clauses.zip(cases).map({ - case ((constructor, clause), (expr, rule)) => - val new_variables = pred.constructor_existentials(constructor).map({ - case (variable, ty) => - fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) - }) - val variable_map = new_variables.toMap - val new_context = add_new_variables(variable_map)(context) - - val (args, constructor_args) = generate_args(new_variables)(constructor.constructor_args) - - ((pred.constructor_name(constructor), constructor, constructor_args), - ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), - args, - translate_proof_rules(rule)(new_context)) - }).toList - ) + /** + * Translates a Suslik Read rule to a series of VST tactics implementing the same operation. + * + * A suslik read rule, such as Read (x -> y, y = *p) performs the following operations: + * - updates all instances of variable x with y + * - if y is used in the rest of the program, emits a read operation + * + * This then corresponds to the VST rules: + * - rename x (if defined) with y + * - if y used in rest of program, then: + * - first assert that the pointer being read from is non-null (VST idiosynracy) + * - emit a forward tactic to move over the operation + */ + def handle_read_rule(rule: ProofRule.Read, context: Context): ProofSteps = rule match { + case ProofRule.Read(subst, option, next) => + subst.toList match { + case ::((Var(old_var), Var(new_var)), _) => + def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { + case Var(name) => (name == variable) + case const: Expressions.Const => false + case Expressions.BinaryExpr(op, left, right) => + is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => + is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + case Expressions.UnaryExpr(op, arg) => is_variable_used_in_exp(variable)(arg) + case Expressions.SetLiteral(elems) => elems.exists(is_variable_used_in_exp(variable)) + case Expressions.IfThenElse(cond, left, right) => + is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) + } - /** nilnotval(r) => assert_PROP(isptr r). {entailer!.} */ - case ProofRule.NilNotLval(vars, next) => - vars.foldRight(translate_proof_rules(next)(context))({ - case (_@Var(name), rest) => ProofSteps.ValidPointer( - name, rest - ) - }) - case ProofRule.CheckPost(next) => - translate_proof_rules(next)(context) - case ProofRule.Pick(subst, next) => - subst.toList match { - case ::((Var(_), Var(m)), tl) => - translate_proof_rules(next)(push_existential(m)(context)) - } - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => - ProofSteps.ForwardIf(List( - translate_proof_rules(ifTrue)(context), - translate_proof_rules(ifFalse)(context), - )) - case ProofRule.Write(stmt, next) => - ProofSteps.Forward(translate_proof_rules(next)(context)) - case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.EmpRule => - ProofSteps.Forward( - context.existentials.foldRight - (ProofSteps.Qed : ProofSteps) - ((variable, next) => ProofSteps.Exists(variable, ProofSteps.Entailer(next))) - ) - case ProofRule.PureSynthesis(is_final, subst, next) => - subst.toList match { - case ::((Var (_), Var(m)), _) => - translate_proof_rules(next)(push_existential(m)(context)) - } - case ProofRule.SubstL(map, next) => - map.toList.foldRight(translate_proof_rules(next)(context))({ - case ((Var(name), expr), next) => - AssertPropSubst( - name, - ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr), - next) - }) - case ProofRule.SubstR(map, next) => - map.toList match { - case ::((Var(old_name), Var(new_name)), _) => - val new_context = record_variable_mapping(map)(context) - ProofSteps.Rename(old_name, new_name, - translate_proof_rules(next)(new_context) - ) - } - case ProofRule.Read(subst, option, next) => - subst.toList match { - case ::((Var(old_var), Var(new_var)), _) => - def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { - case Var(name) => (name == variable) - case const: Expressions.Const => false - case Expressions.BinaryExpr(op, left, right) => - is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => - is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - case Expressions.UnaryExpr(op, arg) => is_variable_used_in_exp(variable)(arg) - case Expressions.SetLiteral(elems) => elems.exists(is_variable_used_in_exp(variable)) - case Expressions.IfThenElse(cond, left, right) => - is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - } - def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { - def map_varaible(map: Map[Var, Expr]): Ident = - map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) - rule match { - case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.CheckPost(next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickCard(next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickArg(subst, next) => - val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.Pick(subst, next) => - val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => - is_variable_used_in_exp(variable)(cond) || - is_variable_used_in_proof(variable)(ifTrue) || - is_variable_used_in_proof(variable)(ifFalse) - case ProofRule.Write(Statements.Store(Var(tov), offset, e), next) => - (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) - case ProofRule.WeakenPre(unused, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.Open(pred, heaplet, cases) => - cases.exists({ case (expr, rule) => - is_variable_used_in_exp(variable)(expr) || - is_variable_used_in_proof(variable)(rule) - }) - case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnify(next) => is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) => + def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { + def map_varaible(map: Map[Var, Expr]): Ident = + map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) + + rule match { + case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.CheckPost(next) => is_variable_used_in_proof(variable)(next) + case ProofRule.PickCard(_, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.PickArg(_, next) => + val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) + case ProofRule.Pick(subst, next) => + val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + is_variable_used_in_exp(variable)(cond) || + is_variable_used_in_proof(variable)(ifTrue) || + is_variable_used_in_proof(variable)(ifFalse) + case ProofRule.Write(Statements.Store(Var(tov), offset, e), next) => + (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) + case ProofRule.WeakenPre(unused, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.EmpRule => false + case ProofRule.PureSynthesis(is_final, assignments, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.Open(pred, heaplet, cases) => + cases.exists({ case (expr, rule) => + is_variable_used_in_exp(variable)(expr) || + is_variable_used_in_proof(variable)(rule) + }) + case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.HeapUnify(_,next) => is_variable_used_in_proof(variable)(next) + case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.Close(app, selector, asn, fresh_exist, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => + is_variable_used_in_proof(variable)(next) + case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset), next) => + (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) + case ProofRule.Call(_, Call(_, args, _), next) => + args.exists(is_variable_used_in_exp(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset), next) => - (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) - case ProofRule.Call(Call(_, args, _), next) => - args.exists(is_variable_used_in_exp(variable)) || - is_variable_used_in_proof(variable)(next) - case ProofRule.Free(Free(Var(v)), _, next) => - (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz), next) => - (toe != variable) && is_variable_used_in_proof(variable)(next) - } - } - - val new_context = record_variable_mapping(subst)(context) - val rest = (retrieve_typing_context(context).get(old_var)) match { - case Some(CoqPtrType) => - ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) - case _ => translate_proof_rules(next)(new_context) + case ProofRule.Free(Free(Var(v)), _, next) => + (v == variable) || is_variable_used_in_proof(variable)(next) + case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz), next) => + (toe != variable) && is_variable_used_in_proof(variable)(next) } + } - if (is_variable_used_in_proof(new_var)(next)) { - ProofSteps.Rename(old_var, new_var, - ProofSteps.Forward( - rest - ) - ) - } else { - ProofSteps.Rename(old_var, new_var, + val new_context = record_variable_mapping(subst)(context) + val rest = (retrieve_typing_context(context).get(old_var)) match { + case Some(CoqPtrType) => + ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) + case _ => translate_proof_rules(next)(new_context) + } + if (is_variable_used_in_proof(new_var)(next)) { + ProofSteps.Rename(old_var, new_var, + ProofSteps.Forward( rest ) + ) + } else { + ProofSteps.Rename(old_var, new_var, + rest + ) + } + } + } + + /** + * Translates a suslik Open Rule into the corresponding VST rules + * + * Does this by mapping each constructor of the opened predicate to a branch of the rule, + * and then for each branch introducing the variables that it uses. + */ + def handle_open_rule(rule: ProofRule.Open, context: Context): ProofSteps = rule match { + case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, cases) => + val pred = pred_map(predicate_name) + ProofSteps.ForwardIfConstructor( + card_variable, + predicate_name, + pred.clauses.zip(cases).map({ + case ((constructor, clause), (expr, rule)) => + // each clause of the type introduces existentials + val new_variables = pred.constructor_existentials(constructor).map({ + // rename existential variables if they have been assigned fresh variables + case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) + }) + val new_context = add_new_variables(new_variables.toMap)(context) + val (args, constructor_args) = partition_cardinality_args(new_variables)(constructor.constructor_args) + ((pred.constructor_name(constructor), constructor, constructor_args), + ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), + args, + translate_proof_rules(rule)(new_context)) + }).toList + ) + } + + def handle_pick_rule(rule: ProofRule.Pick, context: Context): ProofSteps = rule match { + case ProofRule.Pick(subst, next) => + val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + case ((name,expr), context) => record_variable_assignment(name,expr)(context) + }) + translate_proof_rules(next)(new_context) + } + + def handle_pick_card_rule(rule: ProofRule.PickCard, context: Context): ProofSteps = rule match { + case ProofRule.PickCard(subst, next) => + + /** Given an expression representing a pick of a cardinality variable + * returns the corresponding cardinality constructor + * + * i.e + * _alpha_512 -> _alpha_514 + 1 + * + * produces + * (lseg_card_1 _alpha_514), [(_alpha_514, lseg_card)] + * (i.e, the picked cardinality is that alpha_512 maps to lseg_card_1 _alpha_514, where _alpha_514 is a new existential variable) + */ + def cardinality_expr_mapping_to_cardinality_map(base_expr: String)(expr: Expressions.Expr) : (ProofCExpr, List[(Ident, VSTProofType)]) = + expr match { + case Var(name) => + context.gamma.get(name) match { + case Some(ty) => (ProofCVar(name, ty), Nil) + case None => (ProofCVar(name, context.gamma(base_expr)), List((name, context.gamma(base_expr)))) } + case rule@Expressions.BinaryExpr(Expressions.OpPlus, expr, Expressions.IntConst(_)) => + val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(base_expr)(expr) + val pred_name = context.gamma(base_expr) match { case ProofTypes.CoqCardType(pred_type) => pred_type } + val predicate = pred_map(pred_name) + // NOTE: Assumes that all predicates have a 1-argument constructor + (ProofCCardinalityConstructor(predicate.inductive_name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) } - case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, next) => - var typing_context = retrieve_typing_context(context) - f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { - typing_context = typing_context + (name -> CoqPtrType) + + val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + case ((name,expr), context) => + val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(name)(expr) + record_variable_assignment_card(name, translated_expr)( + add_new_variables(new_vars.toMap)(context) + ) + }) + translate_proof_rules(next)(new_context) + } + + def handle_pick_arg_rule(rule: ProofRule.PickArg, context: Context): ProofSteps = rule match { + case ProofRule.PickArg(subst, next) => + val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + case ((name,expr), context) => record_variable_assignment(name,expr)(context) + }) + translate_proof_rules(next)(new_context) + } + + def handle_emp_rule(context: Context) = { + def instantiate_existentials(existentials: List[(Ident, VSTProofType)])(then: ProofSteps) : ProofSteps = + existentials.foldRight(then)( + (variable, next) => ProofSteps.Exists(context.variable_map(variable._1), next) + ) + def unfold_predicates(then: ProofSteps) : ProofSteps = + context.queued_unfolds.foldRight(then)( + {case (unfold, next) => + val predicate : VSTPredicate = unfold.VSTPredicate + ProofSteps.Unfold ( + predicate, + unfold.VSTPredicate.params.length, + ProofCCardinalityConstructor( + predicate.inductive_name, + predicate.constructor_name(unfold.cardinality), + unfold.cardinality.constructor_args.map(v => ProofCVar(v, CoqCardType(predicate.inductive_name)))), + unfold.existentials.foldRight(next) + ({case ((variable,ty), next) => ProofSteps.Exists(context.variable_map.getOrElse(variable, ProofCVar(variable,ty)) , next)}) + )} + ) + + ProofSteps.Forward( + instantiate_existentials(context.post)( + ProofSteps.Entailer ( + unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed)) + ) + ) + ) + } + + def handle_pure_synthesis_rule(rule: ProofRule.PureSynthesis, context: Context): ProofSteps = rule match { + case ProofRule.PureSynthesis(is_final, subst, next) => + val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + case ((name,expr), context) => record_variable_assignment(name,expr)(context) + }) + translate_proof_rules(next)(new_context) + } + + def handle_heap_unify(rule: ProofRule.HeapUnify, context: Context): ProofSteps = rule match { + case ProofRule.HeapUnify(_, next) => +// val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ +// case ((name,expr), context) => record_variable_assignment(name,expr)(context) +// }) + translate_proof_rules(next)(context) + } + + def handle_heap_unify_pointer(rule: ProofRule.HeapUnifyPointer, context: Context): ProofSteps = rule match { + case ProofRule.HeapUnifyPointer(subst, next) => + val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + case ((name,expr), context) => record_variable_assignment(name,expr)(context) + }) + translate_proof_rules(next)(new_context) + } + + def handle_substl_rule(rule: ProofRule.SubstL, context: Context): ProofSteps = rule match { + case ProofRule.SubstL(map, next) => + map.toList.foldRight(translate_proof_rules(next)(context))({ + case ((Var(name), expr), next) => + AssertPropSubst( + name, + ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr), + next) + }) + } + + def handle_substr_rule(rule: ProofRule.SubstR, context: Context): ProofSteps = rule match { + case ProofRule.SubstR(map, next) => + def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofSteps = + map match { + case Nil => translate_proof_rules(next)(context) + case ::((Var(old_name), Var(new_name)), rest) => + val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) + ProofSteps.Rename(old_name, new_name, + apply_subst(new_context)(rest) + ) + case ::((Var(name), expr), rest) => + val new_context = record_variable_assignment(name, expr)(context) + apply_subst(new_context)(rest) } + + apply_subst(context)(map.toList) + } + + def handle_abduce_call(rule: ProofRule.AbduceCall, context: Context): ProofSteps = rule match { + case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, next) => + var typing_context = retrieve_typing_context(context) + f_pre.vars.foreach({ case Var(name) => + if (!typing_context.contains(name)) { + typing_context = typing_context + (name -> CoqPtrType) + }}) + val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) + val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) + val existentials = spec.existensial_params.toList.map({case (name,ty) => (freshSub(Var(name)).name, ty)}) + var new_context = push_function((fun, call_args, existentials))(context) + translate_proof_rules(next)(new_context) + } + + def handle_nilnotnval_rule(rule: ProofRule.NilNotLval, context: Context) = rule match { + case ProofRule.NilNotLval(vars, next) => + vars.foldRight(translate_proof_rules(next)(context))({ + case (_@Var(name), rest) => ProofSteps.ValidPointer( + name, rest + ) + }) + } + + def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofSteps = rule match { + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + ProofSteps.ForwardIf(List( + translate_proof_rules(ifTrue)(context), + translate_proof_rules(ifFalse)(context) + )) + } + + def handle_call_rule(rule: ProofRule.Call, context: Context): ProofSteps = rule match { + case ProofRule.Call(_, call, next) => + val ((fun, args, existentials), new_context) = pop_function(context) + ProofSteps.ForwardCall(args, + existentials match { + case Nil => translate_proof_rules(next)(new_context) + case _ => + ProofSteps.IntrosTuple(existentials, translate_proof_rules(next)(new_context)) }) - val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) - val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) - var new_context = push_function((fun, call_args))(context) - translate_proof_rules(next)(new_context) - case ProofRule.HeapUnify(next) => translate_proof_rules(next)(context) - case ProofRule.HeapUnifyPointer(map, next) => translate_proof_rules(next)(context) + } + + def handle_write_rule(rule: ProofRule.Write, context: Context): ProofSteps = rule match { + case ProofRule.Write(stmt, next) => ProofSteps.Forward(translate_proof_rules(next)(context)) + } + + def handle_free_rule(rule: ProofRule.Free, context: Context): ProofSteps = rule match { + case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) + } + + + def handle_close_rule(rule: ProofRule.Close, context: Context): ProofSteps = rule match { + case ProofRule.Close(app, o_selector, asn, fresh_exist, next) => + + // Use application of of constructor to infer mapping of variables + val predicate = pred_map(app.pred) + val selector: ProofCExpr = ProofSpecTranslation.translate_expression(predicate.params.toMap)(o_selector) + val (cardinality, clause) = predicate(selector) + val substitution: Map[String, ProofCExpr] = + predicate.params.zip(app.args).map({ case ((name, ty), arg) => (name, ProofSpecTranslation.translate_expression(context.gamma)(arg)) }).toMap + + val clause_existentials = predicate.find_existentials(cardinality)(clause).map({ + case (name, ty) => (fresh_exist(Var(name)).name, ty) + }) + + val card_equality: List[(String, ProofCExpr)] = List((app.card match { + case Var(name) => name + }, ProofSpecTranslation.translate_cardinality(predicate, cardinality))) + + /** + * Given two equal expressions, attempts to extract a mapping on variables + */ + def equal_variables_to_variable_mapping(expr_a: ProofCExpr)(expr_b: ProofCExpr) = (expr_a, expr_b) match { + case (ProofCVar(var_a, a_ty), ProofCVar(var_b, b_ty)) => + if (context.gamma.contains(var_a)) { + List((var_a, ProofCVar(var_b, b_ty))) + } + else { + List((var_b, ProofCVar(var_a, a_ty))) + } + case (ProofCVar(var_a, _), b_expr) => + List((var_a, b_expr)) + case (a_expr, ProofCVar(var_b, _)) => + List((var_b, a_expr)) + case _ => List() + } + + def extract_equalities(expr: ProofCExpr): List[(String, ProofCExpr)] = expr match { + case ProofCBinaryExpr(ProofTerms.Expressions.ProofCOpAnd, left, right) => + extract_equalities(left) ++ extract_equalities(right) + case ProofCBinaryExpr(op, left, right) => op match { + case ProofTerms.Expressions.ProofCOpIntEq => equal_variables_to_variable_mapping(left)(right) + case ProofTerms.Expressions.ProofCOpBoolEq => equal_variables_to_variable_mapping(left)(right) + case ProofTerms.Expressions.ProofCOpSetEq => equal_variables_to_variable_mapping(left)(right) + case ProofTerms.Expressions.ProofCOpPtrEq => equal_variables_to_variable_mapping(left)(right) + case _ => List() + } + case _ => List() + } + +// val expr_equalities = clause.pure.map(_.subst(substitution)).flatMap(extract_equalities) + + val new_equalities = card_equality.toMap + + val args = cardinality.constructor_args.map( + name => (name, CoqCardType(predicate.inductive_name)) + ) + val unfolding = Unfold(predicate, cardinality, args, clause_existentials) + val new_context = push_unfolding(context)(unfolding, new_equalities) + val new_context_2 = record_variable_mapping(fresh_exist)(new_context) + + translate_proof_rules(next)(new_context_2) + } + + def handle_malloc_rule(rule: ProofRule.Malloc, context: Context) = rule match { + case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => + val new_context = + map.foldRight( + add_new_variables(map.map({case (Var(original), Var(name)) => (name, CoqPtrType)}))(context) + )({case ((Var(old_name), new_expr), context) => record_variable_assignment(old_name,new_expr)(context)}) + ProofSteps.Malloc(sz, + ProofSteps.Intros( + List((to_var, CoqPtrType)), + translate_proof_rules(next)(new_context) + )) + } + + def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { + rule match { + // Branching rules + case rule@ProofRule.Open(SApp(_, _, _, Var(_)), _, _) => handle_open_rule(rule, context) + case rule@ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => handle_abduce_branch_rule(rule, context) + + // Read and write Operations + case rule@ProofRule.Write(_, _) => handle_write_rule(rule, context) + case rule@ProofRule.Read(subst, option, next) => handle_read_rule(rule, context) + + // Memory management rules + case rule@ProofRule.Free(Free(Var(_)), _, _) => handle_free_rule(rule, context) + case rule@ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => handle_malloc_rule(rule, context) + + // Abduce call & Existentials + case rule@ProofRule.AbduceCall(_, _, _, Call(Var(_), _, _), _, _) => handle_abduce_call(rule, context) + case rule@ProofRule.Pick(_, _) => handle_pick_rule(rule, context) + case rule@ProofRule.PureSynthesis(_, _, _) => handle_pure_synthesis_rule(rule, context) + case rule@ProofRule.PickCard(_,_) => handle_pick_card_rule(rule, context) + case rule@ProofRule.PickArg(_, _) => handle_pick_arg_rule(rule, context) + case rule@ProofRule.Call(_, _, _) => handle_call_rule(rule, context) + case rule@ProofRule.Close(_, _, _, _, _) => handle_close_rule(rule, context) + case rule@ProofRule.HeapUnify(_, next) => handle_heap_unify(rule, context) + case rule@ProofRule.HeapUnifyPointer(_, _) => handle_heap_unify_pointer(rule, context) + + + // Completion rule + case ProofRule.EmpRule => handle_emp_rule(context) + + // Context changing rules + case rule@ProofRule.NilNotLval(_, _) => handle_nilnotnval_rule(rule, context) + case rule@ProofRule.SubstL(_, _) => handle_substl_rule(rule, context) + case rule@ProofRule.SubstR(_, _) => handle_substr_rule(rule, context) + + // Ignored rules + case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) + case ProofRule.CheckPost(next) => translate_proof_rules(next)(context) + case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) - case ProofRule.Call(call, next) => - val ((fun, args), new_context) = pop_function(context) - ProofSteps.ForwardCall(args, translate_proof_rules(next)(new_context)) - case ProofRule.Free(Free(Var(name)), size, next) => - ProofSteps.Free(name, size, translate_proof_rules(next)(context)) - case ProofRule.Malloc(map, stmt, next) => ??? - case ProofRule.Close(app, selector, asn, fresh_exist, next) => ??? - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => ??? - case ProofRule.PickCard(next) => ??? - case ProofRule.PickArg(map, next) => ??? + + + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => translate_proof_rules(next)(context) } } @@ -358,7 +701,7 @@ object ProofTranslation { Proof(name, predicates, spec, vst_proof, contains_free(simplified)) } - def contains_free (proof: ProofRule) : Boolean = proof match { + def contains_free(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_free(next) case ProofRule.CheckPost(next) => contains_free(next) case ProofRule.Pick(subst, next) => contains_free(next) @@ -372,19 +715,19 @@ object ProofTranslation { case ProofRule.SubstR(map, next) => contains_free(next) case ProofRule.Read(map, operation, next) => contains_free(next) case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => contains_free(next) - case ProofRule.HeapUnify(next) => contains_free(next) + case ProofRule.HeapUnify(_, next) => contains_free(next) case ProofRule.HeapUnifyPointer(map, next) => contains_free(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_free(next) - case ProofRule.Call(call, next) => contains_free(next) + case ProofRule.Call(_, call, next) => contains_free(next) case ProofRule.Free(stmt, size, next) => true - case ProofRule.Malloc(map, stmt, next) =>contains_free(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) =>contains_free(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) =>contains_free(next) - case ProofRule.PickCard(next) =>contains_free(next) - case ProofRule.PickArg(map, next) =>contains_free(next) + case ProofRule.Malloc(map, stmt, next) => contains_free(next) + case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_free(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_free(next) + case ProofRule.PickCard(_, next) => contains_free(next) + case ProofRule.PickArg(map, next) => contains_free(next) } - def contains_malloc (proof: ProofRule) : Boolean = proof match { + def contains_malloc(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_malloc(next) case ProofRule.CheckPost(next) => contains_malloc(next) case ProofRule.Pick(subst, next) => contains_malloc(next) @@ -398,17 +741,16 @@ object ProofTranslation { case ProofRule.SubstR(map, next) => contains_malloc(next) case ProofRule.Read(map, operation, next) => contains_malloc(next) case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => contains_malloc(next) - case ProofRule.HeapUnify(next) => contains_malloc(next) + case ProofRule.HeapUnify(_,next) => contains_malloc(next) case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) - case ProofRule.Call(call, next) => contains_malloc(next) + case ProofRule.Call(_, call, next) => contains_malloc(next) case ProofRule.Free(stmt, size, next) => contains_malloc(next) case ProofRule.Malloc(map, stmt, next) => true - case ProofRule.Close(app, selector, asn, fresh_exist, next) =>contains_malloc(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) =>contains_malloc(next) - case ProofRule.PickCard(next) =>contains_malloc(next) - case ProofRule.PickArg(map, next) =>contains_malloc(next) + case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_malloc(next) + case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_malloc(next) + case ProofRule.PickCard(_, next) => contains_malloc(next) + case ProofRule.PickArg(map, next) => contains_malloc(next) } - } diff --git a/src/main/scala/org/tygus/suslik/logic/Declarations.scala b/src/main/scala/org/tygus/suslik/logic/Declarations.scala index c85ca822c..f2a3afd9b 100644 --- a/src/main/scala/org/tygus/suslik/logic/Declarations.scala +++ b/src/main/scala/org/tygus/suslik/logic/Declarations.scala @@ -37,6 +37,13 @@ case class FunSpec(name: Ident, rType: SSLType, params: Formals, this.copy(pre = pre.resolveOverloading(gamma), post = post.resolveOverloading(gamma)) } + def existentials() : List[Var] = { + val params = this.params.map(_._1).toSet + val formal_params = pre.ghosts(params) + val existentials = post.ghosts(formal_params ++ params) + existentials.toList + } + override def pp: String = { ("" + s"${rType.pp} " diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index b7dda66ad..49dfac47a 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -126,7 +126,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPost = callGoal.callerPost val newGoal = goal.spawnChild(pre = newPre, post = newPost, callGoal = None) val postCallTransition = Transition(goal, newGoal) - val kont: StmtProducer = PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstProducer(callGoal.freshToActual) >> PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, List(postCallTransition) ++ companionTransition(callGoal, goal))) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 4929af6b4..128dbf198 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -48,7 +48,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils // val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) val newPost = Assertion(post.phi && subExpr, newPostSigma) val newGoal = goal.spawnChild(post = newPost) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(sub.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } nubBy[RuleResult, Assertion](alternatives, sub => sub.subgoals.head.post) @@ -88,10 +88,11 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils alternatives.sortBy{ case (e1, e2) => -lcpLen(e1.pp, e2.pp) }.headOption match { case None => Nil case Some((y, x)) => { - val subExpr = goal.substToFormula(Map(y -> x)) + val subst = Map(y -> x) + val subExpr = goal.substToFormula(subst) val newPost = Assertion(post.phi && subExpr, post.sigma) val newGoal = goal.spawnChild(post = newPost) - val kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(subst.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) } } @@ -207,7 +208,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils newPost = goal.post.subst(sigma) newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) - kont = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, this, goal) } } From 36028f5d1546b2f0039a28eaa612d32ae65601a4 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 02:57:40 +0800 Subject: [PATCH 053/211] Add more substitution maps from synthesis to ProofRules - Read: Refine type of ghost variable map - AbduceCall: Capture entire funspec of subroutine from synthesis and use it for reasoning in proof - Init: Add new rule to capture top level goal at root of ProofRules tree --- .../suslik/certification/ProofRule.scala | 27 ++++++++++++------- .../htt/translation/ProgramTranslation.scala | 2 +- .../vst/translation/ProofTranslation.scala | 8 +++--- .../tygus/suslik/synthesis/StmtProducer.scala | 7 +++-- .../rules/SymbolicExecutionRules.scala | 4 +-- .../synthesis/rules/UnfoldingRules.scala | 10 +++---- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 9987f0536..9fc50d8b8 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -1,11 +1,11 @@ package org.tygus.suslik.certification import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException -import org.tygus.suslik.language.Expressions.{Expr, NilPtr, SubstVar, Var} +import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements.{Load, Skip, Store} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} -import org.tygus.suslik.logic.Specifications.{Assertion, SuspendedCallGoal} +import org.tygus.suslik.logic.Specifications.{Assertion, Goal, SuspendedCallGoal} import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules._ @@ -77,8 +77,8 @@ object ProofRule { } /** open constructor cases */ - case class Open(pred: SApp, fresh_vars: SubstVar, cases: List[(Expr, ProofRule)]) extends ProofRule { - override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[(Expr, ProofRule)]) extends ProofRule { + override def pp: String = s"${ind}Open(${pred.pp});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" } /** subst L */ @@ -93,7 +93,7 @@ object ProofRule { /** read rule */ - case class Read(map: Map[Var,Expr], operation: Load, next:ProofRule) extends ProofRule { + case class Read(map: Map[Var,Var], operation: Load, next:ProofRule) extends ProofRule { override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" } @@ -104,6 +104,9 @@ case class AbduceCall( callePost: Specifications.Assertion, call: Statements.Call, freshSub: SubstVar, + freshToActual: Subst, + f: FunSpec, + gamma: Gamma, next: ProofRule ) extends ProofRule { override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" @@ -159,6 +162,10 @@ case class AbduceCall( override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" } + case class Init(goal: Goal, next: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Init(${goal.pp});\n${next.pp}" + } + /** converts a Suslik CertTree node into the unified ProofRule structure */ def of_certtree(node: CertTree.Node): ProofRule = { def fail_with_bad_proof_structure(): Nothing = @@ -244,8 +251,8 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnfoldingRules.Open => node.kont match { - case ChainedProducer(ChainedProducer(BranchProducer(Some((heaplet, fresh_vars)), selectors), HandleGuard(_)), ExtractHelper(_)) => - ProofRule.Open(heaplet, fresh_vars, selectors.zip(node.children).map({ case (expr, node) => (expr, of_certtree(node)) }).toList) + case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => + ProofRule.Open(pred, fresh_vars, sbst, selectors.zip(node.children).map({ case (expr, node) => (expr, of_certtree(node)) }).toList) case _ => fail_with_bad_proof_structure() } case LogicalRules.SubstLeft => node.kont match { @@ -273,7 +280,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnfoldingRules.AbduceCall => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(AbduceCallProducer(f), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => @@ -282,8 +289,8 @@ case class AbduceCall( head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) val f_pre = head.goal.post - var SuspendedCallGoal(_, _, callePost, call, freshSub, _) = head.goal.callGoal.get - ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, of_certtree(head)) + var SuspendedCallGoal(_, _, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get + ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 21d31ee35..0d395411b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -48,7 +48,7 @@ object ProgramTranslation { case PrependProducer(s) => val stmt = translateOperation(s) PrependCStmtProducer(stmt) - case BranchProducer(_, selectors) => + case BranchProducer(_, _, _, selectors) => BranchCStmtProducer(selectors.map(translateExpr)) case GuardedProducer(cond, _) => GuardedCStmtProducer(translateExpr(cond)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 56f06753c..dec6798b8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -151,7 +151,7 @@ object ProofTranslation { def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { rule match { - case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, cases) => + case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => val arg_set = args.toSet val pred = pred_map(predicate_name) ProofSteps.ForwardIfConstructor( @@ -243,14 +243,14 @@ object ProofTranslation { case ProofRule.EmpRule => false case ProofRule.PureSynthesis(is_final, assignments, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Open(pred, heaplet, cases) => + case ProofRule.Open(pred, heaplet, _, cases) => cases.exists({ case (expr, rule) => is_variable_used_in_exp(variable)(expr) || is_variable_used_in_proof(variable)(rule) }) case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, next) => + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshSub, freshToActual, gamma, next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnify(next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) @@ -290,7 +290,7 @@ object ProofTranslation { ) } } - case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, next) => + case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _, next) => var typing_context = retrieve_typing_context(context) f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { typing_context = typing_context + (name -> CoqPtrType) diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index c8bb788e6..ea19e270a 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.{Heaplet, InductiveClause, SApp, SFormula} +import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils @@ -145,7 +145,7 @@ case class HandleGuard(goal: Goal) extends StmtProducer { } // Produces a conditional that branches on the selectors -case class BranchProducer(pred: Option[(SApp, SubstVar)], selectors: Seq[Expr]) extends StmtProducer { +case class BranchProducer(pred: Option[SApp], freshVars: SubstVar, sbst: Subst, selectors: Seq[Expr]) extends StmtProducer { val arity: Int = selectors.length val fn: Kont = liftToSolutions(stmts => { if (stmts.length == 1) stmts.head else { @@ -175,3 +175,6 @@ case class GhostSubstProducer(subst: SubstVar) extends StmtProducer with Noop // Captures an unfolded predicate application case class UnfoldProducer(app: SApp, selector: Expr, asn: Assertion, substEx: SubstVar) extends StmtProducer with Noop + +// Abduce Call +case class AbduceCallProducer(f: FunSpec) extends StmtProducer with Noop diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala index bc4208600..e9e1f7a9c 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala @@ -249,7 +249,7 @@ object SymbolicExecutionRules extends SepLogicUtils with RuleUtils { val pre = goal.pre val thenGoal = goal.spawnChild(Assertion(pre.phi && cond, pre.sigma), sketch = tb) val elseGoal = goal.spawnChild(Assertion(pre.phi && cond.not, pre.sigma), sketch = eb) - List(RuleResult(List(thenGoal, elseGoal), BranchProducer(None, List(cond, cond.not)), this, goal)) + List(RuleResult(List(thenGoal, elseGoal), BranchProducer(None, Map.empty, Map.empty, List(cond, cond.not)), this, goal)) } case (If(_, _, _), _) => { throw SynthesisException("Found conditional in the middle of the program. Conditionals only allowed at the end.") @@ -268,7 +268,7 @@ object SymbolicExecutionRules extends SepLogicUtils with RuleUtils { def apply(goal: Goal): Seq[RuleResult] = { for { h <- goal.pre.sigma.chunks - (selGoals, _,_) <- UnfoldingRules.Open.mkInductiveSubGoals(goal, h).toList + (selGoals, _, _, _) <- UnfoldingRules.Open.mkInductiveSubGoals(goal, h).toList (selector, subGoal) <- selGoals if SMTSolving.valid(goal.pre.phi ==> selector) } yield RuleResult(List(subGoal), IdProducer, this, goal) diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index b7dda66ad..4706639c5 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -24,7 +24,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { override def toString: Ident = "Open" - def mkInductiveSubGoals(goal: Goal, h: Heaplet): Option[(Seq[(Expr, Goal)], SApp, SubstVar)] = { + def mkInductiveSubGoals(goal: Goal, h: Heaplet): Option[(Seq[(Expr, Goal)], SApp, SubstVar, Subst)] = { val pre = goal.pre val env = goal.env @@ -52,7 +52,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { // We can make the conditional without additional reading // TODO: Generalise this in the future val noGhosts = newGoals.forall { case (sel, _) => sel.vars.subsetOf(goal.programVars.toSet) } - if (noGhosts) Some((newGoals, h, fresh_sbst)) else None + if (noGhosts) Some((newGoals, h, fresh_sbst, sbst)) else None case _ => None } } @@ -62,9 +62,9 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { heaplet <- goal.pre.sigma.chunks s <- mkInductiveSubGoals(goal, heaplet) match { case None => None - case Some((selGoals, heaplet, fresh_subst)) => + case Some((selGoals, heaplet, fresh_subst, sbst)) => val (selectors, subGoals) = selGoals.unzip - val kont = BranchProducer(Some (heaplet, fresh_subst), selectors) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = BranchProducer(Some (heaplet), fresh_subst, sbst, selectors) >> HandleGuard(goal) >> ExtractHelper(goal) Some(RuleResult(subGoals, kont, this, goal)) } } yield s @@ -94,7 +94,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { suspendedCallGoal = Some(SuspendedCallGoal(goal.pre, goal.post, callePost, call, freshSub)) newGoal = goal.spawnChild(post = f.pre, gamma = newGamma, callGoal = suspendedCallGoal) } yield { - val kont: StmtProducer = IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = AbduceCallProducer(f) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } } From 68afbca837c166ec7bbf359e330edfd554efd5aa Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 03:56:22 +0800 Subject: [PATCH 054/211] Define translation from ProofRule to IR representation One pass to collect substitutions and convert to HTT-specific AST. Another pass to back-propagate the collected substitutions to earlier nodes. --- .../targets/htt/language/Expressions.scala | 16 +- .../targets/htt/logic/Sentences.scala | 13 +- .../targets/htt/translation/IR.scala | 238 ++++++++++++++++++ 3 files changed, 255 insertions(+), 12 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 0950d5df6..41c086d28 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.LanguageUtils.cardinalityPrefix -import org.tygus.suslik.certification.targets.htt.logic.Proof.{Subst, SubstVar} +import org.tygus.suslik.certification.targets.htt.translation.IR.{CSubst, CSubstVar} import org.tygus.suslik.logic.Specifications.selfCardVar object Expressions { @@ -48,7 +48,7 @@ object Expressions { collector(Seq.empty)(this) } - def subst(sigma: Subst): CExpr = this match { + def subst(sigma: CSubst): CExpr = this match { case v: CVar => sigma.get(v) match { case None => v @@ -98,7 +98,7 @@ object Expressions { case class CVar(name: String) extends CExpr { override def pp: String = if (name.startsWith(cardinalityPrefix)) name.drop(cardinalityPrefix.length) else name override val isCard: Boolean = name.startsWith(cardinalityPrefix) || name == selfCardVar.name - def substVar(sub: Subst): CVar = sub.get(this) match { + def substVar(sub: CSubst): CVar = sub.get(this) match { case Some(v@CVar(_)) => v case _ => this } @@ -139,14 +139,14 @@ object Expressions { case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" override def pp: String = s"$locPP :-> ${value.pp}" - override def subst(sigma: Subst): CPointsTo = + override def subst(sigma: CSubst): CPointsTo = CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) } case class CSApp(pred: String, var args: Seq[CExpr], card: CExpr) extends CExpr { override def pp: String = s"$pred ${args.map(arg => arg.pp).mkString(" ")}" - override def subst(sigma: Subst): CSApp = + override def subst(sigma: CSubst): CSApp = CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) val uniqueName: String = s"${pred}_${args.flatMap(_.vars).map(_.pp).mkString("")}_${card.pp}" @@ -155,8 +155,8 @@ object Expressions { } case class CSFormula(heapName: String, apps: Seq[CSApp], ptss: Seq[CPointsTo]) extends CExpr { - def unify(source: CSFormula): SubstVar = { - val initialMap: SubstVar = Map.empty + def unify(source: CSFormula): CSubstVar = { + val initialMap: CSubstVar = Map.empty val m1 = source.ptss.zip(ptss).foldLeft(initialMap) { case (acc, (p1, p2)) => acc ++ p1.vars.zip(p2.vars).filterNot(v => v._1 == v._2).toMap } @@ -177,7 +177,7 @@ object Expressions { s"$heapName = $ppHeap /\\ $appsStr" } - override def subst(sigma: Subst): CSFormula = { + override def subst(sigma: CSubst): CSFormula = { val apps1 = apps.map(_.subst(sigma)) val ptss1 = ptss.map(_.subst(sigma)) CSFormula(heapName, apps1, ptss1) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 5982ca8b4..8139f6653 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.{CFormals, CGamma, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.logic.Proof.Subst +import org.tygus.suslik.certification.targets.htt.translation.IR.CSubst object Sentences { case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { @@ -28,7 +28,7 @@ object Sentences { getIndent(depth) + builder.toString().replaceAll("\n", s"\n${getIndent(depth)}") } - def subst(sub: Subst): CAssertion = + def subst(sub: CSubst): CAssertion = CAssertion(phi.subst(sub), sigma.subst(sub)) val valueVars: Seq[CVar] = @@ -39,14 +39,14 @@ object Sentences { } case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { - def subst(sub: Subst): CInductiveClause = + def subst(sub: CSubst): CInductiveClause = CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) } case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends PrettyPrinting { val paramVars: Seq[CVar] = params.map(_._2) - def subst(sub: Subst): CInductivePredicate = + def subst(sub: CSubst): CInductivePredicate = CInductivePredicate( name, params.map(p => (p._1, p._2.substVar(sub))), @@ -75,6 +75,11 @@ object Sentences { val progVarsAlias = "vprogs" val ghostVarsAlias = "vghosts" + val existentials: Seq[CVar] = post.valueVars.diff(paramVars ++ ghostParamVars) + + def subst(subst: CSubst): CFunSpec = + CFunSpec(name, rType, params.map(p => (p._1, p._2.substVar(subst))), ghostParams.map(p => (p._1, p._2.substVar(subst))), pre.subst(subst), post.subst(subst)) + private def ppFormals(formals: CFormals): (String, String) = { val (typs, names) = formals.unzip val typsStr = typs.map(_.pp).mkString(" * ") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala new file mode 100644 index 000000000..7dfbb741d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -0,0 +1,238 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.ProofRule +import org.tygus.suslik.certification.targets.htt.language.CGamma +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CFunSpec, CInductiveClause, CInductivePredicate} +import org.tygus.suslik.certification.targets.htt.program.Statements._ +import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateAsn, translateExpr, translateParam, translateSApp, translateType, translateVar} +import org.tygus.suslik.language.Expressions.{Subst, SubstVar} +import org.tygus.suslik.language.Statements +import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} +import org.tygus.suslik.logic.Specifications.Goal + +object IR { + type Unfoldings = Map[CSApp, CInductiveClause] + type SAppNames = Map[CSApp, String] + type CSubst = Map[CVar, CExpr] + type CSubstVar = Map[CVar, CVar] + type PredicateEnv = Map[String, CInductivePredicate] + + case class CGoal(pre: CAssertion, + post: CAssertion, + gamma: CGamma, + programVars: Seq[CVar], + universalGhosts: Seq[CVar], + fname: String) { + val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) + def subst(sub: CSubst): CGoal = CGoal( + pre.subst(sub), + post.subst(sub), + gamma.map { case (v, t) => v.substVar(sub) -> t }, + programVars.map(_.substVar(sub)), + universalGhosts.map(_.substVar(sub)), + fname + ) + + def toFunspec: CFunSpec = { + val params = programVars.map(v => (gamma(v), v)) + val ghosts = universalGhosts.map(v => (gamma(v), v)) + CFunSpec(fname, CUnitType, params, ghosts, pre, post) + } + } + + case class Context( + unfoldings: Unfoldings, + sappNames: SAppNames, + subst: CSubst, + substVar: CSubstVar, + predicateEnv: PredicateEnv, + nestedContext: Option[NestedContext], + topLevelGoal: Option[CGoal], + ) + + val emptyContext: Context = Context(Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, None, None) + + case class NestedContext(funspec: CFunSpec, call: CCall, freshToActual: CSubst = Map.empty, companionToFresh: CSubstVar) { + def updateSubstitution(sigma: CSubst): NestedContext = { + this.copy(freshToActual = freshToActual.map { case (k, e) => k -> e.subst(sigma) } ++ sigma) + } + + def applySubstitution: NestedContext = { + val newPost = funspec.post.subst(freshToActual) + val newFunSpec = funspec.copy(post = newPost) + val newCall = call.copy(args = call.args.map(_.subst(freshToActual))) + this.copy(funspec = newFunSpec, call = newCall) + } + } + + abstract class Node { + val ctx : Context + val next : Seq[Node] + + def propagateContext: IR.Node = this match { + case n:IR.EmpRule => n + case n:IR.Open => n.copy(next = n.next.map(_.propagateContext)) + case n:IR.AbduceBranch =>n.copy(next = n.next.map(_.propagateContext)) + case n:IR.Read => + val next1 = n.next.head.propagateContext + n.copy(ctx = next1.ctx, next = Seq(next1)) + case n:IR.Call => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Free => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Write => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Malloc => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.PureSynthesis => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Close => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.AbduceCall => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Init => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) + } + } + + case class Open(app: CSApp, clauses: Seq[CInductiveClause], selectors: Seq[CExpr], next: Seq[Node], ctx: Context) extends Node + + case class Close(app: CSApp, selector: CExpr, asn: CAssertion, fresh_exist: CSubstVar, next: Seq[Node], ctx: Context) extends Node + + case class AbduceBranch(cond: CExpr, next: Seq[Node], ctx: Context) extends Node + + case class PureSynthesis(is_final: Boolean, next: Seq[Node], ctx: Context) extends Node + + case class Read(op: CLoad, next: Seq[Node], ctx: Context) extends Node + + case class Write(stmt: CStore, next: Seq[Node], ctx: Context) extends Node + + case class Free(stmt: CFree, next: Seq[Node], ctx: Context) extends Node + + case class Malloc(stmt: CMalloc, next: Seq[Node], ctx: Context) extends Node + + case class AbduceCall(new_vars: Map[CVar, HTTType], + f_pre: CAssertion, + calleePost: CAssertion, + call: CCall, + freshSub: CSubstVar, + freshToActual: CSubst, + next: Seq[Node], + ctx: Context + ) extends Node + + case class Call(call: CCall, next: Seq[Node], ctx: Context) extends Node + + case class EmpRule(ctx: Context) extends Node { + val next: Seq[Node] = Seq.empty + } + + case class Init(ctx: Context, next: Seq[Node]) extends Node + + private def translateSbst(sbst: Subst) = sbst.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} + private def translateSbstVar(sbst: SubstVar) = sbst.map{ case (k,v) => CVar(k.name) -> CVar(v.name)} + def translateGoal(goal: Goal): CGoal = { + val pre = translateAsn(goal.pre) + val post = translateAsn(goal.post) + val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } + val programVars = goal.programVars.map(translateVar) + val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(v => gamma(v) == CCardType) + CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) + } + + def fromRule(rule: ProofRule, ctx: IR.Context) : IR.Node = rule match { + case ProofRule.Init(goal, next) => + val cgoal = translateGoal(goal) + val ctx1 = ctx.copy(topLevelGoal = Some(cgoal)) + IR.Init(ctx1, Seq(fromRule(next, ctx1))) + case ProofRule.Open(sapp, fresh_vars, sbst, cases) => + val csapp = translateSApp(sapp) + val freshCVars = fresh_vars.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} + val csbst = translateSbst(sbst) + val pred = ctx.predicateEnv(sapp.pred).subst(freshCVars).subst(csbst) + val (selectors, next) = cases.map{ case (s, r) => (translateExpr(s), fromRule(r, ctx)) }.unzip + IR.Open(csapp, pred.clauses, selectors, next, ctx) + case ProofRule.Close(sapp, selector, asn, sbst, next) => + val csapp = translateSApp(sapp) + val cselector = translateExpr(selector) + val casn = translateAsn(asn) + val csbst = translateSbst(sbst) + val pred = ctx.predicateEnv(csapp.pred) + val cclause = pred.clauses.find(_.selector == cselector).get + val ex = cclause.existentials.map(_.subst(csbst)) + val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) + fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) + case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + IR.AbduceBranch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) + case ProofRule.PureSynthesis(is_final, sbst, next) => + val csbst = translateSbst(sbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + IR.PureSynthesis(is_final, Seq(fromRule(next, ctx1)), ctx1) + case ProofRule.SubstL(sbst, next) => + val csbst = translateSbst(sbst) + fromRule(next, ctx.copy(subst = ctx.subst ++ csbst)) + case ProofRule.SubstR(sbst, next) => + val csbst = translateSbst(sbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + fromRule(next, ctx1) + case ProofRule.Pick(sbst, next) => + val csbst = translateSbst(sbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + fromRule(next, ctx1) + case ProofRule.Read(ghosts, Load(to, tpe, from, offset), next) => + val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) + IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(next, ctx1)), ctx1) + case ProofRule.Write(Store(to, offset, e), next) => + IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(next, ctx)), ctx) + case ProofRule.Free(Statements.Free(v), size, next) => + IR.Free(CFree(CVar(v.name), size), Seq(fromRule(next, ctx)), ctx) + case ProofRule.Malloc(ghosts, Statements.Malloc(to, tpe, sz), next) => + val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) + IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(next, ctx1)), ctx1) + case ProofRule.Call(Statements.Call(fun, args, _), next) => + val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) + val ctx2 = ctx1.copy(nestedContext = None) + IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(next, ctx2)), ctx1) + case ProofRule.PickArg(sbst, next) => + val csbst = translateSbst(sbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + fromRule(next, ctx1) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma, next) => + val ghosts = f.pre.vars.toSeq.diff(f.params.map(_._1)).map(v => (v, gamma(v))) + val cfunspec = CFunSpec(f.name, translateType(f.rType), f.params.map(translateParam), ghosts.map(translateParam), translateAsn(f.pre), translateAsn(f.post)) + val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) + val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) + val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) + fromRule(next, ctx1) + case ProofRule.HeapUnifyPointer(sbst, next) => + val ctx1 = ctx.copy(subst = ctx.subst ++ translateSbst(sbst)) + fromRule(next, ctx1) + case ProofRule.EmpRule => IR.EmpRule(ctx) + // unused rules: + case ProofRule.HeapUnify(next) => fromRule(next, ctx) + case ProofRule.NilNotLval(_, next) => fromRule(next, ctx) + case ProofRule.CheckPost(next) => fromRule(next, ctx) + case ProofRule.WeakenPre(_, next) => fromRule(next, ctx) + case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) + case ProofRule.PickCard(next) => fromRule(next, ctx) + case ProofRule.FrameUnfold(h_pre, h_post, next) => fromRule(next, ctx) + } +} From e8c7d405934e4d019a54ff34a723f801bdce3870 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 03:59:15 +0800 Subject: [PATCH 055/211] Define translation from IR to HTT proof steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make the final AST representation granular and composable so that pretty-printing doesn’t have to be context-dependent. --- .../targets/htt/logic/Proof.scala | 180 ++++++--- .../targets/htt/logic/ProofProducers.scala | 157 -------- .../targets/htt/logic/ProofSteps.scala | 372 ------------------ .../htt/translation/ProofTranslation.scala | 283 +++++++------ .../targets/htt/translation/Translation.scala | 23 +- 5 files changed, 295 insertions(+), 720 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 9f9361a30..38b476fbf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -1,64 +1,136 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language.CGamma -import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.ProofSteps.{ProofStep, nestedDestructL} -import org.tygus.suslik.certification.targets.htt.logic.Sentences._ +import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} +import org.tygus.suslik.certification.targets.htt.language.Types.HTTType object Proof { - private var currCallId = 0 - def freshCallId: String = { currCallId += 1; s"call$currCallId" } - - type Unfoldings = Map[CSApp, CInductiveClause] - type Subst = Map[CVar, CExpr] - type SubstVar = Map[CVar, CVar] - type PredicateEnv = Map[String, CInductivePredicate] + abstract class Step { + val isNoop: Boolean = false + def pp: String + def >>(that: Step): Step = SeqComp(this, that) + def >>>(that: Step): Step = SeqCompAlt(this, that) + def simplify: Step = this match { + case SeqComp(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqComp(s1.simplify, s2.simplify) + case SeqCompAlt(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqCompAlt(s1.simplify, s2.simplify) + case Branch(branches) => Branch(branches.map(_.simplify)) + case _ => this + } + } - case class Proof(root: ProofStep, params: Seq[CVar]) { + case class Branch(branches: Seq[Step]) extends Step { + override val isNoop: Boolean = branches.forall(_.isNoop) + def pp: String = branches.map(_.pp).mkString(".\n") + } + case class SeqComp(s1: Step, s2: Step) extends Step { + override val isNoop: Boolean = s1.isNoop && s2.isNoop + def pp: String = s"${s1.pp}.\n${s2.pp}" + } + case class SeqCompAlt(s1: Step, s2: Step) extends Step { + override val isNoop: Boolean = s1.isNoop && s2.isNoop + def pp: String = s"${s1.pp};\n${s2.pp}" + } + case class MoveToCtx(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"move=>${items.map(_.pp).mkString(" ")}" + } + case class MoveToCtxDestruct(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"move=>${items.map(i => s"[${i.pp}]").mkString(" ")}" + } + case class MoveToCtxDestructFoldLeft(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"move=>${items.map(_.pp).reduceLeft[String]{ case (acc, el) => s"[$acc $el]"}}" + } + case class MoveToCtxDestructFoldRight(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"move=>${items.map(_.pp).reduceRight[String]{ case (el, acc) => s"[$el $acc]"}}" + } + case class MoveToGoal(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"move: ${items.map(_.pp).mkString(" ")}" + } + case class ElimExistential(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString("\n") + } + case object Sbst extends Step { + def pp: String = "subst" + } + case class Exists(items: Seq[CExpr]) extends Step { + override val isNoop: Boolean = items.isEmpty + def pp: String = s"exists ${items.map { + case h:CSFormula => s"(${h.ppHeap})" + case i => s"(${i.pp})" + }.mkString(", ")}" + } + case object Auto extends Step { + def pp: String = "sslauto" + } + case class UnfoldConstructor(idx: Int) extends Step { + def pp: String = s"unfold_constructor $idx" + } + case class Write(to: CVar, offset: Int = 0, e: CExpr) extends Step { def pp: String = { - val obligationTactic = s"Obligation Tactic := intro; move=>${nestedDestructL(params)}; ssl_program_simpl." - val nextObligation = "Next Obligation." - val body = root.pp - val qed = "Qed.\n" - List(obligationTactic, nextObligation, body, qed).mkString("\n") + val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" + s"ssl_write $ptr" } } - - case class CGoal(pre: CAssertion, - post: CAssertion, - gamma: CGamma, - programVars: Seq[CVar], - universalGhosts: Seq[CVar], - fname: String) { - val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) - def subst(sub: Subst): CGoal = CGoal( - pre.subst(sub), - post.subst(sub), - gamma.map { case (v, t) => v.substVar(sub) -> t }, - programVars.map(_.substVar(sub)), - universalGhosts.map(_.substVar(sub)), - fname - ) - - def toFunspec: CFunSpec = { - val params = programVars.map(v => (gamma(v), v)) - val ghosts = universalGhosts.map(v => (gamma(v), v)) - CFunSpec(fname, CUnitType, params, ghosts, pre, post) + case class WritePost(to: CVar, offset: Int = 0) extends Step { + def pp: String = { + val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" + s"ssl_write_post $ptr" } } - - case class CEnvironment(initialGoal: CGoal, - predicates: PredicateEnv, - ghostSubst: SubstVar = Map.empty, - subst: Subst = Map.empty, - unfoldings: Unfoldings = Map.empty) { - def copy(initialGoal: CGoal = this.initialGoal, - predicates: PredicateEnv = this.predicates, - ghostSubst: SubstVar = this.ghostSubst, - subst: Subst = this.subst, - unfoldings: Unfoldings = this.unfoldings, - ): CEnvironment = - CEnvironment(initialGoal, predicates, ghostSubst, subst, unfoldings) - } -} \ No newline at end of file + case class Read(to: CVar, from: CVar, offset: Int = 0) extends Step { + def pp: String = { + val ptr = if (offset == 0) from.pp else s"(${from.pp} .+ $offset)" + s"ssl_read $ptr" + } + } + case class Alloc(to: CVar, tpe: HTTType, sz: Int) extends Step { + def pp: String = s"ssl_alloc ${to.pp}" + } + case class Dealloc(v: CVar, offset: Int) extends Step { + def pp: String = { + val ptr = if (offset == 0) v.pp else s"(${v.pp} .+ $offset)" + s"ssl_dealloc $ptr" + } + } + case object Open extends Step { + def pp: String = "ssl_open" + } + case class OpenPost(app: CSApp) extends Step { + def pp: String = s"ssl_open_post ${app.hypName}" + } + case class CallPre(heap: CSFormula) extends Step { + def pp: String = s"ssl_call_pre (${heap.ppHeap})" + } + case class Call(args: Seq[CExpr], ghosts: Seq[CVar]) extends Step { + def pp: String = s"ssl_call (${ghosts.map(_.pp).mkString(", ")})" + } + case object StoreValid extends Step { + def pp: String = "store_valid" + } + case object GhostElimPre extends Step { + def pp: String = "ssl_ghostelim_pre" + } + case object GhostElimPost extends Step { + def pp: String = "ssl_ghostelim_post" + } + case object AbduceBranch extends Step { + def pp: String = "ssl_abduce_branch" + } + case object Emp extends Step { + def pp: String = "ssl_emp" + } + case object Noop extends Step { + override val isNoop: Boolean = true + def pp: String = "" + } + case class StartProof(params: Seq[CVar]) extends Step { + def pp: String = s"Obligation Tactic := intro; ${MoveToCtxDestructFoldLeft(params).pp}; ssl_program_simpl.\nNext Obligation" + } + case object EndProof extends Step { + def pp: String = "Qed.\n" + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala deleted file mode 100644 index 9bf91d159..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofProducers.scala +++ /dev/null @@ -1,157 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.logic - -import org.tygus.suslik.certification.targets.htt.logic.Proof.CEnvironment -import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ - -object ProofProducers { - type KontResult = (ProofStep, CEnvironment) - type Kont = Seq[KontResult] => KontResult - - trait Branching - - sealed abstract class ProofProducer { - val arity: Int - val fn: Kont - - def apply(children: Seq[KontResult]): KontResult = { - assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") - fn(children) - } - - def >>(p: ProofProducer): ProofProducer = { - ChainedProofProducer(this, p) - } - - def partApply(s: ProofStep): ProofProducer = { - PartiallyAppliedProofProducer(this, s) - } - - def simplify: ProofProducer = this match { - case ChainedProofProducer(p1, IdProofProducer) => p1.simplify - case ChainedProofProducer(IdProofProducer, p2) => p2.simplify - case ChainedProofProducer(_, p2@ConstProofProducer(_, _)) => p2.simplify - case _ => this - } - } - - case class ChainedProofProducer(p1: ProofProducer, p2: ProofProducer) extends ProofProducer { - val arity: Int = p1.arity + p2.arity - 1 - val fn: Kont = res => { - val (res1, res2) = res.splitAt(p1.arity) - val r = p1.fn(res1) - p2.fn(r +: res2) - } - } - - case class PartiallyAppliedProofProducer(p: ProofProducer, s: ProofStep) extends ProofProducer { - val arity: Int = p.arity - 1 - val fn: Kont = res => { - p.apply((s, res.head._2) +: res) - } - } - - case object IdProofProducer extends ProofProducer { - val arity: Int = 1 - val fn: Kont = _.head - } - - case class ConstProofProducer(step: ProofStep, env: CEnvironment) extends ProofProducer { - val arity: Int = 0 - val fn: Kont = _ => (step, env) - } - - case class PrependProofProducer(s: ProofStep) extends ProofProducer { - val arity: Int = 1 - val fn: Kont = res => { - val (step, env) = res.head - (SeqCompStep(s.refreshGhostVars(env.ghostSubst), step).simplify, env) - } - } - - case class AppendProofProducer(s: ProofStep) extends ProofProducer { - val arity: Int = 1 - val fn: Kont = res => { - val (step, env) = res.head - (SeqCompStep(step, s.refreshGhostVars(env.ghostSubst)).simplify, env) - } - } - - /** - * - * @param openPostSteps the steps to perform upon entering each branch - * @param branchEnv the state of the environment at the time of branching - */ - case class BranchProofProducer(openPostSteps: Seq[OpenPostStep], branchEnv: CEnvironment) extends ProofProducer with Branching { - val arity: Int = openPostSteps.length - val fn: Kont = res => - if (res.length == 1) res.head else { - // For each certified branch, prepend the step that prepares the branch's execution - val condBranches = res.zip(openPostSteps).reverse.map { case ((s, env), openPost) => - SeqCompStep(openPost.refreshGhostVars(env.ghostSubst), s) - } - val ctail = condBranches.tail - val finalBranch = condBranches.head - - // Compose the branch certification code in order, and prepend the Open step at the very beginning. - // Discard envs propagated from child branches, and return the env that was preserved at the time of branching. - (SeqCompStep(OpenStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), branchEnv) - } - } - - case class GuardedProofProducer(branchEnv: CEnvironment) extends ProofProducer with Branching { - val arity: Int = 2 - val fn: Kont = res => - if (res.length == 1) res.head else { - val condBranches = res.reverse.map(_._1) - val ctail = condBranches.tail - val finalBranch = condBranches.head - (SeqCompStep(AbduceBranchStep, ctail.foldLeft(finalBranch) { case (eb, tb) => SeqCompStep(tb, eb) }), branchEnv) - } - } - - /** - * One step in the CPS traversal of multiple branches. Partially applies the current branch's result to the - * branch producer (`bp`), and then initiates the certification of the next branch. A BranchProofProducer of arity N - * will have been fully applied after the N-th invocation of this FoldProofProducer. - * @param op a procedure that generates a KontResult - * @param item The start of the next branch to traverse - * @param bp the branch producer or its partially applied form - * @tparam T the unit of traversal in the algorithm - */ - case class FoldProofProducer[T](op: (T, ProofProducer) => KontResult, item: T, bp: ProofProducer) extends ProofProducer { - val arity: Int = 1 - val fn: Kont = res => { - val step = res.head._1 - - // Partially apply a produced step to the BranchProducer of the downstream `bp` - @scala.annotation.tailrec - def isBase(curr: ProofProducer): Boolean = curr match { - case PartiallyAppliedProofProducer(p, _) => isBase(p) - case _: Branching => true - case _ => false - } - - // Find the BranchProducer in the `bp`, and then partially apply `step` - def update(curr: ProofProducer): (ProofProducer, Boolean) = curr match { - case FoldProofProducer(op, item, bp) => - val (bp1, modified) = update(bp) - (FoldProofProducer(op, item, bp1), modified) - case ChainedProofProducer(p1, p2) => - val (p11, modified1) = update(p1) - if (modified1) { - (ChainedProofProducer(p11, p2), modified1) - } else { - val (p21, modified2) = update(p2) - (ChainedProofProducer(p11, p21), modified2) - } - case _: PartiallyAppliedProofProducer | _: Branching if isBase(curr) => - (curr.partApply(step), true) - case _ => - (curr, false) - } - - // Update the `bp` with the new result and step into the next branch - op(item, update(bp)._1) - } - } -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala deleted file mode 100644 index 14a4ec196..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofSteps.scala +++ /dev/null @@ -1,372 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.logic - -import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.Proof._ -import org.tygus.suslik.certification.targets.htt.logic.Sentences._ - -object ProofSteps { - - // Generate right-associative destructing pattern for a tuple of Coq expressions - def nestedDestructR(items: Seq[CExpr]): String = items match { - case Seq(e1, e2, rest @ _*) => - s"[${e1.pp} ${nestedDestructR(e2 +: rest)}]" - case Seq(e, _*) => - e.pp - case Seq() => - "" - } - - // Generate left-associative destructing pattern for a tuple of Coq expressions - def nestedDestructL(items: Seq[CExpr]): String = { - def visit(items: Seq[CExpr]): String = items match { - case Seq(e1, e2, rest @ _*) => - s"[${visit(e2 +: rest)} ${e1.pp}]" - case Seq(e, _*) => - e.pp - case Seq() => - "" - } - visit(items.reverse) - } - - sealed abstract class ProofStep { - def pp: String = "" - - /** - * Retroactively refresh any ghost variables that were instantiated after this proof step was first generated - * - * @param sbst the certification environment - * @return the new proof step - */ - def refreshGhostVars(sbst: SubstVar): ProofStep = this - - /** - * Move a sequence of existential terms from goal to context - * @param builder the string builder - * @param ex the existential terms - * @param nested whether or not to format the existentials as tuples - */ - protected def elimExistentials(builder: StringBuilder, ex: Seq[CExpr], nested: Boolean = false): Unit = { - if (ex.nonEmpty) { - if (nested) { - builder.append(s"move=>${nestedDestructL(ex)}.\n") - } else { - ex.map(_.pp).grouped(5).foreach { s => - builder.append(s"ex_elim ${s.mkString(" ")}.\n") - } - } - } - } - - /** - * Given a precondition assertion, move all terms to the context - * @param builder the string builder - * @param asn the precondition assertion - * @param uniqueName a unique identifier - */ - protected def initPre(builder: StringBuilder, asn: CAssertion, uniqueName: String): Unit = { - val phi = asn.phi - val sigma = asn.sigma - val phiName = s"phi_$uniqueName" - val sigmaName = s"sigma_$uniqueName" - - // move pure part to context - if (!phi.isTrivial) { - val numConjuncts = phi.conjuncts.length - val hyps = if (numConjuncts == 0) s"[$phiName]" else (0 to numConjuncts).map(i => s"[$phiName$i]").mkString("") - builder.append(s"move=>$hyps.\n") - } - - // move spatial part to context, and then substitute where appropriate - builder.append(s"move=>[$sigmaName].\n") - builder.append(s"subst.\n") - - // move predicate apps to context, if any - if (sigma.apps.nonEmpty) { - val appNames = sigma.apps.map(app => CVar(app.hypName)) - val hApps = nestedDestructR(appNames) - builder.append(s"move=>$hApps.\n") - } - } - - /** - * Execute the correct sequence of existential eliminations and constructor unfoldings needed to - * transform the goal into a given post condition assertion - * @param builder the string builder - * @param asn an assertion (or part of an assertion) in the post condition - * @param ex a sequence of existentials to eliminate - * @param unfoldings a map from predicate applications to their unfolded state - */ - protected def solvePost(builder: StringBuilder, asn: CAssertion, ex: Seq[CExpr], unfoldings: Unfoldings): Unit = { - // Eliminate value existentials - if (ex.nonEmpty) { - builder.append(s"exists ${ex.map(v => s"(${v.pp})").mkString(", ")};\n") - } - - /** - * Existentials for the current assertion need to be provided eagerly, so look ahead to find out - * the maximally unfolded, "flattened" form of the heap - * @param app the predicate app to flatten - * @return the points-to's and pred apps in the unfolded heap; any pred apps that remain have already been - * established as facts earlier on, so there is no need to unfold further - */ - def toUnfoldedHeap(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { - case Some(a) => - val sigma = a.asn.sigma - val (ptss, apps) = sigma.apps.map(toUnfoldedHeap).unzip - (sigma.ptss ++ ptss.flatten, apps.flatten) - case None => - (Seq.empty, Seq(app)) - } - - // Eliminate heap existentials (one for each predicate application in the assertion) - val apps = asn.sigma.apps - for (app <- apps) { - val (unfoldedPtss, unfoldedApps) = toUnfoldedHeap(app) - val h = CSFormula("", unfoldedApps, unfoldedPtss) - builder.append(s"exists (${h.ppHeap});\n") - } - - // Solve everything except the predicate applications - builder.append("sslauto.\n") - - // For each predicate application, unfold the correct constructor and recursively solve its expanded form - for { - app <- apps - // If `app` isn't found in the unfoldings, its truth was already established earlier on - c <- unfoldings.get(app) - } { - builder.append(s"unfold_constructor ${c.idx};\n") - solvePost(builder, c.asn, c.existentials, unfoldings) - } - } - - def vars: Set[CVar] = { - def collector(acc: Set[CVar])(st: ProofStep): Set[CVar] = st match { - case WriteStep(to, _, e, _) => - acc ++ to.vars ++ e.vars - case ReadStep(to, from, _) => - acc ++ to.vars ++ from.vars - case AllocStep(to, _, _) => - acc ++ to.vars - case SeqCompStep(s1,s2) => - val acc1 = collector(acc)(s1) - collector(acc1)(s2) - case CallStep(goal, _) => - acc ++ goal.programVars - case _ => - acc - } - - collector(Set.empty)(this) - } - } - - /** - * Sequentially compose two proof steps - * @param s1 the first step - * @param s2 the second step - */ - case class SeqCompStep(s1: ProofStep, s2: ProofStep) extends ProofStep { - override def pp: String = s"${s1.pp}${s2.pp}" - - /** - * The synthesis removes some extraneous program statements that are unused in the final result, but - * the CertTree retains them as nodes. So, we remove them from our proof script here. - * @return A simplified proof step - */ - def simplify: ProofStep = { - (s1, s2) match { - case (ErrorStep, _) => s2 - case (_, ErrorStep) => s1 - case (ReadStep(to, _, _), _) => simplifyBinding(to) -// case (WriteStep(to), _) => simplifyBinding(to) -// case (AllocStep(to, _, _), _) => simplifyBinding(to) - case _ => this - } - } - - def simplifyBinding(newvar: CVar): ProofStep = { - val used = s2.vars - if (used.contains(newvar)) { - this - } else s2 // Do not generate bindings for unused variables - } - } - - /** - * Perform a write - * @param to the variable to write to - * @param offset the variable offset - * @param e the expression to write - * @param frame whether or not to frame out the heaplet after writing - */ - case class WriteStep(to: CVar, offset: Int, e: CExpr, frame: Boolean = true) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): WriteStep = - WriteStep(to.substVar(sbst), offset, e.subst(sbst), frame) - - override def pp: String = { - val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" - val writeStep = s"ssl_write $ptr.\n" - - // SSL's `Write` rule does an implicit frame under normal circumstances, but not during a call synthesis - val writePostStep = if (frame) s"ssl_write_post $ptr.\n" else "" - - writeStep + writePostStep - } - } - - /** - * Perform a read - * @param to the dst variable - * @param from the src variable - * @param offset the src offset - */ - case class ReadStep(to: CVar, from: CVar, offset: Int) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): ReadStep = - ReadStep(to.substVar(sbst), from.substVar(sbst), offset) - - override def pp: String = { - val ptr = if (offset == 0) from.pp else s"(${from.pp} .+ $offset)" - s"ssl_read $ptr.\n" - } - } - - /** - * Allocate a memory block of size `sz` and bind it to a variable - * @param to the dst variable - * @param tpe the alloc type - * @param sz the size allocated - */ - case class AllocStep(to: CVar, tpe: HTTType, sz: Int) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): AllocStep = - AllocStep(to.substVar(sbst), tpe, sz) - - override def pp: String = s"ssl_alloc ${to.pp}.\n" - } - - /** - * Free a memory block of size `sz` - * @param size the size of the block to free - */ - case class FreeStep(v: CVar, size: Int) extends ProofStep { - override def pp: String = { - // In HTT, each location offset needs to be freed individually - val deallocStmts = (0 until size).map(i => s"ssl_dealloc (${v.pp}${if (i == 0) "" else s" .+ $i"}).") - s"${deallocStmts.mkString("\n")}\n" - } - } - - /** - * Execute the Open rule - */ - case object OpenStep extends ProofStep { - override def pp: String = "ssl_open.\n" - } - - /** - * Perform clean-up tasks at the start of each branch generated by the Open rule - * @param app the predicate application used in the Open rule - * @param pre the precondition for the predicate's constructor that was used for this branch of the Open rule - * @param preEx the value existentials for this precondition - */ - case class OpenPostStep(app: CSApp, pre: CAssertion, preEx: Seq[CExpr]) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): OpenPostStep = OpenPostStep( - app.subst(sbst), - pre.subst(sbst), - preEx.map(v => v.subst(sbst)) - ) - - override def pp: String = { - val builder = new StringBuilder() - builder.append(s"ssl_open_post ${app.hypName}.\n") - - elimExistentials(builder, preEx) - elimExistentials(builder, pre.heapVars) - initPre(builder, pre, app.uniqueName) - - builder.toString() - } - } - - /** - * Call a function - * @param goal the goal of the call synthesis - * @param callId a unique call identifier - */ - case class CallStep(goal: CGoal, callId: String) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): CallStep = - CallStep(goal.subst(sbst), callId) - - override def pp: String = { - val builder = new StringBuilder() - - // Move the part of the heap relevant to the call abduction to the beginning - builder.append(s"ssl_call_pre (${goal.pre.sigma.ppHeap}).\n") - - // Provide the necessary existentials so that the precondition of the call goal is met, - // and then execute the call - builder.append(s"ssl_call (${goal.universalGhosts.map(_.pp).mkString(", ")}).\n") - solvePost(builder, goal.pre, Seq.empty, Map.empty) - - builder.append(s"move=>h_$callId.\n") - - // The postcondition of the call abduction becomes the precondition of the companion - elimExistentials(builder, goal.existentials) - elimExistentials(builder, goal.post.heapVars) - initPre(builder, goal.post, callId) - - // Store validity hypotheses in context - builder.append("store_valid.\n") - - builder.toString() - } - } - - /** - * Eliminate the program's ghost variables and move them to the proof context - * @param goal the goal - */ - case class GhostElimStep(goal: CGoal) extends ProofStep { - override def refreshGhostVars(sbst: SubstVar): GhostElimStep = - GhostElimStep(goal.subst(sbst)) - - override def pp: String = { - val builder = new StringBuilder() - - // Pull out any precondition ghosts and move precondition heap to the context - builder.append("ssl_ghostelim_pre.\n") - - elimExistentials(builder, goal.universalGhosts, nested = true) - elimExistentials(builder, goal.pre.heapVars) - initPre(builder, goal.pre, "self") - - // store heap validity assertions - builder.append("ssl_ghostelim_post.\n") - - builder.toString() - } - } - - case object AbduceBranchStep extends ProofStep { - override def pp: String = "ssl_abduce_branch.\n" - } - - /** - * At the end of a branch, transform the goal into the funspec's post-condition - * @param post the post-condition - * @param postEx the post-condition's value existentials - * @param unfoldings a map from predicate applications to their unfolded state - */ - case class EmpStep(post: CAssertion, postEx: Seq[CExpr], unfoldings: Unfoldings) extends ProofStep { - override def pp: String = { - val builder = new StringBuilder() - builder.append("ssl_emp;\n") - solvePost(builder, post, postEx, unfoldings) - builder.toString() - } - } - - case object ErrorStep extends ProofStep -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 0eb89864e..5af2d6d17 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,141 +1,180 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar -import org.tygus.suslik.certification.targets.htt.logic.Proof._ -import org.tygus.suslik.certification.targets.htt.logic.ProofProducers._ -import org.tygus.suslik.certification.targets.htt.logic.ProofSteps._ -import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductiveClause -import org.tygus.suslik.certification.targets.htt.translation.Translation._ -import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.InductiveClause -import org.tygus.suslik.synthesis._ -import org.tygus.suslik.synthesis.rules.LogicalRules.Inconsistency +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.Sentences._ +import org.tygus.suslik.certification.targets.htt.program.Statements._ object ProofTranslation { + def translate(ir: IR.Node): Proof.Step = irToProofSteps(ir) - private case class TraversalItem(node: CertTree.Node, cenv: CEnvironment) + def irToProofSteps(node: IR.Node) : Proof.Step = { + def elimExistentials(ex: Seq[CExpr], nested: Boolean = false): Proof.Step = { + if (ex.nonEmpty) { + if (nested) Proof.MoveToCtxDestructFoldLeft(ex) else Proof.ElimExistential(ex) + } else Proof.Noop + } - def translate(node: CertTree.Node, cenv: CEnvironment): Proof = { - val initialGoal = translateGoal(node.goal) - val traversalItem = TraversalItem(node, cenv) - val initialKont = PrependProofProducer(GhostElimStep(initialGoal)) - val (proofBody, _) = traverseProof(traversalItem, initialKont) - Proof(proofBody, initialGoal.programVars) - } + def initPre(asn: CAssertion, uniqueName: String): Proof.Step = { + val phi = asn.phi + val sigma = asn.sigma + + // move pure part to context + val pureToCtx = if (!phi.isTrivial) { + val hyps = if (phi.conjuncts.isEmpty) Seq(CVar(s"phi_$uniqueName")) else (0 to phi.conjuncts.length).map(i => CVar(s"phi_$uniqueName$i")) + Proof.MoveToCtxDestruct(hyps) + } else Proof.Noop + + // move spatial part to context, and then substitute where appropriate + val spatialToCtx = Proof.MoveToCtxDestruct(Seq(CVar(s"sigma_$uniqueName"))) >> Proof.Sbst - def traverseProof(item: TraversalItem, kont: ProofProducer): KontResult = { - def translateOperation(s: Statement, cenv: CEnvironment): KontResult = s match { - case Skip => - val goal = cenv.initialGoal.subst(cenv.ghostSubst) - val post = goal.post.subst(cenv.subst) - val postEx = goal.existentials.map(_.subst(cenv.subst)) - val unfoldings = cenv.unfoldings.map { case (app, c) => app.subst(cenv.subst) -> c.subst(cenv.subst) } - (EmpStep(post, postEx, unfoldings), cenv) - case Load(to, _, from, off) => - (ReadStep(translateVar(to), translateVar(from), off), cenv) - case Store(to, offset, e) => - val frame = item.node.goal.callGoal.isEmpty - (WriteStep(translateVar(to), offset, translateExpr(e), frame), cenv) - case Malloc(to, tpe, sz) => - (AllocStep(translateVar(to), translateType(tpe), sz), cenv) - case Free(v) => - val block = item.node.footprint.pre.sigma.blocks.find(_.loc == v) - assert(block.nonEmpty) - (FreeStep(CVar(v.name), block.get.sz), cenv) - case Call(_, _, _) => - assert(item.node.goal.callGoal.isDefined) - val callGoal = item.node.goal.callGoal.get - - // create call step - val companionToFresh = callGoal.companionToFresh.map{ case (k, v) => translateVar(k) -> translateVar(v)} - val freshToActual = callGoal.freshToActual.map{ case (k, v) => translateVar(k) -> translateExpr(v)} - val cgoal = cenv.initialGoal.subst(companionToFresh).subst(freshToActual) - - (CallStep(cgoal, freshCallId), cenv) - case Error => - (ErrorStep, cenv) - case _ => throw TranslationException("Operation not supported") + // move predicate apps to context, if any + val sappToCtx = if (sigma.apps.nonEmpty) { + val appNames = sigma.apps.map(app => CVar(app.hypName)) + Proof.MoveToCtxDestructFoldRight(appNames) + } else Proof.Noop + + pureToCtx >> spatialToCtx >> sappToCtx } - def translateProducer(stmtProducer: StmtProducer, cenv: CEnvironment): (ProofProducer, CEnvironment) = { - stmtProducer match { - case ChainedProducer(p1, p2) => - val (k1, cenv1) = translateProducer(p1, cenv) - val (k2, cenv2) = translateProducer(p2, cenv1) - (k1 >> k2, cenv2) - case PartiallyAppliedProducer(p, _) => - translateProducer(p, cenv) - case SubstProducer(subst) => - val csub = subst.map { case (v, e) => translateVar(v) -> translateExpr(e).subst(cenv.ghostSubst) } - val cenv1 = cenv.copy(subst = cenv.subst ++ csub) - (IdProofProducer, cenv1) - case GhostSubstProducer(ghostSubst) => - val newGhostSubst = ghostSubst.map { case (v, e) => translateVar(v) -> translateVar(e) } - val newSubst = cenv.subst.map { case (v, e) => v.substVar(newGhostSubst) -> e.subst(newGhostSubst)} - val newUnfoldings = cenv.unfoldings.map { case (app, e) => app.subst(newGhostSubst) -> e.subst(newGhostSubst) } - val cenv1 = cenv.copy(subst = newSubst, ghostSubst = cenv.ghostSubst ++ newGhostSubst, unfoldings = newUnfoldings) - (IdProofProducer, cenv1) - case UnfoldProducer(app, selector, asn, substEx) => - val cselector = translateExpr(selector) - val capp = translateSApp(app) - val casn = translateAsn(asn).subst(cenv.ghostSubst) - - // get clause with substitutions - val predicate = cenv.predicates(app.pred) - val cclause = predicate.clauses.find(_.selector == cselector).get - val csub = substEx.map { case (k, v) => CVar(k.name) -> translateExpr(v)} - val ex = cclause.existentials.map(_.subst(csub)) - val actualClause = CInductiveClause(app.pred, cclause.idx, cselector, casn, ex) - val cenv1 = cenv.copy(unfoldings = cenv.unfoldings ++ Map(capp -> actualClause)) - (IdProofProducer, cenv1) - case ConstProducer(s) => - val (step, cenv1) = translateOperation(s, cenv) - (ConstProofProducer(step, cenv1), cenv1) - case PrependProducer(s) => - val (step, cenv1) = translateOperation(s, cenv) - (PrependProofProducer(step), cenv1) - case BranchProducer(_, _) => - val sapp = translateSApp(item.node.footprint.pre.sigma.apps.head) - val pred = cenv.predicates(sapp.pred) - val subgoals = item.node.children.map(n => translateGoal(n.goal)) - val initialSub: SubstVar = Map.empty - val sub = pred.clauses.zip(subgoals).foldLeft(initialSub){ case (acc, (c, g)) => acc ++ g.pre.sigma.unify(c.asn.sigma) } - val actualPred = pred.subst(sub) - val openPostSteps = actualPred.clauses.map(c => OpenPostStep(sapp, c.asn, c.existentials)) - (BranchProofProducer(openPostSteps, cenv), cenv) - case GuardedProducer(_, _) => - (GuardedProofProducer(cenv), cenv) - case _ => - (IdProofProducer, cenv) + def solvePost(asn: CAssertion, ex: Seq[CExpr], unfoldings: IR.Unfoldings): Proof.Step = { + val valueExElim = Proof.Exists(ex) + + /** + * Existentials for the current assertion need to be provided eagerly, so look ahead to find out + * the maximally unfolded, "flattened" form of the heap + * + * @param app the predicate app to flatten + * @return the points-to's and pred apps in the unfolded heap; any pred apps that remain have already been + * established as facts earlier on, so there is no need to unfold further + */ + def toUnfoldedHeap(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { + case Some(a) => + val sigma = a.asn.sigma + val (ptss, apps) = sigma.apps.map(toUnfoldedHeap).unzip + (sigma.ptss ++ ptss.flatten, apps.flatten) + case None => + (Seq.empty, Seq(app)) } - } - def generateNextItems(p: ProofProducer, cenv: CEnvironment): Seq[TraversalItem] = { - item.node.children.map(node => TraversalItem(node, cenv)) + // Eliminate heap existentials (one for each predicate application in the assertion) + val apps = asn.sigma.apps + val heapExElim = for (app <- apps) yield { + val (unfoldedPtss, unfoldedApps) = toUnfoldedHeap(app) + Proof.Exists(Seq(CSFormula("", unfoldedApps, unfoldedPtss))) + } + + // For each predicate application, unfold the correct constructor and recursively solve its expanded form + val rest = for { + app <- apps + // If `app` isn't found in the unfoldings, its truth was already established earlier on + c <- unfoldings.get(app) + } yield Proof.UnfoldConstructor(c.idx) >>> solvePost(c.asn, c.existentials, unfoldings) + + val p = valueExElim >>> heapExElim.foldLeft[Proof.Step](Proof.Noop)(_ >>> _) >>> Proof.Auto >> rest.foldLeft[Proof.Step](Proof.Noop)(_ >> _) + p } - // If at a branching point, queue up the traversal of each branch as nested calls of `traverseProof` in the continuation - def updateProducerPost(nextItems: Seq[TraversalItem], nextKont: ProofProducer, cenv: CEnvironment): ProofProducer = nextKont match { - case _: Branching => - nextItems.tail.foldLeft(nextKont >> kont) { - case (foldedP, item) => FoldProofProducer(traverseProof, item, foldedP) + var currCallId = 0 + def freshCallId: String = { currCallId += 1; s"call$currCallId" } + + def visit(node: IR.Node): Proof.Step = node match { + case IR.Init(ctx, next) => + val goal = ctx.topLevelGoal.get.subst(ctx.substVar) + Proof.StartProof(goal.programVars) >> + // Pull out any precondition ghosts and move precondition heap to the context + Proof.GhostElimPre >> + Proof.MoveToCtxDestructFoldLeft(goal.universalGhosts) >> + Proof.ElimExistential(goal.pre.heapVars) >> + initPre(goal.pre, "self") >> + // store heap validity assertions + Proof.GhostElimPost >> + visit(next.head) + case IR.EmpRule(ctx) => + val topLevelGoal = ctx.topLevelGoal.get + val post = topLevelGoal.post.subst(ctx.subst).subst(ctx.substVar) + val existentials = topLevelGoal.existentials.map(_.substVar(ctx.substVar).subst(ctx.subst)) + val unfoldings = ctx.unfoldings.map { case (app, e) => app.subst(ctx.substVar).subst(ctx.subst) -> e.subst(ctx.substVar).subst(ctx.subst) } + Proof.Emp >>> solvePost(post, existentials, unfoldings) + case IR.AbduceCall(new_vars, f_pre, calleePost, call, freshSub, freshToActual, next, ctx) => + visit(next.head) + case IR.Call(CCall(_, args), next, ctx) => + val callId = freshCallId + val nestedContext = ctx.nestedContext.get + val f = ctx.nestedContext.get.funspec.subst(ctx.substVar).subst(nestedContext.freshToActual).subst(ctx.substVar) + // Move the part of the heap relevant to the call abduction to the beginning + Proof.CallPre(f.pre.sigma) >> + // Provide the necessary existentials so that the precondition of the call goal is met, + // and then execute the call + Proof.Call(args, f.ghostParamVars.filterNot(_.isCard)) >> + solvePost(f.pre, Seq.empty, Map.empty) >> + Proof.MoveToCtx(Seq(CVar(s"h_$callId"))) >> + // The postcondition of the call abduction becomes the precondition of the companion + elimExistentials(f.existentials) >> + elimExistentials(f.post.heapVars) >> + initPre(f.post, callId) >> + // Store validity hypotheses in context + Proof.StoreValid >> + visit(next.head) + case IR.Free(CFree(v, sz), next, _) => + (0 until sz).map(o => Proof.Dealloc(v, o)).reduceLeft[Proof.Step](_ >> _) >> visit(next.head) + case IR.Read(CLoad(to, _, from, offset), next, ctx) => + Proof.Read(to.substVar(ctx.substVar), from.substVar(ctx.substVar), offset) >> visit(next.head) + case IR.Write(CStore(to, offset, e), next, ctx) => + val step = Proof.Write(to.substVar(ctx.substVar), offset, e) + // SSL's `Write` rule does an implicit frame under normal circumstances, but not during a call synthesis + val step1 = if (ctx.nestedContext.isDefined) step else step >> Proof.WritePost(to, offset) + step1 >> visit(next.head) + case IR.Malloc(CMalloc(to, tpe, sz), next, ctx) => + Proof.Alloc(to.substVar(ctx.substVar), tpe, sz) >> visit(next.head) + case IR.PureSynthesis(_, next, _) => + visit(next.head) + case IR.Close(_, _, _, _, next, _) => + visit(next.head) + case IR.AbduceBranch(_, next, _) => + Proof.AbduceBranch >> Proof.Branch(next.map(visit)) + case IR.Open(app, clauses, selectors, next, ctx) => + val branchSteps = next.zip(clauses).map { case (n, c) => + val c1 = c.subst(n.ctx.substVar) + Proof.OpenPost(app.subst(ctx.substVar)) >> + elimExistentials(c1.existentials) >> + elimExistentials(c1.asn.heapVars) >> + initPre(c1.asn, app.uniqueName) >> + visit(n) } - case _ => - nextKont >> kont + Proof.Open >> Proof.Branch(branchSteps) } - val (p0, cenv1) = translateProducer(item.node.kont, item.cenv) - val p = p0.simplify - val nextItems = generateNextItems(p, cenv1) - val nextKont = updateProducerPost(nextItems, p, cenv1).simplify - - nextItems.headOption match { - case Some(childHead) => - traverseProof(childHead, nextKont) - case None => - if (item.node.rule == Inconsistency) {} - nextKont(Nil) + pruneUnusedReads(visit(node).simplify) >> Proof.EndProof + } + + def pruneUnusedReads(step: Proof.Step): Proof.Step = { + def visit(step: Proof.Step): (Proof.Step, Set[CVar]) = step match { + case Proof.SeqComp(s1, s2) => + val (s2New, s2Used) = visit(s2) + val (s1New, s1Used) = visit(s1) + s1 match { + case Proof.Read(to, _, _) if !s2Used.contains(to) => (s2New, s2Used) + case _ => (s1New >> s2New, s1Used ++ s2Used) + } + case Proof.SeqCompAlt(s1, s2) => + val (s2New, s2Used) = visit(s2) + val (s1New, s1Used) = visit(s1) + s1 match { + case Proof.Read(to, _, _) if !s2Used.contains(to) => (s2New, s2Used) + case _ => (s1New >>> s2New, s1Used ++ s2Used) + } + case Proof.Branch(branches) => + val (branchesNew, branchesUsed) = branches.map(visit).unzip + (Proof.Branch(branchesNew), branchesUsed.reduce(_ ++ _)) + case Proof.Read(to, from, offset) => (step, Set(to, from)) + case Proof.Write(to, offset, e) => (step, Set(to) ++ e.vars.toSet) + case Proof.WritePost(to, offset) => (step, Set(to)) + case Proof.Alloc(to, tpe, sz) => (step, Set(to)) + case Proof.Dealloc(v, offset) => (step, Set(v)) + case Proof.Call(args, _) => (step, args.flatMap(_.vars).toSet) + case _ => (step, Set.empty) } + visit(step)._1 } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 2a89e689f..e8c1f21de 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -1,15 +1,16 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.{CertTree, ProofRule} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.logic.Proof._ +import org.tygus.suslik.certification.targets.htt.logic.Proof import org.tygus.suslik.certification.targets.htt.logic.Sentences._ +import org.tygus.suslik.certification.targets.htt.translation.IR.translateGoal import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.language._ -import org.tygus.suslik.logic.Specifications.{Assertion, Goal} +import org.tygus.suslik.logic.Specifications.Assertion import org.tygus.suslik.logic._ object Translation { @@ -23,15 +24,16 @@ object Translation { * @return the inductive predicates, fun spec, proof, and program translated to HTT */ def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): - (Map[String, CInductivePredicate], CFunSpec, Proof, CProcedure) = { + (Map[String, CInductivePredicate], CFunSpec, Proof.Step, CProcedure) = { val cpreds = env.predicates.mapValues(p => { val gamma = p.resolve(p.params.toMap, env).get val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) translateInductivePredicate(p1, gamma) }) val goal = translateGoal(node.goal) - val initialCEnv = CEnvironment(goal, cpreds) - val proof = ProofTranslation.translate(node, initialCEnv) + val ctx = IR.emptyContext.copy(predicateEnv = cpreds) + val ir = IR.fromRule(ProofRule.Init(node.goal, ProofRule.of_certtree(node)), ctx).propagateContext + val proof = ProofTranslation.translate(ir) val cproc = ProgramTranslation.translate(node, proc) (cpreds, goal.toFunspec, proof, cproc) } @@ -62,15 +64,6 @@ object Translation { case CardType => CCardType } - def translateGoal(goal: Goal): CGoal = { - val pre = translateAsn(goal.pre) - val post = translateAsn(goal.post) - val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } - val programVars = goal.programVars.map(translateVar) - val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(v => gamma(v) == CCardType) - CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) - } - def translateExpr(el: Expr): CExpr = el match { case Var(name) => CVar(name) case BoolConst(value) => CBoolConst(value) From 7e832e64a067a42ed55692dba1a4246af2a67e3f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 12:30:56 +0800 Subject: [PATCH 056/211] Define translation from IR to HTT prog stmts --- .../htt/program/StatementProducers.scala | 118 ------------------ .../targets/htt/program/Statements.scala | 32 ++--- .../htt/translation/ProgramTranslation.scala | 106 ++++++---------- .../targets/htt/translation/Translation.scala | 3 +- 4 files changed, 51 insertions(+), 208 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala deleted file mode 100644 index 4c6af3a83..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/StatementProducers.scala +++ /dev/null @@ -1,118 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.program - -import org.tygus.suslik.certification.targets.htt.language.Expressions.CExpr -import org.tygus.suslik.certification.targets.htt.program.Statements.{CGuarded, CIf, CSeqComp, CStatement} - -object StatementProducers { - type Kont = Seq[CStatement] => CStatement - - trait Branching - - sealed abstract class CStmtProducer { - val arity: Int - val fn: Kont - - def apply(children: Seq[CStatement]): CStatement = { - assert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") - fn(children) - } - - def >>(p: CStmtProducer): CStmtProducer = { - ChainedCStmtProducer(this, p) - } - - def partApply(s: CStatement): CStmtProducer = { - PartiallyAppliedCStmtProducer(this, s) - } - - def simplify: CStmtProducer = this match { - case ChainedCStmtProducer(p1, IdCStmtProducer) => p1.simplify - case ChainedCStmtProducer(IdCStmtProducer, p2) => p2.simplify - case ChainedCStmtProducer(_, p2@ConstCStmtProducer(_)) => p2.simplify - case _ => this - } - } - - case class ChainedCStmtProducer(p1: CStmtProducer, p2: CStmtProducer) extends CStmtProducer { - val arity: Int = p1.arity + p2.arity - 1 - val fn: Kont = stmts => { - val (stmts1, stmts2) = stmts.splitAt(p1.arity) - val s = p1.fn(stmts1) - p2.fn(s +: stmts2) - } - } - - case class PartiallyAppliedCStmtProducer(p: CStmtProducer, s: CStatement) extends CStmtProducer with Branching { - val arity: Int = p.arity - 1 - val fn: Kont = stmts => { - p.apply(s +: stmts) - } - } - - case object IdCStmtProducer extends CStmtProducer { - val arity: Int = 1 - val fn: Kont = _.head - } - - case class ConstCStmtProducer(s: CStatement) extends CStmtProducer { - val arity: Int = 0 - val fn: Kont = _ => s - } - - case class PrependCStmtProducer(s: CStatement) extends CStmtProducer { - val arity: Int = 1 - val fn: Kont = stmts => CSeqComp(s, stmts.head).simplify - } - - case class AppendCStmtProducer(s: CStatement) extends CStmtProducer { - val arity: Int = 1 - val fn: Kont = stmts => CSeqComp(stmts.head, s).simplify - } - - case class BranchCStmtProducer(selectors: Seq[CExpr]) extends CStmtProducer with Branching { - val arity: Int = selectors.length - val fn: Kont = stmts => - if (stmts.length == 1) stmts.head else { - val cond_branches = selectors.zip(stmts).reverse - val ctail = cond_branches.tail - val finalBranch = cond_branches.head._2 - ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb).simplify } - } - } - - case class GuardedCStmtProducer(cond: CExpr) extends CStmtProducer with Branching { - val arity: Int = 2 - val fn: Kont = stmts => CGuarded(cond, stmts.head, stmts.last) - } - - case class FoldCStmtProducer[T](op: (T, CStmtProducer) => CStatement, item: T, bp: CStmtProducer) extends CStmtProducer { - val arity: Int = 1 - val fn: Kont = steps => { - // partially apply a produced step to the BranchProducer of the downstream `bp` - @scala.annotation.tailrec - def isBase(curr: CStmtProducer): Boolean = curr match { - case PartiallyAppliedCStmtProducer(p, _) => isBase(p) - case _: Branching => true - case _ => false - } - def update(curr: CStmtProducer): (CStmtProducer, Boolean) = curr match { - case FoldCStmtProducer(op, item, bp) => - val (bp1, modified) = update(bp) - (FoldCStmtProducer(op, item, bp1), modified) - case ChainedCStmtProducer(p1, p2) => - val (p11, modified1) = update(p1) - if (modified1) { - (ChainedCStmtProducer(p11, p2), modified1) - } else { - val (p21, modified2) = update(p2) - (ChainedCStmtProducer(p11, p21), modified2) - } - case _: PartiallyAppliedCStmtProducer | _: Branching if isBase(curr) => - (curr.partApply(steps.head), true) - case _ => - (curr, false) - } - op(item, update(bp)._1) - } - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index d5d064b1d..9e165070a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -3,7 +3,6 @@ package org.tygus.suslik.certification.targets.htt.program import org.tygus.suslik.certification.targets.htt.language.PrettyPrinting import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.util.StringUtil.mkSpaces object Statements { sealed abstract class CStatement extends PrettyPrinting { @@ -97,6 +96,15 @@ object Statements { build(this) builder.toString() } + + def >>(that: CStatement): CStatement = CSeqComp(this, that) + + def simplify: CStatement = this match { + case CIf(_, CSkip, CSkip) => CSkip + case CSeqComp(CGuarded(cond, b, eb), s2) => CGuarded(cond, CSeqComp(b, s2).simplify, eb) + case CSeqComp(s1, CGuarded(cond, b, eb)) => CGuarded(cond, CSeqComp(s1, b).simplify, eb) + case _ => this + } } trait ReturnsValue @@ -115,27 +123,9 @@ object Statements { case class CCall(fun: CVar, args: Seq[CExpr]) extends CStatement - case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement { - def simplify: CStatement = (tb, eb) match { - case (CSkip, CSkip) => CSkip - case _ => this - } - } + case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement - case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement { - /** - * The synthesis removes some extraneous program statements that are unused in the final result, but - * the CertTree retains them as nodes. So, we remove them from our statements here. - * @return A simplified statement - */ - def simplify: CStatement = (s1, s2) match { - case (CGuarded(cond, b, eb), _) => CGuarded(cond, CSeqComp(b, s2).simplify, eb) - case (CLoad(y, _, _, _), CGuarded(cond, _, _)) if cond.vars.contains(y) => this - case (_, CGuarded(cond, b, eb)) => CGuarded(cond, CSeqComp(s1, b).simplify, eb) - case (CLoad(y, _, _, _), _) => if (s2.usedVars.contains(y)) this else s2 // Do not generate read for unused variables - case _ => this - } - } + case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 0d395411b..330e6a119 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -1,80 +1,50 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.htt.program.StatementProducers._ +import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar import org.tygus.suslik.certification.targets.htt.program.Statements._ -import org.tygus.suslik.certification.targets.htt.translation.Translation._ -import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.synthesis._ object ProgramTranslation { - def translate(node: CertTree.Node, proc: Procedure): CProcedure = { - val stmtBody = traverseStmt(node, IdCStmtProducer) - CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), stmtBody) - } - - def traverseStmt(node: CertTree.Node, kont: CStmtProducer): CStatement = { - def translateOperation(s: Statement): CStatement = s match { - case Skip => - CSkip - case Load(to, tpe, from, offset) => - CLoad(translateVar(to), translateType(tpe), translateVar(from), offset) - case Store(to, offset, e) => - CStore(translateVar(to), offset, translateExpr(e)) - case Malloc(to, tpe, sz) => - CMalloc(translateVar(to), translateType(tpe), sz) - case Free(v) => - val block = node.footprint.pre.sigma.blocks.find(_.loc == v) - assert(block.nonEmpty) - CFree(translateVar(v), block.get.sz) - case Call(v, args, _) => - CCall(translateVar(v), args.map(translateExpr)) - case Error => - CError - case _ => - throw TranslationException("Operation not supported") - } + def translate(ir: IR.Node): CStatement = irToProgramStatements(ir) - def translateProducer(stmtProducer: StmtProducer): CStmtProducer = stmtProducer match { - case ChainedProducer(p1, p2) => - val k1 = translateProducer(p1) - val k2 = translateProducer(p2) - k1 >> k2 - case PartiallyAppliedProducer(p, _) => - translateProducer(p) - case ConstProducer(s) => - val stmt = translateOperation(s) - ConstCStmtProducer(stmt) - case PrependProducer(s) => - val stmt = translateOperation(s) - PrependCStmtProducer(stmt) - case BranchProducer(_, _, _, selectors) => - BranchCStmtProducer(selectors.map(translateExpr)) - case GuardedProducer(cond, _) => - GuardedCStmtProducer(translateExpr(cond)) - case _ => - IdCStmtProducer + def irToProgramStatements(node: IR.Node): CStatement = { + def visit(node: IR.Node): CStatement = node match { + case IR.EmpRule(_) => CSkip + case IR.Call(stmt, next, _) => stmt >> visit(next.head) + case IR.Free(stmt, next, _) => stmt >> visit(next.head) + case IR.Malloc(stmt, next, _) => stmt >> visit(next.head) + case IR.Read(stmt, next, _) => stmt >> visit(next.head) + case IR.Write(stmt, next, _) => stmt >> visit(next.head) + case IR.AbduceBranch(cond, Seq(tb, eb), _) => CIf(cond, visit(tb), visit(eb)) + case IR.Open(app, clauses, selectors, next, ctx) => + val cond_branches = selectors.zip(next.map(visit)).reverse + val ctail = cond_branches.tail + val finalBranch = cond_branches.head._2 + ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb) } + case _ => visit(node.next.head) } + pruneUnusedReads(visit(node).simplify) + } - def updateProducerPost(nextItems: Seq[CertTree.Node], nextKont: CStmtProducer): CStmtProducer = nextKont match { - case _: Branching => - nextItems.tail.foldLeft(nextKont >> kont) { - case (foldedP, item) => FoldCStmtProducer(traverseStmt, item, foldedP) + def pruneUnusedReads(stmts: CStatement): CStatement = { + def visit(stmt: CStatement): (CStatement, Set[CVar]) = stmt match { + case CSeqComp(s1, s2) => + val (s2New, s2Used) = visit(s2) + val (s1New, s1Used) = visit(s1) + s1 match { + case CLoad(to, _, _, _) if !s2Used.contains(to) => (s2New, s2Used) + case _ => (s1New >> s2New, s1Used ++ s2Used) } - case _ => - nextKont >> kont - } - - // generated nested continuations for children - val p = translateProducer(node.kont).simplify - val nextItems = node.children - val nextKont = updateProducerPost(nextItems, p).simplify - - nextItems.headOption match { - case Some(childHead) => - traverseStmt(childHead, nextKont) - case None => - nextKont(Nil) + case CIf(cond, tb, eb) => + val (tbNew, tbUsed) = visit(tb) + val (ebNew, ebUsed) = visit(eb) + (CIf(cond, tbNew, ebNew), tbUsed ++ ebUsed ++ cond.vars) + case CLoad(to, tpe, from, offset) => (stmt, Set(to, from)) + case CStore(to, offset, e) => (stmt, Set(to) ++ e.vars.toSet) + case CMalloc(to, tpe, sz) => (stmt, Set(to)) + case CFree(v, sz) => (stmt, Set(v)) + case CCall(fun, args) => (stmt, args.flatMap(_.vars).toSet) + case _ => (stmt, Set.empty) } + visit(stmts)._1 } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index e8c1f21de..fb65de846 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -34,7 +34,8 @@ object Translation { val ctx = IR.emptyContext.copy(predicateEnv = cpreds) val ir = IR.fromRule(ProofRule.Init(node.goal, ProofRule.of_certtree(node)), ctx).propagateContext val proof = ProofTranslation.translate(ir) - val cproc = ProgramTranslation.translate(node, proc) + val progBody = ProgramTranslation.translate(ir) + val cproc = CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), progBody) (cpreds, goal.toFunspec, proof, cproc) } From dc0048dd0d28b7c5421dc29bbaa955f95ad4c521 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 17:27:36 +0800 Subject: [PATCH 057/211] HTT: Enforce ghost variable order in funspec and goal --- .../targets/htt/translation/IR.scala | 17 +++++++++++++---- .../htt/translation/ProofTranslation.scala | 2 +- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 36a5f5785..422433c46 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -7,9 +7,10 @@ import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CFunSpec, CInductiveClause, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateAsn, translateExpr, translateParam, translateSApp, translateType, translateVar} -import org.tygus.suslik.language.Expressions.{Subst, SubstVar} +import org.tygus.suslik.language.Expressions.{Subst, SubstVar, Var} import org.tygus.suslik.language.Statements import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} +import org.tygus.suslik.logic.{FunSpec, Gamma} import org.tygus.suslik.logic.Specifications.Goal object IR { @@ -154,9 +155,18 @@ object IR { val post = translateAsn(goal.post) val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } val programVars = goal.programVars.map(translateVar) - val universalGhosts = goal.universalGhosts.map(translateVar).toSeq.filterNot(v => gamma(v) == CCardType) + val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) } + def translateFunSpec(f: FunSpec, gamma: Gamma): CFunSpec = { + val rType = translateType(f.rType) + val pre = translateAsn(f.pre) + val post = translateAsn(f.post) + val valueVars = (pre.valueVars ++ post.valueVars).distinct + val params = f.params.map(translateParam) + val ghosts = pre.valueVars.diff(params.map(_._2)).map(v => (translateType(gamma(Var(v.name))), v)) + CFunSpec(f.name, rType, params, ghosts, pre, post) + } def fromRule(rule: ProofRule, ctx: IR.Context) : IR.Node = rule match { case ProofRule.Init(goal, next) => @@ -216,8 +226,7 @@ object IR { val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) fromRule(next, ctx1) case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma, next) => - val ghosts = f.pre.vars.toSeq.diff(f.params.map(_._1)).map(v => (v, gamma(v))) - val cfunspec = CFunSpec(f.name, translateType(f.rType), f.params.map(translateParam), ghosts.map(translateParam), translateAsn(f.pre), translateAsn(f.post)) + val cfunspec = translateFunSpec(f, gamma) val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 5af2d6d17..639f3e44f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -93,7 +93,7 @@ object ProofTranslation { case IR.EmpRule(ctx) => val topLevelGoal = ctx.topLevelGoal.get val post = topLevelGoal.post.subst(ctx.subst).subst(ctx.substVar) - val existentials = topLevelGoal.existentials.map(_.substVar(ctx.substVar).subst(ctx.subst)) + val existentials = topLevelGoal.existentials.map(_.substVar(ctx.substVar).subst(ctx.subst).subst(ctx.substVar)) val unfoldings = ctx.unfoldings.map { case (app, e) => app.subst(ctx.substVar).subst(ctx.subst) -> e.subst(ctx.substVar).subst(ctx.subst) } Proof.Emp >>> solvePost(post, existentials, unfoldings) case IR.AbduceCall(new_vars, f_pre, calleePost, call, freshSub, freshToActual, next, ctx) => From 978716dc67cb8309c67ddf7ccf483155c900aa3b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 17:41:36 +0800 Subject: [PATCH 058/211] HTT: Apply all substitutions before naming predicate heaplets --- .../targets/htt/translation/ProofTranslation.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 639f3e44f..d0ce90640 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -111,7 +111,7 @@ object ProofTranslation { Proof.MoveToCtx(Seq(CVar(s"h_$callId"))) >> // The postcondition of the call abduction becomes the precondition of the companion elimExistentials(f.existentials) >> - elimExistentials(f.post.heapVars) >> + elimExistentials(f.post.subst(ctx.subst).heapVars) >> initPre(f.post, callId) >> // Store validity hypotheses in context Proof.StoreValid >> From 5bfb3fad106a51011bb9112a333783ba5ae83652 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 20:14:27 +0800 Subject: [PATCH 059/211] HTT: move the pretty-printing logic inside the certificate --- .../certification/targets/htt/HTT.scala | 22 +--------- .../targets/htt/HTTCertificate.scala | 40 +++++++++++++++++-- 2 files changed, 38 insertions(+), 24 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 31ebf9b70..def715d63 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -10,32 +10,12 @@ object HTT extends CertificationTarget { val name: String = "HTT" val suffix: String = ".v" - // Import Coq dependencies - private val prelude = s"""From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -""" - def certify(proc: Procedure, env: Environment): HTTCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val builder = new StringBuilder - builder.append(prelude) val (preds, spec, proof, cproc) = Translation.translate(root, proc)(env) - preds.values.foreach(pred => builder.append(pred.pp + "\n")) - builder.append(spec.pp) - builder.append("\n") - builder.append(cproc.pp) - builder.append("\n") - builder.append(proof.pp) CertTree.clear() // [Certify]: Clear tree after certification complete - HTTCertificate(builder.toString(), proc.name) + HTTCertificate(proc.name, preds, spec, proof, cproc) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 67a483bab..2ab0b084a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -1,12 +1,46 @@ package org.tygus.suslik.certification.targets.htt +import org.tygus.suslik.certification.targets.htt.language.Types.CNatSeqType +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CFunSpec +import org.tygus.suslik.certification.targets.htt.program.Statements.CProcedure +import org.tygus.suslik.certification.targets.htt.translation.IR.PredicateEnv import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class HTTCertificate(body: String, name: String) extends Certificate { +case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof.Step, proc: CProcedure, hints: Seq[Hint.Item] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT - def sanitize(txt: String): String = txt.replace('-', '_') // Replace hyphens with underscores - override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), body)) + def sanitize(txt: String): String = txt.replace('-', '_') + + // Import Coq dependencies + private val prelude = s"""From mathcomp +Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. +From fcsl +Require Import prelude pred pcm unionmap heap. +From HTT +Require Import stmod stsep stlog stlogR. +From SSL +Require Import core. + +""" + + def pp: String = { + val builder = new StringBuilder + builder.append(prelude) + preds.values.foreach(pred => builder.append(pred.pp + "\n")) + builder.append(spec.pp) + builder.append("\n") + if (hints.nonEmpty) { + builder.append(hints.map(_.pp).mkString(".\n")) + builder.append(".\n") + } + builder.append(proc.pp) + builder.append("\n") + builder.append(proof.pp) + builder.toString + } + + override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), pp)) } From 4b18f6dd09aebbc544be84a842e9d4b48ff0b3d5 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 13 Jan 2021 20:39:09 +0800 Subject: [PATCH 060/211] HTT: generate hints for predicate assertions (sll_append now works) --- .../certification/targets/htt/HTT.scala | 2 +- .../targets/htt/HTTCertificate.scala | 11 +++++-- .../targets/htt/logic/Hint.scala | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 3 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index def715d63..b22fa6c5e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -16,6 +16,6 @@ object HTT extends CertificationTarget { CertTree.clear() // [Certify]: Clear tree after certification complete - HTTCertificate(proc.name, preds, spec, proof, cproc) + HTTCertificate(proc.name, preds, spec, proof, cproc).inferHints } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 2ab0b084a..6e58a76d5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -32,8 +32,9 @@ Require Import core. builder.append(spec.pp) builder.append("\n") if (hints.nonEmpty) { - builder.append(hints.map(_.pp).mkString(".\n")) - builder.append(".\n") + builder.append("\n") + builder.append(hints.map(_.pp).mkString("\n")) + builder.append("\n\n") } builder.append(proc.pp) builder.append("\n") @@ -41,6 +42,12 @@ Require Import core. builder.toString } + def inferHints: HTTCertificate = { + val predHints = preds.values.filter(p => p.params.map(_._1).contains(CNatSeqType)).map(p => Hint.PredicateSetTransitive(p)).toSeq + + this.copy(hints = predHints) + } + override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala new file mode 100644 index 000000000..9d4e171fd --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -0,0 +1,33 @@ +package org.tygus.suslik.certification.targets.htt.logic + +import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar +import org.tygus.suslik.certification.targets.htt.language.Types.CNatSeqType +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate + +object Hint { + abstract class Item { + val dbName: String + def pp: String + def ppResolve(ident: String): String = s"Hint Resolve $ident: $dbName" + } + + case class PredicateSetTransitive(pred: CInductivePredicate) extends Item { + val name: String = s"${pred.name}_perm_eq_trans" + val dbName: String = "ssl_pred" + def pp: String = { + val items = pred.params.zipWithIndex.filter(_._1._1 == CNatSeqType).map { case ((tp, v), i) => + val (before, after) = pred.params.map(_._2).splitAt(i) + val s1 = CVar("s1") + val s2 = CVar("s2") + val params1 = before ++ Seq(s1) ++ after.tail + val params2 = before ++ Seq(s2) ++ after.tail + val args = before ++ after.tail ++ Seq(s1, s2) + val hyp = s"Hypothesis $name$i: forall ${args.map(_.pp).mkString(" ")}, perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" + val resolve = ppResolve(s"$name$i") + s"$hyp.\n$resolve" + } + assert(items.nonEmpty, "can't generate hypothesis for predicate with no seq nat parameters") + s"${items.mkString(".\n")}." + } + } +} From aabeb5bb429372b3ac539c58e871ea252cd8ea4b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 00:22:45 +0800 Subject: [PATCH 061/211] Add Inconsistency rule to ProofRules --- .../tygus/suslik/certification/ProofRule.scala | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 9a19f33cd..c73a2c70b 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} -import org.tygus.suslik.language.Statements.{Load, Skip, Store} +import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} import org.tygus.suslik.logic.Specifications.{Assertion, Goal, SuspendedCallGoal} @@ -166,6 +166,10 @@ case class AbduceCall( override def pp: String = s"${ind}Init(${goal.pp});\n${next.pp}" } + case object Inconsistency extends ProofRule { + override def pp: String = "Inconsistency" + } + /** converts a Suslik CertTree node into the unified ProofRule structure */ def of_certtree(node: CertTree.Node): ProofRule = { def fail_with_bad_proof_structure(): Nothing = @@ -224,6 +228,14 @@ case class AbduceCall( } case _ => fail_with_bad_proof_structure() } + case LogicalRules.Inconsistency => node.kont match { + case ConstProducer(Error) => + node.children match { + case Nil => ProofRule.Inconsistency + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } case LogicalRules.WeakenPre => node.kont match { case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) @@ -464,6 +476,4 @@ case class AbduceCall( } } } - - } \ No newline at end of file From 621457dcd318b6f6f567cc2ceb22c3e2f477832f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 00:23:58 +0800 Subject: [PATCH 062/211] =?UTF-8?q?HTT:=20Don=E2=80=99t=20generate=20proof?= =?UTF-8?q?=20branch=20prelude=20if=20branch=20is=20inconsistent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../suslik/certification/targets/htt/logic/Proof.scala | 6 +++++- .../certification/targets/htt/translation/IR.scala | 7 ++++++- .../targets/htt/translation/ProgramTranslation.scala | 1 + .../targets/htt/translation/ProofTranslation.scala | 10 ++++++++-- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 38b476fbf..7b81991e2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -51,7 +51,7 @@ object Proof { } case class ElimExistential(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString("\n") + def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString(".\n") } case object Sbst extends Step { def pp: String = "subst" @@ -123,6 +123,10 @@ object Proof { case object Emp extends Step { def pp: String = "ssl_emp" } + case object Error extends Step { + override val isNoop: Boolean = true + def pp: String = "" + } case object Noop extends Step { override val isNoop: Boolean = true def pp: String = "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 422433c46..1d169c939 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -111,6 +111,7 @@ object IR { val next1 = n.next.head.propagateContext val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.Inconsistency => n } } @@ -148,6 +149,10 @@ object IR { case class Init(ctx: Context, next: Seq[Node]) extends Node + case class Inconsistency(ctx: Context) extends Node { + val next: Seq[Node] = Seq.empty + } + private def translateSbst(sbst: Subst) = sbst.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} private def translateSbstVar(sbst: SubstVar) = sbst.map{ case (k,v) => CVar(k.name) -> CVar(v.name)} def translateGoal(goal: Goal): CGoal = { @@ -162,7 +167,6 @@ object IR { val rType = translateType(f.rType) val pre = translateAsn(f.pre) val post = translateAsn(f.post) - val valueVars = (pre.valueVars ++ post.valueVars).distinct val params = f.params.map(translateParam) val ghosts = pre.valueVars.diff(params.map(_._2)).map(v => (translateType(gamma(Var(v.name))), v)) CFunSpec(f.name, rType, params, ghosts, pre, post) @@ -243,5 +247,6 @@ object IR { case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) case ProofRule.PickCard(_, next) => fromRule(next, ctx) case ProofRule.FrameUnfold(h_pre, h_post, next) => fromRule(next, ctx) + case ProofRule.Inconsistency => IR.Inconsistency(ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 330e6a119..474635362 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -20,6 +20,7 @@ object ProgramTranslation { val ctail = cond_branches.tail val finalBranch = cond_branches.head._2 ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb) } + case IR.Inconsistency(_) => CSkip case _ => visit(node.next.head) } pruneUnusedReads(visit(node).simplify) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index d0ce90640..6b69b09f6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -136,13 +136,19 @@ object ProofTranslation { case IR.Open(app, clauses, selectors, next, ctx) => val branchSteps = next.zip(clauses).map { case (n, c) => val c1 = c.subst(n.ctx.substVar) - Proof.OpenPost(app.subst(ctx.substVar)) >> + val res = visit(n) + if (res == Proof.Error) { + Proof.OpenPost(app.subst(ctx.substVar)) // Don't emit branch prelude if inconsistent + } else { + Proof.OpenPost(app.subst(ctx.substVar)) >> elimExistentials(c1.existentials) >> elimExistentials(c1.asn.heapVars) >> initPre(c1.asn, app.uniqueName) >> - visit(n) + res + } } Proof.Open >> Proof.Branch(branchSteps) + case IR.Inconsistency(_) => Proof.Error } pruneUnusedReads(visit(node).simplify) >> Proof.EndProof From beb547cebbc7a5c81ec492e92ca4b26870151e26 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 08:39:52 +0800 Subject: [PATCH 063/211] HTT: Ignore assertion conjuncts that concern cardinality vars --- .../targets/htt/language/Expressions.scala | 10 ++++------ .../certification/targets/htt/logic/Sentences.scala | 11 +++++++++++ .../targets/htt/translation/ProofTranslation.scala | 2 +- .../targets/htt/translation/Translation.scala | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 41c086d28..9b8be3219 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -35,8 +35,6 @@ object Expressions { case a@CSApp(_, args, card) => val acc1 = if (p(a)) acc :+ a.asInstanceOf[R] else acc args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) - val acc2 = args.foldLeft(acc1)((acc, arg) => collector(acc)(arg)) - collector(acc2)(card) case CPointsTo(loc, _, value) => collector(collector(acc)(loc))(value) case CSFormula(_, apps, ptss) => @@ -89,10 +87,10 @@ object Expressions { def cardVars: Seq[CVar] = collect(_.isInstanceOf[CSApp]).flatMap {v: CSApp => v.card.vars} - def conjuncts: Seq[CExpr] = collect({ - case CBinaryExpr(COpAnd, _, _) => true - case _ => false - }) + def conjuncts: Seq[CExpr] = this match { + case CBinaryExpr(COpAnd, left, right) => left.conjuncts ++ right.conjuncts + case _ => Seq(this) + } } case class CVar(name: String) extends CExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 8139f6653..0ceaf320c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -36,6 +36,17 @@ object Sentences { val heapVars: Seq[CVar] = sigma.heapVars + + def removeCardConstraints: CAssertion = { + val cardVars = sigma.cardVars + val conjuncts = phi.conjuncts.filter { + case CBinaryExpr(_, v:CVar, _) if cardVars.contains(v) => false + case CBinaryExpr(_, _, v:CVar) if cardVars.contains(v) => false + case _ => true + } + val phi1 = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce[CExpr] { case (c1, c2) => CBinaryExpr(COpAnd, c1, c2) } + this.copy(phi = phi1) + } } case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 6b69b09f6..37bd3dbce 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -21,7 +21,7 @@ object ProofTranslation { // move pure part to context val pureToCtx = if (!phi.isTrivial) { - val hyps = if (phi.conjuncts.isEmpty) Seq(CVar(s"phi_$uniqueName")) else (0 to phi.conjuncts.length).map(i => CVar(s"phi_$uniqueName$i")) + val hyps = if (phi.conjuncts.isEmpty) Seq(CVar(s"phi_$uniqueName")) else phi.conjuncts.indices.map(i => CVar(s"phi_$uniqueName$i")) Proof.MoveToCtxDestruct(hyps) } else Proof.Noop diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index fb65de846..c4759d505 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -87,7 +87,7 @@ object Translation { val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) val sigma = translateSFormula(el.sigma) - CAssertion(phi, sigma) + CAssertion(phi, sigma).removeCardConstraints } def translateSFormula(el: SFormula): CSFormula = { From 54cd4f12c6e3ba042c477f529876959db85d8270 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 08:41:52 +0800 Subject: [PATCH 064/211] Capture SMT entailments in ProofRule tree --- .../org/tygus/suslik/certification/ProofRule.scala | 10 +++++----- .../certification/targets/htt/translation/IR.scala | 2 +- .../targets/vst/translation/ProofTranslation.scala | 8 ++++---- .../org/tygus/suslik/synthesis/StmtProducer.scala | 5 ++++- .../org/tygus/suslik/synthesis/rules/FailRules.scala | 2 +- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index c73a2c70b..25a349c7f 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -41,9 +41,9 @@ object ProofRule { override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" } - /** check post doesn't provide any information useful for certification, so just included as empty rule */ - case class CheckPost(next:ProofRule) extends ProofRule { - override def pp: String = s"${ind}CheckPost;\n${next.pp}" + /** solves a pure entailment with SMT */ + case class CheckPost(prePhi: PFormula, postPhi: PFormula, next:ProofRule) extends ProofRule { + override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next.pp}" } /** picks an arbitrary instantiation of the proof rules */ @@ -198,8 +198,8 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case FailRules.CheckPost => node.kont match { - case IdProducer => node.children match { - case ::(head, Nil) => ProofRule.CheckPost(of_certtree(head)) + case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { + case ::(head, Nil) => ProofRule.CheckPost(prePhi, postPhi, of_certtree(head)) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 1d169c939..a13dcd42a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -242,7 +242,7 @@ object IR { // unused rules: case ProofRule.HeapUnify(_, next) => fromRule(next, ctx) case ProofRule.NilNotLval(_, next) => fromRule(next, ctx) - case ProofRule.CheckPost(next) => fromRule(next, ctx) + case ProofRule.CheckPost(prePhi, postPhi, next) => fromRule(next, ctx) case ProofRule.WeakenPre(_, next) => fromRule(next, ctx) case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) case ProofRule.PickCard(_, next) => fromRule(next, ctx) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 7539185de..4fd7bc850 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -283,7 +283,7 @@ object ProofTranslation { rule match { case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.CheckPost(next) => is_variable_used_in_proof(variable)(next) + case ProofRule.CheckPost(prePhi, postPhi, next) => is_variable_used_in_proof(variable)(next) case ProofRule.PickCard(_, next) => is_variable_used_in_proof(variable)(next) case ProofRule.PickArg(_, next) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet @@ -678,7 +678,7 @@ object ProofTranslation { // Ignored rules case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.CheckPost(next) => translate_proof_rules(next)(context) + case ProofRule.CheckPost(pre_phi, post_phi, next) => translate_proof_rules(next)(context) case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) @@ -703,7 +703,7 @@ object ProofTranslation { def contains_free(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_free(next) - case ProofRule.CheckPost(next) => contains_free(next) + case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_free(next) case ProofRule.Pick(subst, next) => contains_free(next) case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) case ProofRule.Write(stmt, next) => contains_free(next) @@ -729,7 +729,7 @@ object ProofTranslation { def contains_malloc(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_malloc(next) - case ProofRule.CheckPost(next) => contains_malloc(next) + case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_malloc(next) case ProofRule.Pick(subst, next) => contains_malloc(next) case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) case ProofRule.Write(stmt, next) => contains_malloc(next) diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index ea19e270a..da8462a91 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, SApp, SFormula} +import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, PFormula, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils @@ -178,3 +178,6 @@ case class UnfoldProducer(app: SApp, selector: Expr, asn: Assertion, substEx: Su // Abduce Call case class AbduceCallProducer(f: FunSpec) extends StmtProducer with Noop + +// Captures entailments emitted by SMT +case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula) extends StmtProducer with Noop \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala index 5dd08c59e..bc13d5566 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala @@ -46,7 +46,7 @@ object FailRules extends PureLogicUtils with SepLogicUtils with RuleUtils { else { val newPost = Assertion(exPost - PFormula(validExConjuncts), goal.post.sigma) val newGoal = goal.spawnChild(post = newPost) - List(RuleResult(List(newGoal), IdProducer, this, goal)) + List(RuleResult(List(newGoal), PureEntailmentProducer(goal.pre.phi, uniPost) >> IdProducer, this, goal)) } } From ab9d40fa7acee1d5684681dba6de4a2a4e7c328f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 19:30:30 +0800 Subject: [PATCH 065/211] HTT: Generate pure synthesis hints using info emitted by SMT solver --- .../targets/htt/HTTCertificate.scala | 2 +- .../targets/htt/language/Expressions.scala | 4 +- .../targets/htt/logic/Hint.scala | 120 +++++++++++++++--- .../targets/htt/logic/Proof.scala | 4 + .../targets/htt/translation/IR.scala | 9 +- .../htt/translation/ProofTranslation.scala | 4 +- 6 files changed, 118 insertions(+), 25 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 6e58a76d5..5b52d1e76 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -7,7 +7,7 @@ import org.tygus.suslik.certification.targets.htt.program.Statements.CProcedure import org.tygus.suslik.certification.targets.htt.translation.IR.PredicateEnv import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof.Step, proc: CProcedure, hints: Seq[Hint.Item] = Seq.empty) extends Certificate { +case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof.Step, proc: CProcedure, hints: Seq[Hint] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT // Replace hyphens with underscores diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 9b8be3219..a54e4f5c8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -124,8 +124,8 @@ object Expressions { case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { override def pp: String = op match { - case COpSubset => s"{subset ${left.pp} ${op.pp} ${right.pp}}" - case COpSetEq => s"perm_eq (${left.pp}) (${right.pp})" + case COpSubset => s"@sub_mem nat_eqType (mem (${left.pp})) (mem (${right.pp}))" + case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" case _ => s"${left.pp} ${op.pp} ${right.pp}" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 9d4e171fd..8ff9799c1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -1,33 +1,113 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar +import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types.CNatSeqType import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate +import scala.annotation.tailrec +import scala.collection.immutable.Queue + +abstract class Hint { + val dbName: String + val numHypotheses: Int + def pp: String + def ppResolve(ident: String): String = s"Hint Resolve $ident: $dbName" +} + object Hint { - abstract class Item { - val dbName: String - def pp: String - def ppResolve(ident: String): String = s"Hint Resolve $ident: $dbName" - } + private var _hintId: Int = 0 + private def freshHintId: Int = {_hintId += 1; _hintId} - case class PredicateSetTransitive(pred: CInductivePredicate) extends Item { - val name: String = s"${pred.name}_perm_eq_trans" + case class PredicateSetTransitive(pred: CInductivePredicate) extends Hint { val dbName: String = "ssl_pred" + + case class Hypothesis(params: Seq[CVar], idx: Int) { + val name = s"${pred.name}_perm_eq_trans$freshHintId" + val (before, after) = pred.params.map(_._2).splitAt(idx) + val s1: CVar = CVar("s_1") + val s2: CVar = CVar("s_2") + val params1: Seq[CVar] = before ++ Seq(s1) ++ after.tail + val params2: Seq[CVar] = before ++ Seq(s2) ++ after.tail + val args: Seq[CVar] = before ++ after.tail ++ Seq(s1, s2) + + def pp: String = { + val hyp = s"Hypothesis $name: forall ${args.map(_.pp).mkString(" ")}, perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" + s"$hyp.\n${ppResolve(name)}" + } + } + + private val hypotheses: Seq[Hypothesis] = { + val paramVars = pred.params.map(_._2) + pred.params.zipWithIndex.filter(_._1._1 == CNatSeqType).map(_._2).map(i => Hypothesis(paramVars, i)) + } + + val numHypotheses: Int = hypotheses.length + def pp: String = { - val items = pred.params.zipWithIndex.filter(_._1._1 == CNatSeqType).map { case ((tp, v), i) => - val (before, after) = pred.params.map(_._2).splitAt(i) - val s1 = CVar("s1") - val s2 = CVar("s2") - val params1 = before ++ Seq(s1) ++ after.tail - val params2 = before ++ Seq(s2) ++ after.tail - val args = before ++ after.tail ++ Seq(s1, s2) - val hyp = s"Hypothesis $name$i: forall ${args.map(_.pp).mkString(" ")}, perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" - val resolve = ppResolve(s"$name$i") - s"$hyp.\n$resolve" + s"${hypotheses.map(_.pp).mkString(".\n")}." + } + } + + case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr]) extends Hint { + val dbName: String = "ssl_pure" + case class Hypothesis(args: Set[CVar], ctx: Set[CExpr], goal: CExpr) { + val name = s"pure$freshHintId" + def pp: String = { + val ctxStr = ctx.map(_.pp).mkString(" -> ") + val goalStr = goal.pp + val hypStr = if (ctx.isEmpty) goalStr else s"$ctxStr -> $goalStr" + val quantifyStr = if (args.isEmpty) "" else s"forall ${args.map(_.pp).mkString(" ")}, " + s"Hypothesis $name : $quantifyStr$hypStr.\n${ppResolve(name)}" } - assert(items.nonEmpty, "can't generate hypothesis for predicate with no seq nat parameters") - s"${items.mkString(".\n")}." + } + private type ReachableMap = Map[CExpr, Set[CExpr]] + private type NeighborMap = Map[CExpr, Set[CExpr]] + private type VarMap = Map[CExpr, Set[CVar]] + + private def reachableConjuncts(m: NeighborMap): ReachableMap = { + m.keySet.foldLeft[ReachableMap](Map.empty){ case (reachable, e) => reachable + (e -> reachableFromSrc(e, m, reachable))} + } + + private def reachableFromSrc(src: CExpr, m: NeighborMap, reachable: ReachableMap): Set[CExpr] = { + @tailrec + def bfs(curr: CExpr, q: Queue[CExpr], visited: Set[CExpr]): Set[CExpr] = { + val (visited1, q1) = reachable.get(curr) match { + case Some(r) => (visited ++ r + curr, q) + case None => (visited + curr, m.getOrElse(curr, Set.empty).diff(visited).foldLeft(q){ case (q, e) => q.enqueue(e)}) + } + q1.dequeueOption match { + case None => visited1 + case Some((next, q1)) => bfs(next, q1, visited1) + } + } + bfs(src, Queue.empty, Set.empty) - src + } + + private val hypotheses: Seq[Hypothesis] = { + val preConjuncts = prePhi.filterNot({ + case e if e.isCard => true + case CUnaryExpr(COpNot, CBinaryExpr(COpBoolEq, CVar(_), CPtrConst(0))) => true + case _ => false + }) + val preConjunctToVar = preConjuncts.map(c => (c, c.vars.toSet)).toMap + val neighborMap = preConjunctToVar.map { case (c, vars) => + c -> (preConjuncts.filter(c1 => preConjunctToVar(c1).intersect(vars).nonEmpty) - c) + } + val reachableMap = reachableConjuncts(neighborMap) + val postConjuncts = postPhi.filterNot(_.isCard).diff(preConjuncts) + postConjuncts.toSeq.map(goal => { + val goalVars = goal.vars.toSet + val goalNeighbors = preConjuncts.filter(c1 => preConjunctToVar(c1).intersect(goalVars).nonEmpty) + val relevantPreConjuncts = reachableFromSrc(goal, neighborMap + (goal -> goalNeighbors), reachableMap) + val vars = preConjunctToVar.filterKeys(relevantPreConjuncts.contains).values.foldLeft(goalVars) { case (s1, s2) => s1 ++ s2} + Hypothesis(vars, relevantPreConjuncts, goal) + }) + } + + val numHypotheses: Int = hypotheses.length + + def pp: String = { + hypotheses.map(_.pp).mkString(".\n") } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 7b81991e2..fd04a213d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -127,6 +127,10 @@ object Proof { override val isNoop: Boolean = true def pp: String = "" } + case class EmitHint(hint: Hint) extends Step { + override val isNoop: Boolean = hint.numHypotheses == 0 + def pp: String = hint.pp + } case object Noop extends Step { override val isNoop: Boolean = true def pp: String = "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index a13dcd42a..5361b37d8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -111,6 +111,10 @@ object IR { val next1 = n.next.head.propagateContext val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) n.copy(ctx = ctx1, next = Seq(next1)) + case n:IR.CheckPost => + val next1 = n.next.head.propagateContext + val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) + n.copy(ctx = ctx1, next = Seq(next1)) case n:IR.Inconsistency => n } } @@ -153,6 +157,8 @@ object IR { val next: Seq[Node] = Seq.empty } + case class CheckPost(prePhi: Set[CExpr], postPhi: Set[CExpr], next: Seq[Node], ctx: Context) extends Node + private def translateSbst(sbst: Subst) = sbst.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} private def translateSbstVar(sbst: SubstVar) = sbst.map{ case (k,v) => CVar(k.name) -> CVar(v.name)} def translateGoal(goal: Goal): CGoal = { @@ -239,10 +245,11 @@ object IR { val ctx1 = ctx.copy(subst = ctx.subst ++ translateSbst(sbst)) fromRule(next, ctx1) case ProofRule.EmpRule => IR.EmpRule(ctx) + case ProofRule.CheckPost(prePhi, postPhi, next) => + IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(next, ctx)), ctx) // unused rules: case ProofRule.HeapUnify(_, next) => fromRule(next, ctx) case ProofRule.NilNotLval(_, next) => fromRule(next, ctx) - case ProofRule.CheckPost(prePhi, postPhi, next) => fromRule(next, ctx) case ProofRule.WeakenPre(_, next) => fromRule(next, ctx) case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) case ProofRule.PickCard(_, next) => fromRule(next, ctx) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 37bd3dbce..7a1a4ca9c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences._ import org.tygus.suslik.certification.targets.htt.program.Statements._ @@ -149,6 +149,8 @@ object ProofTranslation { } Proof.Open >> Proof.Branch(branchSteps) case IR.Inconsistency(_) => Proof.Error + case IR.CheckPost(prePhi, postPhi, next, _) => + Proof.EmitHint(Hint.PureEntailment(prePhi, postPhi)) >> visit(next.head) } pruneUnusedReads(visit(node).simplify) >> Proof.EndProof From 3d798daf32f98e020823f227df9d3dad4480cf1d Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 14 Jan 2021 19:46:16 +0800 Subject: [PATCH 066/211] HTT: Emit all hints inside proof --- .../org/tygus/suslik/certification/targets/htt/HTT.scala | 2 +- .../suslik/certification/targets/htt/HTTCertificate.scala | 6 ------ .../tygus/suslik/certification/targets/htt/logic/Hint.scala | 2 +- .../targets/htt/translation/ProofTranslation.scala | 3 +++ 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index b22fa6c5e..def715d63 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -16,6 +16,6 @@ object HTT extends CertificationTarget { CertTree.clear() // [Certify]: Clear tree after certification complete - HTTCertificate(proc.name, preds, spec, proof, cproc).inferHints + HTTCertificate(proc.name, preds, spec, proof, cproc) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 5b52d1e76..c5fa45ec7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -42,12 +42,6 @@ Require Import core. builder.toString } - def inferHints: HTTCertificate = { - val predHints = preds.values.filter(p => p.params.map(_._1).contains(CNatSeqType)).map(p => Hint.PredicateSetTransitive(p)).toSeq - - this.copy(hints = predHints) - } - override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 8ff9799c1..7a42eef70 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -44,7 +44,7 @@ object Hint { val numHypotheses: Int = hypotheses.length def pp: String = { - s"${hypotheses.map(_.pp).mkString(".\n")}." + s"${hypotheses.map(_.pp).mkString(".\n")}" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 7a1a4ca9c..9ad169e21 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -81,7 +81,10 @@ object ProofTranslation { def visit(node: IR.Node): Proof.Step = node match { case IR.Init(ctx, next) => val goal = ctx.topLevelGoal.get.subst(ctx.substVar) + val hints = if (ctx.predicateEnv.isEmpty) Proof.Noop else ctx.predicateEnv.values.map(p => Proof.EmitHint(Hint.PredicateSetTransitive(p))).reduce[Proof.Step](_ >> _) Proof.StartProof(goal.programVars) >> + // Include hints inferred from any of the predicate assertions + hints >> // Pull out any precondition ghosts and move precondition heap to the context Proof.GhostElimPre >> Proof.MoveToCtxDestructFoldLeft(goal.universalGhosts) >> From 9802d672f93af9eefe79b010e00a5a219c506463 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 15 Jan 2021 11:10:56 +0800 Subject: [PATCH 067/211] HTT: emit hints to top-level and use lemmas instead --- .../certification/targets/htt/HTT.scala | 4 +-- .../targets/htt/HTTCertificate.scala | 5 ++-- .../targets/htt/logic/Hint.scala | 16 +++++------- .../targets/htt/logic/Proof.scala | 4 --- .../htt/translation/ProofTranslation.scala | 25 +++++++++++++------ .../targets/htt/translation/Translation.scala | 9 ++++--- 6 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index def715d63..0df7f85c0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -12,10 +12,10 @@ object HTT extends CertificationTarget { def certify(proc: Procedure, env: Environment): HTTCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val (preds, spec, proof, cproc) = Translation.translate(root, proc)(env) + val cert = Translation.translate(root, proc)(env) CertTree.clear() // [Certify]: Clear tree after certification complete - HTTCertificate(proc.name, preds, spec, proof, cproc) + cert } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index c5fa45ec7..a5c953897 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -29,13 +29,12 @@ Require Import core. val builder = new StringBuilder builder.append(prelude) preds.values.foreach(pred => builder.append(pred.pp + "\n")) - builder.append(spec.pp) - builder.append("\n") if (hints.nonEmpty) { - builder.append("\n") builder.append(hints.map(_.pp).mkString("\n")) builder.append("\n\n") } + builder.append(spec.pp) + builder.append("\n\n") builder.append(proc.pp) builder.append("\n") builder.append(proof.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 7a42eef70..3570d6be8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -31,8 +31,8 @@ object Hint { val args: Seq[CVar] = before ++ after.tail ++ Seq(s1, s2) def pp: String = { - val hyp = s"Hypothesis $name: forall ${args.map(_.pp).mkString(" ")}, perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" - s"$hyp.\n${ppResolve(name)}" + val hyp = s"Lemma $name ${args.map(_.pp).mkString(" ")} : perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" + s"$hyp. Admitted.\n${ppResolve(name)}" } } @@ -43,9 +43,7 @@ object Hint { val numHypotheses: Int = hypotheses.length - def pp: String = { - s"${hypotheses.map(_.pp).mkString(".\n")}" - } + def pp: String = hypotheses.map(_.pp).mkString(".\n") + "." } case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr]) extends Hint { @@ -56,8 +54,8 @@ object Hint { val ctxStr = ctx.map(_.pp).mkString(" -> ") val goalStr = goal.pp val hypStr = if (ctx.isEmpty) goalStr else s"$ctxStr -> $goalStr" - val quantifyStr = if (args.isEmpty) "" else s"forall ${args.map(_.pp).mkString(" ")}, " - s"Hypothesis $name : $quantifyStr$hypStr.\n${ppResolve(name)}" + val argsStr = if (args.isEmpty) "" else s"${args.map(_.pp).mkString(" ")} " + s"Lemma $name $argsStr: $hypStr. Admitted.\n${ppResolve(name)}" } } private type ReachableMap = Map[CExpr, Set[CExpr]] @@ -106,8 +104,6 @@ object Hint { val numHypotheses: Int = hypotheses.length - def pp: String = { - hypotheses.map(_.pp).mkString(".\n") - } + def pp: String = hypotheses.map(_.pp).mkString(".\n") + "." } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index fd04a213d..7b81991e2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -127,10 +127,6 @@ object Proof { override val isNoop: Boolean = true def pp: String = "" } - case class EmitHint(hint: Hint) extends Step { - override val isNoop: Boolean = hint.numHypotheses == 0 - def pp: String = hint.pp - } case object Noop extends Step { override val isNoop: Boolean = true def pp: String = "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 9ad169e21..468c975fd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -6,8 +6,6 @@ import org.tygus.suslik.certification.targets.htt.logic.Sentences._ import org.tygus.suslik.certification.targets.htt.program.Statements._ object ProofTranslation { - def translate(ir: IR.Node): Proof.Step = irToProofSteps(ir) - def irToProofSteps(node: IR.Node) : Proof.Step = { def elimExistentials(ex: Seq[CExpr], nested: Boolean = false): Proof.Step = { if (ex.nonEmpty) { @@ -81,10 +79,7 @@ object ProofTranslation { def visit(node: IR.Node): Proof.Step = node match { case IR.Init(ctx, next) => val goal = ctx.topLevelGoal.get.subst(ctx.substVar) - val hints = if (ctx.predicateEnv.isEmpty) Proof.Noop else ctx.predicateEnv.values.map(p => Proof.EmitHint(Hint.PredicateSetTransitive(p))).reduce[Proof.Step](_ >> _) Proof.StartProof(goal.programVars) >> - // Include hints inferred from any of the predicate assertions - hints >> // Pull out any precondition ghosts and move precondition heap to the context Proof.GhostElimPre >> Proof.MoveToCtxDestructFoldLeft(goal.universalGhosts) >> @@ -152,13 +147,29 @@ object ProofTranslation { } Proof.Open >> Proof.Branch(branchSteps) case IR.Inconsistency(_) => Proof.Error - case IR.CheckPost(prePhi, postPhi, next, _) => - Proof.EmitHint(Hint.PureEntailment(prePhi, postPhi)) >> visit(next.head) + case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) } pruneUnusedReads(visit(node).simplify) >> Proof.EndProof } + def irToHints(node: IR.Node) : Seq[Hint] = { + def visit(node: IR.Node, hints: Seq[Hint]) : Seq[Hint] = node match { + case IR.Init(ctx, next) => + visit(next.head, ctx.predicateEnv.values.map(p => Hint.PredicateSetTransitive(p)).toSeq) + case IR.CheckPost(prePhi, postPhi, next, _) => + visit(next.head, hints :+ Hint.PureEntailment(prePhi, postPhi)) + case _:IR.Inconsistency | _:IR.EmpRule => + hints + case _:IR.Open | _:IR.AbduceBranch => + node.next.foldLeft(hints){ case (hints, next) => visit(next, hints) } + case _ => + visit(node.next.head, hints) + } + + visit(node, Seq.empty).filter(_.numHypotheses > 0) + } + def pruneUnusedReads(step: Proof.Step): Proof.Step = { def visit(step: Proof.Step): (Proof.Step, Set[CVar]) = step match { case Proof.SeqComp(s1, s2) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index c4759d505..f5ca00357 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.htt.translation +import org.tygus.suslik.certification.targets.htt.HTTCertificate import org.tygus.suslik.certification.{CertTree, ProofRule} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.program.Statements._ @@ -23,8 +24,7 @@ object Translation { * @param env the synthesis environment * @return the inductive predicates, fun spec, proof, and program translated to HTT */ - def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): - (Map[String, CInductivePredicate], CFunSpec, Proof.Step, CProcedure) = { + def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): HTTCertificate = { val cpreds = env.predicates.mapValues(p => { val gamma = p.resolve(p.params.toMap, env).get val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) @@ -33,10 +33,11 @@ object Translation { val goal = translateGoal(node.goal) val ctx = IR.emptyContext.copy(predicateEnv = cpreds) val ir = IR.fromRule(ProofRule.Init(node.goal, ProofRule.of_certtree(node)), ctx).propagateContext - val proof = ProofTranslation.translate(ir) + val proof = ProofTranslation.irToProofSteps(ir) + val hints = ProofTranslation.irToHints(ir) val progBody = ProgramTranslation.translate(ir) val cproc = CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), progBody) - (cpreds, goal.toFunspec, proof, cproc) + HTTCertificate(cproc.name, cpreds, goal.toFunspec, proof, cproc, hints) } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { From 16cd5a9bca527db10fed3ad8ac9d6a71e75f525e Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 18 Jan 2021 09:46:34 +0000 Subject: [PATCH 068/211] fixes tree free and tree copy outputs --- .../targets/vst/VSTCertificate.scala | 2 +- .../targets/vst/logic/Proof.scala | 20 +- .../targets/vst/logic/ProofSteps.scala | 6 + .../targets/vst/logic/ProofTerms.scala | 389 +++++++++++------- .../vst/translation/CTranslation.scala | 2 + .../vst/translation/ProofTranslation.scala | 49 ++- 6 files changed, 294 insertions(+), 174 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index d2c470796..71a3d2c3a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -10,6 +10,6 @@ case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinitio override def outputs: List[CertificateOutput] = List( CertificateOutput(Some(name + ".c"), name, CProcedureDefinition.pp), - CertificateOutput(None, name, Proof.pp) + CertificateOutput(None, "verif_" + name, Proof.pp) ) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index d655b47e5..c40f4e68a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -15,7 +15,7 @@ import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, Logic import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} -case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps, uses_free: Boolean = false) extends PrettyPrinting { +case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps, uses_free: Boolean = false, uses_malloc: Boolean = false) extends PrettyPrinting { /** prelude for Coq file */ private def coq_prelude = s""" @@ -41,6 +41,19 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. | SEP (emp). |""".stripMargin + private def malloc_defs : String = """Definition malloc_spec := + | DECLARE _malloc + | WITH t: type + | PRE [ tuint ] + | PROP() + | PARAMS(Vint (Int.repr (sizeof t))) + | SEP() + | POST [tptr tvoid] EX p:_, + | PROP() + | RETURN(p) + | SEP(data_at_ Tsh t p). + |""".stripMargin + /** prelude for the lemma */ private def lemma_prelude : String = s"""Lemma body_$name : semax_body Vprog Gprog f_$name ${name}_spec. @@ -48,12 +61,15 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. |""".stripMargin private def library_spec : String = s"""Definition Gprog : funspecs := - | ltac:(with_library prog [${name}_spec${ if (uses_free) {"; free_spec"} else {""}}]). + | ltac:(with_library prog [${name}_spec${ if (uses_free) {"; free_spec"} else {""}}${ + if (uses_malloc) {"; malloc_spec"} else {""} + }]). |""".stripMargin override def pp: String = { coq_prelude + (if (uses_free) { free_defs + "\n" } else { "" }) + + (if (uses_malloc) { malloc_defs + "\n" } else { "" }) + predicates.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"+ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala index 183c03176..3942ba3e8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala @@ -160,4 +160,10 @@ object ProofSteps { } + case class ForwardEntailer(next: ProofSteps) extends ProofSteps { + override def pp: String = + s"forward; entailer!.\n${next.pp}" + + } + } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index fecc30933..c49a5f34d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -22,12 +22,12 @@ object ProofTerms { /** predicate encoding that C-parameter (of type val) is a valid int */ case class IsValidInt(name: CVar) extends PureFormula { - override def pp:String = + override def pp: String = s"ssl_is_valid_int(${name.pp})" } /** Redefinition of expressions for use in VST proofs - * */ + **/ object Expressions { /** encoding of expressions in VST proof script @@ -37,13 +37,13 @@ object ProofTerms { sealed abstract class ProofCExpr extends PrettyPrinting { /** Applies a substitution to an expression */ - def subst (mapping: Map[String, ProofCExpr]) : ProofCExpr = this match { + def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { case expr@ProofCVar(name, _) => mapping.get(name) match { case Some(value) => value.subst(mapping) case None => expr } - case expr@ProofCBoolConst(_) =>expr - case expr@ProofCIntConst(_, _) =>expr + case expr@ProofCBoolConst(_) => expr + case expr@ProofCIntConst(_, _) => expr case ProofCSetLiteral(elems) => ProofCSetLiteral(elems.map(_.subst(mapping))) case ProofCIfThenElse(cond, left, right) => @@ -76,7 +76,7 @@ object ProofTerms { /** prints the expression as though it were an element * of type val (vst's encoding of C-values) */ - def pp_as_c_value : String = this match { + def pp_as_c_value: String = this match { case ProofCVar(name, typ) => typ match { case ProofTypes.CoqParamType(ty) => name case ProofTypes.CoqPtrType => name @@ -90,8 +90,8 @@ object ProofTerms { case value@ProofCBinaryExpr(op, _, _) => val is_int = op match { case ProofCOpPlus => true - case ProofCOpMinus =>true - case ProofCOpMultiply =>true + case ProofCOpMinus => true + case ProofCOpMultiply => true case _ => false } if (is_int) { @@ -106,26 +106,26 @@ object ProofTerms { case v => v.pp } - def pp_as_ssl_union_value : String = this match { + def pp_as_ssl_union_value: String = this match { case ProofCVar(name, typ) => typ match { case ProofTypes.CoqParamType(ty) => ty match { case CTypes.CIntType => s"inl ${name}" - case CTypes.CVoidPtrType =>s"inl ${name}" + case CTypes.CVoidPtrType => s"inl ${name}" } case ProofTypes.CoqPtrType => s"inr ${name}" case ProofTypes.CoqIntType => s"inl (Vint (Int.repr ${name}))" case ProofTypes.CoqListType(elem, length) => name case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") } - case ProofCIntConst(value,false) => s"inl (Vint (Int.repr ${value.toString}))" - case ProofCIntConst(0,true) => s"inr nullval" + case ProofCIntConst(value, false) => s"inl (Vint (Int.repr ${value.toString}))" + case ProofCIntConst(0, true) => s"inr nullval" case ProofCSetLiteral(elems) => s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" case value@ProofCBinaryExpr(op, _, _) => val is_int = op match { case ProofCOpPlus => true - case ProofCOpMinus =>true - case ProofCOpMultiply =>true + case ProofCOpMinus => true + case ProofCOpMultiply => true case _ => false } if (is_int) { @@ -133,17 +133,63 @@ object ProofTerms { } else { throw TranslationException("Error: inconsistent assumptions, attempting to print an arithmetic operation on non-integral values as a c expression.") } - case value@ProofCUnaryExpr(op, _) => op match { case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" } + case value@ProofCUnaryExpr(op, _) => op match { + case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" + } case v => v.pp } + def pp_as_bool_value: String = this match { + case value@ProofCBinaryExpr(op, expr_a, expr_b) => + op match { + case ProofCOpImplication => s"(implb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpIntEq => s"(Nat.eqb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpBoolEq => s"(eqb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpLeq => s"(Z.leb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpLt => s"(Z.ltb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpAnd => s"(andb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpOr => s"(orb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpIn => ??? // TODO: how to handle in operations? don't seem to show up in proofs + case ProofCOpSetEq => ??? // TODO: see above + case ProofCOpPtrEq => ??? // TODO: ditto + } + case value@ProofCUnaryExpr(op, _) => op match { + case ProofCOpNot => value.pp + } + } + + def pp_as_int_value: String = this match { + case ProofCVar(name, typ) => typ match { + case ProofTypes.CoqParamType(CTypes.CIntType) => s"(force_signed_int ${name})" + case ProofTypes.CoqIntType => s"${name}" + } + case ProofCIntConst(value, is_ptr) => value.toString + case ProofCIfThenElse(cond, left, right) => s"(if ${cond.pp_as_bool_value} then ${left.pp_as_int_value} else ${right.pp_as_int_value})" + case ProofCBinaryExpr(op, left, right) => op match { + case ProofCOpPlus => s"(${left.pp_as_int_value} + ${right.pp_as_int_value})" + case ProofCOpMinus => s"(${left.pp_as_int_value} - ${right.pp_as_int_value})" + case ProofCOpMultiply => s"(${left.pp_as_int_value} * ${right.pp_as_int_value})" + case ProofCOpIntEq => s"(${left.pp_as_int_value} =? ${right.pp_as_int_value})" + case ProofCOpLeq => s"(${left.pp_as_int_value} <=? ${right.pp_as_int_value})" + case ProofCOpLt => s"(${left.pp_as_int_value} op match { + case ProofCOpUnaryMinus => s"(- ${e.pp_as_int_value})" + } + } + /** print as a pointer value + * * @throws NotImplementedError if expression is not a variable or 0-int */ - def pp_as_ptr_value : String = this match { + def pp_as_ptr_value: String = this match { case ProofCVar(name, typ) => name case ProofCBoolConst(value) => this.pp - case ProofCIntConst(value, true) => if (value == 0) { "nullval" } else { this.pp } + case ProofCIntConst(value, _) => if (value == 0) { + "nullval" + } else { + this.pp + } case ProofCSetLiteral(elems) => this.pp case ProofCIfThenElse(cond, left, right) => this.pp case ProofCBinaryExpr(op, left, right) => this.pp @@ -168,7 +214,7 @@ object ProofTerms { // its actually of type val, and we need to // extract it's contained value ty match { - case CTypes.CIntType => s"(force_signed_int ${name})" + case CTypes.CIntType => s"(${name})" case CTypes.CVoidPtrType => s"${name}" case CTypes.CUnitType => throw new TranslationException("Error: inconsistent assumptions, attempting to create a variable of unit type") } @@ -183,7 +229,11 @@ object ProofTerms { /** integer constant in a VST proof */ case class ProofCIntConst(value: Int, is_ptr: Boolean) extends ProofCExpr { - override def pp: String = if (is_ptr && value == 0) { "nullval" } else { value.toString } + override def pp: String = if (is_ptr && value == 0) { + "nullval" + } else { + value.toString + } } /** set literal (encoded as set) in a VST proof */ @@ -194,7 +244,8 @@ object ProofTerms { /** encodes a ternary expression in a VST proof */ case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = ??? + override def pp: String = s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" + // TODO: if statements don't seem to be used inside suslik proofs, so implementing this would be pointless // if this assumption changes, then the correct implementation will look something like: // @@ -206,8 +257,8 @@ object ProofTerms { case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { override def pp: String = op match { - case ProofCOpLt => s"(${left.pp} < ${right.pp})" - case ProofCOpLeq => s"(${left.pp} <= ${right.pp})" + case ProofCOpLt => s"(${left.pp_as_int_value} < ${right.pp_as_int_value})" + case ProofCOpLeq => s"(${left.pp_as_int_value} <= ${right.pp_as_int_value})" case ProofCOpOr => s"(${left.pp} \/ ${right.pp})" case ProofCOpAnd => s"(${left.pp} /\ ${right.pp})" case ProofCOpPlus => s"(${left.pp} + ${right.pp})" @@ -239,6 +290,7 @@ object ProofTerms { sealed abstract class ProofCBinOp object ProofCOpImplication extends ProofCBinOp + object ProofCOpPlus extends ProofCBinOp object ProofCOpMinus extends ProofCBinOp @@ -266,13 +318,15 @@ object ProofTerms { object ProofCOpSubset extends ProofCBinOp object ProofCOpUnion extends ProofCBinOp + object ProofCOpDiff extends ProofCBinOp + object ProofCOpIntersect extends ProofCBinOp } /** prop predicate encoding that a given propositional expression is true - * */ + **/ case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { s"${expr.pp_as_c_value}" @@ -288,29 +342,30 @@ object ProofTerms { sealed case class FormalCondition( - pure_constraints: List[PureFormula], - spatial_constraints: List[VSTHeaplet] - ) + pure_constraints: List[PureFormula], + spatial_constraints: List[VSTHeaplet] + ) /** * Type encoding VST-compliant formal specifications of a C Function - * @param name name of the function - * @param c_params parameters of the function - * @param formal_params parameters of the specification + * + * @param name name of the function + * @param c_params parameters of the function + * @param formal_params parameters of the specification * @param existensial_params existential params of the function - * @param precondition precondtion for the function - * @param postcondition post condition of the function - * @param return_type return type of the function + * @param precondition precondtion for the function + * @param postcondition post condition of the function + * @param return_type return type of the function */ case class FormalSpecification( name: Ident, - c_params: Seq[(Ident,VSTCType)], - formal_params: Seq[(Ident,VSTProofType)], - existensial_params: Seq[(Ident,VSTProofType)], + c_params: Seq[(Ident, VSTCType)], + formal_params: Seq[(Ident, VSTProofType)], + existensial_params: Seq[(Ident, VSTProofType)], precondition: FormalCondition, postcondition: FormalCondition, return_type: VSTCType - ) extends PrettyPrinting{ + ) extends PrettyPrinting { def as_vst_type(var_type: VSTCType) = var_type match { case CTypes.CIntType => "tint" @@ -318,27 +373,29 @@ object ProofTerms { case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" } - def params : List[(Ident, VSTProofType)] = - (c_params.map({case (name,ty) => (name, CoqParamType(ty))}) ++ formal_params).toList + def params: List[(Ident, VSTProofType)] = + (c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ formal_params).toList override def pp: String = { - val formal_args = formal_params.map({case (var_name, var_type) => s"${var_name}: ${var_type.pp}"}) - val c_args = c_params.map({case (var_name, _) => s"${var_name}: val"}) + val formal_args = formal_params.map({ case (var_name, var_type) => s"${var_name}: ${var_type.pp}" }) + val c_args = c_params.map({ case (var_name, _) => s"${var_name}: val" }) val FormalCondition(pre_pure_constraints, pre_spatial_constraints) = precondition val FormalCondition(post_pure_constraints, post_spatial_constraints) = postcondition s"""Definition ${name}_spec := | DECLARE _${name} | WITH ${(c_args ++ formal_args).mkString(", ")} - | PRE [ ${c_params.map({case (_, var_type) => s"${as_vst_type(var_type)}"}).mkString(", ") } ] + | PRE [ ${c_params.map({ case (_, var_type) => s"${as_vst_type(var_type)}" }).mkString(", ")} ] | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) - | PARAMS(${c_params.map({case (var_name, _) => var_name}).mkString("; ")}) + | PARAMS(${c_params.map({ case (var_name, _) => var_name }).mkString("; ")}) | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) - | POST[ ${as_vst_type(return_type)} ]${existensial_params match { - case Nil => "" + | POST[ ${as_vst_type(return_type)} ]${ + existensial_params match { + case Nil => "" case _ => "\n" ++ - existensial_params.map({case (param_name, param_type) => s"| EX ${param_name}: ${param_type.pp},"}).mkString("\n") - }} + existensial_params.map({ case (param_name, param_type) => s"| EX ${param_name}: ${param_type.pp}," }).mkString("\n") + } + } | PROP( ${post_pure_constraints.map(_.pp).mkString("; ")} ) | LOCAL() | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). @@ -351,31 +408,38 @@ object ProofTerms { * termination measures in Coq */ sealed abstract class CardConstructor extends PrettyPrinting { - def constructor_args : List[Ident] = + def constructor_args: List[Ident] = this match { case CardNull => Nil case CardOf(args) => args } } + /** * Null constructor of 0 cardinality */ case object CardNull extends CardConstructor {} + /** Cardinality constructor of multiple components + * * @param args the variables produced by unwrwapping this element */ case class CardOf(args: List[Ident]) extends CardConstructor {} /** * Represents helper lemmas and operations that are required to make VST handle the predicate automatically - * */ + **/ sealed trait VSTPredicateHelper extends PrettyPrinting + object VSTPredicateHelper { + case class ValidPointer(predicate: String, args: List[String], ptr: String) extends VSTPredicateHelper { def lemma_name: String = s"${predicate}_${ptr}_valid_pointerP" + override def pp: String = s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. Admitted." } + case class HintResolve(lemma_name: String, hint_db: String) extends VSTPredicateHelper { override def pp: String = s"Hint Resolve ${lemma_name} : ${hint_db}." @@ -383,11 +447,11 @@ object ProofTerms { case class LocalFacts(predicate: VSTPredicate) extends VSTPredicateHelper { - def lemma_name : String = s"${predicate.name}_local_factsP" + def lemma_name: String = s"${predicate.name}_local_factsP" override def pp: String = { - def constructor_to_equality_term (vl: String, cons: CardConstructor) = + def constructor_to_equality_term(vl: String, cons: CardConstructor) = if (cons.constructor_args.isEmpty) { s"${vl} = ${predicate.constructor_name(cons)}" } else { @@ -397,38 +461,40 @@ object ProofTerms { /** Converts a predicate clause into a clause that mutually exclusively matches the clause * * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause - * */ - def predicate_to_determininant_term(clause: ProofTerms.VSTPredicateClause) : String = + **/ + def predicate_to_determininant_term(clause: ProofTerms.VSTPredicateClause): String = clause.pure.head.pp_as_ptr_value - /*** + /** * * Converts a predicate clause into a corresponding fact. * * NOTE: !!!ASSUMPTION!!! We assume that the first clause of the vst predicate is mutually exclusive - i.e - * if the first clause holds, then no other clause can hold. + * if the first clause holds, then no other clause can hold. */ - def clause_fact (cardConstructor: CardConstructor, predicate: VSTPredicateClause) : String = + def clause_fact(cardConstructor: CardConstructor, predicate: VSTPredicateClause): String = s"((${predicate_to_determininant_term(predicate)}) -> (${constructor_to_equality_term(predicate.cardinality_param, cardConstructor)}))" - s"""Lemma ${lemma_name} ${predicate.formal_params.mkString(" ")} : - | ${predicate.name} ${predicate.formal_params.mkString(" ")} |-- !!(${ - ( - predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ - predicate.params.flatMap({case (param, ProofTypes.CoqPtrType) => Some (s"(is_pointer_or_null ${param})") case _ => None}) - ).mkString("/\\")}). Proof. Admitted.""".stripMargin + | ${predicate.name} ${predicate.formal_params.mkString(" ")}|-- !!(${ + ( + predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ + predicate.params.flatMap({ case (param, ProofTypes.CoqPtrType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) + ).mkString("/\\") + }). Proof. Admitted.""".stripMargin } } + } /** represents a clause of the VST predicate, - * @param pure are the pure assertions - * @param spatial are the spatial assertions + * + * @param pure are the pure assertions + * @param spatial are the spatial assertions * @param sub_constructor are the subconstructors - * */ - case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String,CardConstructor]) { + **/ + case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) { val cardinality_param: String = "self_card" @@ -438,44 +504,49 @@ object ProofTerms { def selector: ProofCExpr = pure.head /** finds existential variables in the expression using args */ - def find_existentials_args (args: Set[String]): List[(Ident,VSTProofType)] = { - def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { + def find_existentials_args(args: Set[String]): List[(Ident, VSTProofType)] = { + def expr_existential: ProofCExpr => List[(Ident, VSTProofType)] = { case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil case Expressions.ProofCBoolConst(value) => Nil case Expressions.ProofCIntConst(value, _) => Nil case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) - case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) + case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) } - def spatial_expr_existential: VSTHeaplet => List[(Ident,VSTProofType)] = { + + def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTProofType)] = { case Formulae.CSApp(pred, args, card) => expr_existential(card) ::: args.flatMap(expr_existential).toList } + pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) } /** finds existential variables in the expression using args */ - def find_existentials (existentials: Map[Ident, VSTProofType]): List[(Ident,VSTProofType)] = { - def expr_existential : ProofCExpr => List[(Ident, VSTProofType)] = { + def find_existentials(existentials: Map[Ident, VSTProofType]): List[(Ident, VSTProofType)] = { + def expr_existential: ProofCExpr => List[(Ident, VSTProofType)] = { case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList case Expressions.ProofCBoolConst(value) => Nil case Expressions.ProofCIntConst(value, _) => Nil case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) - case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left,right).flatMap(expr_existential) + case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) } - def spatial_expr_existential: VSTHeaplet => List[(Ident,VSTProofType)] = { + + def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTProofType)] = { case Formulae.CSApp(pred, args, card) => expr_existential(card) ::: args.flatMap(expr_existential).toList case Formulae.CDataAt(loc, elem_typ, count, elem) => (expr_existential(loc) ++ expr_existential(elem)) } + pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) } } + /** * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker * @@ -484,57 +555,60 @@ object ProofTerms { * * Constructors are identified by their cardinality, and each suslik predicate maps to a unique cardinality datatype * - * @param name is the name of the predicate - * @param params is the list of arguments to the predicate + * @param name is the name of the predicate + * @param params is the list of arguments to the predicate * @param clauses is the mapping from cardinality constructors to clauses - * */ + **/ case class VSTPredicate( - name: Ident, params: List[(String,VSTProofType)], + name: Ident, params: List[(String, VSTProofType)], existentials: List[(String, VSTProofType)], clauses: Map[CardConstructor, VSTPredicateClause]) extends PrettyPrinting { /** * Returns the constructor of the predicate with n arguments + * * @param n number of arguments */ def constructor_by_arg(n: Int): CardConstructor = - clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n}).get._1 + clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n }).get._1 /** * @param selector a expression corresponding to a selector of the predicate * @return cardinality and clause matched by predicate */ - def apply(selector: ProofCExpr) : (CardConstructor, VSTPredicateClause) = - clauses.find({case (_, clause) => clause.selector.equals(selector) }).get + def apply(selector: ProofCExpr): (CardConstructor, VSTPredicateClause) = + clauses.find({ case (_, clause) => clause.selector.equals(selector) }).get /** returns any helper lemmas that need to be constructed for the helper */ - def get_helpers : List[VSTPredicateHelper] = - params.flatMap({ - case (param, ProofTypes.CoqPtrType) => - val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) - val local_facts = VSTPredicateHelper.LocalFacts(this) - List( - valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer"), - local_facts, VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") - ) - case _ => List() - }) + def get_helpers: List[VSTPredicateHelper] = + params.flatMap({ + case (param, ProofTypes.CoqPtrType) => + val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) + val local_facts = VSTPredicateHelper.LocalFacts(this) + List( + valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer"), + local_facts, VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") + ) + case _ => List() + }) /** returns the existential variables introduced by a constructor invokation */ - def constructor_existentials (constructor: CardConstructor) : List[(Ident,VSTProofType)] = { + def constructor_existentials(constructor: CardConstructor): List[(Ident, VSTProofType)] = { val param_map = params.toMap val existential_map = existentials.toMap.filterKeys(key => !param_map.contains(key)) - clauses(constructor).find_existentials(existential_map) + val predicate = clauses(constructor) + // clauses(constructor).find_existentials(existential_map) + find_existentials(constructor)(predicate) } /** returns all instances of constructors and the bindings they expose */ def constructors: List[CardConstructor] = clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => - constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor}) + constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor }) }).toList /** returns all the constructors used in the base match, assumed to be a superset @@ -551,9 +625,9 @@ object ProofTerms { * * `pred_1 ((pred_1 b) as a) => ... ` * - * I don't this this actually happens in practice, so this is probably just a bit of - * over-engineering - * */ + * I don't this this actually happens in practice, so this is probably just a bit of + * over-engineering + **/ def base_constructors: List[CardConstructor] = clauses.map({ case (constructor, _) => constructor @@ -562,39 +636,43 @@ object ProofTerms { /** * For a given clause of the predicate and its associated constructor, * return the list of existential variables used in the body of the clause - * @param cons a constructor matching some clause of the predicate + * + * @param cons a constructor matching some clause of the predicate * @param pclause the corresponding clause of the predicate * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause - * */ + **/ def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { val param_map = params.toMap - val exist_map : Map[String, VSTProofType] = existentials.toMap + val exist_map: Map[String, VSTProofType] = existentials.toMap val card_map = cons.constructor_args pclause match { case VSTPredicateClause(pure, spatial, sub_clauses) => - val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructor_args})).toSet - def to_variables(exp: ProofCExpr) : List[String] = exp match { - case Expressions.ProofCVar(name, typ) => - param_map.get(name) match { - case None if !clause_card_map.contains(name) => List(name) - case _ => List() - } - case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) - case Expressions.ProofCIfThenElse(cond, left, right) => - to_variables(cond) ++ to_variables(left) ++ to_variables(right) - case Expressions.ProofCBinaryExpr(op, left, right) => - to_variables(left) ++ to_variables(right) - case Expressions.ProofCUnaryExpr(op, e) => - to_variables(e) - case _ => List() - } - def to_variables_heap(heap: VSTHeaplet) : List[String] = heap match { - case Formulae.CDataAt(loc, elem_typ, count, elem) => - to_variables(loc) ++ to_variables(elem) - case Formulae.CSApp(pred, args, card) => - args.flatMap(to_variables).toList - } - (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap) : List[String]) + val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructor_args })).toSet + + def to_variables(exp: ProofCExpr): List[String] = exp match { + case Expressions.ProofCVar(name, typ) => + param_map.get(name) match { + case None if !clause_card_map.contains(name) => List(name) + case _ => List() + } + case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) + case Expressions.ProofCIfThenElse(cond, left, right) => + to_variables(cond) ++ to_variables(left) ++ to_variables(right) + case Expressions.ProofCBinaryExpr(op, left, right) => + to_variables(left) ++ to_variables(right) + case Expressions.ProofCUnaryExpr(op, e) => + to_variables(e) + case _ => List() + } + + def to_variables_heap(heap: VSTHeaplet): List[String] = heap match { + case Formulae.CDataAt(loc, elem_typ, count, elem) => + to_variables(loc) ++ to_variables(elem) + case Formulae.CSApp(pred, args, card) => + args.flatMap(to_variables).toList + } + + (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap): List[String]) .toSet .map((v: String) => (v, exist_map(v))).toList } @@ -602,11 +680,11 @@ object ProofTerms { /** returns the name of the associated cardinality datatype * for this predicate */ - def inductive_name : String = s"${name}_card" + def inductive_name: String = s"${name}_card" /** Given a cardinality constructor, return the Coq name of the * associated cardinality constructor */ - def constructor_name (constructor: CardConstructor) : String = { + def constructor_name(constructor: CardConstructor): String = { val count = constructor match { case CardNull => 0 case CardOf(args) => args.length @@ -616,16 +694,16 @@ object ProofTerms { /** formal parameters of the predicate. * This is the sequence of identifiers that will need to be passed to the predicate to instantiate it. */ - def formal_params : List[String] = params.map({case (a,_) => a}) ++ List("self_card") + def formal_params: List[String] = params.map({ case (a, _) => a }) ++ List("self_card") /** pretty print the constructor */ override def pp: String = { val constructor_map = base_constructors.map({ - case CardNull => (0, CardNull ) + case CardNull => (0, CardNull) case v@CardOf(args) => (args.length, v) }).toMap - def pp_constructor (constructor: CardConstructor) = { + def pp_constructor(constructor: CardConstructor) = { constructor match { case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" case CardOf(args) => @@ -635,18 +713,20 @@ object ProofTerms { val inductive_definition = { s"""Inductive ${inductive_name} : Set := - ${constructor_map.map({ case (_, constructor) => - s"| | ${pp_constructor(constructor)}" - }).mkString("\n")}. - | - |""".stripMargin + ${ + constructor_map.map({ case (_, constructor) => + s"| | ${pp_constructor(constructor)}" + }).mkString("\n") + }. + | + |""".stripMargin } // This function expands the arguments of a constructor and // creates recursive pattern matches if necassary - i.e // // S ((S b) as a) => ..... - def expand_args(sub_constructor: Map[String, CardConstructor]) (idents: List[Ident]) : String = { + def expand_args(sub_constructor: Map[String, CardConstructor])(idents: List[Ident]): String = { idents.map(arg => sub_constructor.get(arg) match { case Some(constructor) => @@ -657,23 +737,28 @@ object ProofTerms { } val predicate_definition = - s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})"}).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with - ${clauses.map({case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => - s"| | ${constructor_name(constructor)} ${ - expand_args(sub_constructor)(constructor.constructor_args) - } => ${ - val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) - val str = clause_existentials.map({case (name, ty) => s"| EX ${name} : ${ty.pp},"}).mkString("\n") - clause_existentials match { - case Nil => "" - case ::(_, _) => "\n" + str + "\n" - } - } ${ - (pure.map(v => s"!!${v.pp}") - ++ - List((spatial match { case Nil => List("emp") case v => v.map(_.pp)}).mkString(" * "))).mkString(" && ") - }" - }).mkString("\n")} + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with + ${ + clauses.map({ case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => + s"| | ${constructor_name(constructor)} ${ + expand_args(sub_constructor)(constructor.constructor_args) + } => ${ + val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) + val str = clause_existentials.map({ case (name, ty) => s"| EX ${name} : ${ty.pp}," }).mkString("\n") + clause_existentials match { + case Nil => "" + case ::(_, _) => "\n" + str + "\n" + } + } ${ + (pure.map(v => s"!!${v.pp}") + ++ + List((spatial match { + case Nil => List("emp") + case v => v.map(_.pp) + }).mkString(" * "))).mkString(" && ") + }" + }).mkString("\n") + } |end. |""".stripMargin s"${inductive_definition}${predicate_definition}" @@ -681,8 +766,6 @@ object ProofTerms { } - - } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 06bcf88b4..37d261cd4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -119,6 +119,7 @@ object CTranslation { case Var(name) => ctx.get(CVar(name)) case const: Const => const match { case IntConst(value) => Some (CIntType) + case LocConst(value) => Some (CVoidPtrType) case BoolConst(value) => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") } case BinaryExpr(op, left, right) => translate_op(op) @@ -182,6 +183,7 @@ object CTranslation { case CTypes.CVoidPtrPtrType => (ctx, CVoidPtrType) case CTypes.CIntPtrType => (ctx, CIntType) } + case CTypes.CVoidPtrType => (ctx, CVoidPtrType) case _ => ??? } case None => ??? diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 90f326aea..50a0b68fc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -108,7 +108,7 @@ object ProofTranslation { (pure, call) match { case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) case (ProofCBoolConst(_), ProofCBoolConst(_)) => context - case (ProofCIntConst(_), ProofCIntConst(_)) => context + case (ProofCIntConst(_,_), ProofCIntConst(_,_)) => context case (ProofCSetLiteral(elems), ProofCSetLiteral(call_elems)) => elems.zip(call_elems).foldLeft(context)({ case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr) }) case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => @@ -190,14 +190,21 @@ object ProofTranslation { Context(post, gamma, variable_map ++ new_equalities, functions, unfolded_expr :: queued_unfolds) } - def record_variable_assignment(name: String, expr: Expr)(context: Context): Context = + def record_variable_assignment(name: String, expr: Expr)(context: Context): Context = { + // when recording a mapping of a pointer, force any int constants to be pointers (suslik doesn't always place them in loc_consts) + val translated = ProofSpecTranslation.translate_expression(context.gamma)(expr) + val result = (context.gamma.get(name), translated) match { + case (Some (CoqPtrType), ProofCIntConst(value, _)) => ProofCIntConst(value,true) + case (_, translated) => translated + } Context( context.post, context.gamma, - (context.variable_map ++ Map(name -> ProofSpecTranslation.translate_expression(context.gamma)(expr))), + (context.variable_map ++ Map(name -> result)), context.functions, context.queued_unfolds ) + } def record_variable_assignment_card(name: String, expr: ProofCExpr)(context:Context) = Context( @@ -368,11 +375,15 @@ object ProofTranslation { // rename existential variables if they have been assigned fresh variables case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) }) - val new_context = add_new_variables(new_variables.toMap)(context) - val (args, constructor_args) = partition_cardinality_args(new_variables)(constructor.constructor_args) + val constructor_args = constructor.constructor_args.map(v => fresh_vars(Var(v)).name) + val new_context = add_new_variables( + new_variables.toMap ++ + constructor_args.map(v => (v, CoqCardType(pred.inductive_name))).toMap + )(context) + // val (args, constructor_args) = partition_cardinality_args(new_variables)() ((pred.constructor_name(constructor), constructor, constructor_args), ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), - args, + new_variables.map(_._1), translate_proof_rules(rule)(new_context)) }).toList ) @@ -453,11 +464,20 @@ object ProofTranslation { )} ) - ProofSteps.Forward( + ProofSteps.ForwardEntailer( instantiate_existentials(context.post)( - ProofSteps.Entailer ( - unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed)) - ) + context.post match { // If no existentials, only entailer will be at the end of the unfoldings + case Nil => + context.queued_unfolds match { + case Nil => ProofSteps.Qed + case ::(_, _) => unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed)) + } + case ::(_, _) => + context.queued_unfolds match { + case Nil => ProofSteps.Entailer (ProofSteps.Qed) + case ::(_, _) => ProofSteps.Entailer (unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed))) + } + } ) ) } @@ -565,7 +585,6 @@ object ProofTranslation { case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) } - def handle_close_rule(rule: ProofRule.Close, context: Context): ProofSteps = rule match { case ProofRule.Close(app, o_selector, asn, fresh_exist, next) => @@ -692,13 +711,7 @@ object ProofTranslation { val vst_proof: ProofSteps = translate_proof_rules(simplified)(initial_context) - println("Converted proof:") - println(vst_proof.pp) - - //Debug.visualize_ctree(root) - //Debug.visualize_proof_tree(root.kont) - - Proof(name, predicates, spec, vst_proof, contains_free(simplified)) + Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) } def contains_free(proof: ProofRule): Boolean = proof match { From 90140a801f14d681945c9e3fb54d4d3924b971ba Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 00:14:23 +0800 Subject: [PATCH 069/211] HTT: At branch points, intersect substitutions of child branches --- .../targets/htt/translation/IR.scala | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 5361b37d8..d723f5c7a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -72,10 +72,24 @@ object IR { val ctx : Context val next : Seq[Node] + private def mapJoin[T <: CExpr](m1: Map[CVar, T], m2: Map[CVar, T]) : Map[CVar, T] = { + m1.toSeq.intersect(m2.toSeq).toMap + } + def propagateContext: IR.Node = this match { case n:IR.EmpRule => n - case n:IR.Open => n.copy(next = n.next.map(_.propagateContext)) - case n:IR.AbduceBranch =>n.copy(next = n.next.map(_.propagateContext)) + case n:IR.Open => + val children = n.next.map(_.propagateContext) + val childCtxs = children.map(_.ctx) + val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) + val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) + n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar)) + case n:IR.AbduceBranch => + val children = n.next.map(_.propagateContext) + val childCtxs = children.map(_.ctx) + val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) + val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) + n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar)) case n:IR.Read => val next1 = n.next.head.propagateContext n.copy(ctx = next1.ctx, next = Seq(next1)) @@ -200,7 +214,7 @@ object IR { val ex = cclause.existentials.map(_.subst(csbst)) val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => IR.AbduceBranch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) case ProofRule.PureSynthesis(is_final, sbst, next) => val csbst = translateSbst(sbst) From c85287330b0be8afdd1a681b52ea866716aa2dd9 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:20:24 +0800 Subject: [PATCH 070/211] Revise CertTree node collection method to account for AbduceBranch The previous approach failed when AbduceBranch chose different parent goals for the if-branch and else branch. In the new method, collection and pruning are done in two separate phases. --- .../tygus/suslik/certification/CertTree.scala | 3 +- .../org/tygus/suslik/report/ProofTrace.scala | 114 +++++++++++------- 2 files changed, 71 insertions(+), 46 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index c6da069a5..a633072f0 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -71,7 +71,7 @@ object CertTree { // Candidate derivations exist; find and process the correct one val n = for { an <- ans - childOrs <- trace.childOrs(an) + childOrs = trace.childOrs(an) } yield { val node = Node(on.id, on.goal, an.kont, an.rule) val children = childOrs.map(traverse) @@ -83,6 +83,7 @@ object CertTree { n.head } } + trace.pruneFailed() traverse(trace.root) } diff --git a/src/main/scala/org/tygus/suslik/report/ProofTrace.scala b/src/main/scala/org/tygus/suslik/report/ProofTrace.scala index 0641df98c..486ed9fd0 100644 --- a/src/main/scala/org/tygus/suslik/report/ProofTrace.scala +++ b/src/main/scala/org/tygus/suslik/report/ProofTrace.scala @@ -10,6 +10,7 @@ import org.tygus.suslik.synthesis.SearchTree.{AndNode, OrNode, SearchNode} import org.tygus.suslik.synthesis.rules.Rules import upickle.default.{macroRW, ReadWriter => RW} +import scala.annotation.tailrec sealed abstract class ProofTrace { import ProofTrace._ @@ -129,64 +130,87 @@ object ProofTraceJson { class ProofTraceCert extends ProofTrace { import scala.collection.mutable - // The candidate derivations encountered for each OrNode - private val derivations: mutable.Map[OrNode, Seq[AndNode]] = mutable.Map.empty - // The solved subgoals for each AndNode - private val fulfilledSubgoals: mutable.Map[AndNode, Seq[OrNode]] = mutable.Map.empty - // The number of subgoals that need to be solved for each AndNode - private val numSubgoals: mutable.Map[AndNode, Int] = mutable.Map.empty - + val derivations: mutable.HashMap[OrNode, Seq[(Boolean, AndNode)]] = mutable.HashMap.empty + val subgoals: mutable.HashMap[AndNode, Seq[(Boolean, OrNode)]] = mutable.HashMap.empty + val failed: mutable.Set[OrNode] = mutable.Set.empty + val succeeded: mutable.Set[OrNode] = mutable.Set.empty var root: OrNode = _ - override def add(node: AndNode, nChildren: Int): Unit = numSubgoals(node) = nChildren + override def add(node: OrNode): Unit = { + node.parent match { + case None => root = node + case Some(an) => + subgoals.get(an) match { + case None => subgoals(an) = Seq((false, node)) + case Some(ons) => subgoals(an) = ons :+ (false, node) + } + } + } - override def add(node: SearchNode, status: GoalStatus, from: Option[String] = None): Unit = node match { - case node: OrNode => if (status.isInstanceOf[Memoization.Succeeded]) addOr(node) + override def add(node: AndNode, nChildren: Int): Unit = { + derivations.get(node.parent) match { + case None => derivations(node.parent) = Seq((false, node)) + case Some(ans) => derivations(node.parent) = ans :+ (false, node) + } + } + + override def add(node: SearchNode, status: GoalStatus, from: Option[String] = None): Unit = (node, status) match { + case (node: OrNode, Memoization.Succeeded(_)) => + succeeded.add(node) + case (node: OrNode, Memoization.Failed) => + failed.add(node) case _ => } override def add(result: Rules.RuleResult, parent: OrNode): Unit = { - val an = AndNode(-1 +: parent.id, parent, result) - addAnd(an) + if (result.subgoals.isEmpty) { + val an = AndNode(-1 +: parent.id, parent, result) + derivations.get(parent) match { + case None => derivations(parent) = Seq((true, an)) + case Some(ans) => derivations(parent) = ans :+ (true, an) + } + succeeded.add(parent) + } } - private def addAnd(node: AndNode): Unit = { - val parentOr = node.parent - derivations.get(parentOr) match { - case Some(ans) => derivations(parentOr) = ans :+ node - case None => derivations(parentOr) = Seq(node) - } - addOr(parentOr) // Continue adding ancestors - } - - private def addOr(node: OrNode): Unit = node.parent match { - case Some(parentAn) => - fulfilledSubgoals.get(parentAn) match { - case Some(ons) => - if (!ons.contains(node)) { - // Make sure new goal respects ordering of subgoals by goal id - val idx = scala.math.min(node.id.head, ons.length) - val (fst, snd) = ons.splitAt(idx) - fulfilledSubgoals(parentAn) = fst ++ Seq(node) ++ snd - } // Terminate (node already encountered) - case None => - fulfilledSubgoals(parentAn) = Seq(node) - addAnd(parentAn) // Continue adding ancestors + @tailrec + private def handleFail(node: OrNode, original: OrNode): Unit = node.parent match { + case None => + case Some(an) => + derivations(an.parent) = derivations(an.parent).filterNot(_._2.id == an.id) + if (!derivations(an.parent).exists(_._1)) { + // This goal has no more viable candidate derivations, so can prune further up the tree + handleFail(an.parent, original) } + } + + @tailrec + private def handleSuccess(node: OrNode): Unit = node.parent match { case None => - root = node // Terminate (node is root) + case Some(an) => + val newOns = updatePeerStatus(node, subgoals(an), newStatus = true) + subgoals(an) = newOns + if (newOns.forall(_._1)) { + derivations(an.parent) = updatePeerStatus(an, derivations(an.parent), newStatus = true) + handleSuccess(an.parent) + } + } + + def pruneFailed(): Unit = { + for (s <- succeeded) { + handleSuccess(s) + } + for (f <- failed) { + handleFail(f, f) + } } - // Retrieve candidate derivations for a goal - def childAnds(node: OrNode): Seq[AndNode] = derivations.getOrElse(node, Seq.empty) + private def updatePeerStatus[T <: SearchNode](node: T, peers: Seq[(Boolean, T)], newStatus: Boolean): Seq[(Boolean, T)] = + peers.map { case (status, n) => if (n.id == node.id) (newStatus, n) else (status, n )} - // Retrieve subgoals for a derivation if it succeeded - def childOrs(node: AndNode): Option[Seq[OrNode]] = fulfilledSubgoals.get(node) match { - // All subgoals were solved, so this derivation was successful - case Some(ors) if ors.length == numSubgoals(node) => Some(ors) - // This is a terminal node - case None => Some(Seq.empty) - // This is a non-terminal node, but not all subgoals were solved - case _ => None + def childAnds(node: OrNode): Seq[AndNode] = { + derivations.getOrElse(node, Seq.empty).filter(_._1).map(_._2) } + def childOrs(node: AndNode): Seq[OrNode] = + subgoals.getOrElse(node, Seq.empty).filter(_._1).map(_._2) } From 3983405dade923e5574ebd292f581bc0dccf5731 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:21:36 +0800 Subject: [PATCH 071/211] Add listmorph example to certification benchmarks --- .../resources/synthesis/certification/list/common.def | 5 +++++ .../resources/synthesis/certification/list/listmorph.syn | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 src/test/resources/synthesis/certification/list/listmorph.syn diff --git a/src/test/resources/synthesis/certification/list/common.def b/src/test/resources/synthesis/certification/list/common.def index 6636bc06d..3ce0e9a59 100644 --- a/src/test/resources/synthesis/certification/list/common.def +++ b/src/test/resources/synthesis/certification/list/common.def @@ -2,3 +2,8 @@ predicate lseg(loc x, set s) { | x == null => { s =i {} ; emp } | not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } } + +predicate lseg2(loc x, loc y, set s) { +| x == y => { s =i {} ; emp } +| not (x == y) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg2(nxt, y, s1) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/list/listmorph.syn b/src/test/resources/synthesis/certification/list/listmorph.syn new file mode 100644 index 000000000..a55d64958 --- /dev/null +++ b/src/test/resources/synthesis/certification/list/listmorph.syn @@ -0,0 +1,9 @@ +Morphs a list from one representation to another + +### + +{true ; r :-> null ** lseg2(x, null, s)} +void listmorph(loc x, loc r) +{true ; r :-> y ** lseg(y, s) } + +##### From ae1ce33053b54d6bcf533749831ccdc6afbdebc8 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:38:56 +0800 Subject: [PATCH 072/211] Finalize the locations of all branches abduced by AbduceBranch Make ProofRule.AbudceBranch capture the goal label of the branch destination. --- .../suslik/certification/ProofRule.scala | 737 +++++++++++------- .../targets/htt/translation/IR.scala | 3 +- .../vst/translation/ProofTranslation.scala | 10 +- 3 files changed, 453 insertions(+), 297 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 25a349c7f..5d47a2ed9 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} -import org.tygus.suslik.logic.Specifications.{Assertion, Goal, SuspendedCallGoal} +import org.tygus.suslik.logic.Specifications.{Assertion, Goal, GoalLabel, SuspendedCallGoal} import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules._ @@ -17,7 +17,8 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ sealed abstract class ProofRule extends PrettyPrinting { - + val next1: Seq[ProofRule] + val cardinality: Int = 1 } object ProofRule { @@ -38,62 +39,77 @@ object ProofRule { /** corresponds to asserting all the variables in vars are not null */ case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" } /** solves a pure entailment with SMT */ case class CheckPost(prePhi: PFormula, postPhi: PFormula, next:ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next.pp}" } /** picks an arbitrary instantiation of the proof rules */ case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" } /** abduces a condition for the proof */ - case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { + case class AbduceBranch(cond: Expr, bLabel: GoalLabel, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(ifTrue, ifFalse) + override val cardinality: Int = 2 override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" } /** write a value */ case class Write(stmt: Store, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" } /** weaken the precondition by removing unused formulae */ case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" } /** empty rule */ case object EmpRule extends ProofRule { + val next1: Seq[ProofRule] = Seq.empty + override val cardinality: Int = 0 override def pp: String = s"${ind}EmpRule;" } /** pure synthesis rules */ case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" } /** open constructor cases */ case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[(Expr, ProofRule)]) extends ProofRule { + val next1: Seq[ProofRule] = cases.map(_._2) + override val cardinality: Int = cases.length override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" } /** subst L */ case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" } /** subst R */ case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" } /** read rule */ case class Read(map: Map[Var,Var], operation: Load, next:ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" } @@ -109,67 +125,87 @@ case class AbduceCall( gamma: Gamma, next: ProofRule ) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" } /** unification of heap (ignores block/pure distinction) */ case class HeapUnify(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next.pp}" } /** unification of pointers */ case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" } /** unfolds frame */ case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" } /** call operation */ case class Call(subst: Map[Var, Expr], call: Statements.Call, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next.pp}" } /** free operation */ case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" } /** malloc rule */ case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" } /** close rule */ case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" } /** star partial */ case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" } case class PickCard(map: Map[Var,Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next.pp}" } case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" } case class Init(goal: Goal, next: ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(next) override def pp: String = s"${ind}Init(${goal.pp});\n${next.pp}" } case object Inconsistency extends ProofRule { + val next1: Seq[ProofRule] = Seq.empty + override val cardinality: Int = 0 override def pp: String = "Inconsistency" } + case class Branch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { + val next1: Seq[ProofRule] = Seq(ifTrue, ifFalse) + override val cardinality: Int = 2 + override def pp: String = s"${ind}Branch($cond);\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + } + /** converts a Suslik CertTree node into the unified ProofRule structure */ def of_certtree(node: CertTree.Node): ProofRule = { def fail_with_bad_proof_structure(): Nothing = @@ -177,303 +213,422 @@ case class AbduceCall( def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") - node.rule match { - case LogicalRules.NilNotLval => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - // find all pointers that are not yet known to be non-null - def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { - // All pointers - val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet - allPointers.filter( - x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) - ) - } - - val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList - - node.children match { - case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case FailRules.CheckPost => node.kont match { - case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => ProofRule.CheckPost(prePhi, postPhi, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) + def visit(node: CertTree.Node): (ProofRule, Map[ProofRule, GoalLabel]) = { + val (r, labels: Map[ProofRule, GoalLabel]) = node.rule match { + case LogicalRules.NilNotLval => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + // find all pointers that are not yet known to be non-null + def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { + // All pointers + val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet + allPointers.filter( + x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) + ) + } + + val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList + + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.NilNotLval(pre_pointers, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.Pick => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Pick(map, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case FailRules.AbduceBranch => node.kont match { - case GuardedProducer(cond, _) => - node.children match { - case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, of_certtree(if_true), of_certtree(if_false)) - case ls => fail_with_bad_children(ls, 2) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.WriteRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Write(stmt, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.Inconsistency => node.kont match { - case ConstProducer(Error) => - node.children match { - case Nil => ProofRule.Inconsistency - case ls => fail_with_bad_children(ls, 0) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.WeakenPre => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) - node.children match { - case ::(head, Nil) => ProofRule.WeakenPre(unused, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.EmpRule => node.kont match { - case ConstProducer(Skip) => - node.children match { - case Nil => ProofRule.EmpRule - case ls => fail_with_bad_children(ls, 0) - } - case _ => fail_with_bad_proof_structure() - } - case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { + case FailRules.CheckPost => node.kont match { + case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { case ::(head, Nil) => - ProofRule.PureSynthesis(is_final = true, assignments, of_certtree(head)) + val (next, labels) = visit(head) + (ProofRule.CheckPost(prePhi, postPhi, next), labels) case ls => fail_with_bad_children(ls, 1) } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Open => node.kont match { - case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => - ProofRule.Open(pred, fresh_vars, sbst, selectors.zip(node.children).map({ case (expr, node) => (expr, of_certtree(node)) }).toList) - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.SubstLeft => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.SubstL(map, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.SubstRight => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.SubstR(map, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.ReadRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Read(map, stmt, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.AbduceCall => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(AbduceCallProducer(f), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - // find out which new variables were added to the context - val new_vars = - head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) - val f_pre = head.goal.post - var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get - ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPointer => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => ProofRule.HeapUnifyPointer(subst, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfoldingFinal => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameBlock => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameFlat => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => ProofRule.Call(subst, call, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.FreeRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Statements.Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => - val size: Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { - case Some(value) => value - case None => 1 - } - node.children match { - case ::(head, Nil) => ProofRule.Free(stmt, size, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.AllocRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - ProofRule. - Malloc(map, stmt, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Close => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - ProofRule.Close(app, selector, asn, fresh_exist, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - } - case LogicalRules.StarPartial => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) - val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.Pick => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Pick(map, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case FailRules.AbduceBranch => node.kont match { + case GuardedProducer(cond, bgoal) => + node.children match { + case ::(if_true, ::(if_false, Nil)) => + val (if_true1, labels) = visit(if_true) + val (if_false1, labels1) = visit(if_false) + (ProofRule.AbduceBranch(cond, bgoal.label, if_true1, if_false1), labels ++ labels1) + case ls => fail_with_bad_children(ls, 2) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.WriteRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Write(stmt, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.Inconsistency => node.kont match { + case ConstProducer(Error) => + node.children match { + case Nil => (ProofRule.Inconsistency, Map.empty) + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.WeakenPre => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.WeakenPre(unused, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.EmpRule => node.kont match { + case ConstProducer(Skip) => + node.children match { + case Nil => (ProofRule.EmpRule, Map.empty) + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.PureSynthesis(is_final = true, assignments, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Open => node.kont match { + case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => + val (next, labels) = node.children.map(visit).unzip + (ProofRule.Open(pred, fresh_vars, sbst, selectors.zip(next).toList), labels.reduceOption[Map[ProofRule, GoalLabel]](_ ++ _).getOrElse(Map.empty)) + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.SubstLeft => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.SubstL(map, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.SubstRight => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.SubstR(map, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.ReadRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Read(map, stmt, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.AbduceCall => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(AbduceCallProducer(f), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + // find out which new variables were added to the context + val new_vars = + head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) + val f_pre = head.goal.post + var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get + val (next, labels) = visit(head) + (ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPure => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.HeapUnify(subst, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.HeapUnify(subst, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyBlock => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.HeapUnify(subst, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPointer => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.HeapUnifyPointer(subst, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + val (next, labels) = visit(head) + (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfoldingFinal => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + val (next, labels) = visit(head) + (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + val (next, labels) = visit(head) + (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameFlat => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + val (next, labels) = visit(head) + (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.CallRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Call(subst, call, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.FreeRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Statements.Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => + val size: Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { + case Some(value) => value + case None => 1 + } + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Free(stmt, size, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.AllocRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Malloc(map, stmt, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Close => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.Close(app, selector, asn, fresh_exist, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + } + case LogicalRules.StarPartial => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) + val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) + + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.StarPartial(new_pre_phi, new_post_phi, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } - node.children match { - case ::(head, Nil) => - ProofRule.StarPartial(new_pre_phi, new_post_phi, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() + case UnificationRules.PickCard => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.PickCard(map, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + } + case UnificationRules.PickArg => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val (next, labels) = visit(head) + (ProofRule.PickArg(map, next), labels) + case ls => fail_with_bad_children(ls, 1) + } + } } + (r, labels + (r -> node.goal.label)) + } + val (root, labels) = visit(node) + finalize_branches(root, labels) + } - case UnificationRules.PickCard => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => ProofRule.PickCard(map, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } + /** + * + * Finalizes the branches abduced by applications of the AbduceBranch rule. + * + * Consider in the left diagram, an AbduceBranch node B with children C (true case) and D (false case), and + * intended branch destination A. This procedure inserts a new Branch node E with A as its true case child; the + * inserted node E is meant to denote a finalized branching point. + * + * D--- D--- D--- + * / => / / + * --A-----B-C--- --E-A-----B-C--- + * + * @param node the root node of the tree + * @param labels the goal label associated with each proof rule, used to look up the branch destination + * @return a copy of the tree with finalized branches inserted + */ + def finalize_branches(node: ProofRule, labels: Map[ProofRule, GoalLabel]): ProofRule = { + def collect_branch_abductions(node: ProofRule, acc: Set[AbduceBranch] = Set.empty): Set[AbduceBranch] = node match { + case ab:AbduceBranch => node.next1.foldLeft(acc + ab){ case (acc, next) => collect_branch_abductions(next, acc) } + case _ => node.next1.foldLeft(acc){ case (acc, next) => collect_branch_abductions(next, acc) } + } + def apply_branch_abductions(node: ProofRule, known_abductions: Set[AbduceBranch]) : ProofRule = { + def with_next(node: ProofRule, next: Seq[ProofRule]): ProofRule = node match { + case r:ProofRule.NilNotLval=> r.copy(next = next.head) + case r:ProofRule.CheckPost => r.copy(next = next.head) + case r:ProofRule.Pick => r.copy(next = next.head) + case r:ProofRule.AbduceBranch => + val Seq(ifTrue, ifFalse) = next + r.copy(ifTrue = ifTrue, ifFalse = ifFalse) + case r:ProofRule.Branch => + val Seq(ifTrue, ifFalse) = next + r.copy(ifTrue = ifTrue, ifFalse = ifFalse) + case r:ProofRule.Write => r.copy(next = next.head) + case r:ProofRule.WeakenPre => r.copy(next = next.head) + case r:ProofRule.PureSynthesis => r.copy(next = next.head) + case r:ProofRule.Open => r.copy(cases = r.cases.zip(next).map(c => (c._1._1, c._2))) + case r:ProofRule.SubstL => r.copy(next = next.head) + case r:ProofRule.SubstR => r.copy(next = next.head) + case r:ProofRule.Read => r.copy(next = next.head) + case r:ProofRule.AbduceCall => r.copy(next = next.head) + case r:ProofRule.HeapUnify => r.copy(next = next.head) + case r:ProofRule.HeapUnifyPointer => r.copy(next = next.head) + case r:ProofRule.FrameUnfold => r.copy(next = next.head) + case r:ProofRule.Call => r.copy(next = next.head) + case r:ProofRule.Free => r.copy(next = next.head) + case r:ProofRule.Malloc => r.copy(next = next.head) + case r:ProofRule.Close => r.copy(next = next.head) + case r:ProofRule.StarPartial => r.copy(next = next.head) + case r:ProofRule.PickCard => r.copy(next = next.head) + case r:ProofRule.PickArg => r.copy(next = next.head) + case r:ProofRule.Init => r.copy(next = next.head) + case ProofRule.EmpRule => ProofRule.EmpRule + case ProofRule.Inconsistency => ProofRule.Inconsistency } - case UnificationRules.PickArg => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => ProofRule.PickArg(map, of_certtree(head)) - case ls => fail_with_bad_children(ls, 1) - } + val nodeLabel = labels(node) + known_abductions.find(_.bLabel == nodeLabel) match { + case Some(AbduceBranch(cond, bLabel, ifTrue, ifFalse)) => + assert(node.cardinality == 1) + val ifTrue1 = with_next(node, Seq(apply_branch_abductions(node.next1.head, known_abductions))) + val ifFalse1 = apply_branch_abductions(ifFalse, known_abductions) + Branch(cond, ifTrue1, ifFalse1) + case None => + val next = node.next1.map(next => apply_branch_abductions(next, known_abductions)) + with_next(node, next) } } + val abductions = collect_branch_abductions(node) + apply_branch_abductions(node, abductions) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index d723f5c7a..8c4e976f6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -214,7 +214,7 @@ object IR { val ex = cclause.existentials.map(_.subst(csbst)) val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => + case ProofRule.Branch(cond, ifTrue, ifFalse) => IR.AbduceBranch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) case ProofRule.PureSynthesis(is_final, sbst, next) => val csbst = translateSbst(sbst) @@ -269,5 +269,6 @@ object IR { case ProofRule.PickCard(_, next) => fromRule(next, ctx) case ProofRule.FrameUnfold(h_pre, h_post, next) => fromRule(next, ctx) case ProofRule.Inconsistency => IR.Inconsistency(ctx) + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => fromRule(ifTrue, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 5c58abcd0..34a8795a4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -298,7 +298,7 @@ object ProofTranslation { case ProofRule.Pick(subst, next) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => is_variable_used_in_exp(variable)(cond) || is_variable_used_in_proof(variable)(ifTrue) || is_variable_used_in_proof(variable)(ifFalse) @@ -559,7 +559,7 @@ object ProofTranslation { } def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofSteps = rule match { - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => ProofSteps.ForwardIf(List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) @@ -665,7 +665,7 @@ object ProofTranslation { rule match { // Branching rules case rule@ProofRule.Open(SApp(_, _, _, Var(_)), _, _, _) => handle_open_rule(rule, context) - case rule@ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => handle_abduce_branch_rule(rule, context) + case rule@ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => handle_abduce_branch_rule(rule, context) // Read and write Operations case rule@ProofRule.Write(_, _) => handle_write_rule(rule, context) @@ -718,7 +718,7 @@ object ProofTranslation { case ProofRule.NilNotLval(vars, next) => contains_free(next) case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_free(next) case ProofRule.Pick(subst, next) => contains_free(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) case ProofRule.Write(stmt, next) => contains_free(next) case ProofRule.WeakenPre(unused, next) => contains_free(next) case ProofRule.EmpRule => false @@ -744,7 +744,7 @@ object ProofTranslation { case ProofRule.NilNotLval(vars, next) => contains_malloc(next) case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_malloc(next) case ProofRule.Pick(subst, next) => contains_malloc(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) + case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) case ProofRule.Write(stmt, next) => contains_malloc(next) case ProofRule.WeakenPre(unused, next) => contains_malloc(next) case ProofRule.EmpRule => false From 297de947bdc984aa1d3778cdf350c7d6b35ee0f3 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:47:23 +0800 Subject: [PATCH 073/211] HTT: Make read rule pruning sensitive to branch selectors --- .../suslik/certification/targets/htt/logic/Proof.scala | 4 ++-- .../targets/htt/translation/ProofTranslation.scala | 8 +++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 7b81991e2..a720ef618 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -96,7 +96,7 @@ object Proof { s"ssl_dealloc $ptr" } } - case object Open extends Step { + case class Open(selectors: Seq[CExpr]) extends Step { def pp: String = "ssl_open" } case class OpenPost(app: CSApp) extends Step { @@ -117,7 +117,7 @@ object Proof { case object GhostElimPost extends Step { def pp: String = "ssl_ghostelim_post" } - case object AbduceBranch extends Step { + case class AbduceBranch(cond: CExpr) extends Step { def pp: String = "ssl_abduce_branch" } case object Emp extends Step { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 468c975fd..810375d3f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -129,8 +129,8 @@ object ProofTranslation { visit(next.head) case IR.Close(_, _, _, _, next, _) => visit(next.head) - case IR.AbduceBranch(_, next, _) => - Proof.AbduceBranch >> Proof.Branch(next.map(visit)) + case IR.AbduceBranch(cond, next, ctx) => + Proof.AbduceBranch(cond.subst(ctx.substVar)) >> Proof.Branch(next.map(visit)) case IR.Open(app, clauses, selectors, next, ctx) => val branchSteps = next.zip(clauses).map { case (n, c) => val c1 = c.subst(n.ctx.substVar) @@ -145,7 +145,7 @@ object ProofTranslation { res } } - Proof.Open >> Proof.Branch(branchSteps) + Proof.Open(selectors.map(_.subst(ctx.substVar))) >> Proof.Branch(branchSteps) case IR.Inconsistency(_) => Proof.Error case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) } @@ -195,6 +195,8 @@ object ProofTranslation { case Proof.Alloc(to, tpe, sz) => (step, Set(to)) case Proof.Dealloc(v, offset) => (step, Set(v)) case Proof.Call(args, _) => (step, args.flatMap(_.vars).toSet) + case Proof.Open(selectors) => (step, selectors.flatMap(_.vars).toSet) + case Proof.AbduceBranch(cond) => (step, cond.vars.toSet) case _ => (step, Set.empty) } visit(step)._1 From f966348859358e94502ef7a387987a64216ab08b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:48:31 +0800 Subject: [PATCH 074/211] HTT: Rename AbduceBranch -> Branch --- .../certification/targets/htt/logic/Proof.scala | 6 +++--- .../certification/targets/htt/translation/IR.scala | 6 +++--- .../htt/translation/ProgramTranslation.scala | 2 +- .../targets/htt/translation/ProofTranslation.scala | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index a720ef618..28a977bba 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -12,12 +12,12 @@ object Proof { def simplify: Step = this match { case SeqComp(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqComp(s1.simplify, s2.simplify) case SeqCompAlt(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqCompAlt(s1.simplify, s2.simplify) - case Branch(branches) => Branch(branches.map(_.simplify)) + case SubProof(branches) => SubProof(branches.map(_.simplify)) case _ => this } } - case class Branch(branches: Seq[Step]) extends Step { + case class SubProof(branches: Seq[Step]) extends Step { override val isNoop: Boolean = branches.forall(_.isNoop) def pp: String = branches.map(_.pp).mkString(".\n") } @@ -117,7 +117,7 @@ object Proof { case object GhostElimPost extends Step { def pp: String = "ssl_ghostelim_post" } - case class AbduceBranch(cond: CExpr) extends Step { + case class Branch(cond: CExpr) extends Step { def pp: String = "ssl_abduce_branch" } case object Emp extends Step { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 8c4e976f6..848d30663 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -84,7 +84,7 @@ object IR { val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar)) - case n:IR.AbduceBranch => + case n:IR.Branch => val children = n.next.map(_.propagateContext) val childCtxs = children.map(_.ctx) val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) @@ -137,7 +137,7 @@ object IR { case class Close(app: CSApp, selector: CExpr, asn: CAssertion, fresh_exist: CSubstVar, next: Seq[Node], ctx: Context) extends Node - case class AbduceBranch(cond: CExpr, next: Seq[Node], ctx: Context) extends Node + case class Branch(cond: CExpr, next: Seq[Node], ctx: Context) extends Node case class PureSynthesis(is_final: Boolean, next: Seq[Node], ctx: Context) extends Node @@ -215,7 +215,7 @@ object IR { val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) case ProofRule.Branch(cond, ifTrue, ifFalse) => - IR.AbduceBranch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) + IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) case ProofRule.PureSynthesis(is_final, sbst, next) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala index 474635362..8a8323270 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala @@ -14,7 +14,7 @@ object ProgramTranslation { case IR.Malloc(stmt, next, _) => stmt >> visit(next.head) case IR.Read(stmt, next, _) => stmt >> visit(next.head) case IR.Write(stmt, next, _) => stmt >> visit(next.head) - case IR.AbduceBranch(cond, Seq(tb, eb), _) => CIf(cond, visit(tb), visit(eb)) + case IR.Branch(cond, Seq(tb, eb), _) => CIf(cond, visit(tb), visit(eb)) case IR.Open(app, clauses, selectors, next, ctx) => val cond_branches = selectors.zip(next.map(visit)).reverse val ctail = cond_branches.tail diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 810375d3f..67221dd87 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -129,8 +129,8 @@ object ProofTranslation { visit(next.head) case IR.Close(_, _, _, _, next, _) => visit(next.head) - case IR.AbduceBranch(cond, next, ctx) => - Proof.AbduceBranch(cond.subst(ctx.substVar)) >> Proof.Branch(next.map(visit)) + case IR.Branch(cond, next, ctx) => + Proof.Branch(cond.subst(ctx.substVar)) >> Proof.SubProof(next.map(visit)) case IR.Open(app, clauses, selectors, next, ctx) => val branchSteps = next.zip(clauses).map { case (n, c) => val c1 = c.subst(n.ctx.substVar) @@ -145,7 +145,7 @@ object ProofTranslation { res } } - Proof.Open(selectors.map(_.subst(ctx.substVar))) >> Proof.Branch(branchSteps) + Proof.Open(selectors.map(_.subst(ctx.substVar))) >> Proof.SubProof(branchSteps) case IR.Inconsistency(_) => Proof.Error case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) } @@ -161,7 +161,7 @@ object ProofTranslation { visit(next.head, hints :+ Hint.PureEntailment(prePhi, postPhi)) case _:IR.Inconsistency | _:IR.EmpRule => hints - case _:IR.Open | _:IR.AbduceBranch => + case _:IR.Open | _:IR.Branch => node.next.foldLeft(hints){ case (hints, next) => visit(next, hints) } case _ => visit(node.next.head, hints) @@ -186,9 +186,9 @@ object ProofTranslation { case Proof.Read(to, _, _) if !s2Used.contains(to) => (s2New, s2Used) case _ => (s1New >>> s2New, s1Used ++ s2Used) } - case Proof.Branch(branches) => + case Proof.SubProof(branches) => val (branchesNew, branchesUsed) = branches.map(visit).unzip - (Proof.Branch(branchesNew), branchesUsed.reduce(_ ++ _)) + (Proof.SubProof(branchesNew), branchesUsed.reduce(_ ++ _)) case Proof.Read(to, from, offset) => (step, Set(to, from)) case Proof.Write(to, offset, e) => (step, Set(to) ++ e.vars.toSet) case Proof.WritePost(to, offset) => (step, Set(to)) @@ -196,7 +196,7 @@ object ProofTranslation { case Proof.Dealloc(v, offset) => (step, Set(v)) case Proof.Call(args, _) => (step, args.flatMap(_.vars).toSet) case Proof.Open(selectors) => (step, selectors.flatMap(_.vars).toSet) - case Proof.AbduceBranch(cond) => (step, cond.vars.toSet) + case Proof.Branch(cond) => (step, cond.vars.toSet) case _ => (step, Set.empty) } visit(step)._1 From 1aa33443edb0a756cc418bde80188bbfe4d497bd Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 01:48:50 +0800 Subject: [PATCH 075/211] HTT: Small pretty-printing tweaks --- .../certification/targets/htt/language/Expressions.scala | 7 +++++-- .../suslik/certification/targets/htt/logic/Sentences.scala | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index a54e4f5c8..f600aca84 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -131,7 +131,10 @@ object Expressions { } case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { - override def pp: String = s"${op.pp} (${e.pp})" + override def pp: String = op match { + case COpNot => s"(${e.pp}) = false" + case _ => s"${op.pp} (${e.pp})" + } } case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { @@ -147,7 +150,7 @@ object Expressions { override def subst(sigma: CSubst): CSApp = CSApp(pred, args.map(_.subst(sigma)), card.subst(sigma)) - val uniqueName: String = s"${pred}_${args.flatMap(_.vars).map(_.pp).mkString("")}_${card.pp}" + val uniqueName: String = s"${pred}_${args.flatMap(_.vars).map(_.pp).mkString("")}_${card.vars.map(_.pp).mkString("")}" val heapName: String = s"h_$uniqueName" val hypName: String = s"H_$uniqueName" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 0ceaf320c..6d1b8364b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -72,7 +72,7 @@ object Sentences { // clauses val clausesStr = for { CInductiveClause(pred, idx, selector, asn, _) <- clauses - } yield s"| $pred$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1, gamma)}" + } yield s"| ${pred}_$idx of ${selector.pp} of\n${asn.ppQuantified(paramVars, 1, gamma)}" builder.append(s"${clausesStr.mkString("\n")}.\n") builder.toString() From e2f164efe1ec2bd051357d183d337cb270f3d4ee Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 02:19:48 +0800 Subject: [PATCH 076/211] VST: Temporary handling of Branch rules --- .../targets/vst/translation/ProofTranslation.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 34a8795a4..343aa70e0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -703,6 +703,7 @@ object ProofTranslation { case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => translate_proof_rules(next)(context) + case ProofRule.Branch(cond, ifTrue, ifFalse) => translate_proof_rules(ifTrue)(context) } } @@ -738,6 +739,7 @@ object ProofTranslation { case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_free(next) case ProofRule.PickCard(_, next) => contains_free(next) case ProofRule.PickArg(map, next) => contains_free(next) + case ProofRule.Branch(cond, ifTrue, ifFalse) => contains_free(ifTrue) } def contains_malloc(proof: ProofRule): Boolean = proof match { @@ -764,6 +766,7 @@ object ProofTranslation { case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_malloc(next) case ProofRule.PickCard(_, next) => contains_malloc(next) case ProofRule.PickArg(map, next) => contains_malloc(next) + case ProofRule.Branch(cond, ifTrue, ifFalse) => contains_malloc(ifTrue) } } From b7d7606d425c084eb0e362d415350581d4229ffe Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 23 Jan 2021 23:04:01 +0800 Subject: [PATCH 077/211] Create wrapper class around ProofRule to isolate traversal-specific info --- .../suslik/certification/ProofRule.scala | 336 +++++++----------- .../targets/htt/translation/IR.scala | 86 ++--- .../targets/htt/translation/Translation.scala | 2 +- .../vst/translation/ProofTranslation.scala | 286 +++++++-------- 4 files changed, 284 insertions(+), 426 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 5d47a2ed9..91477c3c1 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -17,8 +17,6 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ sealed abstract class ProofRule extends PrettyPrinting { - val next1: Seq[ProofRule] - val cardinality: Int = 1 } object ProofRule { @@ -36,81 +34,78 @@ object ProofRule { result } + case class Node(rule: ProofRule, label: GoalLabel, next: Seq[Node]) extends PrettyPrinting { + override def pp: String = rule match { + case ProofRule.AbduceBranch(cond, bLabel) => + val Seq(ifTrue, ifFalse) = next + s"$ind${rule.pp}\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + case ProofRule.Open(pred, fresh_vars, sbst, selectors) => + val cases = selectors.zip(next) + s"$ind${rule.pp}\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + case _ => + s"$ind${rule.pp}\n${next.map(_.pp).mkString("")}" + } + } /** corresponds to asserting all the variables in vars are not null */ - case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" + case class NilNotLval(vars: List[Expr]) extends ProofRule { + override def pp: String = s"NilNotLval(${vars.map(_.pp).mkString(", ")});" } /** solves a pure entailment with SMT */ - case class CheckPost(prePhi: PFormula, postPhi: PFormula, next:ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next.pp}" + case class CheckPost(prePhi: PFormula, postPhi: PFormula) extends ProofRule { + override def pp: String = s"CheckPost(${prePhi.pp}; ${postPhi.pp});" } /** picks an arbitrary instantiation of the proof rules */ - case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" + case class Pick(subst: Map[Var, Expr]) extends ProofRule { + override def pp: String = s"Pick(${subst.mkString(", ")});" } /** abduces a condition for the proof */ - case class AbduceBranch(cond: Expr, bLabel: GoalLabel, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(ifTrue, ifFalse) - override val cardinality: Int = 2 - override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + case class AbduceBranch(cond: Expr, bLabel: GoalLabel) extends ProofRule { + override def pp: String = s"AbduceBranch(${cond});" } /** write a value */ - case class Write(stmt: Store, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" + case class Write(stmt: Store) extends ProofRule { + override def pp: String = s"Write(${sanitize(stmt.pp)});" } /** weaken the precondition by removing unused formulae */ - case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" + case class WeakenPre(unused: PFormula) extends ProofRule { + override def pp: String = s"WeakenPre(${unused.pp});" } /** empty rule */ case object EmpRule extends ProofRule { - val next1: Seq[ProofRule] = Seq.empty - override val cardinality: Int = 0 - override def pp: String = s"${ind}EmpRule;" + override def pp: String = s"EmpRule;" } /** pure synthesis rules */ - case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr]) extends ProofRule { + override def pp: String = s"PureSynthesis(${is_final}, ${assignments.mkString(",")});" } /** open constructor cases */ - case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[(Expr, ProofRule)]) extends ProofRule { - val next1: Seq[ProofRule] = cases.map(_._2) - override val cardinality: Int = cases.length - override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends ProofRule { + override def pp: String = s"Open(${pred.pp}, ${fresh_vars.mkString(", ")});" } /** subst L */ - case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" + case class SubstL(map: Map[Var, Expr]) extends ProofRule { + override def pp: String = s"SubstL(${map.mkString(",")});" } /** subst R */ - case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" + case class SubstR(map: Map[Var, Expr]) extends ProofRule { + override def pp: String = s"SubstR(${map.mkString(",")});" } /** read rule */ - case class Read(map: Map[Var,Var], operation: Load, next:ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" + case class Read(map: Map[Var,Var], operation: Load) extends ProofRule { + override def pp: String = s"Read(${map.mkString(",")}, ${sanitize(operation.pp)});" } // /** abduce a call */ @@ -122,99 +117,82 @@ case class AbduceCall( freshSub: SubstVar, freshToActual: Subst, f: FunSpec, - gamma: Gamma, - next: ProofRule + gamma: Gamma ) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" + override def pp: String = s"AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" } /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next.pp}" + case class HeapUnify(subst: Map[Var, Expr]) extends ProofRule { + override def pp: String = s"HeapUnify(${subst.mkString(",")});" } /** unification of pointers */ - case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" + case class HeapUnifyPointer(map: Map[Var,Expr]) extends ProofRule { + override def pp: String = s"HeapUnifyPointer(${map.mkString(",")});" } /** unfolds frame */ - case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet) extends ProofRule { + override def pp: String = s"FrameUnfold(${h_pre.pp}, ${h_post.pp});" } /** call operation */ - case class Call(subst: Map[Var, Expr], call: Statements.Call, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next.pp}" + case class Call(subst: Map[Var, Expr], call: Statements.Call) extends ProofRule { + override def pp: String = s"Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } /** free operation */ - case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" + case class Free(stmt: Statements.Free, size: Int) extends ProofRule { + override def pp: String = s"Free(${sanitize(stmt.pp)});" } /** malloc rule */ - case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" + case class Malloc(map: SubstVar, stmt: Statements.Malloc) extends ProofRule { + override def pp: String = s"Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" } /** close rule */ - case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends ProofRule { + override def pp: String = s"Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" } /** star partial */ - case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula) extends ProofRule { + override def pp: String = s"StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } - case class PickCard(map: Map[Var,Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next.pp}" + case class PickCard(map: Map[Var,Expr]) extends ProofRule { + override def pp: String = s"PickCard(${map.mkString(",")});" } - case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" + case class PickArg(map: Map[Var, Expr]) extends ProofRule { + override def pp: String = s"PickArg(${map.mkString(",")});" } - case class Init(goal: Goal, next: ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(next) - override def pp: String = s"${ind}Init(${goal.pp});\n${next.pp}" + case class Init(goal: Goal) extends ProofRule { + override def pp: String = s"Init(${goal.pp});" } case object Inconsistency extends ProofRule { - val next1: Seq[ProofRule] = Seq.empty - override val cardinality: Int = 0 override def pp: String = "Inconsistency" } - case class Branch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { - val next1: Seq[ProofRule] = Seq(ifTrue, ifFalse) - override val cardinality: Int = 2 - override def pp: String = s"${ind}Branch($cond);\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + case class Branch(cond: Expr) extends ProofRule { + override def pp: String = s"Branch($cond);" } /** converts a Suslik CertTree node into the unified ProofRule structure */ - def of_certtree(node: CertTree.Node): ProofRule = { + def of_certtree(node: CertTree.Node): Node = { def fail_with_bad_proof_structure(): Nothing = throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") - def visit(node: CertTree.Node): (ProofRule, Map[ProofRule, GoalLabel]) = { - val (r, labels: Map[ProofRule, GoalLabel]) = node.rule match { + def visit(node: CertTree.Node): Node = { + val rule = node.rule match { case LogicalRules.NilNotLval => node.kont match { case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => // find all pointers that are not yet known to be non-null @@ -229,18 +207,14 @@ case class AbduceCall( val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.NilNotLval(pre_pointers, next), labels) + case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case FailRules.CheckPost => node.kont match { case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.CheckPost(prePhi, postPhi, next), labels) + case ::(head, Nil) => ProofRule.CheckPost(prePhi, postPhi) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -248,20 +222,15 @@ case class AbduceCall( case UnificationRules.Pick => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Pick(map, next), labels) + case ::(head, Nil) => ProofRule.Pick(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case FailRules.AbduceBranch => node.kont match { - case GuardedProducer(cond, bgoal) => + case GuardedProducer(cond, bGoal) => node.children match { - case ::(if_true, ::(if_false, Nil)) => - val (if_true1, labels) = visit(if_true) - val (if_false1, labels1) = visit(if_false) - (ProofRule.AbduceBranch(cond, bgoal.label, if_true1, if_false1), labels ++ labels1) + case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, bGoal.label) case ls => fail_with_bad_children(ls, 2) } case _ => fail_with_bad_proof_structure() @@ -269,9 +238,7 @@ case class AbduceCall( case OperationalRules.WriteRule => node.kont match { case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Write(stmt, next), labels) + case ::(head, Nil) => ProofRule.Write(stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -279,7 +246,7 @@ case class AbduceCall( case LogicalRules.Inconsistency => node.kont match { case ConstProducer(Error) => node.children match { - case Nil => (ProofRule.Inconsistency, Map.empty) + case Nil => ProofRule.Inconsistency case ls => fail_with_bad_children(ls, 0) } case _ => fail_with_bad_proof_structure() @@ -288,9 +255,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.WeakenPre(unused, next), labels) + case ::(head, Nil) => ProofRule.WeakenPre(unused) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -298,7 +263,7 @@ case class AbduceCall( case LogicalRules.EmpRule => node.kont match { case ConstProducer(Skip) => node.children match { - case Nil => (ProofRule.EmpRule, Map.empty) + case Nil => ProofRule.EmpRule case ls => fail_with_bad_children(ls, 0) } case _ => fail_with_bad_proof_structure() @@ -307,24 +272,20 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.PureSynthesis(is_final = true, assignments, next), labels) + ProofRule.PureSynthesis(is_final = true, assignments) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnfoldingRules.Open => node.kont match { case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => - val (next, labels) = node.children.map(visit).unzip - (ProofRule.Open(pred, fresh_vars, sbst, selectors.zip(next).toList), labels.reduceOption[Map[ProofRule, GoalLabel]](_ ++ _).getOrElse(Map.empty)) + ProofRule.Open(pred, fresh_vars, sbst, selectors.toList) case _ => fail_with_bad_proof_structure() } case LogicalRules.SubstLeft => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.SubstL(map, next), labels) + case ::(head, Nil) => ProofRule.SubstL(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -332,9 +293,7 @@ case class AbduceCall( case UnificationRules.SubstRight => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.SubstR(map, next), labels) + case ::(head, Nil) => ProofRule.SubstR(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -342,9 +301,7 @@ case class AbduceCall( case OperationalRules.ReadRule => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Read(map, stmt, next), labels) + case ::(head, Nil) => ProofRule.Read(map, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -358,8 +315,7 @@ case class AbduceCall( head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) val f_pre = head.goal.post var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get - val (next, labels) = visit(head) - (ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma, next), labels) + ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -367,9 +323,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPure => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.HeapUnify(subst, next), labels) + case ::(head, Nil) => ProofRule.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -377,9 +331,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyUnfolding => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.HeapUnify(subst, next), labels) + case ::(head, Nil) => ProofRule.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -387,9 +339,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyBlock => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.HeapUnify(subst, next), labels) + case ::(head, Nil) => ProofRule.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -397,9 +347,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPointer => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.HeapUnifyPointer(subst, next), labels) + case ::(head, Nil) => ProofRule.HeapUnifyPointer(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -416,8 +364,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - val (next, labels) = visit(head) - (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + ProofRule.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -435,8 +382,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - val (next, labels) = visit(head) - (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + ProofRule.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -454,8 +400,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - val (next, labels) = visit(head) - (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + ProofRule.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -473,19 +418,16 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - val (next, labels) = visit(head) - (ProofRule.FrameUnfold(h_pre, h_post, next), labels) + ProofRule.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Call(subst, call, next), labels) + case ::(head, Nil) => ProofRule.Call(subst, call) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -497,9 +439,7 @@ case class AbduceCall( case None => 1 } node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Free(stmt, size, next), labels) + case ::(head, Nil) => ProofRule.Free(stmt, size) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -508,8 +448,8 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => node.children match { case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Malloc(map, stmt, next), labels) + ProofRule. + Malloc(map, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -518,8 +458,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.Close(app, selector, asn, fresh_exist, next), labels) + ProofRule.Close(app, selector, asn, fresh_exist) case ls => fail_with_bad_children(ls, 1) } } @@ -530,8 +469,7 @@ case class AbduceCall( node.children match { case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.StarPartial(new_pre_phi, new_post_phi, next), labels) + ProofRule.StarPartial(new_pre_phi, new_post_phi) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -540,26 +478,25 @@ case class AbduceCall( case UnificationRules.PickCard => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.PickCard(map, next), labels) + case ::(head, Nil) => ProofRule.PickCard(map) case ls => fail_with_bad_children(ls, 1) } } case UnificationRules.PickArg => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => - val (next, labels) = visit(head) - (ProofRule.PickArg(map, next), labels) + case ::(head, Nil) => ProofRule.PickArg(map) case ls => fail_with_bad_children(ls, 1) } } } - (r, labels + (r -> node.goal.label)) + + Node(rule, node.goal.label, node.children.map(visit)) } - val (root, labels) = visit(node) - finalize_branches(root, labels) + + val rest = visit(node) + val tree = Node(Init(node.goal), null, Seq(rest)) + finalize_branches(tree) } /** @@ -575,60 +512,27 @@ case class AbduceCall( * --A-----B-C--- --E-A-----B-C--- * * @param node the root node of the tree - * @param labels the goal label associated with each proof rule, used to look up the branch destination * @return a copy of the tree with finalized branches inserted */ - def finalize_branches(node: ProofRule, labels: Map[ProofRule, GoalLabel]): ProofRule = { - def collect_branch_abductions(node: ProofRule, acc: Set[AbduceBranch] = Set.empty): Set[AbduceBranch] = node match { - case ab:AbduceBranch => node.next1.foldLeft(acc + ab){ case (acc, next) => collect_branch_abductions(next, acc) } - case _ => node.next1.foldLeft(acc){ case (acc, next) => collect_branch_abductions(next, acc) } - } - def apply_branch_abductions(node: ProofRule, known_abductions: Set[AbduceBranch]) : ProofRule = { - def with_next(node: ProofRule, next: Seq[ProofRule]): ProofRule = node match { - case r:ProofRule.NilNotLval=> r.copy(next = next.head) - case r:ProofRule.CheckPost => r.copy(next = next.head) - case r:ProofRule.Pick => r.copy(next = next.head) - case r:ProofRule.AbduceBranch => - val Seq(ifTrue, ifFalse) = next - r.copy(ifTrue = ifTrue, ifFalse = ifFalse) - case r:ProofRule.Branch => - val Seq(ifTrue, ifFalse) = next - r.copy(ifTrue = ifTrue, ifFalse = ifFalse) - case r:ProofRule.Write => r.copy(next = next.head) - case r:ProofRule.WeakenPre => r.copy(next = next.head) - case r:ProofRule.PureSynthesis => r.copy(next = next.head) - case r:ProofRule.Open => r.copy(cases = r.cases.zip(next).map(c => (c._1._1, c._2))) - case r:ProofRule.SubstL => r.copy(next = next.head) - case r:ProofRule.SubstR => r.copy(next = next.head) - case r:ProofRule.Read => r.copy(next = next.head) - case r:ProofRule.AbduceCall => r.copy(next = next.head) - case r:ProofRule.HeapUnify => r.copy(next = next.head) - case r:ProofRule.HeapUnifyPointer => r.copy(next = next.head) - case r:ProofRule.FrameUnfold => r.copy(next = next.head) - case r:ProofRule.Call => r.copy(next = next.head) - case r:ProofRule.Free => r.copy(next = next.head) - case r:ProofRule.Malloc => r.copy(next = next.head) - case r:ProofRule.Close => r.copy(next = next.head) - case r:ProofRule.StarPartial => r.copy(next = next.head) - case r:ProofRule.PickCard => r.copy(next = next.head) - case r:ProofRule.PickArg => r.copy(next = next.head) - case r:ProofRule.Init => r.copy(next = next.head) - case ProofRule.EmpRule => ProofRule.EmpRule - case ProofRule.Inconsistency => ProofRule.Inconsistency + def finalize_branches(node: Node): Node = { + def collect_branch_abductions(node: Node, abductions: Set[Node] = Set.empty): Set[Node] = { + val abductions1 = node.rule match { + case ab:AbduceBranch => abductions + node + case _ => abductions + } + node.next.foldLeft(abductions1) { case (a, n) => collect_branch_abductions(n, a) } } - val nodeLabel = labels(node) - known_abductions.find(_.bLabel == nodeLabel) match { - case Some(AbduceBranch(cond, bLabel, ifTrue, ifFalse)) => - assert(node.cardinality == 1) - val ifTrue1 = with_next(node, Seq(apply_branch_abductions(node.next1.head, known_abductions))) - val ifFalse1 = apply_branch_abductions(ifFalse, known_abductions) - Branch(cond, ifTrue1, ifFalse1) - case None => - val next = node.next1.map(next => apply_branch_abductions(next, known_abductions)) - with_next(node, next) + def apply_branch_abductions(abductions: Set[Node])(node: Node): Node = { + abductions.find(_.rule.asInstanceOf[AbduceBranch].bLabel == node.label) match { + case Some(ab@Node(AbduceBranch(cond, bLabel), _, _)) => + val Seq(ifTrue, ifFalse) = ab.next + val ifTrue1 = node.copy(next = node.next.map(apply_branch_abductions(abductions))) + val ifFalse1 = apply_branch_abductions(abductions)(ifFalse) + Node(Branch(cond), ab.label, Seq(ifTrue1, ifFalse1)) + case None => + node.copy(next = node.next.map(apply_branch_abductions(abductions))) + } } + apply_branch_abductions(collect_branch_abductions(node))(node) } - val abductions = collect_branch_abductions(node) - apply_branch_abductions(node, abductions) - } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 848d30663..d98975401 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -192,19 +192,20 @@ object IR { CFunSpec(f.name, rType, params, ghosts, pre, post) } - def fromRule(rule: ProofRule, ctx: IR.Context) : IR.Node = rule match { - case ProofRule.Init(goal, next) => + def fromRule(node: ProofRule.Node, ctx: IR.Context) : IR.Node = node.rule match { + case ProofRule.Init(goal) => val cgoal = translateGoal(goal) val ctx1 = ctx.copy(topLevelGoal = Some(cgoal)) - IR.Init(ctx1, Seq(fromRule(next, ctx1))) - case ProofRule.Open(sapp, fresh_vars, sbst, cases) => + IR.Init(ctx1, Seq(fromRule(node.next.head, ctx1))) + case ProofRule.Open(sapp, fresh_vars, sbst, selectors) => val csapp = translateSApp(sapp) val freshCVars = fresh_vars.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} val csbst = translateSbst(sbst) val pred = ctx.predicateEnv(sapp.pred).subst(freshCVars).subst(csbst) - val (selectors, next) = cases.map{ case (s, r) => (translateExpr(s), fromRule(r, ctx)) }.unzip - IR.Open(csapp, pred.clauses, selectors, next, ctx) - case ProofRule.Close(sapp, selector, asn, sbst, next) => + val next = node.next.map(n => fromRule(n, ctx)) + val cselectors = selectors.map(translateExpr) + IR.Open(csapp, pred.clauses, cselectors, next, ctx) + case ProofRule.Close(sapp, selector, asn, sbst) => val csapp = translateSApp(sapp) val cselector = translateExpr(selector) val casn = translateAsn(asn) @@ -213,62 +214,63 @@ object IR { val cclause = pred.clauses.find(_.selector == cselector).get val ex = cclause.existentials.map(_.subst(csbst)) val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) - fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) - case ProofRule.Branch(cond, ifTrue, ifFalse) => + fromRule(node.next.head, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) + case ProofRule.Branch(cond) => + val Seq(ifTrue, ifFalse) = node.next IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) - case ProofRule.PureSynthesis(is_final, sbst, next) => + case ProofRule.PureSynthesis(is_final, sbst) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) - IR.PureSynthesis(is_final, Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.SubstL(sbst, next) => + IR.PureSynthesis(is_final, Seq(fromRule(node.next.head, ctx1)), ctx1) + case ProofRule.SubstL(sbst) => val csbst = translateSbst(sbst) - fromRule(next, ctx.copy(subst = ctx.subst ++ csbst)) - case ProofRule.SubstR(sbst, next) => + fromRule(node.next.head, ctx.copy(subst = ctx.subst ++ csbst)) + case ProofRule.SubstR(sbst) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) - fromRule(next, ctx1) - case ProofRule.Pick(sbst, next) => + fromRule(node.next.head, ctx1) + case ProofRule.Pick(sbst) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) - fromRule(next, ctx1) - case ProofRule.Read(ghosts, Load(to, tpe, from, offset), next) => + fromRule(node.next.head, ctx1) + case ProofRule.Read(ghosts, Load(to, tpe, from, offset)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) - IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.Write(Store(to, offset, e), next) => - IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(next, ctx)), ctx) - case ProofRule.Free(Statements.Free(v), size, next) => - IR.Free(CFree(CVar(v.name), size), Seq(fromRule(next, ctx)), ctx) - case ProofRule.Malloc(ghosts, Statements.Malloc(to, tpe, sz), next) => + IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(node.next.head, ctx1)), ctx1) + case ProofRule.Write(Store(to, offset, e)) => + IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(node.next.head, ctx)), ctx) + case ProofRule.Free(Statements.Free(v), size) => + IR.Free(CFree(CVar(v.name), size), Seq(fromRule(node.next.head, ctx)), ctx) + case ProofRule.Malloc(ghosts, Statements.Malloc(to, tpe, sz)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) - IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.Call(_, Statements.Call(fun, args, _), next) => + IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(node.next.head, ctx1)), ctx1) + case ProofRule.Call(_, Statements.Call(fun, args, _)) => val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) val ctx2 = ctx1.copy(nestedContext = None) - IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(next, ctx2)), ctx1) - case ProofRule.PickArg(sbst, next) => + IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(node.next.head, ctx2)), ctx1) + case ProofRule.PickArg(sbst) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) - fromRule(next, ctx1) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma, next) => + fromRule(node.next.head, ctx1) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma) => val cfunspec = translateFunSpec(f, gamma) val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) - fromRule(next, ctx1) - case ProofRule.HeapUnifyPointer(sbst, next) => + fromRule(node.next.head, ctx1) + case ProofRule.HeapUnifyPointer(sbst) => val ctx1 = ctx.copy(subst = ctx.subst ++ translateSbst(sbst)) - fromRule(next, ctx1) + fromRule(node.next.head, ctx1) case ProofRule.EmpRule => IR.EmpRule(ctx) - case ProofRule.CheckPost(prePhi, postPhi, next) => - IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(next, ctx)), ctx) + case ProofRule.CheckPost(prePhi, postPhi) => + IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(node.next.head, ctx)), ctx) // unused rules: - case ProofRule.HeapUnify(_, next) => fromRule(next, ctx) - case ProofRule.NilNotLval(_, next) => fromRule(next, ctx) - case ProofRule.WeakenPre(_, next) => fromRule(next, ctx) - case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) - case ProofRule.PickCard(_, next) => fromRule(next, ctx) - case ProofRule.FrameUnfold(h_pre, h_post, next) => fromRule(next, ctx) + case ProofRule.HeapUnify(_) => fromRule(node.next.head, ctx) + case ProofRule.NilNotLval(_) => fromRule(node.next.head, ctx) + case ProofRule.WeakenPre(_) => fromRule(node.next.head, ctx) + case ProofRule.StarPartial(_, _) => fromRule(node.next.head, ctx) + case ProofRule.PickCard(_) => fromRule(node.next.head, ctx) + case ProofRule.FrameUnfold(h_pre, h_post) => fromRule(node.next.head, ctx) case ProofRule.Inconsistency => IR.Inconsistency(ctx) - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => fromRule(ifTrue, ctx) + case ProofRule.AbduceBranch(cond, bLabel) => fromRule(node.next.head, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index f5ca00357..0c27600cc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -32,7 +32,7 @@ object Translation { }) val goal = translateGoal(node.goal) val ctx = IR.emptyContext.copy(predicateEnv = cpreds) - val ir = IR.fromRule(ProofRule.Init(node.goal, ProofRule.of_certtree(node)), ctx).propagateContext + val ir = IR.fromRule(ProofRule.of_certtree(node), ctx).propagateContext val proof = ProofTranslation.irToProofSteps(ir) val hints = ProofTranslation.irToHints(ir) val progBody = ProgramTranslation.translate(ir) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 343aa70e0..c82a294ee 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -267,8 +267,8 @@ object ProofTranslation { * - first assert that the pointer being read from is non-null (VST idiosynracy) * - emit a forward tactic to move over the operation */ - def handle_read_rule(rule: ProofRule.Read, context: Context): ProofSteps = rule match { - case ProofRule.Read(subst, option, next) => + def handle_read_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Read(subst, option) => subst.toList match { case ::((Var(old_var), Var(new_var)), _) => def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { @@ -284,65 +284,54 @@ object ProofTranslation { is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) } - def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { + def is_variable_used_in_proof(variable: Ident)(node: ProofRule.Node): Boolean = { def map_varaible(map: Map[Var, Expr]): Ident = map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) - rule match { - case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.CheckPost(prePhi, postPhi, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickCard(_, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickArg(_, next) => + node.rule match { + case ProofRule.PickArg(_) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.Pick(subst, next) => + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(node.next.head) + case ProofRule.Pick(subst) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => + (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(node.next.head) + case ProofRule.AbduceBranch(cond, bLabel) => + val Seq(ifTrue, ifFalse) = node.next is_variable_used_in_exp(variable)(cond) || is_variable_used_in_proof(variable)(ifTrue) || is_variable_used_in_proof(variable)(ifFalse) - case ProofRule.Write(Statements.Store(Var(tov), offset, e), next) => - (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) - case ProofRule.WeakenPre(unused, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.Write(Statements.Store(Var(tov), offset, e)) => + (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(node.next.head) case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => - cases.exists({ case (expr, rule) => + case ProofRule.Open(pred, fresh_vars, sbst, selectors) => + val cases = selectors.zip(node.next) + cases.exists({ case (expr, next) => is_variable_used_in_exp(variable)(expr) || - is_variable_used_in_proof(variable)(rule) + is_variable_used_in_proof(variable)(next) }) - case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnify(_,next) => is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => - is_variable_used_in_proof(variable)(next) - case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset), next) => - (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) - case ProofRule.Call(_, Call(_, args, _), next) => + case ProofRule.SubstL(map) => is_variable_used_in_proof(map_varaible(map))(node.next.head) + case ProofRule.SubstR(map) => is_variable_used_in_proof(map_varaible(map))(node.next.head) + case ProofRule.HeapUnifyPointer(map) => is_variable_used_in_proof(map_varaible(map))(node.next.head) + case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset)) => + (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(node.next.head)) + case ProofRule.Call(_, Call(_, args, _)) => args.exists(is_variable_used_in_exp(variable)) || - is_variable_used_in_proof(variable)(next) - case ProofRule.Free(Free(Var(v)), _, next) => - (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz), next) => - (toe != variable) && is_variable_used_in_proof(variable)(next) + is_variable_used_in_proof(variable)(node.next.head) + case ProofRule.Free(Free(Var(v)), _) => + (v == variable) || is_variable_used_in_proof(variable)(node.next.head) + case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz)) => + (toe != variable) && is_variable_used_in_proof(variable)(node.next.head) + case _ => is_variable_used_in_proof(variable)(node.next.head) } } val new_context = record_variable_mapping(subst)(context) val rest = (retrieve_typing_context(context).get(old_var)) match { case Some(CoqPtrType) => - ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) - case _ => translate_proof_rules(next)(new_context) + ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(node.next.head)(new_context)) + case _ => translate_proof_rules(node.next.head)(new_context) } - if (is_variable_used_in_proof(new_var)(next)) { + if (is_variable_used_in_proof(new_var)(node.next.head)) { ProofSteps.Rename(old_var, new_var, ProofSteps.Forward( rest @@ -362,14 +351,15 @@ object ProofTranslation { * Does this by mapping each constructor of the opened predicate to a branch of the rule, * and then for each branch introducing the variables that it uses. */ - def handle_open_rule(rule: ProofRule.Open, context: Context): ProofSteps = rule match { - case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, sbst, cases) => + def handle_open_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, sbst, selectors) => val pred = pred_map(predicate_name) + val cases = selectors.zip(node.next) ProofSteps.ForwardIfConstructor( card_variable, predicate_name, pred.clauses.zip(cases).map({ - case ((constructor, clause), (expr, rule)) => + case ((constructor, clause), (expr, next)) => // each clause of the type introduces existentials val new_variables = pred.constructor_existentials(constructor).map({ // rename existential variables if they have been assigned fresh variables @@ -384,21 +374,21 @@ object ProofTranslation { ((pred.constructor_name(constructor), constructor, constructor_args), ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), new_variables.map(_._1), - translate_proof_rules(rule)(new_context)) + translate_proof_rules(next)(new_context)) }).toList ) } - def handle_pick_rule(rule: ProofRule.Pick, context: Context): ProofSteps = rule match { - case ProofRule.Pick(subst, next) => + def handle_pick_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Pick(subst) => val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ case ((name,expr), context) => record_variable_assignment(name,expr)(context) }) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } - def handle_pick_card_rule(rule: ProofRule.PickCard, context: Context): ProofSteps = rule match { - case ProofRule.PickCard(subst, next) => + def handle_pick_card_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.PickCard(subst) => /** Given an expression representing a pick of a cardinality variable * returns the corresponding cardinality constructor @@ -432,15 +422,15 @@ object ProofTranslation { add_new_variables(new_vars.toMap)(context) ) }) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } - def handle_pick_arg_rule(rule: ProofRule.PickArg, context: Context): ProofSteps = rule match { - case ProofRule.PickArg(subst, next) => + def handle_pick_arg_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.PickArg(subst) => val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ case ((name,expr), context) => record_variable_assignment(name,expr)(context) }) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } def handle_emp_rule(context: Context) = { @@ -482,33 +472,33 @@ object ProofTranslation { ) } - def handle_pure_synthesis_rule(rule: ProofRule.PureSynthesis, context: Context): ProofSteps = rule match { - case ProofRule.PureSynthesis(is_final, subst, next) => + def handle_pure_synthesis_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.PureSynthesis(is_final, subst) => val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ case ((name,expr), context) => record_variable_assignment(name,expr)(context) }) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } - def handle_heap_unify(rule: ProofRule.HeapUnify, context: Context): ProofSteps = rule match { - case ProofRule.HeapUnify(_, next) => + def handle_heap_unify(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.HeapUnify(_) => // val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ // case ((name,expr), context) => record_variable_assignment(name,expr)(context) // }) - translate_proof_rules(next)(context) + translate_proof_rules(node.next.head)(context) } - def handle_heap_unify_pointer(rule: ProofRule.HeapUnifyPointer, context: Context): ProofSteps = rule match { - case ProofRule.HeapUnifyPointer(subst, next) => + def handle_heap_unify_pointer(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.HeapUnifyPointer(subst) => val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ case ((name,expr), context) => record_variable_assignment(name,expr)(context) }) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } - def handle_substl_rule(rule: ProofRule.SubstL, context: Context): ProofSteps = rule match { - case ProofRule.SubstL(map, next) => - map.toList.foldRight(translate_proof_rules(next)(context))({ + def handle_substl_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.SubstL(map) => + map.toList.foldRight(translate_proof_rules(node.next.head)(context))({ case ((Var(name), expr), next) => AssertPropSubst( name, @@ -517,11 +507,11 @@ object ProofTranslation { }) } - def handle_substr_rule(rule: ProofRule.SubstR, context: Context): ProofSteps = rule match { - case ProofRule.SubstR(map, next) => + def handle_substr_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.SubstR(map) => def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofSteps = map match { - case Nil => translate_proof_rules(next)(context) + case Nil => translate_proof_rules(node.next.head)(context) case ::((Var(old_name), Var(new_name)), rest) => val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) ProofSteps.Rename(old_name, new_name, @@ -535,8 +525,8 @@ object ProofTranslation { apply_subst(context)(map.toList) } - def handle_abduce_call(rule: ProofRule.AbduceCall, context: Context): ProofSteps = rule match { - case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _, next) => + def handle_abduce_call(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _) => var typing_context = retrieve_typing_context(context) f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { @@ -546,47 +536,48 @@ object ProofTranslation { val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) val existentials = spec.existensial_params.toList.map({case (name,ty) => (freshSub(Var(name)).name, ty)}) var new_context = push_function((fun, call_args, existentials))(context) - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) } - def handle_nilnotnval_rule(rule: ProofRule.NilNotLval, context: Context) = rule match { - case ProofRule.NilNotLval(vars, next) => - vars.foldRight(translate_proof_rules(next)(context))({ + def handle_nilnotnval_rule(node: ProofRule.Node, context: Context) = node.rule match { + case ProofRule.NilNotLval(vars) => + vars.foldRight(translate_proof_rules(node.next.head)(context))({ case (_@Var(name), rest) => ProofSteps.ValidPointer( name, rest ) }) } - def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofSteps = rule match { - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => + def handle_abduce_branch_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.AbduceBranch(cond, bLabel) => + val Seq(ifTrue, ifFalse) = node.next ProofSteps.ForwardIf(List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) )) } - def handle_call_rule(rule: ProofRule.Call, context: Context): ProofSteps = rule match { - case ProofRule.Call(_, call, next) => + def handle_call_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Call(_, call) => val ((fun, args, existentials), new_context) = pop_function(context) ProofSteps.ForwardCall(args, existentials match { - case Nil => translate_proof_rules(next)(new_context) + case Nil => translate_proof_rules(node.next.head)(new_context) case _ => - ProofSteps.IntrosTuple(existentials, translate_proof_rules(next)(new_context)) + ProofSteps.IntrosTuple(existentials, translate_proof_rules(node.next.head)(new_context)) }) } - def handle_write_rule(rule: ProofRule.Write, context: Context): ProofSteps = rule match { - case ProofRule.Write(stmt, next) => ProofSteps.Forward(translate_proof_rules(next)(context)) + def handle_write_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Write(stmt) => ProofSteps.Forward(translate_proof_rules(node.next.head)(context)) } - def handle_free_rule(rule: ProofRule.Free, context: Context): ProofSteps = rule match { - case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) + def handle_free_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Free(Free(Var(name)), size) => ProofSteps.Free(name, size, translate_proof_rules(node.next.head)(context)) } - def handle_close_rule(rule: ProofRule.Close, context: Context): ProofSteps = rule match { - case ProofRule.Close(app, o_selector, asn, fresh_exist, next) => + def handle_close_rule(node: ProofRule.Node, context: Context): ProofSteps = node.rule match { + case ProofRule.Close(app, o_selector, asn, fresh_exist) => // Use application of of constructor to infer mapping of variables val predicate = pred_map(app.pred) @@ -645,11 +636,11 @@ object ProofTranslation { val new_context = push_unfolding(context)(unfolding, new_equalities) val new_context_2 = record_variable_mapping(fresh_exist)(new_context) - translate_proof_rules(next)(new_context_2) + translate_proof_rules(node.next.head)(new_context_2) } - def handle_malloc_rule(rule: ProofRule.Malloc, context: Context) = rule match { - case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => + def handle_malloc_rule(node: ProofRule.Node, context: Context) = node.rule match { + case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz)) => val new_context = map.foldRight( add_new_variables(map.map({case (Var(original), Var(name)) => (name, CoqPtrType)}))(context) @@ -657,53 +648,54 @@ object ProofTranslation { ProofSteps.Malloc(sz, ProofSteps.Intros( List((to_var, CoqPtrType)), - translate_proof_rules(next)(new_context) + translate_proof_rules(node.next.head)(new_context) )) } - def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { - rule match { + def translate_proof_rules(node: ProofRule.Node)(context: Context): ProofSteps = { + node.rule match { // Branching rules - case rule@ProofRule.Open(SApp(_, _, _, Var(_)), _, _, _) => handle_open_rule(rule, context) - case rule@ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => handle_abduce_branch_rule(rule, context) + case ProofRule.Open(SApp(_, _, _, Var(_)), _, _, _) => handle_open_rule(node, context) + case ProofRule.AbduceBranch(cond, bLabel) => handle_abduce_branch_rule(node, context) // Read and write Operations - case rule@ProofRule.Write(_, _) => handle_write_rule(rule, context) - case rule@ProofRule.Read(subst, option, next) => handle_read_rule(rule, context) + case ProofRule.Write(_) => handle_write_rule(node, context) + case ProofRule.Read(subst, option) => handle_read_rule(node, context) // Memory management rules - case rule@ProofRule.Free(Free(Var(_)), _, _) => handle_free_rule(rule, context) - case rule@ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => handle_malloc_rule(rule, context) + case ProofRule.Free(Free(Var(_)), _) => handle_free_rule(node, context) + case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz)) => handle_malloc_rule(node, context) // Abduce call & Existentials - case rule@ProofRule.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _, _) => handle_abduce_call(rule, context) - case rule@ProofRule.Pick(_, _) => handle_pick_rule(rule, context) - case rule@ProofRule.PureSynthesis(_, _, _) => handle_pure_synthesis_rule(rule, context) - case rule@ProofRule.PickCard(_,_) => handle_pick_card_rule(rule, context) - case rule@ProofRule.PickArg(_, _) => handle_pick_arg_rule(rule, context) - case rule@ProofRule.Call(_, _, _) => handle_call_rule(rule, context) - case rule@ProofRule.Close(_, _, _, _, _) => handle_close_rule(rule, context) - case rule@ProofRule.HeapUnify(_, next) => handle_heap_unify(rule, context) - case rule@ProofRule.HeapUnifyPointer(_, _) => handle_heap_unify_pointer(rule, context) + case ProofRule.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _) => handle_abduce_call(node, context) + case ProofRule.Pick(_) => handle_pick_rule(node, context) + case ProofRule.PureSynthesis(_, _) => handle_pure_synthesis_rule(node, context) + case ProofRule.PickCard(_) => handle_pick_card_rule(node, context) + case ProofRule.PickArg(_) => handle_pick_arg_rule(node, context) + case ProofRule.Call(_, _) => handle_call_rule(node, context) + case ProofRule.Close(_, _, _, _) => handle_close_rule(node, context) + case ProofRule.HeapUnify(_) => handle_heap_unify(node, context) + case ProofRule.HeapUnifyPointer(_) => handle_heap_unify_pointer(node, context) // Completion rule case ProofRule.EmpRule => handle_emp_rule(context) // Context changing rules - case rule@ProofRule.NilNotLval(_, _) => handle_nilnotnval_rule(rule, context) - case rule@ProofRule.SubstL(_, _) => handle_substl_rule(rule, context) - case rule@ProofRule.SubstR(_, _) => handle_substr_rule(rule, context) + case ProofRule.NilNotLval(_) => handle_nilnotnval_rule(node, context) + case ProofRule.SubstL(_) => handle_substl_rule(node, context) + case ProofRule.SubstR(_) => handle_substr_rule(node, context) // Ignored rules - case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.CheckPost(pre_phi, post_phi, next) => translate_proof_rules(next)(context) + case ProofRule.WeakenPre(unused) => translate_proof_rules(node.next.head)(context) + case ProofRule.CheckPost(pre_phi, post_phi) => translate_proof_rules(node.next.head)(context) - case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) + case ProofRule.FrameUnfold(h_pre, h_post) => translate_proof_rules(node.next.head)(context) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => translate_proof_rules(next)(context) - case ProofRule.Branch(cond, ifTrue, ifFalse) => translate_proof_rules(ifTrue)(context) + case ProofRule.StarPartial(new_pre_phi, new_post_phi) => translate_proof_rules(node.next.head)(context) + case ProofRule.Branch(cond) => translate_proof_rules(node.next.head)(context) + case ProofRule.Init(goal) => translate_proof_rules(node.next.head)(context) } } @@ -715,58 +707,18 @@ object ProofTranslation { Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) } - def contains_free(proof: ProofRule): Boolean = proof match { - case ProofRule.NilNotLval(vars, next) => contains_free(next) - case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_free(next) - case ProofRule.Pick(subst, next) => contains_free(next) - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) - case ProofRule.Write(stmt, next) => contains_free(next) - case ProofRule.WeakenPre(unused, next) => contains_free(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => contains_free(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => cases.exists { case (_, prf) => contains_free(prf) } - case ProofRule.SubstL(map, next) => contains_free(next) - case ProofRule.SubstR(map, next) => contains_free(next) - case ProofRule.Read(map, operation, next) => contains_free(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => contains_free(next) - case ProofRule.HeapUnify(_, next) => contains_free(next) - case ProofRule.HeapUnifyPointer(map, next) => contains_free(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_free(next) - case ProofRule.Call(_, call, next) => contains_free(next) - case ProofRule.Free(stmt, size, next) => true - case ProofRule.Malloc(map, stmt, next) => contains_free(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_free(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_free(next) - case ProofRule.PickCard(_, next) => contains_free(next) - case ProofRule.PickArg(map, next) => contains_free(next) - case ProofRule.Branch(cond, ifTrue, ifFalse) => contains_free(ifTrue) + def contains_free(proof: ProofRule.Node): Boolean = proof.rule match { + case _:ProofRule.Free => true + case ProofRule.EmpRule | ProofRule.Inconsistency => false + case _:ProofRule.Branch => contains_free(proof.next.head) + case _ => proof.next.exists(contains_free) } - def contains_malloc(proof: ProofRule): Boolean = proof match { - case ProofRule.NilNotLval(vars, next) => contains_malloc(next) - case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_malloc(next) - case ProofRule.Pick(subst, next) => contains_malloc(next) - case ProofRule.AbduceBranch(cond, bLabel, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) - case ProofRule.Write(stmt, next) => contains_malloc(next) - case ProofRule.WeakenPre(unused, next) => contains_malloc(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => contains_malloc(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => cases.exists { case (_, prf) => contains_malloc(prf) } - case ProofRule.SubstL(map, next) => contains_malloc(next) - case ProofRule.SubstR(map, next) => contains_malloc(next) - case ProofRule.Read(map, operation, next) => contains_malloc(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => contains_malloc(next) - case ProofRule.HeapUnify(_,next) => contains_malloc(next) - case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) - case ProofRule.Call(_, call, next) => contains_malloc(next) - case ProofRule.Free(stmt, size, next) => contains_malloc(next) - case ProofRule.Malloc(map, stmt, next) => true - case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_malloc(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_malloc(next) - case ProofRule.PickCard(_, next) => contains_malloc(next) - case ProofRule.PickArg(map, next) => contains_malloc(next) - case ProofRule.Branch(cond, ifTrue, ifFalse) => contains_malloc(ifTrue) + def contains_malloc(proof: ProofRule.Node): Boolean = proof.rule match { + case _:ProofRule.Malloc => true + case ProofRule.EmpRule | ProofRule.Inconsistency => false + case _:ProofRule.Branch => contains_malloc(proof.next.head) + case _ => proof.next.exists(contains_malloc) } } From a819bcc8b9579ee00120634fe21ee4877dfb3993 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 25 Jan 2021 00:48:30 +0800 Subject: [PATCH 078/211] HTT: Correctly track sapp hyp name aliases and enforce selector in open/branch --- .../targets/htt/logic/Proof.scala | 8 ++-- .../targets/htt/translation/IR.scala | 48 ++++++++++++++----- .../htt/translation/ProofTranslation.scala | 9 ++-- 3 files changed, 44 insertions(+), 21 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 28a977bba..27b698d68 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -53,8 +53,8 @@ object Proof { override val isNoop: Boolean = items.isEmpty def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString(".\n") } - case object Sbst extends Step { - def pp: String = "subst" + case class Sbst(vars: Seq[CVar]) extends Step { + def pp: String = if (vars.isEmpty) s"subst" else s"subst ${vars.map(_.pp).mkString(" ")}" } case class Exists(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty @@ -97,7 +97,7 @@ object Proof { } } case class Open(selectors: Seq[CExpr]) extends Step { - def pp: String = "ssl_open" + def pp: String = s"ssl_open (${selectors.head.pp})" } case class OpenPost(app: CSApp) extends Step { def pp: String = s"ssl_open_post ${app.hypName}" @@ -118,7 +118,7 @@ object Proof { def pp: String = "ssl_ghostelim_post" } case class Branch(cond: CExpr) extends Step { - def pp: String = "ssl_abduce_branch" + def pp: String = s"ssl_abduce_branch (${cond.pp})" } case object Emp extends Step { def pp: String = "ssl_emp" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index d98975401..6ba308b7e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -15,7 +15,7 @@ import org.tygus.suslik.logic.Specifications.Goal object IR { type Unfoldings = Map[CSApp, CInductiveClause] - type SAppNames = Map[CSApp, String] + type SAppNames = Map[CSApp, CSApp] type CSubst = Map[CVar, CExpr] type CSubstVar = Map[CVar, CVar] type PredicateEnv = Map[String, CInductivePredicate] @@ -72,7 +72,7 @@ object IR { val ctx : Context val next : Seq[Node] - private def mapJoin[T <: CExpr](m1: Map[CVar, T], m2: Map[CVar, T]) : Map[CVar, T] = { + private def mapJoin[K, V](m1: Map[K, V], m2: Map[K, V]) : Map[K, V] = { m1.toSeq.intersect(m2.toSeq).toMap } @@ -83,13 +83,15 @@ object IR { val childCtxs = children.map(_.ctx) val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) - n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar)) + val childSAppNames = childCtxs.map(_.sappNames).reduceOption[SAppNames](mapJoin).getOrElse(Map.empty) + n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar, sappNames = childSAppNames)) case n:IR.Branch => val children = n.next.map(_.propagateContext) val childCtxs = children.map(_.ctx) val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) - n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar)) + val childSAppNames = childCtxs.map(_.sappNames).reduceOption[SAppNames](mapJoin).getOrElse(Map.empty) + n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar, sappNames = childSAppNames)) case n:IR.Read => val next1 = n.next.head.propagateContext n.copy(ctx = next1.ctx, next = Seq(next1)) @@ -175,6 +177,11 @@ object IR { private def translateSbst(sbst: Subst) = sbst.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} private def translateSbstVar(sbst: SubstVar) = sbst.map{ case (k,v) => CVar(k.name) -> CVar(v.name)} + private def updateSAppAliases(m: SAppNames, sub: CSubst): SAppNames = { + val affected = m.map { case (app, alias) => app -> (app.subst(sub) -> alias) }.filter { case (app, (app1, _)) => app != app1 } + m ++ affected.values.toMap + } + def translateGoal(goal: Goal): CGoal = { val pre = translateAsn(goal.pre) val post = translateAsn(goal.post) @@ -195,7 +202,8 @@ object IR { def fromRule(node: ProofRule.Node, ctx: IR.Context) : IR.Node = node.rule match { case ProofRule.Init(goal) => val cgoal = translateGoal(goal) - val ctx1 = ctx.copy(topLevelGoal = Some(cgoal)) + val sappNames = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap + val ctx1 = ctx.copy(topLevelGoal = Some(cgoal), sappNames = sappNames) IR.Init(ctx1, Seq(fromRule(node.next.head, ctx1))) case ProofRule.Open(sapp, fresh_vars, sbst, selectors) => val csapp = translateSApp(sapp) @@ -214,24 +222,33 @@ object IR { val cclause = pred.clauses.find(_.selector == cselector).get val ex = cclause.existentials.map(_.subst(csbst)) val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) - fromRule(node.next.head, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) + val unfoldings = ctx.unfoldings + (csapp -> actualClause) + val sappNames = ctx.sappNames ++ casn.sigma.apps.map(a => a -> a) + fromRule(node.next.head, ctx.copy(unfoldings = unfoldings, sappNames = sappNames)) case ProofRule.Branch(cond) => val Seq(ifTrue, ifFalse) = node.next IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) case ProofRule.PureSynthesis(is_final, sbst) => val csbst = translateSbst(sbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) IR.PureSynthesis(is_final, Seq(fromRule(node.next.head, ctx1)), ctx1) case ProofRule.SubstL(sbst) => val csbst = translateSbst(sbst) - fromRule(node.next.head, ctx.copy(subst = ctx.subst ++ csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + fromRule(node.next.head, ctx.copy(subst = ctx.subst ++ csbst, sappNames = sappNames)) case ProofRule.SubstR(sbst) => val csbst = translateSbst(sbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.next.head, ctx1) case ProofRule.Pick(sbst) => val csbst = translateSbst(sbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.next.head, ctx1) case ProofRule.Read(ghosts, Load(to, tpe, from, offset)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) @@ -249,7 +266,9 @@ object IR { IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(node.next.head, ctx2)), ctx1) case ProofRule.PickArg(sbst) => val csbst = translateSbst(sbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) + val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.next.head, ctx1) case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma) => val cfunspec = translateFunSpec(f, gamma) @@ -258,11 +277,15 @@ object IR { val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) fromRule(node.next.head, ctx1) case ProofRule.HeapUnifyPointer(sbst) => - val ctx1 = ctx.copy(subst = ctx.subst ++ translateSbst(sbst)) + val csbst = translateSbst(sbst) + val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) + val sappNames = updateSAppAliases(ctx.sappNames, csbst) + val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.next.head, ctx1) case ProofRule.EmpRule => IR.EmpRule(ctx) case ProofRule.CheckPost(prePhi, postPhi) => IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(node.next.head, ctx)), ctx) + case ProofRule.Inconsistency => IR.Inconsistency(ctx) // unused rules: case ProofRule.HeapUnify(_) => fromRule(node.next.head, ctx) case ProofRule.NilNotLval(_) => fromRule(node.next.head, ctx) @@ -270,7 +293,6 @@ object IR { case ProofRule.StarPartial(_, _) => fromRule(node.next.head, ctx) case ProofRule.PickCard(_) => fromRule(node.next.head, ctx) case ProofRule.FrameUnfold(h_pre, h_post) => fromRule(node.next.head, ctx) - case ProofRule.Inconsistency => IR.Inconsistency(ctx) case ProofRule.AbduceBranch(cond, bLabel) => fromRule(node.next.head, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 67221dd87..0e288da47 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -24,7 +24,7 @@ object ProofTranslation { } else Proof.Noop // move spatial part to context, and then substitute where appropriate - val spatialToCtx = Proof.MoveToCtxDestruct(Seq(CVar(s"sigma_$uniqueName"))) >> Proof.Sbst + val spatialToCtx = Proof.MoveToCtxDestruct(Seq(CVar(s"sigma_$uniqueName"))) >> Proof.Sbst(Seq(CVar(s"h_$uniqueName"))) // move predicate apps to context, if any val sappToCtx = if (sigma.apps.nonEmpty) { @@ -134,14 +134,15 @@ object ProofTranslation { case IR.Open(app, clauses, selectors, next, ctx) => val branchSteps = next.zip(clauses).map { case (n, c) => val c1 = c.subst(n.ctx.substVar) + val app1 = ctx.sappNames.getOrElse(app, app) val res = visit(n) if (res == Proof.Error) { - Proof.OpenPost(app.subst(ctx.substVar)) // Don't emit branch prelude if inconsistent + Proof.OpenPost(app1) // Don't emit branch prelude if inconsistent } else { - Proof.OpenPost(app.subst(ctx.substVar)) >> + Proof.OpenPost(app1) >> elimExistentials(c1.existentials) >> elimExistentials(c1.asn.heapVars) >> - initPre(c1.asn, app.uniqueName) >> + initPre(c1.asn, app1.uniqueName) >> res } } From ef48e7b3a34e18f60ae86bb0108ce6f002361508 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 25 Jan 2021 20:33:04 +0800 Subject: [PATCH 079/211] Added mapper class --- .../suslik/certification/ProofRule.scala | 247 ++++++++++++++---- .../targets/vst/logic/Formulae.scala | 1 + .../targets/vst/logic/ProofSteps.scala | 12 +- .../targets/vst/logic/ProofTerms.scala | 51 ++-- .../translation/ProofSpecTranslation.scala | 17 +- .../vst/translation/ProofTranslation.scala | 202 +++++++------- 6 files changed, 347 insertions(+), 183 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala index 25a349c7f..5adb7a166 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/ProofRule.scala @@ -16,8 +16,8 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ -sealed abstract class ProofRule extends PrettyPrinting { - +sealed trait ProofRule extends PrettyPrinting { + def next: List[ProofRule] } object ProofRule { @@ -37,64 +37,76 @@ object ProofRule { /** corresponds to asserting all the variables in vars are not null */ - case class NilNotLval(vars: List[Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next.pp}" + case class NilNotLval(vars: List[Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** solves a pure entailment with SMT */ - case class CheckPost(prePhi: PFormula, postPhi: PFormula, next:ProofRule) extends ProofRule { - override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next.pp}" + case class CheckPost(prePhi: PFormula, postPhi: PFormula, next_rule:ProofRule) extends ProofRule { + override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** picks an arbitrary instantiation of the proof rules */ - case class Pick(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next.pp}" + case class Pick(subst: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** abduces a condition for the proof */ case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + override def next: List[ProofRule] = List(ifTrue, ifFalse) } /** write a value */ - case class Write(stmt: Store, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next.pp}" + case class Write(stmt: Store, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** weaken the precondition by removing unused formulae */ - case class WeakenPre(unused: PFormula, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next.pp}" + case class WeakenPre(unused: PFormula, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** empty rule */ case object EmpRule extends ProofRule { override def pp: String = s"${ind}EmpRule;" + override def next: List[ProofRule] = List() } /** pure synthesis rules */ - case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next.pp}" + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** open constructor cases */ case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[(Expr, ProofRule)]) extends ProofRule { override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + override def next: List[ProofRule] = cases.map({ case (_, rule) => rule }) } /** subst L */ - case class SubstL(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next.pp}" + case class SubstL(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** subst R */ - case class SubstR(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next.pp}" + case class SubstR(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** read rule */ - case class Read(map: Map[Var,Var], operation: Load, next:ProofRule) extends ProofRule { - override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next.pp}" + case class Read(map: Map[Var,Var], operation: Load, next_rule:ProofRule) extends ProofRule { + override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } // /** abduce a call */ @@ -107,67 +119,214 @@ case class AbduceCall( freshToActual: Subst, f: FunSpec, gamma: Gamma, - next: ProofRule + next_rule: ProofRule ) extends ProofRule { - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next.pp}" + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(subst: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next.pp}" + case class HeapUnify(subst: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** unification of pointers */ - case class HeapUnifyPointer(map: Map[Var,Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next.pp}" + case class HeapUnifyPointer(map: Map[Var,Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** unfolds frame */ - case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next.pp}" + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** call operation */ - case class Call(subst: Map[Var, Expr], call: Statements.Call, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next.pp}" + case class Call(subst: Map[Var, Expr], call: Statements.Call, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** free operation */ - case class Free(stmt: Statements.Free, size: Int, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next.pp}" + case class Free(stmt: Statements.Free, size: Int, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** malloc rule */ - case class Malloc(map: SubstVar, stmt: Statements.Malloc, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next.pp}" + case class Malloc(map: SubstVar, stmt: Statements.Malloc, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** close rule */ - case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next.pp}" + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } /** star partial */ - case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next.pp}" + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } - case class PickCard(map: Map[Var,Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next.pp}" + case class PickCard(map: Map[Var,Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } - case class PickArg(map: Map[Var, Expr], next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next.pp}" + case class PickArg(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } - case class Init(goal: Goal, next: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Init(${goal.pp});\n${next.pp}" + case class Init(goal: Goal, next_rule: ProofRule) extends ProofRule { + override def pp: String = s"${ind}Init(${goal.pp});\n${next_rule.pp}" + override def next: List[ProofRule] = List(next_rule) } case object Inconsistency extends ProofRule { override def pp: String = "Inconsistency" + override def next: List[ProofRule] = List() + } + + /** + * Abstract class for traversing and manipulating proof tree structures. + * + * override map_next, map_if_true, map_if_false, map_cases to handle mapping next + */ + abstract class ProofRuleMapper { + + def map (rule: NilNotLval) = + NilNotLval(rule.vars, map_next(rule.next_rule)) + + def map (rule: CheckPost) = + CheckPost(rule.prePhi, rule.postPhi, map_next(rule.next_rule)) + + def map(rule: Pick) = + Pick(rule.subst, map_next(rule.next_rule)) + + def map(rule: AbduceBranch) = + AbduceBranch(rule.cond, map_if_true(rule.ifTrue), map_if_false(rule.ifFalse)) + + def map(rule: Write) = + Write(rule.stmt, map_next(rule.next_rule)) + + def map(rule: WeakenPre) = + WeakenPre(rule.unused, map_next(rule.next_rule)) + + def map_emp = EmpRule + + def map (rule: PureSynthesis) = + PureSynthesis(rule.is_final, rule.assignments, map_next(rule.next_rule)) + + def map (rule: Open) = + Open(rule.pred, rule.fresh_vars, rule.sbst, map_cases(rule.cases)) + + def map(rule: SubstL) = + SubstL(rule.map, map_next(rule.next_rule)) + + def map(rule: SubstR) = + SubstR(rule.map, map_next(rule.next_rule)) + + def map(rule: Read) = + Read(rule.map, rule.operation, map_next(rule.next_rule)) + + def map(rule: AbduceCall) = + AbduceCall(rule.new_vars, rule.f_pre, rule.callePost, rule.call, rule.freshSub, rule.freshToActual, rule.f, rule.gamma, map_next(rule.next_rule)) + + def map(rule: HeapUnify) = + HeapUnify(rule.subst, map_next(rule.next_rule)) + + def map(rule: HeapUnifyPointer) = + HeapUnifyPointer(rule.map, map_next(rule.next_rule)) + + def map(rule: FrameUnfold) = + FrameUnfold(rule.h_pre, rule.h_post, map_next(rule.next_rule)) + + def map(rule: Call) = + Call(rule.subst, rule.call, map_next(rule.next_rule)) + + def map(rule: Free) = + Free(rule.stmt, rule.size, map_next(rule.next_rule)) + + def map(rule: Malloc) = + Malloc(rule.map, rule.stmt, map_next(rule.next_rule)) + + def map(rule: Close) = + Close(rule.app, rule.selector, rule.asn, rule.fresh_exist, map_next(rule.next_rule)) + + def map(rule: StarPartial) = + StarPartial(rule.new_pre_phi, rule.new_post_phi, map_next(rule.next_rule)) + + def map(rule: PickCard) = + PickCard(rule.map, map_next(rule.next_rule)) + + def map(rule: PickArg) = + PickArg(rule.map, map_next(rule.next_rule)) + + def map(rule: Init) = + Init(rule.goal, map_next(rule.next_rule)) + + def map_inconsistency = Inconsistency + + def map(rule: ProofRule) : ProofRule = rule match { + case rule@NilNotLval(_, _) => map(rule) + case rule@CheckPost(_, _, _) => map(rule) + case rule@Pick(_, _) => map(rule) + case rule@AbduceBranch(_, _, _) => map(rule) + case rule@Write(_, _) => map(rule) + case rule@WeakenPre(_, _) => map(rule) + case rule@EmpRule => map_emp + case rule@PureSynthesis(_, _, _) => map(rule) + case rule@Open(_, _, _, _) => map(rule) + case rule@SubstL(_, _) => map(rule) + case rule@SubstR(_, _) => map(rule) + case rule@Read(_, _, _) => map(rule) + case rule@AbduceCall(_, _, _, _, _, _, _, _, _) => map(rule) + case rule@HeapUnify(_, _) => map(rule) + case rule@HeapUnifyPointer(_, _) => map(rule) + case rule@FrameUnfold(_, _, _) => map(rule) + case rule@Call(_, _, _) => map(rule) + case rule@Free(_, _, _) => map(rule) + case rule@Malloc(_, _, _) => map(rule) + case rule@Close(_, _, _, _, _) => map(rule) + case rule@StarPartial(_, _, _) => map(rule) + case rule@PickCard(_, _) => map(rule) + case rule@PickArg(_, _) => map(rule) + case rule@Init(_, _) => map(rule) + case Inconsistency => map_inconsistency + } + + /*** + * DO NOT CALL THIS FROM CLIENT CODE!!!! + */ + def map_next(rule: ProofRule): ProofRule + def map_if_true(rule: ProofRule) : ProofRule + def map_if_false(rule: ProofRule) : ProofRule + + def map_cases(cases: List[(Expr, ProofRule)]) : List[(Expr, ProofRule)] + + } + + def copy_proof_rule_with_next(rule: ProofRule, next: List[ProofRule]) = { + class ProofRuleCopy extends ProofRuleMapper { + override def map_next(rule: ProofRule): ProofRule = {assert(next.length == 1, "Expecting arity of 1"); next.head } + override def map_if_true(rule: ProofRule): ProofRule = {assert(next.length == 2, "Expecting arity of 2"); next(0) } + override def map_if_false(rule: ProofRule): ProofRule = {assert(next.length == 2, "Expecting arity of 2"); next(1) } + override def map_cases(cases: List[(Expr, ProofRule)]): List[(Expr, ProofRule)] = { + assert(next.length == cases.length, s"Expecting arity of ${cases.length}") + cases.zip(next).map({case ((expr, _), next) => (expr, next)}) + } + } + + val mapper = new ProofRuleCopy() + mapper.map(rule) } /** converts a Suslik CertTree node into the unified ProofRule structure */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index 619782255..3ab8e3f2a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -24,6 +24,7 @@ object Formulae { override def pp: String = { // s"(data_at Tsh ${elem_typ.pp_as_ctype} ${elem.pp_as_c_value} ${loc.pp})" elem_typ match { + case ProofTypes.CoqParamType(CTypes.CVoidPtrType) => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [${elem.pp_as_ssl_union_value}] ${loc.pp})" case ProofTypes.CoqParamType(_) => s"(data_at Tsh (tarray ${elem_typ.pp_as_ctype} 1) [${elem.pp_as_ssl_union_value}] ${loc.pp})" case ProofTypes.CoqPtrType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inr ${elem.pp_as_c_value}] ${loc.pp})" case ProofTypes.CoqIntType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inl ${elem.pp_as_c_value}] ${loc.pp})" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala index 3942ba3e8..9834b710f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import ProofTerms.CardConstructor -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCCardinalityConstructor, ProofCExpr} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType import org.tygus.suslik.language.Ident @@ -22,6 +22,14 @@ sealed abstract class ProofSteps extends PrettyPrinting { object ProofSteps { + case class IntroEvar(variable: ProofCVar, next: ProofSteps) extends ProofSteps { + override def pp: String = s"try evar ${variable.pp}.\n${next.pp}" + } + + case class InstantiateEvar(name: Ident, value: ProofCExpr, next: ProofSteps) extends ProofSteps { + override def pp: String = s"instantiate (${name} := ${value.pp}).\n${next.pp}" + } + case class Entailer(next: ProofSteps) extends ProofSteps { override def pp: String = s"entailer!.\n${next.pp}" } @@ -154,7 +162,7 @@ object ProofSteps { override def pp: String = "" } - case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCCardinalityConstructor, next: ProofSteps) extends ProofSteps { + case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCExpr, next: ProofSteps) extends ProofSteps { override def pp: String = s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1.\n${next.pp}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index c49a5f34d..933f34a24 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -44,8 +44,8 @@ object ProofTerms { } case expr@ProofCBoolConst(_) => expr case expr@ProofCIntConst(_, _) => expr - case ProofCSetLiteral(elems) => - ProofCSetLiteral(elems.map(_.subst(mapping))) + case ProofCSetLiteral(elems, ty) => + ProofCSetLiteral(elems.map(_.subst(mapping)), ty) case ProofCIfThenElse(cond, left, right) => ProofCIfThenElse(cond.subst(mapping), left.subst(mapping), right.subst(mapping)) case ProofCBinaryExpr(op, left, right) => @@ -61,7 +61,7 @@ object ProofTerms { case ProofCBoolConst(value) => CoqBoolType case ProofCIntConst(value, false) => CoqIntType case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems) => CoqListType(elems.head.type_expr, Some(elems.length)) + case ProofCSetLiteral(elems, ty) => CoqListType(ty.getOrElse(elems.head.type_expr), Some(elems.length)) case ProofCIfThenElse(cond, left, right) => left.type_expr case ProofCBinaryExpr(op, left, right) => op match { case ProofCOpPlus => CoqIntType @@ -85,8 +85,8 @@ object ProofTerms { case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") } case ProofCIntConst(value, false) => s"(Vint (Int.repr ${value.toString}))" - case ProofCSetLiteral(elems) => - s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" + case ProofCSetLiteral(elems, ty) => + s"([${elems.map(_.pp_as_c_value).mkString("; ")}] : list val)" case value@ProofCBinaryExpr(op, _, _) => val is_int = op match { case ProofCOpPlus => true @@ -119,8 +119,8 @@ object ProofTerms { } case ProofCIntConst(value, false) => s"inl (Vint (Int.repr ${value.toString}))" case ProofCIntConst(0, true) => s"inr nullval" - case ProofCSetLiteral(elems) => - s"[${elems.map(_.pp_as_ssl_union_value).mkString("; ")}]" + case ProofCSetLiteral(elems, ty) => + s"([${elems.map(_.pp_as_ssl_union_value).mkString("; ")}])" case value@ProofCBinaryExpr(op, _, _) => val is_int = op match { case ProofCOpPlus => true @@ -190,7 +190,7 @@ object ProofTerms { } else { this.pp } - case ProofCSetLiteral(elems) => this.pp + case ProofCSetLiteral(elems, _) => this.pp case ProofCIfThenElse(cond, left, right) => this.pp case ProofCBinaryExpr(op, left, right) => this.pp case ProofCUnaryExpr(op, e) => this.pp @@ -199,7 +199,7 @@ object ProofTerms { } case class ProofCCardinalityConstructor(pred_type: String, name: String, args: List[ProofCExpr]) extends ProofCExpr { - override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type})" + override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type}_card)" } @@ -208,7 +208,7 @@ object ProofTerms { override def pp: String = typ match { case ProofTypes.CoqPtrType => name case ProofTypes.CoqIntType => name - case ProofTypes.CoqCardType(_) => name + case ProofTypes.CoqCardType(ty) => s"(${name} : ${ty}_card)" case ProofTypes.CoqParamType(ty) => // if the variable has a param type then // its actually of type val, and we need to @@ -237,9 +237,13 @@ object ProofTerms { } /** set literal (encoded as set) in a VST proof */ - case class ProofCSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { + case class ProofCSetLiteral(elems: List[ProofCExpr], elem_type : Option[VSTProofType]=None) extends ProofCExpr { override def pp: String = - s"[${elems.map(_.pp_as_c_value).mkString("; ")}]" + s"([${elems.map(_.pp_as_c_value).mkString("; ")}]${(elems, elem_type) match { + case (::(_,_), _) => "" + case (_, Some(ty)) => s": ${ty.pp}" + case (_, _) => "" + }})" } /** encodes a ternary expression in a VST proof */ @@ -259,8 +263,8 @@ object ProofTerms { op match { case ProofCOpLt => s"(${left.pp_as_int_value} < ${right.pp_as_int_value})" case ProofCOpLeq => s"(${left.pp_as_int_value} <= ${right.pp_as_int_value})" - case ProofCOpOr => s"(${left.pp} \/ ${right.pp})" - case ProofCOpAnd => s"(${left.pp} /\ ${right.pp})" + case ProofCOpOr => s"(${left.pp} \\/ ${right.pp})" + case ProofCOpAnd => s"(${left.pp} /\\ ${right.pp})" case ProofCOpPlus => s"(${left.pp} + ${right.pp})" case ProofCOpMinus => s"(${left.pp} - ${right.pp})" case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" @@ -509,7 +513,7 @@ object ProofTerms { case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil case Expressions.ProofCBoolConst(value) => Nil case Expressions.ProofCIntConst(value, _) => Nil - case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) + case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) @@ -529,7 +533,7 @@ object ProofTerms { case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList case Expressions.ProofCBoolConst(value) => Nil case Expressions.ProofCIntConst(value, _) => Nil - case Expressions.ProofCSetLiteral(elems) => elems.flatMap(expr_existential) + case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) @@ -583,17 +587,20 @@ object ProofTerms { /** returns any helper lemmas that need to be constructed for the helper */ - def get_helpers: List[VSTPredicateHelper] = + def get_helpers: List[VSTPredicateHelper] = { + val local_facts = VSTPredicateHelper.LocalFacts(this) params.flatMap({ case (param, ProofTypes.CoqPtrType) => val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) - val local_facts = VSTPredicateHelper.LocalFacts(this) List( - valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer"), - local_facts, VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") + valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer") ) case _ => List() - }) + }) ++ List( + local_facts, + VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") + ) + } /** returns the existential variables introduced by a constructor invokation */ @@ -655,7 +662,7 @@ object ProofTerms { case None if !clause_card_map.contains(name) => List(name) case _ => List() } - case Expressions.ProofCSetLiteral(elems) => elems.flatMap(to_variables) + case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(to_variables) case Expressions.ProofCIfThenElse(cond, left, right) => to_variables(cond) ++ to_variables(left) ++ to_variables(right) case Expressions.ProofCBinaryExpr(op, left, right) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index bd101a7c6..5b24e6038 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -26,11 +26,11 @@ object ProofSpecTranslation { /** Translates a cardinality to a vst expression that can be passed around */ def translate_cardinality(predicate: VSTPredicate, cardinality: CardConstructor): ProofCExpr = { ProofCCardinalityConstructor( - predicate.inductive_name, + predicate.name, predicate.constructor_name(cardinality), cardinality match { case ProofTerms.CardNull => List() - case CardOf(args) => args.map(arg => ProofCVar(arg, CoqCardType(predicate.inductive_name))) + case CardOf(args) => args.map(arg => ProofCVar(arg, CoqCardType(predicate.name))) } ) } @@ -41,7 +41,7 @@ object ProofSpecTranslation { lType match { case IntType => CoqIntType case LocType => CoqPtrType - case CardType => CoqCardType("") // TODO: add a safe version of this (only used when initializing base context) + case CardType => CoqCardType(???) // TODO: add a safe version of this (only used when initializing base context) // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible @@ -59,7 +59,7 @@ object ProofSpecTranslation { case ProofCIntConst(value, false) => CoqIntType case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems) => CoqListType(CoqPtrType, Some(elems.length)) + case ProofCSetLiteral(elems, _) => CoqListType(CoqPtrType, Some(elems.length)) case ProofCIfThenElse(cond, left, right) => type_expr(left) case ProofCBinaryExpr(op, left, right) => op match { @@ -201,7 +201,7 @@ object ProofSpecTranslation { case ProofCVar(name, typ) => typ case ProofCIntConst(value, false) => CoqIntType case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) + case ProofCSetLiteral(elems, _) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) case ProofCBinaryExpr(op, left, right) => op match { case ProofCOpPlus => CoqIntType @@ -476,8 +476,10 @@ object ProofSpecTranslation { // predicate (even if it occurs at a top level or not) val base_context : List[(Ident, VSTProofType)] = { var gamma: Gamma = Map.empty + var pred_name : Option[Ident] = None predicate match { case InductivePredicate(name, params, clauses) => + pred_name = Some(name) clauses.foreach({case InductiveClause(selector, assn) => selector.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) assn.phi.conjuncts.foreach(expr => @@ -486,7 +488,10 @@ object ProofSpecTranslation { assn.sigma.resolve(gamma, env).foreach(v => gamma = v) }) } - gamma.map({case (Var(name), ty) => (name, translate_type(ty))}).toList + gamma.map({case (Var(name), ty) => (name, ty match { + case CardType => CoqCardType(pred_name.get) + case _ => translate_type(ty) + })}).toList } predicate match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 5c58abcd0..7089a44b6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -4,6 +4,7 @@ import org.tygus.suslik.certification.{CertTree, ProofRule} import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} import org.tygus.suslik.certification.ProofRule.EmpRule +import org.tygus.suslik.certification.targets.vst.clang.CTypes import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.AssertPropSubst import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{VSTPredicate, VSTPredicateClause} @@ -33,40 +34,6 @@ object ProofTranslation { } - /** - * Partitions a list of variables into those which correspond to cardinality arguments and those that do not. - * - * - first list has new variables that do not correspond to cardinality arguments - * - second list has variables that correspond to cardinality arguments - * - * Explanation: cardinality arguments typically have names like _alpha_513, - * but when introduced into the context in a suslik proof, they are given - * fresh names such as _alpha_513x2. - */ - def partition_cardinality_args(new_variables: List[(String, VSTProofType)])(card_args: List[String]) = { - var seen_variables_set: Set[String] = Set() - var contructor_map: Map[Ident, Ident] = Map() - - // partition variables into variables that correspond to arguments to the cardinality - val args = new_variables.flatMap({ - case (variable_name, ty) => - if (!seen_variables_set.contains(variable_name)) { - seen_variables_set = seen_variables_set + variable_name - card_args find (name => variable_name.startsWith(name)) match { - // if the any cardinality argument names are a prefix of the variable name, then this - // variable is a fresh variable for that particular cardinality argument - case Some(name) => - contructor_map = contructor_map + (name -> variable_name) - None - case None => - Some(variable_name) - } - } else { - None - } - }) - (args, card_args.map(arg => contructor_map(arg))) - } def translate_proof( name: String, @@ -84,9 +51,10 @@ object ProofTranslation { */ case class Unfold( VSTPredicate: VSTPredicate, - cardinality: ProofTerms.CardConstructor, + cardinality: String, args: List[(String, VSTProofType)], - existentials: List[(String,VSTProofType)] + existentials: List[(String,VSTProofType)], + new_equalities: Map[String, ProofCExpr], ) /** accumulating context used during proof translation @@ -95,12 +63,13 @@ object ProofTranslation { * @param functions stack of functions being abduced during execution * @param queued_unfolds sequence of queued unfolds * */ - case class Context( - post: List[(Ident, VSTProofType)], - gamma: Map[Ident, VSTProofType], - variable_map: Map[Ident, ProofCExpr], - functions: List[FunSpec], - queued_unfolds: List[Unfold] + case class Context(post: List[(Ident, VSTProofType)], + gamma: Map[Ident, VSTProofType], + variable_map: Map[Ident, ProofCExpr], + functions: List[(Ident, List[Ident], List[(Ident, VSTProofType)])], + queued_unfolds: List[Unfold], + coq_context: Map[Ident, VSTProofType], + card_map: Map[Ident, ProofCExpr] ) @@ -109,7 +78,7 @@ object ProofTranslation { case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) case (ProofCBoolConst(_), ProofCBoolConst(_)) => context case (ProofCIntConst(_,_), ProofCIntConst(_,_)) => context - case (ProofCSetLiteral(elems), ProofCSetLiteral(call_elems)) => + case (ProofCSetLiteral(elems, _), ProofCSetLiteral(call_elems, _)) => elems.zip(call_elems).foldLeft(context)({ case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr) }) case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => var new_context = unify_expr(context)(cond)(call_cond) @@ -157,37 +126,41 @@ object ProofTranslation { } val initial_context: Context = - Context( - spec.existensial_params.toList, - ( - spec.c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ - spec.formal_params ++ - spec.existensial_params - ).toMap, - Map(), - Nil, - Nil - ) + Context(spec.existensial_params.toList, ( + spec.c_params.map({ + case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) + case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) + case (name, CTypes.CUnitType) => ??? +// case (name, CTypes.) => (name, ProofTypes.CoqPtrType) +// case (name, ty) => (name, CoqParamType(ty)) + }) ++ + spec.formal_params ++ + spec.existensial_params + ).toMap, Map(), Nil, Nil, Map(), Map()) def retrieve_typing_context: Context => Map[Ident, VSTProofType] = _.gamma def add_new_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { - case Context(post, old_params, ex_map, funs, ufs) => Context(post, old_params ++ new_params, ex_map, funs, ufs) + case Context(post, old_params, ex_map, funs, ufs, cc, cm) => Context(post, old_params ++ new_params, ex_map, funs, ufs, cc, cm) + } + + def add_new_coq_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { + case Context(post, old_params, ex_map, funs, ufs, cc, cm) => Context(post, old_params, ex_map, funs, ufs, cc ++ new_params, cm) } def pop_function(context: Context): (FunSpec, Context) = context match { - case Context(post, old_params, ex_map, fun :: funs, ufs) => (fun, Context(post, old_params, ex_map, funs,ufs)) + case Context(post, old_params, ex_map, fun :: funs, ufs, cc, cm) => (fun, Context(post, old_params, ex_map, funs, ufs, cc, cm)) case _ => fail_with("Function called without prior abduce call") } def push_function(fun_spec: FunSpec)(context: Context): Context = context match { - case Context(post, old_params, ex_map, old_funs, ufs) => Context(post, old_params, ex_map, fun_spec :: old_funs, ufs) + case Context(post, old_params, ex_map, old_funs, ufs, cc, cm) => Context(post, old_params, ex_map, fun_spec :: old_funs, ufs, cc, cm) } def push_unfolding(context: Context)(unfolded_expr: Unfold, new_equalities: Map[String, ProofCExpr]): Context = context match { - case Context(post, gamma, variable_map, functions, queued_unfolds) => - Context(post, gamma, variable_map ++ new_equalities, functions, unfolded_expr :: queued_unfolds) + case Context(post, gamma, variable_map, functions, queued_unfolds, cc, cm) => + Context(post, gamma, variable_map, functions, unfolded_expr :: queued_unfolds, cc, cm) } def record_variable_assignment(name: String, expr: Expr)(context: Context): Context = { @@ -195,25 +168,16 @@ object ProofTranslation { val translated = ProofSpecTranslation.translate_expression(context.gamma)(expr) val result = (context.gamma.get(name), translated) match { case (Some (CoqPtrType), ProofCIntConst(value, _)) => ProofCIntConst(value,true) + case (Some (ty), ProofCSetLiteral(elems, None)) => ProofCSetLiteral(elems, Some(ty)) case (_, translated) => translated } - Context( - context.post, - context.gamma, - (context.variable_map ++ Map(name -> result)), - context.functions, - context.queued_unfolds - ) + val mapping = Map(name -> result) + val variable_map = context.variable_map.map({case (name, expr) => (name, expr.subst(mapping))}) + Context(context.post, context.gamma, (variable_map ++ mapping), context.functions, context.queued_unfolds, context.coq_context, context.card_map) } def record_variable_assignment_card(name: String, expr: ProofCExpr)(context:Context) = - Context( - context.post, - context.gamma, - (context.variable_map ++ Map(name -> expr)), - context.functions, - context.queued_unfolds - ) + Context(context.post, context.gamma, (context.variable_map ), context.functions, context.queued_unfolds,context.coq_context, context.card_map ++ Map(name -> expr)) @@ -241,17 +205,21 @@ object ProofTranslation { val new_funs = context.functions.map({ case (fun_name, args, existentials) => (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg)), existentials) }) val new_variable_map = update_map(context.variable_map) + val new_card_map = update_map(context.card_map) val new_unfolds = context.queued_unfolds.map({ - case Unfold(predicate, cardinality, args, existentials) => - val new_cardinality = cardinality match { - case ProofTerms.CardNull => ProofTerms.CardNull - case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) - } + case Unfold(predicate, cardinality, args, existentials, new_equalities) => + val new_cardinality = cardinality +// match { +// case ProofTerms.CardNull => ProofTerms.CardNull +// case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) +// } val new_args = args.map({case (name,ty) => (variable_mapping.getOrElse(name,name), ty)}) val new_existentials = existentials.map({case (name, ty) => (variable_mapping.getOrElse(name,name), ty)}) - Unfold(predicate, new_cardinality, new_args, new_existentials) + val new_new_equalities = new_equalities.map({case (name,expr) => (variable_mapping.getOrElse(name,name), expr.subst(expr_map))}) + Unfold(predicate, new_cardinality, new_args, new_existentials, new_new_equalities) }) - Context(post, new_params, new_variable_map, new_funs, new_unfolds) + val coq_context = context.coq_context.map({ case (name,ty) => (variable_mapping.getOrElse(name,name), ty)}) + Context(post, new_params, new_variable_map, new_funs, new_unfolds, coq_context, new_card_map) } /** @@ -290,7 +258,7 @@ object ProofTranslation { rule match { case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.CheckPost(prePhi, postPhi, next) => is_variable_used_in_proof(variable)(next) + case ProofRule.CheckPost(_, _, next) => is_variable_used_in_proof(variable)(next) case ProofRule.PickCard(_, next) => is_variable_used_in_proof(variable)(next) case ProofRule.PickArg(_, next) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet @@ -308,14 +276,14 @@ object ProofTranslation { case ProofRule.EmpRule => false case ProofRule.PureSynthesis(is_final, assignments, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => + case ProofRule.Open(pred, _, heaplet, cases) => cases.exists({ case (expr, rule) => is_variable_used_in_exp(variable)(expr) || is_variable_used_in_proof(variable)(rule) }) case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnify(_,next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) @@ -363,7 +331,7 @@ object ProofTranslation { * and then for each branch introducing the variables that it uses. */ def handle_open_rule(rule: ProofRule.Open, context: Context): ProofSteps = rule match { - case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, sbst, cases) => + case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => val pred = pred_map(predicate_name) ProofSteps.ForwardIfConstructor( card_variable, @@ -376,10 +344,12 @@ object ProofTranslation { case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) }) val constructor_args = constructor.constructor_args.map(v => fresh_vars(Var(v)).name) - val new_context = add_new_variables( + val new_context_1 = add_new_variables( new_variables.toMap ++ - constructor_args.map(v => (v, CoqCardType(pred.inductive_name))).toMap + constructor_args.map(v => (v, CoqCardType(pred.name))).toMap )(context) + val new_context = add_new_coq_variables(new_variables.toMap ++ + constructor_args.map(v => (v, CoqCardType(pred.name))).toMap)(new_context_1) // val (args, constructor_args) = partition_cardinality_args(new_variables)() ((pred.constructor_name(constructor), constructor, constructor_args), ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), @@ -422,7 +392,7 @@ object ProofTranslation { val pred_name = context.gamma(base_expr) match { case ProofTypes.CoqCardType(pred_type) => pred_type } val predicate = pred_map(pred_name) // NOTE: Assumes that all predicates have a 1-argument constructor - (ProofCCardinalityConstructor(predicate.inductive_name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) + (ProofCCardinalityConstructor(predicate.name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) } val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ @@ -446,25 +416,35 @@ object ProofTranslation { def handle_emp_rule(context: Context) = { def instantiate_existentials(existentials: List[(Ident, VSTProofType)])(then: ProofSteps) : ProofSteps = existentials.foldRight(then)( - (variable, next) => ProofSteps.Exists(context.variable_map(variable._1), next) + (variable, next) => ProofSteps.Exists(context.variable_map.getOrElse(variable._1, ProofCVar(variable._1, variable._2)), next) ) + + def add_evars(unfolds: List[Unfold])(then: ProofSteps) : ProofSteps = + unfolds.flatMap(unfold => unfold.new_equalities.map(v => ProofCVar(v._1, v._2.type_expr))).foldRight(then)({case (proof_var, rest) => + ProofSteps.IntroEvar(proof_var, rest) + }) + def unfold_predicates(then: ProofSteps) : ProofSteps = - context.queued_unfolds.foldRight(then)( - {case (unfold, next) => + context.queued_unfolds.foldLeft(then)( + {case (next, unfold) => val predicate : VSTPredicate = unfold.VSTPredicate - ProofSteps.Unfold ( + unfold.new_equalities.foldRight(ProofSteps.Unfold ( predicate, unfold.VSTPredicate.params.length, - ProofCCardinalityConstructor( - predicate.inductive_name, - predicate.constructor_name(unfold.cardinality), - unfold.cardinality.constructor_args.map(v => ProofCVar(v, CoqCardType(predicate.inductive_name)))), + ProofCVar(unfold.cardinality, CoqCardType(predicate.name)), +// ProofCCardinalityConstructor( +// predicate.inductive_name, +// predicate.constructor_name(unfold.cardinality), +// unfold.cardinality.constructor_args.map(v => ProofCVar(v, CoqCardType(predicate.inductive_name)))), unfold.existentials.foldRight(next) ({case ((variable,ty), next) => ProofSteps.Exists(context.variable_map.getOrElse(variable, ProofCVar(variable,ty)) , next)}) - )} + ) : ProofSteps)({case ((evar_name, evar_value), (next : ProofSteps)) => + ProofSteps.InstantiateEvar(evar_name, context.card_map.getOrElse(evar_name, evar_value), next) + })} ) ProofSteps.ForwardEntailer( + add_evars(context.queued_unfolds)( instantiate_existentials(context.post)( context.post match { // If no existentials, only entailer will be at the end of the unfoldings case Nil => @@ -479,6 +459,7 @@ object ProofTranslation { } } ) + ) ) } @@ -568,11 +549,12 @@ object ProofTranslation { def handle_call_rule(rule: ProofRule.Call, context: Context): ProofSteps = rule match { case ProofRule.Call(_, call, next) => - val ((fun, args, existentials), new_context) = pop_function(context) + val ((fun, args, existentials), new_context_1) = pop_function(context) ProofSteps.ForwardCall(args, existentials match { - case Nil => translate_proof_rules(next)(new_context) + case Nil => translate_proof_rules(next)(new_context_1) case _ => + val new_context = add_new_coq_variables(existentials.toMap)(new_context_1) ProofSteps.IntrosTuple(existentials, translate_proof_rules(next)(new_context)) }) } @@ -639,21 +621,23 @@ object ProofTranslation { val new_equalities = card_equality.toMap val args = cardinality.constructor_args.map( - name => (name, CoqCardType(predicate.inductive_name)) + name => (fresh_exist(Var(name)).name, CoqCardType(predicate.name)) ) - val unfolding = Unfold(predicate, cardinality, args, clause_existentials) + val unfolding = Unfold(predicate, app.card.asInstanceOf[Var].name, args, clause_existentials, new_equalities) val new_context = push_unfolding(context)(unfolding, new_equalities) - val new_context_2 = record_variable_mapping(fresh_exist)(new_context) + val new_context_1 = record_variable_mapping(fresh_exist)(new_context) // record_variable_mapping(fresh_exist)(new_context) + val new_context_2 = add_new_variables(clause_existentials.toMap)(new_context_1) // record_variable_mapping(fresh_exist)(new_context) translate_proof_rules(next)(new_context_2) } def handle_malloc_rule(rule: ProofRule.Malloc, context: Context) = rule match { case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => - val new_context = + val new_context_1 = map.foldRight( add_new_variables(map.map({case (Var(original), Var(name)) => (name, CoqPtrType)}))(context) )({case ((Var(old_name), new_expr), context) => record_variable_assignment(old_name,new_expr)(context)}) + val new_context = add_new_coq_variables(Map(to_var -> CoqPtrType))(new_context_1) ProofSteps.Malloc(sz, ProofSteps.Intros( List((to_var, CoqPtrType)), @@ -697,7 +681,7 @@ object ProofTranslation { // Ignored rules case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.CheckPost(pre_phi, post_phi, next) => translate_proof_rules(next)(context) + case ProofRule.CheckPost(_, _, next) => translate_proof_rules(next)(context) case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) @@ -716,18 +700,18 @@ object ProofTranslation { def contains_free(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_free(next) - case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_free(next) + case ProofRule.CheckPost(_, _, next) => contains_free(next) case ProofRule.Pick(subst, next) => contains_free(next) case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) case ProofRule.Write(stmt, next) => contains_free(next) case ProofRule.WeakenPre(unused, next) => contains_free(next) case ProofRule.EmpRule => false case ProofRule.PureSynthesis(is_final, assignments, next) => contains_free(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => cases.exists { case (_, prf) => contains_free(prf) } + case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_free(prf) } case ProofRule.SubstL(map, next) => contains_free(next) case ProofRule.SubstR(map, next) => contains_free(next) case ProofRule.Read(map, operation, next) => contains_free(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => contains_free(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => contains_free(next) case ProofRule.HeapUnify(_, next) => contains_free(next) case ProofRule.HeapUnifyPointer(map, next) => contains_free(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_free(next) @@ -742,18 +726,18 @@ object ProofTranslation { def contains_malloc(proof: ProofRule): Boolean = proof match { case ProofRule.NilNotLval(vars, next) => contains_malloc(next) - case ProofRule.CheckPost(pre_phi, post_phi, next) => contains_malloc(next) + case ProofRule.CheckPost(_, _, next) => contains_malloc(next) case ProofRule.Pick(subst, next) => contains_malloc(next) case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) case ProofRule.Write(stmt, next) => contains_malloc(next) case ProofRule.WeakenPre(unused, next) => contains_malloc(next) case ProofRule.EmpRule => false case ProofRule.PureSynthesis(is_final, assignments, next) => contains_malloc(next) - case ProofRule.Open(pred, fresh_vars, sbst, cases) => cases.exists { case (_, prf) => contains_malloc(prf) } + case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_malloc(prf) } case ProofRule.SubstL(map, next) => contains_malloc(next) case ProofRule.SubstR(map, next) => contains_malloc(next) case ProofRule.Read(map, operation, next) => contains_malloc(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma, next) => contains_malloc(next) + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => contains_malloc(next) case ProofRule.HeapUnify(_,next) => contains_malloc(next) case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) From 913166dd3bd3324552be099265cdeec28c010639 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 26 Jan 2021 01:51:07 +0800 Subject: [PATCH 080/211] HTT: Handle cases where all goals are proven prematurely --- certification.md | 3 ++- .../suslik/certification/targets/htt/logic/Proof.scala | 7 ++++++- .../targets/htt/translation/ProofTranslation.scala | 10 ++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/certification.md b/certification.md index 8b07b665f..087fe5118 100644 --- a/certification.md +++ b/certification.md @@ -14,6 +14,7 @@ Visit the [`ssl-htt`](https://github.com/yasunariw/ssl-htt) repository to see ex - [Mathematical Components](http://math-comp.github.io/math-comp/) `ssreflect` (>= "1.10.0" & < "1.11~") - [FCSL PCM library](https://github.com/imdea-software/fcsl-pcm) (>= "1.0.0" & < "1.3~") - [HTT](https://github.com/TyGuS/htt) +- [CoqHammer](https://coqhammer.github.io) - [SSL-HTT](https://github.com/TyGuS/ssl-htt) ### Building Definitions and Proofs @@ -24,7 +25,7 @@ We recommend installing with [OPAM](https://opam.ocaml.org/doc/Install.html): opam repo add coq-released https://coq.inria.fr/opam/released opam pin add coq-htt git+https://github.com/TyGuS/htt\#master --no-action --yes opam pin add coq-ssl-htt git+https://github.com/TyGuS/ssl-htt\#master --no-action --yes -opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt coq-ssl-htt +opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt coq-hammer coq-ssl-htt ``` ### Running Synthesis with Certification diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 27b698d68..c42512fa4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -12,7 +12,8 @@ object Proof { def simplify: Step = this match { case SeqComp(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqComp(s1.simplify, s2.simplify) case SeqCompAlt(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqCompAlt(s1.simplify, s2.simplify) - case SubProof(branches) => SubProof(branches.map(_.simplify)) + case SubProof(branches) => SubProof(branches.filterNot(_.isNoop).map(_.simplify)) + case Solve(steps) => Solve(steps.filterNot(_.isNoop).map(_.simplify)) case _ => this } } @@ -29,6 +30,10 @@ object Proof { override val isNoop: Boolean = s1.isNoop && s2.isNoop def pp: String = s"${s1.pp};\n${s2.pp}" } + case class Solve(steps: Seq[Step]) extends Step { + override val isNoop: Boolean = steps.forall(_.isNoop) + def pp: String = s"solve [\n${steps.map(_.pp).mkString(" |\n")} ]" + } case class MoveToCtx(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty def pp: String = s"move=>${items.map(_.pp).mkString(" ")}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index 0e288da47..ca3942038 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -69,8 +69,7 @@ object ProofTranslation { c <- unfoldings.get(app) } yield Proof.UnfoldConstructor(c.idx) >>> solvePost(c.asn, c.existentials, unfoldings) - val p = valueExElim >>> heapExElim.foldLeft[Proof.Step](Proof.Noop)(_ >>> _) >>> Proof.Auto >> rest.foldLeft[Proof.Step](Proof.Noop)(_ >> _) - p + valueExElim >>> heapExElim.foldLeft[Proof.Step](Proof.Noop)(_ >>> _) >>> Proof.Auto >>> Proof.Solve(rest) } var currCallId = 0 @@ -132,21 +131,20 @@ object ProofTranslation { case IR.Branch(cond, next, ctx) => Proof.Branch(cond.subst(ctx.substVar)) >> Proof.SubProof(next.map(visit)) case IR.Open(app, clauses, selectors, next, ctx) => + val app1 = ctx.sappNames.getOrElse(app, app) val branchSteps = next.zip(clauses).map { case (n, c) => val c1 = c.subst(n.ctx.substVar) - val app1 = ctx.sappNames.getOrElse(app, app) val res = visit(n) if (res == Proof.Error) { - Proof.OpenPost(app1) // Don't emit branch prelude if inconsistent + Proof.Noop // Don't emit branch prelude if inconsistent } else { - Proof.OpenPost(app1) >> elimExistentials(c1.existentials) >> elimExistentials(c1.asn.heapVars) >> initPre(c1.asn, app1.uniqueName) >> res } } - Proof.Open(selectors.map(_.subst(ctx.substVar))) >> Proof.SubProof(branchSteps) + Proof.Open(selectors.map(_.subst(ctx.substVar))) >>> Proof.OpenPost(app1) >> Proof.SubProof(branchSteps) case IR.Inconsistency(_) => Proof.Error case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) } From a5a492836e8c6662b1f162bdc51d935a3cfa2a8b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 26 Jan 2021 17:41:49 +0800 Subject: [PATCH 081/211] Add more benchmarks --- .../certification/dll/dll-append.syn | 29 +++++++++++++ .../synthesis/certification/dll/dll-copy.syn | 41 +++++++++++++++++++ .../certification/sll-bounds/common.def | 5 +++ .../certification/sll-bounds/sll-len.syn | 22 ++++++++++ .../certification/sll-bounds/sll-max.syn | 22 ++++++++++ .../certification/sll-bounds/sll-min.syn | 22 ++++++++++ .../synthesis/certification/srtl/common.def | 11 +++++ .../certification/srtl/srtl-insert.syn | 34 +++++++++++++++ .../certification/srtl/srtl-prepend.syn | 16 ++++++++ .../synthesis/certification/tree/common.def | 5 +++ .../certification/tree/tree-flatten-acc.syn | 26 ++++++++++++ 11 files changed, 233 insertions(+) create mode 100644 src/test/resources/synthesis/certification/dll/dll-append.syn create mode 100644 src/test/resources/synthesis/certification/dll/dll-copy.syn create mode 100644 src/test/resources/synthesis/certification/sll-bounds/common.def create mode 100644 src/test/resources/synthesis/certification/sll-bounds/sll-len.syn create mode 100644 src/test/resources/synthesis/certification/sll-bounds/sll-max.syn create mode 100644 src/test/resources/synthesis/certification/sll-bounds/sll-min.syn create mode 100644 src/test/resources/synthesis/certification/srtl/common.def create mode 100644 src/test/resources/synthesis/certification/srtl/srtl-insert.syn create mode 100644 src/test/resources/synthesis/certification/srtl/srtl-prepend.syn create mode 100644 src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn diff --git a/src/test/resources/synthesis/certification/dll/dll-append.syn b/src/test/resources/synthesis/certification/dll/dll-append.syn new file mode 100644 index 000000000..b933bba99 --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/dll-append.syn @@ -0,0 +1,29 @@ +# -c 2 -f 1 + +doubly-linked list: append + +##### + +{true ; dll(x1, a, s1) ** dll(x2, b, s2) ** r :-> x2} +void dll_append (loc x1, loc r) +{s =i s1 ++ s2 ; dll(y, c, s) ** r :-> y } + +##### + +void dll_append (loc x1, loc r) { + if (x1 == null) { + } else { + let w = *(x1 + 1); + dll_append(w, r); + let y = *r; + if (y == null) { + *(x1 + 1) = null; + *r = x1; + } else { + *(y + 2) = x1; + *(x1 + 1) = y; + *r = x1; + } + } +} + diff --git a/src/test/resources/synthesis/certification/dll/dll-copy.syn b/src/test/resources/synthesis/certification/dll/dll-copy.syn new file mode 100644 index 000000000..a6c137c3f --- /dev/null +++ b/src/test/resources/synthesis/certification/dll/dll-copy.syn @@ -0,0 +1,41 @@ +# -c 2 -f 1 + +should be able to copy a list + +##### + +{true ; r :-> x ** dll(x, a, s)} +void dll_copy(loc r) +{true ; r :-> y ** dll(x, a, s) ** dll(y, b, s) } + +##### + +void dll_copy (loc r) { + let x = *r; + if (x == null) { + *r = null; + } else { + let vx = *x; + let w = *(x + 1); + *r = w; + dll_copy(r); + let y1 = *r; + if (y1 == null) { + let y = malloc(3); + *r = y; + *(y + 1) = null; + *y = vx; + *(y + 2) = null; + } else { + let v = *y1; + let y = malloc(3); + *(y1 + 2) = y; + *r = y; + *(y + 1) = y1; + *y1 = vx; + *y = v; + *(y + 2) = null; + } + } +} + diff --git a/src/test/resources/synthesis/certification/sll-bounds/common.def b/src/test/resources/synthesis/certification/sll-bounds/common.def new file mode 100644 index 000000000..a391b2cd8 --- /dev/null +++ b/src/test/resources/synthesis/certification/sll-bounds/common.def @@ -0,0 +1,5 @@ +predicate sll(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/sll-bounds/sll-len.syn b/src/test/resources/synthesis/certification/sll-bounds/sll-len.syn new file mode 100644 index 000000000..458109d1a --- /dev/null +++ b/src/test/resources/synthesis/certification/sll-bounds/sll-len.syn @@ -0,0 +1,22 @@ +singly-linked list: length + +##### + +{0 <= n ; r :-> a ** sll(x, n, lo, hi) } +void sll_len (loc x, loc r) +{true ; r :-> n ** sll(x, n, lo, hi) } + +##### + +void sll_len (loc x, loc r) {s + if (x == null) { + *r = 0; + } else { + let v = *x; + let n = *(x + 1); + sll_len(n, x); + let l = *x; + *r = 1 + l; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/sll-bounds/sll-max.syn b/src/test/resources/synthesis/certification/sll-bounds/sll-max.syn new file mode 100644 index 000000000..6dedebf36 --- /dev/null +++ b/src/test/resources/synthesis/certification/sll-bounds/sll-max.syn @@ -0,0 +1,22 @@ +singly-linked list: max + +##### + +{true ; r :-> a ** sll(x, n, lo, hi) } +void sll_max (loc x, loc r) +{true ; r :-> hi ** sll(x, n, lo, hi) } + +##### + +void sll_max (loc x, loc r) { + if (x == null) { + *r = 0; + } else { + let v = *x; + let n = *(x + 1); + sll_max(n, x); + let h = *x; + *r = h <= v ? v : h; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/sll-bounds/sll-min.syn b/src/test/resources/synthesis/certification/sll-bounds/sll-min.syn new file mode 100644 index 000000000..19c32069f --- /dev/null +++ b/src/test/resources/synthesis/certification/sll-bounds/sll-min.syn @@ -0,0 +1,22 @@ +singly-linked list: min + +##### + +{true ; r :-> a ** sll(x, n, lo, hi) } +void sll_min (loc x, loc r) +{true ; r :-> lo ** sll(x, n, lo, hi) } + +##### + +void sll_min (loc x, loc r) { + if (x == 0) { + *r = 7; + } else { + let v = *x; + let n = *(x + 1); + sll_min(n, x); + let l = *x; + *r = v <= l ? v : l; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/srtl/common.def b/src/test/resources/synthesis/certification/srtl/common.def new file mode 100644 index 000000000..bbd264f9d --- /dev/null +++ b/src/test/resources/synthesis/certification/srtl/common.def @@ -0,0 +1,11 @@ +predicate sll(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } +} + +predicate srtl(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ v <= lo1 /\ 0 <= v /\ v <= 7 ; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** srtl(nxt, len1, lo1, hi1) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/srtl/srtl-insert.syn b/src/test/resources/synthesis/certification/srtl/srtl-insert.syn new file mode 100644 index 000000000..1e2d36b49 --- /dev/null +++ b/src/test/resources/synthesis/certification/srtl/srtl-insert.syn @@ -0,0 +1,34 @@ +#. -b true -c 2 +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == n + 1 /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +##### + +void srtl_insert (loc x, loc r) { + let k = *r; + if (x == null) { + let y = malloc(2); + *r = y; + *(y + 1) = null; + *y = k; + } else { + let v = *x; + if (v <= k) { + let n = *(x + 1); + srtl_insert(n, r); + let y = *r; + *r = x; + *(x + 1) = y; + } else { + let y = malloc(2); + *r = y; + *(y + 1) = x; + *y = k; + } + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/srtl/srtl-prepend.syn b/src/test/resources/synthesis/certification/srtl/srtl-prepend.syn new file mode 100644 index 000000000..ce20fc4b9 --- /dev/null +++ b/src/test/resources/synthesis/certification/srtl/srtl-prepend.syn @@ -0,0 +1,16 @@ +sorted list: prepend an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 /\ k <= lo ; r :-> a ** srtl(x, n, lo, hi) } +void srtl_prepend (loc x, int k, loc r) +{n1 == n + 1 ; r :-> y ** srtl(y, n1, k, hi1) } + +##### + +void srtl_prepend (loc x, int k, loc r) { + let y = malloc(2); + *r = y; + *(y + 1) = x; + *y = k; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/tree/common.def b/src/test/resources/synthesis/certification/tree/common.def index ef07c0105..80cd4fc04 100644 --- a/src/test/resources/synthesis/certification/tree/common.def +++ b/src/test/resources/synthesis/certification/tree/common.def @@ -8,3 +8,8 @@ predicate treeN(loc x, int n) { | not (x == null) => { n == 1 + n1 + n2 /\ 0 <= n1 /\ 0 <= n2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** treeN(l, n1) ** treeN(r, n2)} } + +predicate sll(loc x, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} diff --git a/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn b/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn new file mode 100644 index 000000000..d808e986c --- /dev/null +++ b/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn @@ -0,0 +1,26 @@ +should be able to flatten the tree into a list given a list accumulator + +#### + +{ true ; tree(x, s) ** z :-> y ** sll(y, acc)} +void tree_flatten(loc x, loc z) +{ s1 =i s ++ acc ; z :-> t ** sll(t, s1)} + +#### + +void tree_flatten (loc x, loc z) { + if (x == null) { + } else { + let v = *x; + let l = *(x + 1); + let r = *(x + 2); + tree_flatten(l, z); + tree_flatten(r, z); + let tr = *z; + let t = malloc(2); + free(x); + *z = t; + *(t + 1) = tr; + *t = v; + } +} \ No newline at end of file From 91a358ecddbd9d6186e6e34795f60431449367d7 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 27 Jan 2021 19:10:10 +0800 Subject: [PATCH 082/211] converted vst proof output to more generic class --- logback.xml | 20 - .../suslik/certification/ProofTree.scala | 19 + .../targets/vst/logic/Proof.scala | 4 +- .../targets/vst/logic/VSTProofStep.scala | 157 +++++++ .../vst/translation/ProofTranslation.scala | 410 ++++++++++-------- .../targets/vst/translation/Translation.scala | 3 +- 6 files changed, 399 insertions(+), 214 deletions(-) delete mode 100644 logback.xml create mode 100644 src/main/scala/org/tygus/suslik/certification/ProofTree.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala diff --git a/logback.xml b/logback.xml deleted file mode 100644 index f77d22d9b..000000000 --- a/logback.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - %d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n - - - - - - - - - \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/ProofTree.scala b/src/main/scala/org/tygus/suslik/certification/ProofTree.scala new file mode 100644 index 000000000..1f9d04033 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/ProofTree.scala @@ -0,0 +1,19 @@ +package org.tygus.suslik.certification + +import org.tygus.suslik.language.PrettyPrinting + +/** + * Represents an abstract encoding of a proof tree + * + * @param rule individual rule + * @param children list of child nodes + */ +sealed case class ProofTree[T](rule: T, children: List[ProofTree[T]]) + (implicit printer: ProofTreePrinter[T]) + extends PrettyPrinting { + override def pp : String = printer.pp(this) +} + +trait ProofTreePrinter[T] { + def pp (tree: ProofTree[T]) : String +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index c40f4e68a..cf60ec5bc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.language.Expressions.Expr -import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.{CertTree, ProofTree} import org.tygus.suslik.certification.targets.vst.{Debug, State} import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate @@ -15,7 +15,7 @@ import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, Logic import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} -case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofSteps, uses_free: Boolean = false, uses_malloc: Boolean = false) extends PrettyPrinting { +case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], uses_free: Boolean = false, uses_malloc: Boolean = false) extends PrettyPrinting { /** prelude for Coq file */ private def coq_prelude = s""" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala new file mode 100644 index 000000000..9d319183b --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -0,0 +1,157 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.CardConstructor +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType +import org.tygus.suslik.language.{Ident, PrettyPrinting} + +sealed abstract class VSTProofStep extends PrettyPrinting {} + +object VSTProofStep { + implicit object ProofTreePrinter extends ProofTreePrinter[VSTProofStep] { + override def pp(tree: ProofTree[VSTProofStep]): String = tree.rule match { + case rule@ForwardIf => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) + case rule@ForwardIfConstructor(_,_,_) => tree.rule.pp ++ "\n" ++ rule.branch_strings(tree.children) + case _ => tree.rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + } + } + + case class IntroEvar(variable: ProofCVar) extends VSTProofStep { + override def pp: String = s"try evar ${variable.pp}." + } + + case class InstantiateEvar(name: Ident, value: ProofCExpr) extends VSTProofStep { + override def pp: String = s"instantiate (${name} := ${value.pp})." + } + + case object Entailer extends VSTProofStep { + override def pp: String = s"entailer!." + } + + case class ForwardIfConstructor( + card_variable: String, + predicate_name: String, + branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String])] + ) extends VSTProofStep { + + def branch_strings[T <: PrettyPrinting] (children : List[T]): String = { + val branches = this.branches.zip(children).map({ case ((clause, selector, args), value) => (clause, selector, args, value)}) + def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { + case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" + case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + } + branches match { + case Nil => "" + case _ => + "\n" ++ branches.map( + { case ((cons_name, cons, cons_args), expr, args, ls) => + " - {\n" ++ + s"assert_PROP (${constructor_prop(cons_name, cons)}) as ssl_card_assert. { entailer!; ssl_dispatch_card. }\n" ++ + s"ssl_card ${predicate_name} ssl_card_assert ${cons_args.mkString(" ")}.\n" ++ + s"assert_PROP (${expr.pp}). { entailer!. }\n" ++ + (args match { + case Nil => "" + case args => s"Intros ${args.mkString(" ")}.\n" + }) ++ + ls.pp ++ + "\n}" + } + ).mkString("\n") + } + } + + override def pp: String = { + "forward_if." + } + } + + case object ForwardIf extends VSTProofStep { + def branch_strings[T <: PrettyPrinting] (children: List[T]) = { + val branches = children + branches match { + case Nil => "" + case _ => "\n" ++ branches.map(ls => " - {\n" ++ ls.pp ++ "\n}").mkString("\n") + } + } + override def pp: String = { + "forward_if." + } + } + + case object Forward extends VSTProofStep { + override def pp: String = s"forward." + } + + case class Intros(variables: List[(Ident, VSTProofType)]) extends VSTProofStep { + override def pp: String = { + s"Intros ${variables.map(_._1).mkString(" ")}." + } + } + + case class IntrosTuple(variables: List[(Ident, VSTProofType)]) extends VSTProofStep { + variables match { + case Nil => "" + case ::((variable, _), Nil) => + s"Intros ${variable}." + case _ => + def to_destruct_pattern(base: Option[String])(ls: List[(Ident, VSTProofType)]): String = { + (base, ls) match { + case (None, ::((vara, _), ::((varb, _), rest))) => to_destruct_pattern(Some(s"[${vara} ${varb}]"))(rest) + case (Some(base), ::((vara, _), rest)) => + to_destruct_pattern(Some(s"[${base} ${vara}]"))(rest) + case (Some(base), Nil) => base + } + } + val destruct_pattern: String = to_destruct_pattern(None)(variables) + s"let ret := fresh vret in Intros ret; destruct ret as ${destruct_pattern}." + } + } + + case class ValidPointerOrNull(variable: Ident) extends VSTProofStep { + override def pp: String = s"assert_PROP (is_pointer_or_null ${variable}). { entailer!. }" + } + + case class ValidPointer(variable: Ident) extends VSTProofStep { + override def pp: String = s"assert_PROP (isptr ${variable}). { entailer!. }" + } + + case class ForwardCall(args: List[Ident]) extends VSTProofStep { + override def pp: String = s"forward_call (${args.mkString(", ")})." + } + + case class Rename(old_name: Ident, new_name: Ident) extends VSTProofStep { + override def pp: String = s"try rename ${old_name} into ${new_name}." + } + + case class Exists(variable: ProofCExpr) extends VSTProofStep { + override def pp: String = s"Exists ${variable.pp}." + } + + case class Free(variable: Ident, sz: Int) extends VSTProofStep { + override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${sz}, ${variable})." + } + + case class Malloc(size: Int) extends VSTProofStep { + override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${size})." + } + + case class AssertPropSubst(variable: Ident, expr: ProofCExpr) extends VSTProofStep { + override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp_as_ptr_value}) as ssl_var; try rewrite ssl_var. { entailer!. }" + } + + case object Qed extends VSTProofStep { + override def pp: String = "" + } + + case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCExpr) extends VSTProofStep { + override def pp: String = + s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1." + + } + + case object ForwardEntailer extends VSTProofStep { + override def pp: String = s"forward; entailer!." + } + +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 7089a44b6..33a36c892 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -1,29 +1,20 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.{CertTree, ProofRule} +import org.tygus.suslik.certification.targets.vst.clang.CTypes import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} -import org.tygus.suslik.certification.ProofRule.EmpRule -import org.tygus.suslik.certification.targets.vst.clang.CTypes -import org.tygus.suslik.certification.targets.vst.logic.ProofSteps.AssertPropSubst -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCSetLiteral, ProofCUnaryExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{VSTPredicate, VSTPredicateClause} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqParamType, CoqPtrType, VSTProofType} -import org.tygus.suslik.certification.targets.vst.{Debug, State} -import org.tygus.suslik.certification.targets.vst.logic.{Formulae, Proof, ProofSteps, ProofTerms, ProofTypes} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions._ +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, ProofTypes, VSTProofStep} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with -import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} -import org.tygus.suslik.language.{Expressions, Ident, PrettyPrinting, Statements} -import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Skip, Store} -import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} -import org.tygus.suslik.logic.Specifications.SuspendedCallGoal -import org.tygus.suslik.logic.{Block, Heaplet, PFormula, PointsTo, SApp, SFormula, Specifications} -import org.tygus.suslik.synthesis.rules.LogicalRules.FrameBlock.profilesMatch -import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure -import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnfoldingRules, UnificationRules} -import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} - -import scala.annotation.tailrec +import org.tygus.suslik.certification.{CertTree, ProofRule, ProofTree} +import org.tygus.suslik.language.Expressions.{Expr, Var} +import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc} +import org.tygus.suslik.language.{Expressions, Ident, Statements} +import org.tygus.suslik.logic.SApp + import scala.collection.immutable.Map @@ -34,13 +25,13 @@ object ProofTranslation { } - def translate_proof( name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, root: CertTree.Node, - pre_cond: ProofTerms.FormalCondition + pre_cond: ProofTerms.FormalCondition, + post_cond: ProofTerms.FormalCondition ): Proof = { val pred_map = predicates.map(v => (v.name, v)).toMap @@ -53,16 +44,16 @@ object ProofTranslation { VSTPredicate: VSTPredicate, cardinality: String, args: List[(String, VSTProofType)], - existentials: List[(String,VSTProofType)], + existentials: List[(String, VSTProofType)], new_equalities: Map[String, ProofCExpr], ) /** accumulating context used during proof translation * - * @param gamma typing context - * @param functions stack of functions being abduced during execution - * @param queued_unfolds sequence of queued unfolds - * */ + * @param gamma typing context + * @param functions stack of functions being abduced during execution + * @param queued_unfolds sequence of queued unfolds + **/ case class Context(post: List[(Ident, VSTProofType)], gamma: Map[Ident, VSTProofType], variable_map: Map[Ident, ProofCExpr], @@ -77,7 +68,7 @@ object ProofTranslation { (pure, call) match { case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) case (ProofCBoolConst(_), ProofCBoolConst(_)) => context - case (ProofCIntConst(_,_), ProofCIntConst(_,_)) => context + case (ProofCIntConst(_, _), ProofCIntConst(_, _)) => context case (ProofCSetLiteral(elems, _), ProofCSetLiteral(call_elems, _)) => elems.zip(call_elems).foldLeft(context)({ case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr) }) case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => @@ -127,16 +118,27 @@ object ProofTranslation { val initial_context: Context = Context(spec.existensial_params.toList, ( - spec.c_params.map({ - case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) - case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) - case (name, CTypes.CUnitType) => ??? -// case (name, CTypes.) => (name, ProofTypes.CoqPtrType) -// case (name, ty) => (name, CoqParamType(ty)) - }) ++ - spec.formal_params ++ - spec.existensial_params - ).toMap, Map(), Nil, Nil, Map(), Map()) + spec.c_params.map({ + case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) + case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) + case (name, CTypes.CUnitType) => ??? + // case (name, CTypes.) => (name, ProofTypes.CoqPtrType) + // case (name, ty) => (name, CoqParamType(ty)) + }) ++ + spec.formal_params ++ + spec.existensial_params + ).toMap, Map(), Nil, Nil, + ( + spec.c_params.map({ + case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) + case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) + case (name, CTypes.CUnitType) => ??? + // case (name, CTypes.) => (name, ProofTypes.CoqPtrType) + // case (name, ty) => (name, CoqParamType(ty)) + }) ++ + spec.formal_params + ).toMap, + Map()) def retrieve_typing_context: Context => Map[Ident, VSTProofType] = _.gamma @@ -167,18 +169,24 @@ object ProofTranslation { // when recording a mapping of a pointer, force any int constants to be pointers (suslik doesn't always place them in loc_consts) val translated = ProofSpecTranslation.translate_expression(context.gamma)(expr) val result = (context.gamma.get(name), translated) match { - case (Some (CoqPtrType), ProofCIntConst(value, _)) => ProofCIntConst(value,true) - case (Some (ty), ProofCSetLiteral(elems, None)) => ProofCSetLiteral(elems, Some(ty)) - case (_, translated) => translated + case (Some(CoqPtrType), ProofCIntConst(value, _)) => ProofCIntConst(value, true) + case (Some(ty), ProofCSetLiteral(elems, None)) => ProofCSetLiteral(elems, Some(ty)) + case (_, translated) => translated } val mapping = Map(name -> result) - val variable_map = context.variable_map.map({case (name, expr) => (name, expr.subst(mapping))}) + val variable_map = context.variable_map.map({ case (name, expr) => (name, expr.subst(mapping)) }) Context(context.post, context.gamma, (variable_map ++ mapping), context.functions, context.queued_unfolds, context.coq_context, context.card_map) } - def record_variable_assignment_card(name: String, expr: ProofCExpr)(context:Context) = - Context(context.post, context.gamma, (context.variable_map ), context.functions, context.queued_unfolds,context.coq_context, context.card_map ++ Map(name -> expr)) + def record_variable_assignment_raw(name: String, expr: ProofCExpr)(context: Context): Context = { + // when recording a mapping of a pointer, force any int constants to be pointers (suslik doesn't always place them in loc_consts) + val mapping = Map(name -> expr) + val variable_map = context.variable_map.map({ case (name, expr) => (name, expr.subst(mapping)) }) + Context(context.post, context.gamma, (variable_map ++ mapping), context.functions, context.queued_unfolds, context.coq_context, context.card_map) + } + def record_variable_assignment_card(name: String, expr: ProofCExpr)(context: Context) = + Context(context.post, context.gamma, (context.variable_map), context.functions, context.queued_unfolds, context.coq_context, context.card_map ++ Map(name -> expr)) /** @@ -203,22 +211,23 @@ object ProofTranslation { // Then rename all terms in the context val new_params = context.gamma.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) val new_funs = context.functions.map({ case (fun_name, args, existentials) => - (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg)), existentials) }) + (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg)), existentials) + }) val new_variable_map = update_map(context.variable_map) val new_card_map = update_map(context.card_map) val new_unfolds = context.queued_unfolds.map({ case Unfold(predicate, cardinality, args, existentials, new_equalities) => val new_cardinality = cardinality -// match { -// case ProofTerms.CardNull => ProofTerms.CardNull -// case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) -// } - val new_args = args.map({case (name,ty) => (variable_mapping.getOrElse(name,name), ty)}) - val new_existentials = existentials.map({case (name, ty) => (variable_mapping.getOrElse(name,name), ty)}) - val new_new_equalities = new_equalities.map({case (name,expr) => (variable_mapping.getOrElse(name,name), expr.subst(expr_map))}) + // match { + // case ProofTerms.CardNull => ProofTerms.CardNull + // case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) + // } + val new_args = args.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) + val new_existentials = existentials.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) + val new_new_equalities = new_equalities.map({ case (name, expr) => (variable_mapping.getOrElse(name, name), expr.subst(expr_map)) }) Unfold(predicate, new_cardinality, new_args, new_existentials, new_new_equalities) }) - val coq_context = context.coq_context.map({ case (name,ty) => (variable_mapping.getOrElse(name,name), ty)}) + val coq_context = context.coq_context.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) Context(post, new_params, new_variable_map, new_funs, new_unfolds, coq_context, new_card_map) } @@ -235,7 +244,7 @@ object ProofTranslation { * - first assert that the pointer being read from is non-null (VST idiosynracy) * - emit a forward tactic to move over the operation */ - def handle_read_rule(rule: ProofRule.Read, context: Context): ProofSteps = rule match { + def handle_read_rule(rule: ProofRule.Read, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.Read(subst, option, next) => subst.toList match { case ::((Var(old_var), Var(new_var)), _) => @@ -283,9 +292,9 @@ object ProofTranslation { }) case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => + case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnify(_,next) => is_variable_used_in_proof(variable)(next) + case ProofRule.HeapUnify(_, next) => is_variable_used_in_proof(variable)(next) case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) case ProofRule.Close(app, selector, asn, fresh_exist, next) => @@ -307,19 +316,13 @@ object ProofTranslation { val new_context = record_variable_mapping(subst)(context) val rest = (retrieve_typing_context(context).get(old_var)) match { case Some(CoqPtrType) => - ProofSteps.ValidPointerOrNull(new_var, translate_proof_rules(next)(new_context)) + ProofTree(VSTProofStep.ValidPointerOrNull(new_var), List(translate_proof_rules(next)(new_context))) case _ => translate_proof_rules(next)(new_context) } if (is_variable_used_in_proof(new_var)(next)) { - ProofSteps.Rename(old_var, new_var, - ProofSteps.Forward( - rest - ) - ) + ProofTree(VSTProofStep.Rename(old_var, new_var), List(ProofTree(VSTProofStep.Forward, List(rest)))) } else { - ProofSteps.Rename(old_var, new_var, - rest - ) + ProofTree(VSTProofStep.Rename(old_var, new_var), List(rest)) } } } @@ -330,57 +333,70 @@ object ProofTranslation { * Does this by mapping each constructor of the opened predicate to a branch of the rule, * and then for each branch introducing the variables that it uses. */ - def handle_open_rule(rule: ProofRule.Open, context: Context): ProofSteps = rule match { + def handle_open_rule(rule: ProofRule.Open, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => val pred = pred_map(predicate_name) - ProofSteps.ForwardIfConstructor( + val context_branches = pred.clauses.zip(cases).map({ + case ((constructor, clause), (expr, rule)) => + // each clause of the type introduces existentials + val new_variables = pred.constructor_existentials(constructor).map({ + // rename existential variables if they have been assigned fresh variables + case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) + }) + val constructor_args = constructor.constructor_args.map(v => fresh_vars(Var(v)).name) + val new_context_1 = add_new_variables( + new_variables.toMap ++ + constructor_args.map(v => (v, CoqCardType(pred.name))).toMap + )(context) + val new_context_2 = add_new_coq_variables(new_variables.toMap ++ + constructor_args.map(v => (v, CoqCardType(pred.name))).toMap)(new_context_1) + // val (args, constructor_args) = partition_cardinality_args(new_variables)() + + val card_value = ProofSpecTranslation.translate_cardinality(pred, constructor match { + case ProofTerms.CardNull => ProofTerms.CardNull + case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map(arg => fresh_vars.get(Var(arg)).map(_.name).getOrElse(arg))) + }) + val new_context = record_variable_assignment_raw(card_variable, card_value)(new_context_2) + val selector = ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr) + + ((pred.constructor_name(constructor), constructor, constructor_args), + selector, + new_variables.map(_._1), + translate_proof_rules(rule)(new_context)) + }).toList + val branches = context_branches.map({ case (expr, selector, vars, _) => (expr, selector, vars) }) + val next = context_branches.map({ case (_, _, _, next) => next }) + ProofTree(VSTProofStep.ForwardIfConstructor( card_variable, predicate_name, - pred.clauses.zip(cases).map({ - case ((constructor, clause), (expr, rule)) => - // each clause of the type introduces existentials - val new_variables = pred.constructor_existentials(constructor).map({ - // rename existential variables if they have been assigned fresh variables - case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) - }) - val constructor_args = constructor.constructor_args.map(v => fresh_vars(Var(v)).name) - val new_context_1 = add_new_variables( - new_variables.toMap ++ - constructor_args.map(v => (v, CoqCardType(pred.name))).toMap - )(context) - val new_context = add_new_coq_variables(new_variables.toMap ++ - constructor_args.map(v => (v, CoqCardType(pred.name))).toMap)(new_context_1) - // val (args, constructor_args) = partition_cardinality_args(new_variables)() - ((pred.constructor_name(constructor), constructor, constructor_args), - ProofSpecTranslation.translate_expression(retrieve_typing_context(context).toMap)(expr), - new_variables.map(_._1), - translate_proof_rules(rule)(new_context)) - }).toList + branches + ), + next ) } - def handle_pick_rule(rule: ProofRule.Pick, context: Context): ProofSteps = rule match { + def handle_pick_rule(rule: ProofRule.Pick, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.Pick(subst, next) => - val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - case ((name,expr), context) => record_variable_assignment(name,expr)(context) + val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ + case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_pick_card_rule(rule: ProofRule.PickCard, context: Context): ProofSteps = rule match { + def handle_pick_card_rule(rule: ProofRule.PickCard, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.PickCard(subst, next) => /** Given an expression representing a pick of a cardinality variable * returns the corresponding cardinality constructor * * i.e - * _alpha_512 -> _alpha_514 + 1 + * _alpha_512 -> _alpha_514 + 1 * - * produces - * (lseg_card_1 _alpha_514), [(_alpha_514, lseg_card)] - * (i.e, the picked cardinality is that alpha_512 maps to lseg_card_1 _alpha_514, where _alpha_514 is a new existential variable) + * produces + * (lseg_card_1 _alpha_514), [(_alpha_514, lseg_card)] + * (i.e, the picked cardinality is that alpha_512 maps to lseg_card_1 _alpha_514, where _alpha_514 is a new existential variable) */ - def cardinality_expr_mapping_to_cardinality_map(base_expr: String)(expr: Expressions.Expr) : (ProofCExpr, List[(Ident, VSTProofType)]) = + def cardinality_expr_mapping_to_cardinality_map(base_expr: String)(expr: Expressions.Expr): (ProofCExpr, List[(Ident, VSTProofType)]) = expr match { case Var(name) => context.gamma.get(name) match { @@ -389,14 +405,16 @@ object ProofTranslation { } case rule@Expressions.BinaryExpr(Expressions.OpPlus, expr, Expressions.IntConst(_)) => val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(base_expr)(expr) - val pred_name = context.gamma(base_expr) match { case ProofTypes.CoqCardType(pred_type) => pred_type } + val pred_name = context.gamma(base_expr) match { + case ProofTypes.CoqCardType(pred_type) => pred_type + } val predicate = pred_map(pred_name) // NOTE: Assumes that all predicates have a 1-argument constructor (ProofCCardinalityConstructor(predicate.name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) } - val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - case ((name,expr), context) => + val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ + case ((name, expr), context) => val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(name)(expr) record_variable_assignment_card(name, translated_expr)( add_new_variables(new_vars.toMap)(context) @@ -405,109 +423,117 @@ object ProofTranslation { translate_proof_rules(next)(new_context) } - def handle_pick_arg_rule(rule: ProofRule.PickArg, context: Context): ProofSteps = rule match { + def handle_pick_arg_rule(rule: ProofRule.PickArg, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.PickArg(subst, next) => - val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - case ((name,expr), context) => record_variable_assignment(name,expr)(context) + val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ + case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } def handle_emp_rule(context: Context) = { - def instantiate_existentials(existentials: List[(Ident, VSTProofType)])(then: ProofSteps) : ProofSteps = + def instantiate_existentials(existentials: List[(Ident, VSTProofType)])(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = existentials.foldRight(then)( - (variable, next) => ProofSteps.Exists(context.variable_map.getOrElse(variable._1, ProofCVar(variable._1, variable._2)), next) + (variable, next) => + ProofTree(VSTProofStep.Exists(context.variable_map.getOrElse(variable._1, ProofCVar(variable._1, variable._2))), List(next)) ) - def add_evars(unfolds: List[Unfold])(then: ProofSteps) : ProofSteps = - unfolds.flatMap(unfold => unfold.new_equalities.map(v => ProofCVar(v._1, v._2.type_expr))).foldRight(then)({case (proof_var, rest) => - ProofSteps.IntroEvar(proof_var, rest) + def add_evars(unfolds: List[Unfold])(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = + unfolds.flatMap(unfold => unfold.new_equalities.map(v => ProofCVar(v._1, v._2.type_expr))).foldRight(then)({ case (proof_var, rest) => + if (context.coq_context.contains(proof_var.name)) { + rest + } else { + ProofTree(VSTProofStep.IntroEvar(proof_var), List(rest)) + } }) - def unfold_predicates(then: ProofSteps) : ProofSteps = + def unfold_predicates(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = context.queued_unfolds.foldLeft(then)( - {case (next, unfold) => - val predicate : VSTPredicate = unfold.VSTPredicate - unfold.new_equalities.foldRight(ProofSteps.Unfold ( - predicate, - unfold.VSTPredicate.params.length, - ProofCVar(unfold.cardinality, CoqCardType(predicate.name)), -// ProofCCardinalityConstructor( -// predicate.inductive_name, -// predicate.constructor_name(unfold.cardinality), -// unfold.cardinality.constructor_args.map(v => ProofCVar(v, CoqCardType(predicate.inductive_name)))), - unfold.existentials.foldRight(next) - ({case ((variable,ty), next) => ProofSteps.Exists(context.variable_map.getOrElse(variable, ProofCVar(variable,ty)) , next)}) - ) : ProofSteps)({case ((evar_name, evar_value), (next : ProofSteps)) => - ProofSteps.InstantiateEvar(evar_name, context.card_map.getOrElse(evar_name, evar_value), next) - })} - ) - - ProofSteps.ForwardEntailer( - add_evars(context.queued_unfolds)( - instantiate_existentials(context.post)( - context.post match { // If no existentials, only entailer will be at the end of the unfoldings - case Nil => - context.queued_unfolds match { - case Nil => ProofSteps.Qed - case ::(_, _) => unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed)) - } - case ::(_, _) => - context.queued_unfolds match { - case Nil => ProofSteps.Entailer (ProofSteps.Qed) - case ::(_, _) => ProofSteps.Entailer (unfold_predicates(ProofSteps.Entailer (ProofSteps.Qed))) + { case (next, unfold) => + val predicate: VSTPredicate = unfold.VSTPredicate + unfold.new_equalities.foldRight( + ProofTree( + VSTProofStep.Unfold( + predicate, + unfold.VSTPredicate.params.length, + context.variable_map.getOrElse(unfold.cardinality, ProofCVar(unfold.cardinality, CoqCardType(predicate.name)))), + List(unfold.existentials.foldRight(next)({ case ((variable, ty), next) => ProofTree(VSTProofStep.Exists(context.variable_map.getOrElse(variable, ProofCVar(variable, ty))), List(next)) }))) + : ProofTree[VSTProofStep]) ({ + case ((evar_name, evar_value), (next: ProofTree[VSTProofStep])) => + if (context.coq_context.contains(evar_name)) { + next + } else { + ProofTree(VSTProofStep.InstantiateEvar(evar_name, context.card_map.getOrElse(evar_name, evar_value)), List(next)) } + }) } ) + + ProofTree(VSTProofStep.ForwardEntailer, List( + add_evars(context.queued_unfolds)( + instantiate_existentials(context.post)( + context.post match { // If no existentials, only entailer will be at the end of the unfoldings + case Nil => + context.queued_unfolds match { + case Nil => ProofTree(VSTProofStep.Qed, List()) + case ::(_, _) => unfold_predicates(ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List())))) + } + case ::(_, _) => + context.queued_unfolds match { + case Nil => ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List()))) + case ::(_, _) => ProofTree(VSTProofStep.Entailer, List(unfold_predicates(ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List())))))) + } + } + ) ) - ) + )) } - def handle_pure_synthesis_rule(rule: ProofRule.PureSynthesis, context: Context): ProofSteps = rule match { + def handle_pure_synthesis_rule(rule: ProofRule.PureSynthesis, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.PureSynthesis(is_final, subst, next) => - val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - case ((name,expr), context) => record_variable_assignment(name,expr)(context) + val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ + case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_heap_unify(rule: ProofRule.HeapUnify, context: Context): ProofSteps = rule match { + def handle_heap_unify(rule: ProofRule.HeapUnify, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.HeapUnify(_, next) => -// val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ -// case ((name,expr), context) => record_variable_assignment(name,expr)(context) -// }) + // val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ + // case ((name,expr), context) => record_variable_assignment(name,expr)(context) + // }) translate_proof_rules(next)(context) } - def handle_heap_unify_pointer(rule: ProofRule.HeapUnifyPointer, context: Context): ProofSteps = rule match { + def handle_heap_unify_pointer(rule: ProofRule.HeapUnifyPointer, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.HeapUnifyPointer(subst, next) => - val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - case ((name,expr), context) => record_variable_assignment(name,expr)(context) + val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ + case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_substl_rule(rule: ProofRule.SubstL, context: Context): ProofSteps = rule match { + def handle_substl_rule(rule: ProofRule.SubstL, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.SubstL(map, next) => map.toList.foldRight(translate_proof_rules(next)(context))({ case ((Var(name), expr), next) => - AssertPropSubst( + ProofTree(VSTProofStep.AssertPropSubst( name, - ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr), - next) + ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr)), + List(next)) }) } - def handle_substr_rule(rule: ProofRule.SubstR, context: Context): ProofSteps = rule match { + def handle_substr_rule(rule: ProofRule.SubstR, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.SubstR(map, next) => - def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofSteps = + def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofTree[VSTProofStep] = map match { case Nil => translate_proof_rules(next)(context) case ::((Var(old_name), Var(new_name)), rest) => val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) - ProofSteps.Rename(old_name, new_name, - apply_subst(new_context)(rest) - ) + ProofTree(VSTProofStep.Rename(old_name, new_name, + + ), List(apply_subst(new_context)(rest))) case ::((Var(name), expr), rest) => val new_context = record_variable_assignment(name, expr)(context) apply_subst(new_context)(rest) @@ -516,16 +542,17 @@ object ProofTranslation { apply_subst(context)(map.toList) } - def handle_abduce_call(rule: ProofRule.AbduceCall, context: Context): ProofSteps = rule match { + def handle_abduce_call(rule: ProofRule.AbduceCall, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _, next) => var typing_context = retrieve_typing_context(context) f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { - typing_context = typing_context + (name -> CoqPtrType) - }}) + typing_context = typing_context + (name -> CoqPtrType) + } + }) val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) - val existentials = spec.existensial_params.toList.map({case (name,ty) => (freshSub(Var(name)).name, ty)}) + val existentials = spec.existensial_params.toList.map({ case (name, ty) => (freshSub(Var(name)).name, ty) }) var new_context = push_function((fun, call_args, existentials))(context) translate_proof_rules(next)(new_context) } @@ -533,41 +560,42 @@ object ProofTranslation { def handle_nilnotnval_rule(rule: ProofRule.NilNotLval, context: Context) = rule match { case ProofRule.NilNotLval(vars, next) => vars.foldRight(translate_proof_rules(next)(context))({ - case (_@Var(name), rest) => ProofSteps.ValidPointer( - name, rest - ) + case (_@Var(name), rest) => + ProofTree(VSTProofStep.ValidPointer( + name + ), List(rest)) }) } - def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofSteps = rule match { + def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => - ProofSteps.ForwardIf(List( + ProofTree(VSTProofStep.ForwardIf, List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) )) } - def handle_call_rule(rule: ProofRule.Call, context: Context): ProofSteps = rule match { + def handle_call_rule(rule: ProofRule.Call, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.Call(_, call, next) => val ((fun, args, existentials), new_context_1) = pop_function(context) - ProofSteps.ForwardCall(args, + ProofTree(VSTProofStep.ForwardCall(args), existentials match { - case Nil => translate_proof_rules(next)(new_context_1) + case Nil => List(translate_proof_rules(next)(new_context_1)) case _ => val new_context = add_new_coq_variables(existentials.toMap)(new_context_1) - ProofSteps.IntrosTuple(existentials, translate_proof_rules(next)(new_context)) + List(ProofTree(VSTProofStep.IntrosTuple(existentials), List(translate_proof_rules(next)(new_context)))) }) } - def handle_write_rule(rule: ProofRule.Write, context: Context): ProofSteps = rule match { - case ProofRule.Write(stmt, next) => ProofSteps.Forward(translate_proof_rules(next)(context)) + def handle_write_rule(rule: ProofRule.Write, context: Context): ProofTree[VSTProofStep] = rule match { + case ProofRule.Write(stmt, next) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) } - def handle_free_rule(rule: ProofRule.Free, context: Context): ProofSteps = rule match { - case ProofRule.Free(Free(Var(name)), size, next) => ProofSteps.Free(name, size, translate_proof_rules(next)(context)) + def handle_free_rule(rule: ProofRule.Free, context: Context): ProofTree[VSTProofStep] = rule match { + case ProofRule.Free(Free(Var(name)), size, next) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) } - def handle_close_rule(rule: ProofRule.Close, context: Context): ProofSteps = rule match { + def handle_close_rule(rule: ProofRule.Close, context: Context): ProofTree[VSTProofStep] = rule match { case ProofRule.Close(app, o_selector, asn, fresh_exist, next) => // Use application of of constructor to infer mapping of variables @@ -616,14 +644,14 @@ object ProofTranslation { case _ => List() } -// val expr_equalities = clause.pure.map(_.subst(substitution)).flatMap(extract_equalities) + // val expr_equalities = clause.pure.map(_.subst(substitution)).flatMap(extract_equalities) val new_equalities = card_equality.toMap val args = cardinality.constructor_args.map( name => (fresh_exist(Var(name)).name, CoqCardType(predicate.name)) ) - val unfolding = Unfold(predicate, app.card.asInstanceOf[Var].name, args, clause_existentials, new_equalities) + val unfolding = Unfold(predicate, app.card.asInstanceOf[Var].name, args, clause_existentials, new_equalities) val new_context = push_unfolding(context)(unfolding, new_equalities) val new_context_1 = record_variable_mapping(fresh_exist)(new_context) // record_variable_mapping(fresh_exist)(new_context) val new_context_2 = add_new_variables(clause_existentials.toMap)(new_context_1) // record_variable_mapping(fresh_exist)(new_context) @@ -635,17 +663,17 @@ object ProofTranslation { case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => val new_context_1 = map.foldRight( - add_new_variables(map.map({case (Var(original), Var(name)) => (name, CoqPtrType)}))(context) - )({case ((Var(old_name), new_expr), context) => record_variable_assignment(old_name,new_expr)(context)}) + add_new_variables(map.map({ case (Var(original), Var(name)) => (name, CoqPtrType) }))(context) + )({ case ((Var(old_name), new_expr), context) => record_variable_assignment(old_name, new_expr)(context) }) val new_context = add_new_coq_variables(Map(to_var -> CoqPtrType))(new_context_1) - ProofSteps.Malloc(sz, - ProofSteps.Intros( - List((to_var, CoqPtrType)), - translate_proof_rules(next)(new_context) - )) + ProofTree(VSTProofStep.Malloc(sz), + List(ProofTree(VSTProofStep.Intros( + List((to_var, CoqPtrType))), + List(translate_proof_rules(next)(new_context)) + ))) } - def translate_proof_rules(rule: ProofRule)(context: Context): ProofSteps = { + def translate_proof_rules(rule: ProofRule)(context: Context): ProofTree[VSTProofStep] = { rule match { // Branching rules case rule@ProofRule.Open(SApp(_, _, _, Var(_)), _, _, _) => handle_open_rule(rule, context) @@ -663,7 +691,7 @@ object ProofTranslation { case rule@ProofRule.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _, _) => handle_abduce_call(rule, context) case rule@ProofRule.Pick(_, _) => handle_pick_rule(rule, context) case rule@ProofRule.PureSynthesis(_, _, _) => handle_pure_synthesis_rule(rule, context) - case rule@ProofRule.PickCard(_,_) => handle_pick_card_rule(rule, context) + case rule@ProofRule.PickCard(_, _) => handle_pick_card_rule(rule, context) case rule@ProofRule.PickArg(_, _) => handle_pick_arg_rule(rule, context) case rule@ProofRule.Call(_, _, _) => handle_call_rule(rule, context) case rule@ProofRule.Close(_, _, _, _, _) => handle_close_rule(rule, context) @@ -693,7 +721,7 @@ object ProofTranslation { val simplified = ProofRule.of_certtree(root) println(s"Suslik Proof:\n ${simplified.pp}") - val vst_proof: ProofSteps = translate_proof_rules(simplified)(initial_context) + val vst_proof: ProofTree[VSTProofStep] = translate_proof_rules(simplified)(initial_context) Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) } @@ -707,7 +735,7 @@ object ProofTranslation { case ProofRule.WeakenPre(unused, next) => contains_free(next) case ProofRule.EmpRule => false case ProofRule.PureSynthesis(is_final, assignments, next) => contains_free(next) - case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_free(prf) } + case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_free(prf) } case ProofRule.SubstL(map, next) => contains_free(next) case ProofRule.SubstR(map, next) => contains_free(next) case ProofRule.Read(map, operation, next) => contains_free(next) @@ -738,7 +766,7 @@ object ProofTranslation { case ProofRule.SubstR(map, next) => contains_malloc(next) case ProofRule.Read(map, operation, next) => contains_malloc(next) case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => contains_malloc(next) - case ProofRule.HeapUnify(_,next) => contains_malloc(next) + case ProofRule.HeapUnify(_, next) => contains_malloc(next) case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) case ProofRule.Call(_, call, next) => contains_malloc(next) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index dac1b55a7..32e7d67d0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -27,6 +27,7 @@ object Translation { val procedure = CTranslation.translate_function(proc, root.goal.gamma) val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) + val post_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.post) println(procedure.pp) println(spec.pp) val predicates: List[VSTPredicate] = env.predicates.map({ case (_, predicate) => @@ -35,7 +36,7 @@ object Translation { predicates.foreach(v => println(v.pp)) - val proof = ProofTranslation.translate_proof(proc.f.name, predicates, spec, root, pre_cond) + val proof = ProofTranslation.translate_proof(proc.f.name, predicates, spec, root, pre_cond, post_cond) println(proof.pp) From 11cc743548bb3cb9a2a4bf18a02cc931b11d373c Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 29 Jan 2021 11:21:12 +0800 Subject: [PATCH 083/211] Unified all certification proof trees under a single tree structure ProofTree --- ...{ProofRule.scala => SuslikProofStep.scala} | 344 +++++------------- .../targets/htt/translation/IR.scala | 56 +-- .../targets/htt/translation/Translation.scala | 4 +- .../vst/translation/ProofTranslation.scala | 230 +++++------- 4 files changed, 225 insertions(+), 409 deletions(-) rename src/main/scala/org/tygus/suslik/certification/{ProofRule.scala => SuslikProofStep.scala} (58%) diff --git a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala similarity index 58% rename from src/main/scala/org/tygus/suslik/certification/ProofRule.scala rename to src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index 5adb7a166..030ad88cd 100644 --- a/src/main/scala/org/tygus/suslik/certification/ProofRule.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -16,11 +16,23 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ -sealed trait ProofRule extends PrettyPrinting { - def next: List[ProofRule] +sealed trait SuslikProofStep extends PrettyPrinting { } -object ProofRule { +object SuslikProofStep { + + implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { + override def pp(tree: ProofTree[SuslikProofStep]): String = + tree.rule match { + case rule@AbduceBranch(cond) => + rule.pp ++ "\n" ++ rule.branch_strings(tree.children(0), tree.children(1)) + case rule@Open(_, _, _, _) => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) + case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + } + } + + + var indent : Int = 0 def ind : String = " " * indent @@ -37,76 +49,69 @@ object ProofRule { /** corresponds to asserting all the variables in vars are not null */ - case class NilNotLval(vars: List[Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class NilNotLval(vars: List[Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});" } /** solves a pure entailment with SMT */ - case class CheckPost(prePhi: PFormula, postPhi: PFormula, next_rule:ProofRule) extends ProofRule { - override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class CheckPost(prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { + override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});" } /** picks an arbitrary instantiation of the proof rules */ - case class Pick(subst: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Pick(${subst.mkString(", ")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Pick(subst: Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}Pick(${subst.mkString(", ")});" } /** abduces a condition for the proof */ - case class AbduceBranch(cond: Expr, ifTrue:ProofRule, ifFalse:ProofRule) extends ProofRule { - override def pp: String = s"${ind}AbduceBranch(${cond});\n${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" - override def next: List[ProofRule] = List(ifTrue, ifFalse) + case class AbduceBranch(cond: Expr) extends SuslikProofStep { + def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = + s"${ind}IfTrue:\\n${with_scope(_ => ifTrue.pp)}\\n${ind}IfFalse:\\n${with_scope(_ => ifFalse.pp)}" + + override def pp: String = s"${ind}AbduceBranch(${cond});" } /** write a value */ - case class Write(stmt: Store, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Write(stmt: Store) extends SuslikProofStep { + override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});" } /** weaken the precondition by removing unused formulae */ - case class WeakenPre(unused: PFormula, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}WeakenPre(${unused.pp});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class WeakenPre(unused: PFormula) extends SuslikProofStep { + override def pp: String = s"${ind}WeakenPre(${unused.pp});" } /** empty rule */ - case object EmpRule extends ProofRule { + case object EmpRule extends SuslikProofStep { override def pp: String = s"${ind}EmpRule;" - override def next: List[ProofRule] = List() } /** pure synthesis rules */ - case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});" } /** open constructor cases */ - case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[(Expr, ProofRule)]) extends ProofRule { - override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});\n${with_scope(_ => cases.map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" - override def next: List[ProofRule] = cases.map({ case (_, rule) => rule }) + case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, cases: List[Expr]) extends SuslikProofStep { + def branch_strings[T <: PrettyPrinting] (exprs: List[T]) = + s"${with_scope(_ => exprs.zip(cases).map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});" } /** subst L */ - case class SubstL(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstL(${map.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class SubstL(map: Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}SubstL(${map.mkString(",")});" } /** subst R */ - case class SubstR(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}SubstR(${map.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class SubstR(map: Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}SubstR(${map.mkString(",")});" } /** read rule */ - case class Read(map: Map[Var,Var], operation: Load, next_rule:ProofRule) extends ProofRule { - override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Read(map: Map[Var,Var], operation: Load) extends SuslikProofStep { + override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});" } // /** abduce a call */ @@ -118,219 +123,72 @@ case class AbduceCall( freshSub: SubstVar, freshToActual: Subst, f: FunSpec, - gamma: Gamma, - next_rule: ProofRule - ) extends ProofRule { - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + gamma: Gamma + ) extends SuslikProofStep { + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" } /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(subst: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class HeapUnify(subst: Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});" } /** unification of pointers */ - case class HeapUnifyPointer(map: Map[Var,Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class HeapUnifyPointer(map: Map[Var,Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});" } /** unfolds frame */ - case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { + override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});" } /** call operation */ - case class Call(subst: Map[Var, Expr], call: Statements.Call, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Call(subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { + override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } /** free operation */ - case class Free(stmt: Statements.Free, size: Int, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Free(stmt: Statements.Free, size: Int) extends SuslikProofStep { + override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});" } /** malloc rule */ - case class Malloc(map: SubstVar, stmt: Statements.Malloc, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Malloc(map: SubstVar, stmt: Statements.Malloc) extends SuslikProofStep { + override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" } /** close rule */ - case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends SuslikProofStep { + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" } /** star partial */ - case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula) extends SuslikProofStep { + override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } - case class PickCard(map: Map[Var,Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickCard(${map.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class PickCard(map: Map[Var,Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}PickCard(${map.mkString(",")});" } - case class PickArg(map: Map[Var, Expr], next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}PickArg(${map.mkString(",")});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) - } - - case class Init(goal: Goal, next_rule: ProofRule) extends ProofRule { - override def pp: String = s"${ind}Init(${goal.pp});\n${next_rule.pp}" - override def next: List[ProofRule] = List(next_rule) + case class PickArg(map: Map[Var, Expr]) extends SuslikProofStep { + override def pp: String = s"${ind}PickArg(${map.mkString(",")});" } - case object Inconsistency extends ProofRule { - override def pp: String = "Inconsistency" - override def next: List[ProofRule] = List() + case class Init(goal: Goal) extends SuslikProofStep { + override def pp: String = s"${ind}Init(${goal.pp});" } - /** - * Abstract class for traversing and manipulating proof tree structures. - * - * override map_next, map_if_true, map_if_false, map_cases to handle mapping next - */ - abstract class ProofRuleMapper { - - def map (rule: NilNotLval) = - NilNotLval(rule.vars, map_next(rule.next_rule)) - - def map (rule: CheckPost) = - CheckPost(rule.prePhi, rule.postPhi, map_next(rule.next_rule)) - - def map(rule: Pick) = - Pick(rule.subst, map_next(rule.next_rule)) - - def map(rule: AbduceBranch) = - AbduceBranch(rule.cond, map_if_true(rule.ifTrue), map_if_false(rule.ifFalse)) - - def map(rule: Write) = - Write(rule.stmt, map_next(rule.next_rule)) - - def map(rule: WeakenPre) = - WeakenPre(rule.unused, map_next(rule.next_rule)) - - def map_emp = EmpRule - - def map (rule: PureSynthesis) = - PureSynthesis(rule.is_final, rule.assignments, map_next(rule.next_rule)) - - def map (rule: Open) = - Open(rule.pred, rule.fresh_vars, rule.sbst, map_cases(rule.cases)) - - def map(rule: SubstL) = - SubstL(rule.map, map_next(rule.next_rule)) - - def map(rule: SubstR) = - SubstR(rule.map, map_next(rule.next_rule)) - - def map(rule: Read) = - Read(rule.map, rule.operation, map_next(rule.next_rule)) - - def map(rule: AbduceCall) = - AbduceCall(rule.new_vars, rule.f_pre, rule.callePost, rule.call, rule.freshSub, rule.freshToActual, rule.f, rule.gamma, map_next(rule.next_rule)) - - def map(rule: HeapUnify) = - HeapUnify(rule.subst, map_next(rule.next_rule)) - - def map(rule: HeapUnifyPointer) = - HeapUnifyPointer(rule.map, map_next(rule.next_rule)) - - def map(rule: FrameUnfold) = - FrameUnfold(rule.h_pre, rule.h_post, map_next(rule.next_rule)) - - def map(rule: Call) = - Call(rule.subst, rule.call, map_next(rule.next_rule)) - - def map(rule: Free) = - Free(rule.stmt, rule.size, map_next(rule.next_rule)) - - def map(rule: Malloc) = - Malloc(rule.map, rule.stmt, map_next(rule.next_rule)) - - def map(rule: Close) = - Close(rule.app, rule.selector, rule.asn, rule.fresh_exist, map_next(rule.next_rule)) - - def map(rule: StarPartial) = - StarPartial(rule.new_pre_phi, rule.new_post_phi, map_next(rule.next_rule)) - - def map(rule: PickCard) = - PickCard(rule.map, map_next(rule.next_rule)) - - def map(rule: PickArg) = - PickArg(rule.map, map_next(rule.next_rule)) - - def map(rule: Init) = - Init(rule.goal, map_next(rule.next_rule)) - - def map_inconsistency = Inconsistency - - def map(rule: ProofRule) : ProofRule = rule match { - case rule@NilNotLval(_, _) => map(rule) - case rule@CheckPost(_, _, _) => map(rule) - case rule@Pick(_, _) => map(rule) - case rule@AbduceBranch(_, _, _) => map(rule) - case rule@Write(_, _) => map(rule) - case rule@WeakenPre(_, _) => map(rule) - case rule@EmpRule => map_emp - case rule@PureSynthesis(_, _, _) => map(rule) - case rule@Open(_, _, _, _) => map(rule) - case rule@SubstL(_, _) => map(rule) - case rule@SubstR(_, _) => map(rule) - case rule@Read(_, _, _) => map(rule) - case rule@AbduceCall(_, _, _, _, _, _, _, _, _) => map(rule) - case rule@HeapUnify(_, _) => map(rule) - case rule@HeapUnifyPointer(_, _) => map(rule) - case rule@FrameUnfold(_, _, _) => map(rule) - case rule@Call(_, _, _) => map(rule) - case rule@Free(_, _, _) => map(rule) - case rule@Malloc(_, _, _) => map(rule) - case rule@Close(_, _, _, _, _) => map(rule) - case rule@StarPartial(_, _, _) => map(rule) - case rule@PickCard(_, _) => map(rule) - case rule@PickArg(_, _) => map(rule) - case rule@Init(_, _) => map(rule) - case Inconsistency => map_inconsistency - } - - /*** - * DO NOT CALL THIS FROM CLIENT CODE!!!! - */ - def map_next(rule: ProofRule): ProofRule - def map_if_true(rule: ProofRule) : ProofRule - def map_if_false(rule: ProofRule) : ProofRule - - def map_cases(cases: List[(Expr, ProofRule)]) : List[(Expr, ProofRule)] - + case object Inconsistency extends SuslikProofStep { + override def pp: String = "Inconsistency;" } - def copy_proof_rule_with_next(rule: ProofRule, next: List[ProofRule]) = { - class ProofRuleCopy extends ProofRuleMapper { - override def map_next(rule: ProofRule): ProofRule = {assert(next.length == 1, "Expecting arity of 1"); next.head } - override def map_if_true(rule: ProofRule): ProofRule = {assert(next.length == 2, "Expecting arity of 2"); next(0) } - override def map_if_false(rule: ProofRule): ProofRule = {assert(next.length == 2, "Expecting arity of 2"); next(1) } - override def map_cases(cases: List[(Expr, ProofRule)]): List[(Expr, ProofRule)] = { - assert(next.length == cases.length, s"Expecting arity of ${cases.length}") - cases.zip(next).map({case ((expr, _), next) => (expr, next)}) - } - } - - val mapper = new ProofRuleCopy() - mapper.map(rule) - } /** converts a Suslik CertTree node into the unified ProofRule structure */ - def of_certtree(node: CertTree.Node): ProofRule = { + def of_certtree(node: CertTree.Node): ProofTree[SuslikProofStep] = { def fail_with_bad_proof_structure(): Nothing = throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = @@ -351,14 +209,14 @@ case class AbduceCall( val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList node.children match { - case ::(head, Nil) => ProofRule.NilNotLval(pre_pointers, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.NilNotLval(pre_pointers), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case FailRules.CheckPost => node.kont match { case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => ProofRule.CheckPost(prePhi, postPhi, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.CheckPost(prePhi, postPhi), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -366,7 +224,7 @@ case class AbduceCall( case UnificationRules.Pick => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.Pick(map, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.Pick(map), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -374,7 +232,7 @@ case class AbduceCall( case FailRules.AbduceBranch => node.kont match { case GuardedProducer(cond, _) => node.children match { - case ::(if_true, ::(if_false, Nil)) => ProofRule.AbduceBranch(cond, of_certtree(if_true), of_certtree(if_false)) + case ::(if_true, ::(if_false, Nil)) => ProofTree(SuslikProofStep.AbduceBranch(cond), List(of_certtree(if_true), of_certtree(if_false))) case ls => fail_with_bad_children(ls, 2) } case _ => fail_with_bad_proof_structure() @@ -382,7 +240,7 @@ case class AbduceCall( case OperationalRules.WriteRule => node.kont match { case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.Write(stmt, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.Write(stmt), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -390,7 +248,7 @@ case class AbduceCall( case LogicalRules.Inconsistency => node.kont match { case ConstProducer(Error) => node.children match { - case Nil => ProofRule.Inconsistency + case Nil => ProofTree(SuslikProofStep.Inconsistency, List()) case ls => fail_with_bad_children(ls, 0) } case _ => fail_with_bad_proof_structure() @@ -399,7 +257,8 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) node.children match { - case ::(head, Nil) => ProofRule.WeakenPre(unused, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.WeakenPre(unused), List(of_certtree(head))) + case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -407,7 +266,7 @@ case class AbduceCall( case LogicalRules.EmpRule => node.kont match { case ConstProducer(Skip) => node.children match { - case Nil => ProofRule.EmpRule + case Nil => ProofTree(SuslikProofStep.EmpRule, List()) case ls => fail_with_bad_children(ls, 0) } case _ => fail_with_bad_proof_structure() @@ -416,20 +275,20 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - ProofRule.PureSynthesis(is_final = true, assignments, of_certtree(head)) + ProofTree(SuslikProofStep.PureSynthesis(is_final = true, assignments), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnfoldingRules.Open => node.kont match { case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => - ProofRule.Open(pred, fresh_vars, sbst, selectors.zip(node.children).map({ case (expr, node) => (expr, of_certtree(node)) }).toList) + ProofTree(SuslikProofStep.Open(pred, fresh_vars, sbst, selectors.toList), node.children.map(of_certtree).toList) case _ => fail_with_bad_proof_structure() } case LogicalRules.SubstLeft => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.SubstL(map, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.SubstL(map), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -437,7 +296,7 @@ case class AbduceCall( case UnificationRules.SubstRight => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.SubstR(map, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.SubstR(map), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -445,7 +304,7 @@ case class AbduceCall( case OperationalRules.ReadRule => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.Read(map, stmt, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.Read(map, stmt), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -459,7 +318,7 @@ case class AbduceCall( head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) val f_pre = head.goal.post var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get - ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma, of_certtree(head)) + ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -467,7 +326,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPure => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.HeapUnify(subst), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -475,7 +334,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyUnfolding => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.HeapUnify(subst), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -483,7 +342,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyBlock => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnify(subst, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.HeapUnify(subst), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -491,7 +350,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPointer => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => ProofRule.HeapUnifyPointer(subst, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.HeapUnifyPointer(subst), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -508,7 +367,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(of_certtree(head))) } case ls => fail_with_bad_children(ls, 1) } @@ -526,7 +385,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(of_certtree(head))) } case ls => fail_with_bad_children(ls, 1) } @@ -544,7 +403,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(of_certtree(head))) } case ls => fail_with_bad_children(ls, 1) } @@ -562,7 +421,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - ProofRule.FrameUnfold(h_pre, h_post, of_certtree(head)) + ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(of_certtree(head))) } case ls => fail_with_bad_children(ls, 1) } @@ -571,7 +430,7 @@ case class AbduceCall( case UnfoldingRules.CallRule => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => ProofRule.Call(subst, call, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.Call(subst, call), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -583,7 +442,7 @@ case class AbduceCall( case None => 1 } node.children match { - case ::(head, Nil) => ProofRule.Free(stmt, size, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.Free(stmt, size), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -592,8 +451,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => node.children match { case ::(head, Nil) => - ProofRule. - Malloc(map, stmt, of_certtree(head)) + ProofTree(Malloc(map, stmt), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -602,7 +460,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - ProofRule.Close(app, selector, asn, fresh_exist, of_certtree(head)) + ProofTree(SuslikProofStep.Close(app, selector, asn, fresh_exist), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } } @@ -613,7 +471,7 @@ case class AbduceCall( node.children match { case ::(head, Nil) => - ProofRule.StarPartial(new_pre_phi, new_post_phi, of_certtree(head)) + ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -622,14 +480,14 @@ case class AbduceCall( case UnificationRules.PickCard => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => ProofRule.PickCard(map, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.PickCard(map), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } } case UnificationRules.PickArg => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => ProofRule.PickArg(map, of_certtree(head)) + case ::(head, Nil) => ProofTree(SuslikProofStep.PickArg(map), List(of_certtree(head))) case ls => fail_with_bad_children(ls, 1) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 5361b37d8..77ef8f3f0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.ProofRule +import org.tygus.suslik.certification.{ProofTree, SuslikProofStep} import org.tygus.suslik.certification.targets.htt.language.CGamma import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ @@ -178,19 +178,19 @@ object IR { CFunSpec(f.name, rType, params, ghosts, pre, post) } - def fromRule(rule: ProofRule, ctx: IR.Context) : IR.Node = rule match { - case ProofRule.Init(goal, next) => + def fromRule(rule: ProofTree[SuslikProofStep], ctx: IR.Context) : IR.Node = rule match { + case ProofTree(SuslikProofStep.Init(goal), List(next)) => val cgoal = translateGoal(goal) val ctx1 = ctx.copy(topLevelGoal = Some(cgoal)) IR.Init(ctx1, Seq(fromRule(next, ctx1))) - case ProofRule.Open(sapp, fresh_vars, sbst, cases) => + case ProofTree(SuslikProofStep.Open(sapp, fresh_vars, sbst, cases), children) => val csapp = translateSApp(sapp) val freshCVars = fresh_vars.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} val csbst = translateSbst(sbst) val pred = ctx.predicateEnv(sapp.pred).subst(freshCVars).subst(csbst) - val (selectors, next) = cases.map{ case (s, r) => (translateExpr(s), fromRule(r, ctx)) }.unzip + val (selectors, next) = cases.zip(children).map{ case (s, r) => (translateExpr(s), fromRule(r, ctx)) }.unzip IR.Open(csapp, pred.clauses, selectors, next, ctx) - case ProofRule.Close(sapp, selector, asn, sbst, next) => + case ProofTree(SuslikProofStep.Close(sapp, selector, asn, sbst), List(next)) => val csapp = translateSApp(sapp) val cselector = translateExpr(selector) val casn = translateAsn(asn) @@ -200,60 +200,60 @@ object IR { val ex = cclause.existentials.map(_.subst(csbst)) val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) fromRule(next, ctx.copy(unfoldings = ctx.unfoldings + (csapp -> actualClause))) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + case ProofTree(SuslikProofStep.AbduceBranch(cond), List(ifTrue, ifFalse)) => IR.AbduceBranch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) - case ProofRule.PureSynthesis(is_final, sbst, next) => + case ProofTree(SuslikProofStep.PureSynthesis(is_final, sbst), List(next)) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) IR.PureSynthesis(is_final, Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.SubstL(sbst, next) => + case ProofTree(SuslikProofStep.SubstL(sbst), List(next)) => val csbst = translateSbst(sbst) fromRule(next, ctx.copy(subst = ctx.subst ++ csbst)) - case ProofRule.SubstR(sbst, next) => + case ProofTree(SuslikProofStep.SubstR(sbst), List(next)) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) fromRule(next, ctx1) - case ProofRule.Pick(sbst, next) => + case ProofTree(SuslikProofStep.Pick(sbst), List(next)) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) fromRule(next, ctx1) - case ProofRule.Read(ghosts, Load(to, tpe, from, offset), next) => + case ProofTree(SuslikProofStep.Read(ghosts, Load(to, tpe, from, offset)), List(next)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.Write(Store(to, offset, e), next) => + case ProofTree(SuslikProofStep.Write(Store(to, offset, e)), List(next)) => IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(next, ctx)), ctx) - case ProofRule.Free(Statements.Free(v), size, next) => + case ProofTree(SuslikProofStep.Free(Statements.Free(v), size), List(next)) => IR.Free(CFree(CVar(v.name), size), Seq(fromRule(next, ctx)), ctx) - case ProofRule.Malloc(ghosts, Statements.Malloc(to, tpe, sz), next) => + case ProofTree(SuslikProofStep.Malloc(ghosts, Statements.Malloc(to, tpe, sz)), List(next)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(next, ctx1)), ctx1) - case ProofRule.Call(_, Statements.Call(fun, args, _), next) => + case ProofTree(SuslikProofStep.Call(_, Statements.Call(fun, args, _)), List(next)) => val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) val ctx2 = ctx1.copy(nestedContext = None) IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(next, ctx2)), ctx1) - case ProofRule.PickArg(sbst, next) => + case ProofTree(SuslikProofStep.PickArg(sbst), List(next)) => val csbst = translateSbst(sbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst))) fromRule(next, ctx1) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma, next) => + case ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma), List(next)) => val cfunspec = translateFunSpec(f, gamma) val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) fromRule(next, ctx1) - case ProofRule.HeapUnifyPointer(sbst, next) => + case ProofTree(SuslikProofStep.HeapUnifyPointer(sbst), List(next)) => val ctx1 = ctx.copy(subst = ctx.subst ++ translateSbst(sbst)) fromRule(next, ctx1) - case ProofRule.EmpRule => IR.EmpRule(ctx) - case ProofRule.CheckPost(prePhi, postPhi, next) => + case ProofTree(SuslikProofStep.EmpRule, List()) => IR.EmpRule(ctx) + case ProofTree(SuslikProofStep.CheckPost(prePhi, postPhi), List(next)) => IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(next, ctx)), ctx) // unused rules: - case ProofRule.HeapUnify(_, next) => fromRule(next, ctx) - case ProofRule.NilNotLval(_, next) => fromRule(next, ctx) - case ProofRule.WeakenPre(_, next) => fromRule(next, ctx) - case ProofRule.StarPartial(_, _, next) => fromRule(next, ctx) - case ProofRule.PickCard(_, next) => fromRule(next, ctx) - case ProofRule.FrameUnfold(h_pre, h_post, next) => fromRule(next, ctx) - case ProofRule.Inconsistency => IR.Inconsistency(ctx) + case ProofTree(SuslikProofStep.HeapUnify(_), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.NilNotLval(_), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.WeakenPre(_), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.StarPartial(_, _), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.PickCard(_), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next)) => fromRule(next, ctx) + case ProofTree(SuslikProofStep.Inconsistency, List()) => IR.Inconsistency(ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index f5ca00357..e717ce36b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.HTTCertificate -import org.tygus.suslik.certification.{CertTree, ProofRule} +import org.tygus.suslik.certification.{CertTree, ProofTree, SuslikProofStep} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.language.Types._ @@ -32,7 +32,7 @@ object Translation { }) val goal = translateGoal(node.goal) val ctx = IR.emptyContext.copy(predicateEnv = cpreds) - val ir = IR.fromRule(ProofRule.Init(node.goal, ProofRule.of_certtree(node)), ctx).propagateContext + val ir = IR.fromRule(ProofTree(SuslikProofStep.Init(node.goal), List(SuslikProofStep.of_certtree(node))), ctx).propagateContext val proof = ProofTranslation.irToProofSteps(ir) val hints = ProofTranslation.irToHints(ir) val progBody = ProgramTranslation.translate(ir) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 33a36c892..4b967e147 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -9,7 +9,7 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, ProofTypes, VSTProofStep} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with -import org.tygus.suslik.certification.{CertTree, ProofRule, ProofTree} +import org.tygus.suslik.certification.{CertTree, SuslikProofStep, ProofTree} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc} import org.tygus.suslik.language.{Expressions, Ident, Statements} @@ -244,8 +244,8 @@ object ProofTranslation { * - first assert that the pointer being read from is non-null (VST idiosynracy) * - emit a forward tactic to move over the operation */ - def handle_read_rule(rule: ProofRule.Read, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Read(subst, option, next) => + def handle_read_rule(rule: SuslikProofStep.Read, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Read(subst, option) => subst.toList match { case ::((Var(old_var), Var(new_var)), _) => def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { @@ -261,54 +261,54 @@ object ProofTranslation { is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) } - def is_variable_used_in_proof(variable: Ident)(rule: ProofRule): Boolean = { + def is_variable_used_in_proof(variable: Ident)(rule: ProofTree[SuslikProofStep]): Boolean = { def map_varaible(map: Map[Var, Expr]): Ident = map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) rule match { - case ProofRule.NilNotLval(vars, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.CheckPost(_, _, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickCard(_, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.PickArg(_, next) => + case ProofTree(SuslikProofStep.NilNotLval(vars), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.CheckPost(_, _), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.PickCard(_), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.PickArg(_), List(next)) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.Pick(subst, next) => + case ProofTree(SuslikProofStep.Pick(subst), List(next)) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + case ProofTree(SuslikProofStep.AbduceBranch(cond), List(ifTrue, ifFalse)) => is_variable_used_in_exp(variable)(cond) || is_variable_used_in_proof(variable)(ifTrue) || is_variable_used_in_proof(variable)(ifFalse) - case ProofRule.Write(Statements.Store(Var(tov), offset, e), next) => + case ProofTree(SuslikProofStep.Write(Statements.Store(Var(tov), offset, e)), List(next)) => (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) - case ProofRule.WeakenPre(unused, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => + case ProofTree(SuslikProofStep.WeakenPre(unused), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.EmpRule, List()) => false + case ProofTree(SuslikProofStep.PureSynthesis(is_final, assignments), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofRule.Open(pred, _, heaplet, cases) => - cases.exists({ case (expr, rule) => + case ProofTree(SuslikProofStep.Open(pred, _, heaplet,cases), children) => + cases.zip(children).exists({ case (expr, rule) => is_variable_used_in_exp(variable)(expr) || is_variable_used_in_proof(variable)(rule) }) - case ProofRule.SubstL(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.SubstR(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => + case ProofTree(SuslikProofStep.SubstL(map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.SubstR(map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnify(_, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.HeapUnifyPointer(map, next) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => is_variable_used_in_proof(variable)(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) => + case ProofTree(SuslikProofStep.HeapUnify(_), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.HeapUnifyPointer(map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next)) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.Close(app, selector, asn, fresh_exist), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => + case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofRule.Read(map, Load(Var(toe), _, Var(frome), offset), next) => + case ProofTree(SuslikProofStep.Read(map, Load(Var(toe), _, Var(frome), offset)), List(next)) => (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) - case ProofRule.Call(_, Call(_, args, _), next) => + case ProofTree(SuslikProofStep.Call(_, Call(_, args, _)), List(next)) => args.exists(is_variable_used_in_exp(variable)) || is_variable_used_in_proof(variable)(next) - case ProofRule.Free(Free(Var(v)), _, next) => + case ProofTree(SuslikProofStep.Free(Free(Var(v)), _), List(next)) => (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofRule.Malloc(map, Malloc(Var(toe), tpe, sz), next) => + case ProofTree(SuslikProofStep.Malloc(map, Malloc(Var(toe), tpe, sz)), List(next)) => (toe != variable) && is_variable_used_in_proof(variable)(next) } } @@ -333,10 +333,10 @@ object ProofTranslation { * Does this by mapping each constructor of the opened predicate to a branch of the rule, * and then for each branch introducing the variables that it uses. */ - def handle_open_rule(rule: ProofRule.Open, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => + def handle_open_rule(rule: SuslikProofStep.Open, children: List[ProofTree[SuslikProofStep]], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => val pred = pred_map(predicate_name) - val context_branches = pred.clauses.zip(cases).map({ + val context_branches = pred.clauses.zip(cases.zip(children)).map({ case ((constructor, clause), (expr, rule)) => // each clause of the type introduces existentials val new_variables = pred.constructor_existentials(constructor).map({ @@ -375,16 +375,16 @@ object ProofTranslation { ) } - def handle_pick_rule(rule: ProofRule.Pick, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Pick(subst, next) => + def handle_pick_rule(rule: SuslikProofStep.Pick, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Pick(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_pick_card_rule(rule: ProofRule.PickCard, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.PickCard(subst, next) => + def handle_pick_card_rule(rule: SuslikProofStep.PickCard, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.PickCard(subst) => /** Given an expression representing a pick of a cardinality variable * returns the corresponding cardinality constructor @@ -423,8 +423,8 @@ object ProofTranslation { translate_proof_rules(next)(new_context) } - def handle_pick_arg_rule(rule: ProofRule.PickArg, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.PickArg(subst, next) => + def handle_pick_arg_rule(rule: SuslikProofStep.PickArg, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.PickArg(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) @@ -489,32 +489,32 @@ object ProofTranslation { )) } - def handle_pure_synthesis_rule(rule: ProofRule.PureSynthesis, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.PureSynthesis(is_final, subst, next) => + def handle_pure_synthesis_rule(rule: SuslikProofStep.PureSynthesis, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.PureSynthesis(is_final, subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_heap_unify(rule: ProofRule.HeapUnify, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.HeapUnify(_, next) => + def handle_heap_unify(rule: SuslikProofStep.HeapUnify, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.HeapUnify(_) => // val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ // case ((name,expr), context) => record_variable_assignment(name,expr)(context) // }) translate_proof_rules(next)(context) } - def handle_heap_unify_pointer(rule: ProofRule.HeapUnifyPointer, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.HeapUnifyPointer(subst, next) => + def handle_heap_unify_pointer(rule: SuslikProofStep.HeapUnifyPointer, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.HeapUnifyPointer(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) translate_proof_rules(next)(new_context) } - def handle_substl_rule(rule: ProofRule.SubstL, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.SubstL(map, next) => + def handle_substl_rule(rule: SuslikProofStep.SubstL, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.SubstL(map) => map.toList.foldRight(translate_proof_rules(next)(context))({ case ((Var(name), expr), next) => ProofTree(VSTProofStep.AssertPropSubst( @@ -524,8 +524,8 @@ object ProofTranslation { }) } - def handle_substr_rule(rule: ProofRule.SubstR, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.SubstR(map, next) => + def handle_substr_rule(rule: SuslikProofStep.SubstR, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.SubstR(map) => def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofTree[VSTProofStep] = map match { case Nil => translate_proof_rules(next)(context) @@ -542,8 +542,8 @@ object ProofTranslation { apply_subst(context)(map.toList) } - def handle_abduce_call(rule: ProofRule.AbduceCall, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _, next) => + def handle_abduce_call(rule: SuslikProofStep.AbduceCall, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _) => var typing_context = retrieve_typing_context(context) f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { @@ -557,8 +557,8 @@ object ProofTranslation { translate_proof_rules(next)(new_context) } - def handle_nilnotnval_rule(rule: ProofRule.NilNotLval, context: Context) = rule match { - case ProofRule.NilNotLval(vars, next) => + def handle_nilnotnval_rule(rule: SuslikProofStep.NilNotLval, next: ProofTree[SuslikProofStep], context: Context) = rule match { + case SuslikProofStep.NilNotLval(vars) => vars.foldRight(translate_proof_rules(next)(context))({ case (_@Var(name), rest) => ProofTree(VSTProofStep.ValidPointer( @@ -567,16 +567,16 @@ object ProofTranslation { }) } - def handle_abduce_branch_rule(rule: ProofRule.AbduceBranch, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => + def handle_abduce_branch_rule(rule: SuslikProofStep.AbduceBranch, ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.AbduceBranch(cond) => ProofTree(VSTProofStep.ForwardIf, List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) )) } - def handle_call_rule(rule: ProofRule.Call, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Call(_, call, next) => + def handle_call_rule(rule: SuslikProofStep.Call, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Call(_, call) => val ((fun, args, existentials), new_context_1) = pop_function(context) ProofTree(VSTProofStep.ForwardCall(args), existentials match { @@ -587,16 +587,16 @@ object ProofTranslation { }) } - def handle_write_rule(rule: ProofRule.Write, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Write(stmt, next) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) + def handle_write_rule(rule: SuslikProofStep.Write, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Write(stmt) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) } - def handle_free_rule(rule: ProofRule.Free, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Free(Free(Var(name)), size, next) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) + def handle_free_rule(rule: SuslikProofStep.Free, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Free(Free(Var(name)), size) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) } - def handle_close_rule(rule: ProofRule.Close, context: Context): ProofTree[VSTProofStep] = rule match { - case ProofRule.Close(app, o_selector, asn, fresh_exist, next) => + def handle_close_rule(rule: SuslikProofStep.Close, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Close(app, o_selector, asn, fresh_exist) => // Use application of of constructor to infer mapping of variables val predicate = pred_map(app.pred) @@ -659,8 +659,8 @@ object ProofTranslation { translate_proof_rules(next)(new_context_2) } - def handle_malloc_rule(rule: ProofRule.Malloc, context: Context) = rule match { - case ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => + def handle_malloc_rule(rule: SuslikProofStep.Malloc, next: ProofTree[SuslikProofStep], context: Context) = rule match { + case SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)) => val new_context_1 = map.foldRight( add_new_variables(map.map({ case (Var(original), Var(name)) => (name, CoqPtrType) }))(context) @@ -673,52 +673,52 @@ object ProofTranslation { ))) } - def translate_proof_rules(rule: ProofRule)(context: Context): ProofTree[VSTProofStep] = { + def translate_proof_rules(rule: ProofTree[SuslikProofStep])(context: Context): ProofTree[VSTProofStep] = { rule match { // Branching rules - case rule@ProofRule.Open(SApp(_, _, _, Var(_)), _, _, _) => handle_open_rule(rule, context) - case rule@ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => handle_abduce_branch_rule(rule, context) + case ProofTree(rule@SuslikProofStep.Open(SApp(_, _, _, Var(_)), _, _, _), children) => handle_open_rule(rule, children, context) + case ProofTree(rule@SuslikProofStep.AbduceBranch(cond), List(ifTrue, ifFalse) ) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) // Read and write Operations - case rule@ProofRule.Write(_, _) => handle_write_rule(rule, context) - case rule@ProofRule.Read(subst, option, next) => handle_read_rule(rule, context) + case ProofTree(rule@SuslikProofStep.Write(_), List(next)) => handle_write_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Read(subst, option), List(next)) => handle_read_rule(rule, next, context) // Memory management rules - case rule@ProofRule.Free(Free(Var(_)), _, _) => handle_free_rule(rule, context) - case rule@ProofRule.Malloc(map, Malloc(Var(to_var), _, sz), next) => handle_malloc_rule(rule, context) + case ProofTree(rule@SuslikProofStep.Free(Free(Var(_)), _), List(next)) => handle_free_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)), List(next)) => handle_malloc_rule(rule, next, context) // Abduce call & Existentials - case rule@ProofRule.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _, _) => handle_abduce_call(rule, context) - case rule@ProofRule.Pick(_, _) => handle_pick_rule(rule, context) - case rule@ProofRule.PureSynthesis(_, _, _) => handle_pure_synthesis_rule(rule, context) - case rule@ProofRule.PickCard(_, _) => handle_pick_card_rule(rule, context) - case rule@ProofRule.PickArg(_, _) => handle_pick_arg_rule(rule, context) - case rule@ProofRule.Call(_, _, _) => handle_call_rule(rule, context) - case rule@ProofRule.Close(_, _, _, _, _) => handle_close_rule(rule, context) - case rule@ProofRule.HeapUnify(_, next) => handle_heap_unify(rule, context) - case rule@ProofRule.HeapUnifyPointer(_, _) => handle_heap_unify_pointer(rule, context) + case ProofTree(rule@SuslikProofStep.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _), List(next)) => handle_abduce_call(rule, next, context) + case ProofTree(rule@SuslikProofStep.Pick(_), List(next)) => handle_pick_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PureSynthesis(_, _), List(next)) => handle_pure_synthesis_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PickCard(_), List(next)) => handle_pick_card_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PickArg(_), List(next)) => handle_pick_arg_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Call(_, _), List(next)) => handle_call_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Close(_, _, _, _), List(next)) => handle_close_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.HeapUnify(_), List(next)) => handle_heap_unify(rule, next, context) + case ProofTree(rule@SuslikProofStep.HeapUnifyPointer(_), List(next)) => handle_heap_unify_pointer(rule, next, context) // Completion rule - case ProofRule.EmpRule => handle_emp_rule(context) + case ProofTree(SuslikProofStep.EmpRule, List()) => handle_emp_rule(context) // Context changing rules - case rule@ProofRule.NilNotLval(_, _) => handle_nilnotnval_rule(rule, context) - case rule@ProofRule.SubstL(_, _) => handle_substl_rule(rule, context) - case rule@ProofRule.SubstR(_, _) => handle_substr_rule(rule, context) + case ProofTree(rule@SuslikProofStep.NilNotLval(_), List(next)) => handle_nilnotnval_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.SubstL(_), List(next)) => handle_substl_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.SubstR(_), List(next)) => handle_substr_rule(rule, next, context) // Ignored rules - case ProofRule.WeakenPre(unused, next) => translate_proof_rules(next)(context) - case ProofRule.CheckPost(_, _, next) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.WeakenPre(unused), List(next)) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.CheckPost(_, _), List(next)) => translate_proof_rules(next)(context) - case ProofRule.FrameUnfold(h_pre, h_post, next) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next)) => translate_proof_rules(next)(context) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next)) => translate_proof_rules(next)(context) } } - val simplified = ProofRule.of_certtree(root) + val simplified = SuslikProofStep.of_certtree(root) println(s"Suslik Proof:\n ${simplified.pp}") val vst_proof: ProofTree[VSTProofStep] = translate_proof_rules(simplified)(initial_context) @@ -726,56 +726,14 @@ object ProofTranslation { Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) } - def contains_free(proof: ProofRule): Boolean = proof match { - case ProofRule.NilNotLval(vars, next) => contains_free(next) - case ProofRule.CheckPost(_, _, next) => contains_free(next) - case ProofRule.Pick(subst, next) => contains_free(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_free) - case ProofRule.Write(stmt, next) => contains_free(next) - case ProofRule.WeakenPre(unused, next) => contains_free(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => contains_free(next) - case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_free(prf) } - case ProofRule.SubstL(map, next) => contains_free(next) - case ProofRule.SubstR(map, next) => contains_free(next) - case ProofRule.Read(map, operation, next) => contains_free(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => contains_free(next) - case ProofRule.HeapUnify(_, next) => contains_free(next) - case ProofRule.HeapUnifyPointer(map, next) => contains_free(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_free(next) - case ProofRule.Call(_, call, next) => contains_free(next) - case ProofRule.Free(stmt, size, next) => true - case ProofRule.Malloc(map, stmt, next) => contains_free(next) - case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_free(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_free(next) - case ProofRule.PickCard(_, next) => contains_free(next) - case ProofRule.PickArg(map, next) => contains_free(next) + def contains_free(proof: ProofTree[SuslikProofStep]): Boolean = proof.rule match { + case SuslikProofStep.Free(stmt, size) => true + case _ => proof.children.exists(contains_free) } - def contains_malloc(proof: ProofRule): Boolean = proof match { - case ProofRule.NilNotLval(vars, next) => contains_malloc(next) - case ProofRule.CheckPost(_, _, next) => contains_malloc(next) - case ProofRule.Pick(subst, next) => contains_malloc(next) - case ProofRule.AbduceBranch(cond, ifTrue, ifFalse) => List(ifTrue, ifFalse).exists(contains_malloc) - case ProofRule.Write(stmt, next) => contains_malloc(next) - case ProofRule.WeakenPre(unused, next) => contains_malloc(next) - case ProofRule.EmpRule => false - case ProofRule.PureSynthesis(is_final, assignments, next) => contains_malloc(next) - case ProofRule.Open(pred, fresh_vars, _, cases) => cases.exists { case (_, prf) => contains_malloc(prf) } - case ProofRule.SubstL(map, next) => contains_malloc(next) - case ProofRule.SubstR(map, next) => contains_malloc(next) - case ProofRule.Read(map, operation, next) => contains_malloc(next) - case ProofRule.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _, next) => contains_malloc(next) - case ProofRule.HeapUnify(_, next) => contains_malloc(next) - case ProofRule.HeapUnifyPointer(map, next) => contains_malloc(next) - case ProofRule.FrameUnfold(h_pre, h_post, next) => contains_malloc(next) - case ProofRule.Call(_, call, next) => contains_malloc(next) - case ProofRule.Free(stmt, size, next) => contains_malloc(next) - case ProofRule.Malloc(map, stmt, next) => true - case ProofRule.Close(app, selector, asn, fresh_exist, next) => contains_malloc(next) - case ProofRule.StarPartial(new_pre_phi, new_post_phi, next) => contains_malloc(next) - case ProofRule.PickCard(_, next) => contains_malloc(next) - case ProofRule.PickArg(map, next) => contains_malloc(next) + def contains_malloc(proof: ProofTree[SuslikProofStep]): Boolean = proof.rule match { + case SuslikProofStep.Malloc(map, stmt) => true + case _ => proof.children.exists(contains_malloc) } } From b638404da5e0c83aabe29ef7fc5b88c71296a527 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 4 Feb 2021 13:44:57 +0800 Subject: [PATCH 084/211] Add evaluator --- .../certification/traversal/Context.scala | 24 +++++ .../certification/traversal/Evaluator.scala | 11 +++ .../traversal/StackEvaluator.scala | 97 +++++++++++++++++++ .../suslik/certification/traversal/Step.scala | 16 +++ .../traversal/TranslatableOps.scala | 11 +++ .../certification/traversal/Translator.scala | 7 ++ .../suslik/certification/traversal/Tree.scala | 14 +++ 7 files changed, 180 insertions(+) create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Context.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Step.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala new file mode 100644 index 000000000..a221a61cd --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala @@ -0,0 +1,24 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.certification.traversal.Context._ +import org.tygus.suslik.certification.traversal.Step.DestStep + +import scala.collection.immutable.Queue + +/** + * Composable and stackable evaluation context + * + */ +case class Context[S <: DestStep[S]](deferreds: Queue[AbstractDeferred[S]], tricks: List[AbstractTrick[S]]) { + def +(that: Context[S]): Context[S] = Context(deferreds ++ that.deferreds, tricks ++ that.tricks) +} + +object Context { + type AbstractTrick[S <: DestStep[S]] + type AbstractDeferred[S <: DestStep[S]] = List[AbstractTrick[S]] => S + + abstract class Action + case object Push extends Action + case object Pop extends Action + case object Replace extends Action +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala new file mode 100644 index 000000000..ea0fd90db --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -0,0 +1,11 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.certification.traversal.Step._ + +trait Evaluator[A <: SourceStep[A], B <: DestStep[B]] { + def run(node: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B]): Tree[B] +} + +object Evaluator { + case class EvaluatorException(private val message: String) extends Exception(message) +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala new file mode 100644 index 000000000..c5005dceb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -0,0 +1,97 @@ +package org.tygus.suslik.certification.traversal + +import TranslatableOps._ +import org.tygus.suslik.certification.traversal.Evaluator.EvaluatorException +import org.tygus.suslik.certification.traversal.Step._ +import org.tygus.suslik.logic.Specifications.GoalLabel + +import scala.annotation.tailrec + +class StackEvaluator[A <: SourceStep[A], B <: DestStep[B]] extends Evaluator[A,B] { + // A pending evaluation task; tracks which children have and have not been evaluated + case class Task(value: B, label: GoalLabel, remainingBranches: List[(Tree[A], ContextStack)], resultsSoFar: List[Tree[B]]) + + // A stack of pending translation tasks + type TaskStack = List[Task] + + // A stack of evaluation contexts + type ContextStack = List[Context[B]] + + def run(tree: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B]): Tree[B] = { + // Use a child result to fulfill the evaluation task for a parent + @tailrec + def backward(taskStack: TaskStack, childResult: Tree[B]): Tree[B] = + taskStack match { + case Nil => + // no more tasks; return the result + childResult + case currTask :: remainingStack => + currTask.remainingBranches match { + case Nil => + // received results for all children so topmost task is done; remove from stack and evaluate parent task + val results = childResult :: currTask.resultsSoFar + val translatedTree = Tree(currTask.value, currTask.label, results.reverse) + backward(remainingStack, translatedTree) + case (nextChild, nextContext) :: remainingBranches => + // some siblings remain unvisited; update topmost task with child result and explore next sibling + val updatedTask = currTask.copy(remainingBranches = remainingBranches, resultsSoFar = childResult :: currTask.resultsSoFar) + forward(nextChild, nextContext, updatedTask :: remainingStack) + } + } + + // Do step-wise translation of current tree under current context, and explore next child + @tailrec + def forward(tree: Tree[A], contextStack: ContextStack, taskStack: TaskStack): Tree[B] = { + // translate tree payload + val (translatedStep0, childContexts) = tree.step.translate[B] + + // finalize step based on action + val translatedStep = tree.step.contextAction match { + case Context.Pop => + // process all deferred steps in current context and append to the existing step + contextStack match { + case curr :: _ => curr.deferreds.map(k => k(curr.tricks)).foldLeft(translatedStep0){ case (l, r) => l >> r } + case Nil => throw EvaluatorException(s"step ${tree.step.pp} expects a context pop, but context stack is empty") + } + case _ => + // don't process deferreds yet + translatedStep0 + } + + // finalize child contexts based on action + val childContextStacks = tree.step.contextAction match { + case Context.Push => + // create fresh context + childContexts.map(_ :: contextStack) + case Context.Pop => + // update caller context + contextStack match { + case _ :: next :: rest => childContexts.map(next + _ :: rest) + case _ :: Nil => childContexts.map(List(_)) + case Nil => throw EvaluatorException(s"cannot pop from empty context stack for step ${tree.step.pp}") + } + case Context.Replace => + // update current context + contextStack match { + case curr :: rest => childContexts.map(curr + _ :: rest) + case Nil => throw EvaluatorException(s"cannot replace context stack item for step ${tree.step.pp}") + } + } + + (tree.children, childContextStacks) match { + case (Nil, Nil) => + // terminal; evaluate the converted node in the context of the current stack + val result: Tree[B] = Tree(translatedStep, tree.label, Nil) + backward(taskStack, result) + case (nextChild :: remainingChildren, nextContext :: remainingContexts) if remainingChildren.length == remainingContexts.length => + // non-terminal; create translation task for current tree and visit the first child + val task = Task(translatedStep, tree.label, remainingChildren.zip(remainingContexts), List.empty) + forward(nextChild, nextContext, task :: taskStack) + case _ => + throw EvaluatorException(s"expecting ${tree.children.length} children but ${childContextStacks.length} child contexts were generated") + } + } + + forward(tree, Nil, Nil) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala new file mode 100644 index 000000000..0dc6a4b6d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala @@ -0,0 +1,16 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.logic.Specifications.GoalLabel + +trait Step[A <: Step[A]] extends PrettyPrinting + +object Step { + trait SourceStep[A <: SourceStep[A]] extends Step[A] { + def contextAction: Context.Action = Context.Replace + def abduceBranch: Option[GoalLabel] = None + } + trait DestStep[A <: DestStep[A]] extends Step[A] { + def >>(that: A): A + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala new file mode 100644 index 000000000..3f03063b2 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala @@ -0,0 +1,11 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.certification.traversal.Step._ + +object TranslatableOps { + implicit class Translatable[A <: SourceStep[A]](step: A) { + def translate[B <: DestStep[B]](implicit translator: Translator[A, B]): (B, List[Context[B]]) = { + translator.translate(step) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala new file mode 100644 index 000000000..4b36150a1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.certification.traversal.Step._ + +trait Translator[A <: SourceStep[A], B <: DestStep[B]] { + def translate(value: A): (B, List[Context[B]]) +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala new file mode 100644 index 000000000..e703f981b --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala @@ -0,0 +1,14 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.logic.Specifications.GoalLabel + +case class Tree[S <: Step[S]](step: S, label: GoalLabel, children: List[Tree[S]]) + (implicit printer: TreePrinter[S]) + extends PrettyPrinting { + override def pp : String = printer.pp(this) +} + +trait TreePrinter[S <: Step[S]] { + def pp (tree: Tree[S]) : String +} From 24227a8e56067b54db85f7e2f782d208aa2d2bd2 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 7 Feb 2021 01:07:27 +0800 Subject: [PATCH 085/211] Finalize evaluator --- .../certification/SuslikProofStep.scala | 64 +++++----- .../certification/traversal/Context.scala | 24 ---- .../certification/traversal/Evaluator.scala | 18 ++- .../traversal/StackEvaluator.scala | 118 ++++++++++-------- .../suslik/certification/traversal/Step.scala | 12 +- .../traversal/TranslatableOps.scala | 7 +- .../certification/traversal/Translator.scala | 9 +- .../suslik/certification/traversal/Tree.scala | 4 +- 8 files changed, 135 insertions(+), 121 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Context.scala diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index b2f398e38..fdc338389 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -1,6 +1,8 @@ package org.tygus.suslik.certification import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException +import org.tygus.suslik.certification.traversal.Evaluator.EnvAction +import org.tygus.suslik.certification.traversal.Step.SourceStep import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} @@ -16,7 +18,7 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ -sealed abstract class SuslikProofStep extends PrettyPrinting { +sealed abstract class SuslikProofStep extends SourceStep { val label: Option[GoalLabel] } @@ -49,69 +51,70 @@ object SuslikProofStep { /** corresponds to asserting all the variables in vars are not null */ case class NilNotLval(label: Option[GoalLabel], vars: List[Expr]) extends SuslikProofStep { - override def pp: String = s"NilNotLval(${vars.map(_.pp).mkString(", ")});" + override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});" } /** solves a pure entailment with SMT */ case class CheckPost(label: Option[GoalLabel], prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { - override def pp: String = s"CheckPost(${prePhi.pp}; ${postPhi.pp});" + override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});" } /** picks an arbitrary instantiation of the proof rules */ case class Pick(label: Option[GoalLabel], subst: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"Pick(${subst.mkString(", ")});" + override def pp: String = s"${ind}Pick(${subst.mkString(", ")});" } /** abduces a condition for the proof */ case class AbduceBranch(label: Option[GoalLabel], cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = - s"${ind}IfTrue:\\n${with_scope(_ => ifTrue.pp)}\\n${ind}IfFalse:\\n${with_scope(_ => ifFalse.pp)}" + s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" - override def pp: String = s"AbduceBranch(${cond.pp}, ${bLabel.pp});" + override def pp: String = s"${ind}AbduceBranch(${cond.pp}, ${bLabel.pp});" } /** write a value */ case class Write(label: Option[GoalLabel], stmt: Store) extends SuslikProofStep { - override def pp: String = s"Write(${sanitize(stmt.pp)});" + override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});" } /** weaken the precondition by removing unused formulae */ case class WeakenPre(label: Option[GoalLabel], unused: PFormula) extends SuslikProofStep { - override def pp: String = s"WeakenPre(${unused.pp});" + override def pp: String = s"${ind}WeakenPre(${unused.pp});" } /** empty rule */ case class EmpRule(label: Option[GoalLabel]) extends SuslikProofStep { - override def pp: String = s"EmpRule;" + override def contextAction: EnvAction = EnvAction.PopLayer + override def pp: String = s"${ind}EmpRule;" } /** pure synthesis rules */ case class PureSynthesis(label: Option[GoalLabel], is_final: Boolean, assignments:Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"PureSynthesis(${is_final}, ${assignments.mkString(",")});" + override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});" } /** open constructor cases */ case class Open(label: Option[GoalLabel], pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (exprs: List[T]) = - s"${with_scope(_ => exprs.zip(selectors).map({case (expr,rest) => s"${ind}if ${sanitize(expr.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + s"${with_scope(_ => selectors.zip(exprs).map({case (sel,rest) => s"${ind}if ${sanitize(sel.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" - override def pp: String = s"Open(${pred.pp}, ${fresh_vars.mkString(", ")});" + override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});" } /** subst L */ case class SubstL(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"SubstL(${map.mkString(",")});" + override def pp: String = s"${ind}SubstL(${map.mkString(",")});" } /** subst R */ case class SubstR(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"SubstR(${map.mkString(",")});" + override def pp: String = s"${ind}SubstR(${map.mkString(",")});" } /** read rule */ case class Read(label: Option[GoalLabel], map: Map[Var,Var], operation: Load) extends SuslikProofStep { - override def pp: String = s"Read(${map.mkString(",")}, ${sanitize(operation.pp)});" + override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});" } // /** abduce a call */ @@ -126,61 +129,64 @@ case class AbduceCall( f: FunSpec, gamma: Gamma ) extends SuslikProofStep { - override def pp: String = s"AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" + override def contextAction: EnvAction = EnvAction.PushLayer + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" } /** unification of heap (ignores block/pure distinction) */ case class HeapUnify(label: Option[GoalLabel], subst: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"HeapUnify(${subst.mkString(",")});" + override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});" } /** unification of pointers */ case class HeapUnifyPointer(label: Option[GoalLabel], map: Map[Var,Expr]) extends SuslikProofStep { - override def pp: String = s"HeapUnifyPointer(${map.mkString(",")});" + override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});" } /** unfolds frame */ case class FrameUnfold(label: Option[GoalLabel], h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { - override def pp: String = s"FrameUnfold(${h_pre.pp}, ${h_post.pp});" + override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});" } /** call operation */ case class Call(label: Option[GoalLabel], subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { - override def pp: String = s"Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" + override def contextAction: EnvAction = EnvAction.PopLayer + override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } /** free operation */ case class Free(label: Option[GoalLabel], stmt: Statements.Free, size: Int) extends SuslikProofStep { - override def pp: String = s"Free(${sanitize(stmt.pp)});" + override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});" } /** malloc rule */ case class Malloc(label: Option[GoalLabel], map: SubstVar, stmt: Statements.Malloc) extends SuslikProofStep { - override def pp: String = s"Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" + override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" } /** close rule */ case class Close(label: Option[GoalLabel], app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends SuslikProofStep { - override def pp: String = s"Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" + override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" } /** star partial */ case class StarPartial(label: Option[GoalLabel], new_pre_phi: PFormula, new_post_phi: PFormula) extends SuslikProofStep { - override def pp: String = s"StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" + override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } case class PickCard(label: Option[GoalLabel], map: Map[Var,Expr]) extends SuslikProofStep { - override def pp: String = s"PickCard(${map.mkString(",")});" + override def pp: String = s"${ind}PickCard(${map.mkString(",")});" } case class PickArg(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"PickArg(${map.mkString(",")});" + override def pp: String = s"${ind}PickArg(${map.mkString(",")});" } case class Init(label: Option[GoalLabel], goal: Goal) extends SuslikProofStep { - override def pp: String = s"Init(${goal.pp});" + override def contextAction: EnvAction = EnvAction.PushLayer + override def pp: String = s"${ind}Init(${goal.pp});" } case class Inconsistency(label: Option[GoalLabel]) extends SuslikProofStep { @@ -189,9 +195,9 @@ case class AbduceCall( case class Branch(label: Option[GoalLabel], cond: Expr) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = - s"${ind}IfTrue:\\n${with_scope(_ => ifTrue.pp)}\\n${ind}IfFalse:\\n${with_scope(_ => ifFalse.pp)}" + s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" - override def pp: String = s"Branch(${cond.pp});" + override def pp: String = s"${ind}Branch(${cond.pp});" } /** converts a Suslik CertTree node into the unified ProofRule structure */ diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala deleted file mode 100644 index a221a61cd..000000000 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Context.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.tygus.suslik.certification.traversal - -import org.tygus.suslik.certification.traversal.Context._ -import org.tygus.suslik.certification.traversal.Step.DestStep - -import scala.collection.immutable.Queue - -/** - * Composable and stackable evaluation context - * - */ -case class Context[S <: DestStep[S]](deferreds: Queue[AbstractDeferred[S]], tricks: List[AbstractTrick[S]]) { - def +(that: Context[S]): Context[S] = Context(deferreds ++ that.deferreds, tricks ++ that.tricks) -} - -object Context { - type AbstractTrick[S <: DestStep[S]] - type AbstractDeferred[S <: DestStep[S]] = List[AbstractTrick[S]] => S - - abstract class Action - case object Push extends Action - case object Pop extends Action - case object Replace extends Action -} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index ea0fd90db..2c974faf9 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -1,11 +1,25 @@ package org.tygus.suslik.certification.traversal +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Step._ -trait Evaluator[A <: SourceStep[A], B <: DestStep[B]] { - def run(node: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B]): Tree[B] +import scala.collection.immutable.Queue + +trait Evaluator[A <: SourceStep, B <: DestStep] { + def run(node: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B], initialClientContext: ClientContext[B]): Tree[B] } object Evaluator { case class EvaluatorException(private val message: String) extends Exception(message) + + type ClientContext[S <: DestStep] + type Deferred[S <: DestStep] = ClientContext[S] => (S, ClientContext[S]) + type Deferreds[S <: DestStep] = Queue[Deferred[S]] + + abstract class EnvAction + object EnvAction { + case object PushLayer extends EnvAction + case object PopLayer extends EnvAction + case object CurrentLayer extends EnvAction + } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index c5005dceb..de73cfe34 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -1,23 +1,23 @@ package org.tygus.suslik.certification.traversal import TranslatableOps._ -import org.tygus.suslik.certification.traversal.Evaluator.EvaluatorException +import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ import org.tygus.suslik.logic.Specifications.GoalLabel import scala.annotation.tailrec -class StackEvaluator[A <: SourceStep[A], B <: DestStep[B]] extends Evaluator[A,B] { +class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { // A pending evaluation task; tracks which children have and have not been evaluated - case class Task(value: B, label: GoalLabel, remainingBranches: List[(Tree[A], ContextStack)], resultsSoFar: List[Tree[B]]) + case class Task(values: List[B], label: GoalLabel, remainingBranches: List[(Tree[A], DeferredsStack, ClientContext[B])], resultsSoFar: List[Tree[B]]) - // A stack of pending translation tasks + // A stack of pending evaluation tasks type TaskStack = List[Task] - // A stack of evaluation contexts - type ContextStack = List[Context[B]] + // A stack of queued deferreds + type DeferredsStack = List[Deferreds[B]] - def run(tree: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B]): Tree[B] = { + def run(tree: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B], initialClientContext: ClientContext[B]): Tree[B] = { // Use a child result to fulfill the evaluation task for a parent @tailrec def backward(taskStack: TaskStack, childResult: Tree[B]): Tree[B] = @@ -30,68 +30,82 @@ class StackEvaluator[A <: SourceStep[A], B <: DestStep[B]] extends Evaluator[A,B case Nil => // received results for all children so topmost task is done; remove from stack and evaluate parent task val results = childResult :: currTask.resultsSoFar - val translatedTree = Tree(currTask.value, currTask.label, results.reverse) + val translatedTree = foldStepsIntoTree(currTask.values, currTask.label, results.reverse) backward(remainingStack, translatedTree) - case (nextChild, nextContext) :: remainingBranches => + case (nextChild, nextDeferreds, nextTricks) :: remainingBranches => // some siblings remain unvisited; update topmost task with child result and explore next sibling val updatedTask = currTask.copy(remainingBranches = remainingBranches, resultsSoFar = childResult :: currTask.resultsSoFar) - forward(nextChild, nextContext, updatedTask :: remainingStack) + forward(nextChild, nextDeferreds, nextTricks, updatedTask :: remainingStack) } } - // Do step-wise translation of current tree under current context, and explore next child + // Do step-wise translation of current tree node and explore next child @tailrec - def forward(tree: Tree[A], contextStack: ContextStack, taskStack: TaskStack): Tree[B] = { - // translate tree payload - val (translatedStep0, childContexts) = tree.step.translate[B] - - // finalize step based on action - val translatedStep = tree.step.contextAction match { - case Context.Pop => - // process all deferred steps in current context and append to the existing step - contextStack match { - case curr :: _ => curr.deferreds.map(k => k(curr.tricks)).foldLeft(translatedStep0){ case (l, r) => l >> r } - case Nil => throw EvaluatorException(s"step ${tree.step.pp} expects a context pop, but context stack is empty") - } - case _ => - // don't process deferreds yet - translatedStep0 + def forward(tree: Tree[A], deferredsStack: DeferredsStack, clientContext: ClientContext[B], taskStack: TaskStack): Tree[B] = { + val res = tree.step.translate[B](clientContext) + if (tree.children.length != res.childrenMeta.length) { + throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childrenMeta.length} children") } + val (childDeferreds, childClientContexts0) = res.childrenMeta.unzip + val (steps, childDeferredsStacks, childClientContexts) = tree.step.contextAction match { + case EnvAction.PopLayer => + if (tree.children.length > 1) { + throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children, but pop action expects at most 1 child") + } + deferredsStack match { + case deferreds :: remainingDeferreds => + // translation should have produced results for 0 or 1 children + val childDeferreds0 = childDeferreds.headOption.toList + val childClientContext0 = childClientContexts0.headOption.getOrElse(clientContext) + + // process all deferreds in current stack layer + val (steps, childClientContext) = deferreds.foldLeft((res.steps.reverse, childClientContext0)) { + case ((steps, clientCtx), deferred) => + val (step, clientCtx1) = deferred(clientCtx) + (step :: steps, clientCtx1) + } - // finalize child contexts based on action - val childContextStacks = tree.step.contextAction match { - case Context.Push => - // create fresh context - childContexts.map(_ :: contextStack) - case Context.Pop => - // update caller context - contextStack match { - case _ :: next :: rest => childContexts.map(next + _ :: rest) - case _ :: Nil => childContexts.map(List(_)) - case Nil => throw EvaluatorException(s"cannot pop from empty context stack for step ${tree.step.pp}") + // pop current stack layer and enqueue on next stack layer + val childDeferredsStacks = remainingDeferreds match { + case nextLayer :: remainingLayers => childDeferreds0.map(newDeferreds => (nextLayer ++ newDeferreds) :: remainingLayers) + case Nil => childDeferreds0.map(List(_)) + } + + (steps.reverse, childDeferredsStacks, List(childClientContext)) + case Nil => throw EvaluatorException(s"step ${tree.step.pp} expects a pop, but deferreds stack is empty") } - case Context.Replace => - // update current context - contextStack match { - case curr :: rest => childContexts.map(curr + _ :: rest) - case Nil => throw EvaluatorException(s"cannot replace context stack item for step ${tree.step.pp}") + case EnvAction.PushLayer => + // create fresh deferreds + val childDeferredsStacks = childDeferreds.map(_ :: deferredsStack) + (res.steps, childDeferredsStacks, childClientContexts0) + case EnvAction.CurrentLayer => + deferredsStack match { + case deferreds :: remainingDeferreds => + // enqueue on current deferreds + val childDeferredsStack = childDeferreds.map(deferreds ++ _ :: remainingDeferreds) + (res.steps, childDeferredsStack, childClientContexts0) + case Nil => throw EvaluatorException(s"cannot replace deferreds stack item for step ${tree.step.pp}") } } - (tree.children, childContextStacks) match { - case (Nil, Nil) => + (tree.children, childDeferredsStacks, childClientContexts).zipped.toList match { + case Nil => // terminal; evaluate the converted node in the context of the current stack - val result: Tree[B] = Tree(translatedStep, tree.label, Nil) + val result = foldStepsIntoTree(steps, tree.label, Nil) backward(taskStack, result) - case (nextChild :: remainingChildren, nextContext :: remainingContexts) if remainingChildren.length == remainingContexts.length => - // non-terminal; create translation task for current tree and visit the first child - val task = Task(translatedStep, tree.label, remainingChildren.zip(remainingContexts), List.empty) - forward(nextChild, nextContext, task :: taskStack) - case _ => - throw EvaluatorException(s"expecting ${tree.children.length} children but ${childContextStacks.length} child contexts were generated") + case (nextChild, nextDeferredsStack, nextClientCtx) :: remainingBranches => + // non-terminal; create evaluation task for current tree and visit the first child + val task = Task(steps, tree.label, remainingBranches, List.empty) + forward(nextChild, nextDeferredsStack, nextClientCtx, task :: taskStack) } } + // Create a tree from a list of values + def foldStepsIntoTree(values: List[B], label: GoalLabel, children: List[Tree[B]]): Tree[B] = + values.reverse match { + case last :: rest => rest.foldLeft(Tree(last, label, children)){ case (child, v) => Tree(v, label, List(child)) } + case Nil => throw EvaluatorException("expected at least one translated value for this task") + } - forward(tree, Nil, Nil) + forward(tree, Nil, initialClientContext, Nil) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala index 0dc6a4b6d..20c46f0ad 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala @@ -1,16 +1,14 @@ package org.tygus.suslik.certification.traversal +import org.tygus.suslik.certification.traversal.Evaluator.EnvAction import org.tygus.suslik.language.PrettyPrinting import org.tygus.suslik.logic.Specifications.GoalLabel -trait Step[A <: Step[A]] extends PrettyPrinting +trait Step extends PrettyPrinting object Step { - trait SourceStep[A <: SourceStep[A]] extends Step[A] { - def contextAction: Context.Action = Context.Replace - def abduceBranch: Option[GoalLabel] = None - } - trait DestStep[A <: DestStep[A]] extends Step[A] { - def >>(that: A): A + trait SourceStep extends Step { + def contextAction: EnvAction = EnvAction.CurrentLayer } + trait DestStep extends Step } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala index 3f03063b2..f0eca95be 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala @@ -1,11 +1,12 @@ package org.tygus.suslik.certification.traversal +import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ object TranslatableOps { - implicit class Translatable[A <: SourceStep[A]](step: A) { - def translate[B <: DestStep[B]](implicit translator: Translator[A, B]): (B, List[Context[B]]) = { - translator.translate(step) + implicit class Translatable[A <: SourceStep](step: A) { + def translate[B <: DestStep](clientContext: ClientContext[B])(implicit translator: Translator[A, B]): Translator.Result[B] = { + translator.translate(step, clientContext) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala index 4b36150a1..74425b7fe 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala @@ -1,7 +1,12 @@ package org.tygus.suslik.certification.traversal +import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ -trait Translator[A <: SourceStep[A], B <: DestStep[B]] { - def translate(value: A): (B, List[Context[B]]) +trait Translator[A <: SourceStep, B <: DestStep] { + def translate(value: A, clientContext: ClientContext[B]): Translator.Result[B] } + +object Translator { + case class Result[S <: DestStep](steps: List[S], childrenMeta: List[(Deferreds[S], ClientContext[S])]) +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala index e703f981b..5f97d87a7 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala @@ -3,12 +3,12 @@ package org.tygus.suslik.certification.traversal import org.tygus.suslik.language.PrettyPrinting import org.tygus.suslik.logic.Specifications.GoalLabel -case class Tree[S <: Step[S]](step: S, label: GoalLabel, children: List[Tree[S]]) +case class Tree[S <: Step](step: S, label: GoalLabel, children: List[Tree[S]]) (implicit printer: TreePrinter[S]) extends PrettyPrinting { override def pp : String = printer.pp(this) } -trait TreePrinter[S <: Step[S]] { +trait TreePrinter[S <: Step] { def pp (tree: Tree[S]) : String } From bd401ba2c374d509fade8f11bcdf539b849bbb93 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 7 Feb 2021 11:59:48 +0800 Subject: [PATCH 086/211] Update branch abduction handling --- .../certification/SuslikProofStep.scala | 737 +++++++++--------- .../targets/htt/translation/IR.scala | 3 +- .../vst/translation/ProofTranslation.scala | 8 +- 3 files changed, 390 insertions(+), 358 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index fdc338389..d39e06aa7 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -6,13 +6,14 @@ import org.tygus.suslik.certification.traversal.Step.SourceStep import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} -import org.tygus.suslik.logic.Preprocessor.{findMatchingHeaplets, sameLhs} +import org.tygus.suslik.logic.Preprocessor.findMatchingHeaplets import org.tygus.suslik.logic.Specifications.{Assertion, Goal, GoalLabel, SuspendedCallGoal} import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules._ import org.tygus.suslik.synthesis._ +import scala.annotation.tailrec import scala.collection.immutable.Map @@ -26,10 +27,7 @@ object SuslikProofStep { implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { override def pp(tree: ProofTree[SuslikProofStep]): String = tree.rule match { - case rule:AbduceBranch => - rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) - case rule:Branch => - rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) + case rule:Branch => rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) case rule:Open => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") } @@ -64,8 +62,8 @@ object SuslikProofStep { override def pp: String = s"${ind}Pick(${subst.mkString(", ")});" } - /** abduces a condition for the proof */ - case class AbduceBranch(label: Option[GoalLabel], cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { + /** branches on a condition */ + case class Branch(label: Option[GoalLabel], cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" @@ -193,367 +191,402 @@ case class AbduceCall( override def pp: String = "Inconsistency" } - case class Branch(label: Option[GoalLabel], cond: Expr) extends SuslikProofStep { - def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = - s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" - - override def pp: String = s"${ind}Branch(${cond.pp});" - } - - /** converts a Suslik CertTree node into the unified ProofRule structure */ - def of_certtree(node: CertTree.Node): ProofTree[SuslikProofStep] = { + /** + * Use CertTree node info to generate a SuslikProofStep corresponding to the node's SynthesisRule + * @param node a Suslik CertTree node + * @return a SuslikProofStep + */ + def translate(node: CertTree.Node): SuslikProofStep = { def fail_with_bad_proof_structure(): Nothing = throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") - def visit(node: CertTree.Node): ProofTree[SuslikProofStep] = { - val label = Some(node.goal.label) - val rule = node.rule match { - case LogicalRules.NilNotLval => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => - // find all pointers that are not yet known to be non-null - def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { - // All pointers - val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet - allPointers.filter( - x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) - ) - } - - val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList - - node.children match { - case ::(head, Nil) => SuslikProofStep.NilNotLval(label, pre_pointers) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case FailRules.CheckPost => node.kont match { - case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => SuslikProofStep.CheckPost(label, prePhi, postPhi) - case ls => fail_with_bad_children(ls, 1) + val label = Some(node.goal.label) + node.rule match { + case LogicalRules.NilNotLval => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(_)) => + // find all pointers that are not yet known to be non-null + def find_pointers(p: PFormula, s: SFormula): Set[Expr] = { + // All pointers + val allPointers = (for (PointsTo(l, _, _) <- s.chunks) yield l).toSet + allPointers.filter( + x => !p.conjuncts.contains(x |/=| NilPtr) && !p.conjuncts.contains(NilPtr |/=| x) + ) } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.Pick => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.Pick(label, map) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case FailRules.AbduceBranch => node.kont match { - case GuardedProducer(cond, bGoal) => - node.children match { - case ::(if_true, ::(if_false, Nil)) => SuslikProofStep.AbduceBranch(label, cond, bGoal.label) - case ls => fail_with_bad_children(ls, 2) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.WriteRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.Write(label, stmt) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.Inconsistency => node.kont match { - case ConstProducer(Error) => - node.children match { - case Nil => SuslikProofStep.Inconsistency(label) - case ls => fail_with_bad_children(ls, 0) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.WeakenPre => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) - node.children match { - case ::(head, Nil) => SuslikProofStep.WeakenPre(label, unused) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.EmpRule => node.kont match { - case ConstProducer(Skip) => - node.children match { - case Nil => SuslikProofStep.EmpRule(label) - case ls => fail_with_bad_children(ls, 0) - } - case _ => fail_with_bad_proof_structure() - } - case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - SuslikProofStep.PureSynthesis(label, is_final = true, assignments) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Open => node.kont match { - case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => - SuslikProofStep.Open(label, pred, fresh_vars, sbst, selectors.toList) - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.SubstLeft => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.SubstL(label, map) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.SubstRight => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.SubstR(label, map) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.ReadRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.Read(label, map, stmt) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.AbduceCall => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(AbduceCallProducer(f), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - // find out which new variables were added to the context - val new_vars = - head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) - val f_pre = head.goal.post - var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get - SuslikProofStep.AbduceCall(label, new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.HeapUnifyPointer => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(label, subst) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameUnfoldingFinal => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameBlock => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case LogicalRules.FrameFlat => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - val pre = goal.pre - val post = goal.post - - def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) - - findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { - case None => ??? - case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) - } - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.Call(label, subst, call) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.FreeRule => node.kont match { - case ChainedProducer(ChainedProducer(PrependProducer(stmt@Statements.Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => - val size: Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { - case Some(value) => value - case None => 1 - } - node.children match { - case ::(head, Nil) => SuslikProofStep.Free(label, stmt, size) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case OperationalRules.AllocRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => - SuslikProofStep. - Malloc(label, map, stmt) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnfoldingRules.Close => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => - node.children match { - case ::(head, Nil) => - SuslikProofStep.Close(label, app, selector, asn, fresh_exist) - case ls => fail_with_bad_children(ls, 1) - } - } - case LogicalRules.StarPartial => node.kont match { - case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => - val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) - val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) - - node.children match { - case ::(head, Nil) => - SuslikProofStep.StarPartial(label, new_pre_phi, new_post_phi) - case ls => fail_with_bad_children(ls, 1) - } - case _ => fail_with_bad_proof_structure() - } - case UnificationRules.PickCard => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.PickCard(label, map) - case ls => fail_with_bad_children(ls, 1) - } - } - case UnificationRules.PickArg => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => - node.children match { - case ::(head, Nil) => SuslikProofStep.PickArg(label, map) - case ls => fail_with_bad_children(ls, 1) - } + val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList + + node.children match { + case ::(head, Nil) => SuslikProofStep.NilNotLval(label, pre_pointers) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case FailRules.CheckPost => node.kont match { + case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { + case ::(head, Nil) => SuslikProofStep.CheckPost(label, prePhi, postPhi) + case ls => fail_with_bad_children(ls, 1) } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.Pick => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.Pick(label, map) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case FailRules.AbduceBranch => node.kont match { + case GuardedProducer(cond, bGoal) => + node.children match { + case ::(if_true, ::(if_false, Nil)) => SuslikProofStep.Branch(label, cond, bGoal.label) + case ls => fail_with_bad_children(ls, 2) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.WriteRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.Write(label, stmt) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.Inconsistency => node.kont match { + case ConstProducer(Error) => + node.children match { + case Nil => SuslikProofStep.Inconsistency(label) + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.WeakenPre => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) + node.children match { + case ::(head, Nil) => SuslikProofStep.WeakenPre(label, unused) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.EmpRule => node.kont match { + case ConstProducer(Skip) => + node.children match { + case Nil => SuslikProofStep.EmpRule(label) + case ls => fail_with_bad_children(ls, 0) + } + case _ => fail_with_bad_proof_structure() + } + case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + SuslikProofStep.PureSynthesis(label, is_final = true, assignments) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Open => node.kont match { + case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => + SuslikProofStep.Open(label, pred, fresh_vars, sbst, selectors.toList) + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.SubstLeft => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.SubstL(label, map) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.SubstRight => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.SubstR(label, map) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.ReadRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.Read(label, map, stmt) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.AbduceCall => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(AbduceCallProducer(f), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + // find out which new variables were added to the context + val new_vars = + head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) + val f_pre = head.goal.post + var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get + SuslikProofStep.AbduceCall(label, new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPure => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyBlock => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnificationRules.HeapUnifyPointer => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(label, subst) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfolding => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfolding.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + SuslikProofStep.FrameUnfold(label, h_pre, h_post) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameUnfoldingFinal => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameUnfoldingFinal.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + SuslikProofStep.FrameUnfold(label, h_pre, h_post) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameBlock => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameBlock.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + SuslikProofStep.FrameUnfold(label, h_pre, h_post) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case LogicalRules.FrameFlat => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + val pre = goal.pre + val post = goal.post + + def isMatch(hPre: Heaplet, hPost: Heaplet): Boolean = hPre.eqModTags(hPost) && LogicalRules.FrameFlat.heapletFilter(hPost) + + findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { + case None => ??? + case Some((h_pre, h_post)) => + SuslikProofStep.FrameUnfold(label, h_pre, h_post) + } + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.CallRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.Call(label, subst, call) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.FreeRule => node.kont match { + case ChainedProducer(ChainedProducer(PrependProducer(stmt@Statements.Free(Var(name))), HandleGuard(_)), ExtractHelper(_)) => + val size: Int = node.goal.pre.sigma.blocks.find({ case Block(Var(ploc), sz) => ploc == name }).map({ case Block(_, sz) => sz }) match { + case Some(value) => value + case None => 1 + } + node.children match { + case ::(head, Nil) => SuslikProofStep.Free(label, stmt, size) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case OperationalRules.AllocRule => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => + SuslikProofStep. + Malloc(label, map, stmt) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() + } + case UnfoldingRules.Close => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + node.children match { + case ::(head, Nil) => + SuslikProofStep.Close(label, app, selector, asn, fresh_exist) + case ls => fail_with_bad_children(ls, 1) + } + } + case LogicalRules.StarPartial => node.kont match { + case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => + val new_pre_phi = extendPure(goal.pre.phi, goal.pre.sigma) + val new_post_phi = extendPure(goal.pre.phi && goal.post.phi, goal.post.sigma) + + node.children match { + case ::(head, Nil) => + SuslikProofStep.StarPartial(label, new_pre_phi, new_post_phi) + case ls => fail_with_bad_children(ls, 1) + } + case _ => fail_with_bad_proof_structure() } - ProofTree(rule, node.children.map(visit).toList) + case UnificationRules.PickCard => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.PickCard(label, map) + case ls => fail_with_bad_children(ls, 1) + } + } + case UnificationRules.PickArg => node.kont match { + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + node.children match { + case ::(head, Nil) => SuslikProofStep.PickArg(label, map) + case ls => fail_with_bad_children(ls, 1) + } + } } - - val rest = visit(node) - ProofTree(Init(None, node.goal), List(rest)) } /** - * - * Finalizes the branches abduced by applications of the AbduceBranch rule. - * - * Consider in the left diagram, an AbduceBranch node B with children C (true case) and D (false case), and - * intended branch destination A. This procedure inserts a new Branch node E with A as its true case child; the - * inserted node E is meant to denote a finalized branching point. - * - * D--- D--- D--- - * / => / / - * --A-----B-C--- --E-A-----B-C--- - * - * @param node the root node of the tree - * @return a copy of the tree with finalized branches inserted + * Convert a Suslik CertTree node into the unified ProofTree structure + * @param node a Suslik CertTree node + * @return a corresponding ProofTree */ - def finalize_branches(node: ProofTree[SuslikProofStep]): ProofTree[SuslikProofStep] = { - def collect_branch_abductions(node: ProofTree[SuslikProofStep], abductions: Set[ProofTree[SuslikProofStep]] = Set.empty): Set[ProofTree[SuslikProofStep]] = { - val abductions1 = node.rule match { - case ab:AbduceBranch => abductions + node - case _ => abductions - } - node.children.foldLeft(abductions1) { case (a, n) => collect_branch_abductions(n, a) } - } - def apply_branch_abductions(abductions: Set[ProofTree[SuslikProofStep]])(node: ProofTree[SuslikProofStep]): ProofTree[SuslikProofStep] = { - node.rule.label match { - case Some(label) => - abductions.find(_.rule.asInstanceOf[AbduceBranch].bLabel == label) match { - case Some(ab@ProofTree(AbduceBranch(_, cond, bLabel), _)) => - val List(ifTrue, ifFalse) = ab.children - val ifTrue1 = node.copy(children = node.children.map(apply_branch_abductions(abductions))) - val ifFalse1 = apply_branch_abductions(abductions)(ifFalse) - ProofTree(Branch(None, cond), List(ifTrue1, ifFalse1)) - case None => - node.copy(children = node.children.map(apply_branch_abductions(abductions))) - } - case None => - node.copy(children = node.children.map(apply_branch_abductions(abductions))) + def of_certtree(node: CertTree.Node): ProofTree[SuslikProofStep] = { + case class Item(step: SuslikProofStep, remaining: List[CertTree.Node], done: List[ProofTree[SuslikProofStep]]) + + /** + * Update parent with a child branch we've finished exploring + * @param stack the continuation stack + * @param result a translated child branch + * @return a fully translated proof tree + */ + @tailrec + def backward(stack: List[Item], result: ProofTree[SuslikProofStep]): ProofTree[SuslikProofStep] = stack match { + case Nil => result + case currItem :: stack => + currItem.remaining match { + case Nil => + // finished exploring all child branches of current node; go to parent + val done = result :: currItem.done + backward(stack, ProofTree(currItem.step, done.reverse)) + case nextChild :: remaining => + // current node still has unexplored child branches; explore the next one + val updatedCurr = currItem.copy(remaining = remaining, done = result :: currItem.done) + forward(nextChild, updatedCurr :: stack) } + } + + /** + * Do step-wise translation of current CertTree node, handle any branch abductions, and explore children + * @param tree the current CertTree node being explored + * @param stack the continuation stack + * @return a fully translated proof tree + */ + @tailrec + def forward(tree: CertTree.Node, stack: List[Item]): ProofTree[SuslikProofStep] = { + val step = translate(tree) + tree.children match { + case Nil => backward(stack, ProofTree(step, Nil)) + case next :: remaining => + val item = Item(step, remaining, Nil) + val nextStack = step match { + case ab: Branch => insertBranchPoint(item, ab.bLabel, stack, identity) + case _ => item :: stack + } + forward(next, nextStack) } - apply_branch_abductions(collect_branch_abductions(node))(node) } + + /** + * Look through tree traversal continuation stack to insert correct branching point for a branch abduction step. + * + * Consider in the left diagram, an AbduceBranch node B with children C (true case) and D (false case), and + * intended branch destination A. This procedure inserts a new Branch node E with A as its true case child; the + * inserted node E is meant to denote a finalized branching point. + * + * D--- D--- D--- + * / => / / + * --A-----B-C--- --E-A-----B-C--- + * + * @param item the branch abduction step + * @param label the target goal label + * @param stack the tree traversal continuation stack + * @param k this function's own continuation for traversing the stack (note: different from tree traversal continuation) + * @return the modified traversal continuation stack w/ finalized branch location + */ + @tailrec + def insertBranchPoint(item: Item, label: GoalLabel, stack: List[Item], k: List[Item] => List[Item]): List[Item] = + stack match { + case next :: rest => + next.step match { + // found target goal label ('A' in the diagram); insert branch point immediately before it + case step if step.label.contains(label) => k(next :: item :: rest) + // found a previous branch abduction with matching target goal label (happens if two branch abductions use the same branching point) + case abduceBranch: Branch if abduceBranch.bLabel == label => k(item :: next :: rest) + // didn't find target goal label; check parent + case _ => insertBranchPoint(item, label, rest, ret => k(next :: ret)) + } + case Nil => throw ProofRuleTranslationException(s"branch point ${label.pp} not found for branch abduction step ${item.step.pp}") + } + + val initStep = Init(None, node.goal) + forward(node, List(Item(initStep, Nil, Nil))) + } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 9e2ee13be..6048c1e5d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -225,7 +225,7 @@ object IR { val unfoldings = ctx.unfoldings + (csapp -> actualClause) val sappNames = ctx.sappNames ++ casn.sigma.apps.map(a => a -> a) fromRule(node.children.head, ctx.copy(unfoldings = unfoldings, sappNames = sappNames)) - case SuslikProofStep.Branch(_, cond) => + case SuslikProofStep.Branch(_, cond, _) => val Seq(ifTrue, ifFalse) = node.children IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) case SuslikProofStep.PureSynthesis(_, is_final, sbst) => @@ -293,6 +293,5 @@ object IR { case SuslikProofStep.StarPartial(_, _, _) => fromRule(node.children.head, ctx) case SuslikProofStep.PickCard(_, _) => fromRule(node.children.head, ctx) case SuslikProofStep.FrameUnfold(_, h_pre, h_post) => fromRule(node.children.head, ctx) - case SuslikProofStep.AbduceBranch(_, cond, bLabel) => fromRule(node.children.head, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 6f8ea5e72..3ad4f21ad 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -275,7 +275,7 @@ object ProofTranslation { case ProofTree(SuslikProofStep.Pick(_, subst), List(next)) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.AbduceBranch(_, cond, _), List(ifTrue, ifFalse)) => + case ProofTree(SuslikProofStep.Branch(_, cond, _), List(ifTrue, ifFalse)) => is_variable_used_in_exp(variable)(cond) || is_variable_used_in_proof(variable)(ifTrue) || is_variable_used_in_proof(variable)(ifFalse) @@ -567,8 +567,8 @@ object ProofTranslation { }) } - def handle_abduce_branch_rule(rule: SuslikProofStep.AbduceBranch, ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.AbduceBranch(_, cond, _) => + def handle_abduce_branch_rule(rule: SuslikProofStep.Branch, ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { + case SuslikProofStep.Branch(_, cond, _) => ProofTree(VSTProofStep.ForwardIf, List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) @@ -677,7 +677,7 @@ object ProofTranslation { rule match { // Branching rules case ProofTree(rule@SuslikProofStep.Open(_, SApp(_, _, _, Var(_)), _, _, _), children) => handle_open_rule(rule, children, context) - case ProofTree(rule@SuslikProofStep.AbduceBranch(_, cond, _), List(ifTrue, ifFalse) ) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) + case ProofTree(rule@SuslikProofStep.Branch(_, cond, _), List(ifTrue, ifFalse) ) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) // Read and write Operations case ProofTree(rule@SuslikProofStep.Write(_, _), List(next)) => handle_write_rule(rule, next, context) From 3fd060be0f56356592b059f4c10083cf110fbe51 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 7 Feb 2021 13:39:04 +0800 Subject: [PATCH 087/211] Move goal label constraint from step to tree --- .../suslik/certification/ProofTree.scala | 19 --- .../certification/SuslikProofStep.scala | 119 +++++++-------- .../targets/htt/translation/IR.scala | 51 +++---- .../targets/vst/logic/Proof.scala | 3 +- .../targets/vst/logic/VSTProofStep.scala | 11 +- .../vst/translation/ProofTranslation.scala | 139 +++++++++--------- .../certification/traversal/Evaluator.scala | 2 +- .../certification/traversal/ProofTree.scala | 16 ++ .../traversal/ProofTreePrinter.scala | 5 + .../traversal/StackEvaluator.scala | 16 +- .../suslik/certification/traversal/Tree.scala | 14 -- 11 files changed, 194 insertions(+), 201 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/ProofTree.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/ProofTreePrinter.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala diff --git a/src/main/scala/org/tygus/suslik/certification/ProofTree.scala b/src/main/scala/org/tygus/suslik/certification/ProofTree.scala deleted file mode 100644 index 1f9d04033..000000000 --- a/src/main/scala/org/tygus/suslik/certification/ProofTree.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.tygus.suslik.certification - -import org.tygus.suslik.language.PrettyPrinting - -/** - * Represents an abstract encoding of a proof tree - * - * @param rule individual rule - * @param children list of child nodes - */ -sealed case class ProofTree[T](rule: T, children: List[ProofTree[T]]) - (implicit printer: ProofTreePrinter[T]) - extends PrettyPrinting { - override def pp : String = printer.pp(this) -} - -trait ProofTreePrinter[T] { - def pp (tree: ProofTree[T]) : String -} diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index d39e06aa7..daf15531a 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException import org.tygus.suslik.certification.traversal.Evaluator.EnvAction +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.SourceStep import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} @@ -20,13 +21,12 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ sealed abstract class SuslikProofStep extends SourceStep { - val label: Option[GoalLabel] } object SuslikProofStep { implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { override def pp(tree: ProofTree[SuslikProofStep]): String = - tree.rule match { + tree.step match { case rule:Branch => rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) case rule:Open => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") @@ -48,22 +48,22 @@ object SuslikProofStep { } /** corresponds to asserting all the variables in vars are not null */ - case class NilNotLval(label: Option[GoalLabel], vars: List[Expr]) extends SuslikProofStep { + case class NilNotLval(vars: List[Expr]) extends SuslikProofStep { override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});" } /** solves a pure entailment with SMT */ - case class CheckPost(label: Option[GoalLabel], prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { + case class CheckPost(prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});" } /** picks an arbitrary instantiation of the proof rules */ - case class Pick(label: Option[GoalLabel], subst: Map[Var, Expr]) extends SuslikProofStep { + case class Pick(subst: Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}Pick(${subst.mkString(", ")});" } /** branches on a condition */ - case class Branch(label: Option[GoalLabel], cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { + case class Branch(cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" @@ -71,12 +71,12 @@ object SuslikProofStep { } /** write a value */ - case class Write(label: Option[GoalLabel], stmt: Store) extends SuslikProofStep { + case class Write(stmt: Store) extends SuslikProofStep { override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});" } /** weaken the precondition by removing unused formulae */ - case class WeakenPre(label: Option[GoalLabel], unused: PFormula) extends SuslikProofStep { + case class WeakenPre(unused: PFormula) extends SuslikProofStep { override def pp: String = s"${ind}WeakenPre(${unused.pp});" } @@ -87,12 +87,12 @@ object SuslikProofStep { } /** pure synthesis rules */ - case class PureSynthesis(label: Option[GoalLabel], is_final: Boolean, assignments:Map[Var, Expr]) extends SuslikProofStep { + case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});" } /** open constructor cases */ - case class Open(label: Option[GoalLabel], pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { + case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { def branch_strings[T <: PrettyPrinting] (exprs: List[T]) = s"${with_scope(_ => selectors.zip(exprs).map({case (sel,rest) => s"${ind}if ${sanitize(sel.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" @@ -100,24 +100,23 @@ object SuslikProofStep { } /** subst L */ - case class SubstL(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { + case class SubstL(map: Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}SubstL(${map.mkString(",")});" } /** subst R */ - case class SubstR(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { + case class SubstR(map: Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}SubstR(${map.mkString(",")});" } /** read rule */ - case class Read(label: Option[GoalLabel], map: Map[Var,Var], operation: Load) extends SuslikProofStep { + case class Read(map: Map[Var,Var], operation: Load) extends SuslikProofStep { override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});" } // /** abduce a call */ case class AbduceCall( - label: Option[GoalLabel], new_vars: Map[Var, SSLType], f_pre: Specifications.Assertion, callePost: Specifications.Assertion, @@ -133,56 +132,56 @@ case class AbduceCall( /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(label: Option[GoalLabel], subst: Map[Var, Expr]) extends SuslikProofStep { + case class HeapUnify(subst: Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});" } /** unification of pointers */ - case class HeapUnifyPointer(label: Option[GoalLabel], map: Map[Var,Expr]) extends SuslikProofStep { + case class HeapUnifyPointer(map: Map[Var,Expr]) extends SuslikProofStep { override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});" } /** unfolds frame */ - case class FrameUnfold(label: Option[GoalLabel], h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { + case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});" } /** call operation */ - case class Call(label: Option[GoalLabel], subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { + case class Call(subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { override def contextAction: EnvAction = EnvAction.PopLayer override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } /** free operation */ - case class Free(label: Option[GoalLabel], stmt: Statements.Free, size: Int) extends SuslikProofStep { + case class Free(stmt: Statements.Free, size: Int) extends SuslikProofStep { override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});" } /** malloc rule */ - case class Malloc(label: Option[GoalLabel], map: SubstVar, stmt: Statements.Malloc) extends SuslikProofStep { + case class Malloc(map: SubstVar, stmt: Statements.Malloc) extends SuslikProofStep { override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" } /** close rule */ - case class Close(label: Option[GoalLabel], app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends SuslikProofStep { + case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends SuslikProofStep { override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" } /** star partial */ - case class StarPartial(label: Option[GoalLabel], new_pre_phi: PFormula, new_post_phi: PFormula) extends SuslikProofStep { + case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula) extends SuslikProofStep { override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } - case class PickCard(label: Option[GoalLabel], map: Map[Var,Expr]) extends SuslikProofStep { + case class PickCard(map: Map[Var,Expr]) extends SuslikProofStep { override def pp: String = s"${ind}PickCard(${map.mkString(",")});" } - case class PickArg(label: Option[GoalLabel], map: Map[Var, Expr]) extends SuslikProofStep { + case class PickArg(map: Map[Var, Expr]) extends SuslikProofStep { override def pp: String = s"${ind}PickArg(${map.mkString(",")});" } - case class Init(label: Option[GoalLabel], goal: Goal) extends SuslikProofStep { + case class Init(goal: Goal) extends SuslikProofStep { override def contextAction: EnvAction = EnvAction.PushLayer override def pp: String = s"${ind}Init(${goal.pp});" } @@ -218,14 +217,14 @@ case class AbduceCall( val pre_pointers = find_pointers(node.goal.pre.phi, node.goal.pre.sigma).toList node.children match { - case ::(head, Nil) => SuslikProofStep.NilNotLval(label, pre_pointers) + case ::(head, Nil) => SuslikProofStep.NilNotLval(pre_pointers) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case FailRules.CheckPost => node.kont match { case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => SuslikProofStep.CheckPost(label, prePhi, postPhi) + case ::(head, Nil) => SuslikProofStep.CheckPost(prePhi, postPhi) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -233,7 +232,7 @@ case class AbduceCall( case UnificationRules.Pick => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Pick(label, map) + case ::(head, Nil) => SuslikProofStep.Pick(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -241,7 +240,7 @@ case class AbduceCall( case FailRules.AbduceBranch => node.kont match { case GuardedProducer(cond, bGoal) => node.children match { - case ::(if_true, ::(if_false, Nil)) => SuslikProofStep.Branch(label, cond, bGoal.label) + case ::(if_true, ::(if_false, Nil)) => SuslikProofStep.Branch(cond, bGoal.label) case ls => fail_with_bad_children(ls, 2) } case _ => fail_with_bad_proof_structure() @@ -249,7 +248,7 @@ case class AbduceCall( case OperationalRules.WriteRule => node.kont match { case ChainedProducer(ChainedProducer(PrependProducer(stmt@Store(_, _, _)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Write(label, stmt) + case ::(head, Nil) => SuslikProofStep.Write(stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -266,7 +265,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(IdProducer, HandleGuard(_)), ExtractHelper(goal)) => val unused = goal.pre.phi.indepedentOf(goal.pre.sigma.vars ++ goal.post.vars) node.children match { - case ::(head, Nil) => SuslikProofStep.WeakenPre(label, unused) + case ::(head, Nil) => SuslikProofStep.WeakenPre(unused) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -283,20 +282,20 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - SuslikProofStep.PureSynthesis(label, is_final = true, assignments) + SuslikProofStep.PureSynthesis(is_final = true, assignments) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnfoldingRules.Open => node.kont match { case ChainedProducer(ChainedProducer(BranchProducer(Some(pred), fresh_vars, sbst, selectors), HandleGuard(_)), ExtractHelper(_)) => - SuslikProofStep.Open(label, pred, fresh_vars, sbst, selectors.toList) + SuslikProofStep.Open(pred, fresh_vars, sbst, selectors.toList) case _ => fail_with_bad_proof_structure() } case LogicalRules.SubstLeft => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.SubstL(label, map) + case ::(head, Nil) => SuslikProofStep.SubstL(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -304,7 +303,7 @@ case class AbduceCall( case UnificationRules.SubstRight => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.SubstR(label, map) + case ::(head, Nil) => SuslikProofStep.SubstR(map) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -312,7 +311,7 @@ case class AbduceCall( case OperationalRules.ReadRule => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Read(label, map, stmt) + case ::(head, Nil) => SuslikProofStep.Read(map, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -326,7 +325,7 @@ case class AbduceCall( head.goal.gamma.filterKeys(key => !node.goal.gamma.contains(key)) val f_pre = head.goal.post var SuspendedCallGoal(caller_pre, caller_post, callePost, call, freshSub, freshToActual) = head.goal.callGoal.get - SuslikProofStep.AbduceCall(label, new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma) + SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, head.goal.gamma) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -334,7 +333,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPure => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -342,7 +341,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyUnfolding => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -350,7 +349,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyBlock => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(label, subst) + case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -358,7 +357,7 @@ case class AbduceCall( case UnificationRules.HeapUnifyPointer => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(label, subst) + case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -375,7 +374,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) + SuslikProofStep.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -393,7 +392,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) + SuslikProofStep.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -411,7 +410,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) + SuslikProofStep.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -429,7 +428,7 @@ case class AbduceCall( findMatchingHeaplets(_ => true, isMatch, pre.sigma, post.sigma) match { case None => ??? case Some((h_pre, h_post)) => - SuslikProofStep.FrameUnfold(label, h_pre, h_post) + SuslikProofStep.FrameUnfold(h_pre, h_post) } case ls => fail_with_bad_children(ls, 1) } @@ -438,7 +437,7 @@ case class AbduceCall( case UnfoldingRules.CallRule => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Call(label, subst, call) + case ::(head, Nil) => SuslikProofStep.Call(subst, call) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -450,7 +449,7 @@ case class AbduceCall( case None => 1 } node.children match { - case ::(head, Nil) => SuslikProofStep.Free(label, stmt, size) + case ::(head, Nil) => SuslikProofStep.Free(stmt, size) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -460,7 +459,7 @@ case class AbduceCall( node.children match { case ::(head, Nil) => SuslikProofStep. - Malloc(label, map, stmt) + Malloc(map, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -469,7 +468,7 @@ case class AbduceCall( case ChainedProducer(ChainedProducer(ChainedProducer(UnfoldProducer(app, selector, asn, fresh_exist), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => - SuslikProofStep.Close(label, app, selector, asn, fresh_exist) + SuslikProofStep.Close(app, selector, asn, fresh_exist) case ls => fail_with_bad_children(ls, 1) } } @@ -480,7 +479,7 @@ case class AbduceCall( node.children match { case ::(head, Nil) => - SuslikProofStep.StarPartial(label, new_pre_phi, new_post_phi) + SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -489,14 +488,14 @@ case class AbduceCall( case UnificationRules.PickCard => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.PickCard(label, map) + case ::(head, Nil) => SuslikProofStep.PickCard(map) case ls => fail_with_bad_children(ls, 1) } } case UnificationRules.PickArg => node.kont match { case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.PickArg(label, map) + case ::(head, Nil) => SuslikProofStep.PickArg(map) case ls => fail_with_bad_children(ls, 1) } } @@ -509,7 +508,7 @@ case class AbduceCall( * @return a corresponding ProofTree */ def of_certtree(node: CertTree.Node): ProofTree[SuslikProofStep] = { - case class Item(step: SuslikProofStep, remaining: List[CertTree.Node], done: List[ProofTree[SuslikProofStep]]) + case class Item(step: SuslikProofStep, label: Option[GoalLabel], remaining: List[CertTree.Node], done: List[ProofTree[SuslikProofStep]]) /** * Update parent with a child branch we've finished exploring @@ -525,7 +524,7 @@ case class AbduceCall( case Nil => // finished exploring all child branches of current node; go to parent val done = result :: currItem.done - backward(stack, ProofTree(currItem.step, done.reverse)) + backward(stack, ProofTree(currItem.step, done.reverse, currItem.label)) case nextChild :: remaining => // current node still has unexplored child branches; explore the next one val updatedCurr = currItem.copy(remaining = remaining, done = result :: currItem.done) @@ -542,10 +541,11 @@ case class AbduceCall( @tailrec def forward(tree: CertTree.Node, stack: List[Item]): ProofTree[SuslikProofStep] = { val step = translate(tree) + val label = Some(tree.goal.label) tree.children match { - case Nil => backward(stack, ProofTree(step, Nil)) + case Nil => backward(stack, ProofTree(step, Nil, label)) case next :: remaining => - val item = Item(step, remaining, Nil) + val item = Item(step, label, remaining, Nil) val nextStack = step match { case ab: Branch => insertBranchPoint(item, ab.bLabel, stack, identity) case _ => item :: stack @@ -577,7 +577,7 @@ case class AbduceCall( case next :: rest => next.step match { // found target goal label ('A' in the diagram); insert branch point immediately before it - case step if step.label.contains(label) => k(next :: item :: rest) + case _ if next.label.contains(label) => k(next :: item :: rest) // found a previous branch abduction with matching target goal label (happens if two branch abductions use the same branching point) case abduceBranch: Branch if abduceBranch.bLabel == label => k(item :: next :: rest) // didn't find target goal label; check parent @@ -586,7 +586,8 @@ case class AbduceCall( case Nil => throw ProofRuleTranslationException(s"branch point ${label.pp} not found for branch abduction step ${item.step.pp}") } - val initStep = Init(None, node.goal) - forward(node, List(Item(initStep, Nil, Nil))) + val initStep = Init(node.goal) + val initItem = Item(initStep, None, Nil, Nil) + forward(node, List(initItem)) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 6048c1e5d..e7ea4e884 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -1,12 +1,13 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.{ProofTree, SuslikProofStep} +import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.CGamma import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CFunSpec, CInductiveClause, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateAsn, translateExpr, translateParam, translateSApp, translateType, translateVar} +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Expressions.{Subst, SubstVar, Var} import org.tygus.suslik.language.Statements import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} @@ -199,13 +200,13 @@ object IR { CFunSpec(f.name, rType, params, ghosts, pre, post) } - def fromRule(node: ProofTree[SuslikProofStep], ctx: IR.Context) : IR.Node = node.rule match { - case SuslikProofStep.Init(_, goal) => + def fromRule(node: ProofTree[SuslikProofStep], ctx: IR.Context) : IR.Node = node.step match { + case SuslikProofStep.Init(goal) => val cgoal = translateGoal(goal) val sappNames = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap val ctx1 = ctx.copy(topLevelGoal = Some(cgoal), sappNames = sappNames) IR.Init(ctx1, Seq(fromRule(node.children.head, ctx1))) - case SuslikProofStep.Open(_, sapp, fresh_vars, sbst, selectors) => + case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => val csapp = translateSApp(sapp) val freshCVars = fresh_vars.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} val csbst = translateSbst(sbst) @@ -213,7 +214,7 @@ object IR { val next = node.children.map(n => fromRule(n, ctx)) val cselectors = selectors.map(translateExpr) IR.Open(csapp, pred.clauses, cselectors, next, ctx) - case SuslikProofStep.Close(_, sapp, selector, asn, sbst) => + case SuslikProofStep.Close(sapp, selector, asn, sbst) => val csapp = translateSApp(sapp) val cselector = translateExpr(selector) val casn = translateAsn(asn) @@ -225,73 +226,73 @@ object IR { val unfoldings = ctx.unfoldings + (csapp -> actualClause) val sappNames = ctx.sappNames ++ casn.sigma.apps.map(a => a -> a) fromRule(node.children.head, ctx.copy(unfoldings = unfoldings, sappNames = sappNames)) - case SuslikProofStep.Branch(_, cond, _) => + case SuslikProofStep.Branch(cond, _) => val Seq(ifTrue, ifFalse) = node.children IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) - case SuslikProofStep.PureSynthesis(_, is_final, sbst) => + case SuslikProofStep.PureSynthesis(is_final, sbst) => val csbst = translateSbst(sbst) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) IR.PureSynthesis(is_final, Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.SubstL(_, sbst) => + case SuslikProofStep.SubstL(sbst) => val csbst = translateSbst(sbst) val sappNames = updateSAppAliases(ctx.sappNames, csbst) fromRule(node.children.head, ctx.copy(subst = ctx.subst ++ csbst, sappNames = sappNames)) - case SuslikProofStep.SubstR(_, sbst) => + case SuslikProofStep.SubstR(sbst) => val csbst = translateSbst(sbst) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) - case SuslikProofStep.Pick(_, sbst) => + case SuslikProofStep.Pick(sbst) => val csbst = translateSbst(sbst) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) - case SuslikProofStep.Read(_, ghosts, Load(to, tpe, from, offset)) => + case SuslikProofStep.Read(ghosts, Load(to, tpe, from, offset)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.Write(_, Store(to, offset, e)) => + case SuslikProofStep.Write(Store(to, offset, e)) => IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Free(_, Statements.Free(v), size) => + case SuslikProofStep.Free(Statements.Free(v), size) => IR.Free(CFree(CVar(v.name), size), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Malloc(_, ghosts, Statements.Malloc(to, tpe, sz)) => + case SuslikProofStep.Malloc(ghosts, Statements.Malloc(to, tpe, sz)) => val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.Call(_, _, Statements.Call(fun, args, _)) => + case SuslikProofStep.Call(_, Statements.Call(fun, args, _)) => val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) val ctx2 = ctx1.copy(nestedContext = None) IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(node.children.head, ctx2)), ctx1) - case SuslikProofStep.PickArg(_, sbst) => + case SuslikProofStep.PickArg(sbst) => val csbst = translateSbst(sbst) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) - case SuslikProofStep.AbduceCall(_, new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma) => + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma) => val cfunspec = translateFunSpec(f, gamma) val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) fromRule(node.children.head, ctx1) - case SuslikProofStep.HeapUnifyPointer(_, sbst) => + case SuslikProofStep.HeapUnifyPointer(sbst) => val csbst = translateSbst(sbst) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) case SuslikProofStep.EmpRule(_) => IR.EmpRule(ctx) - case SuslikProofStep.CheckPost(_, prePhi, postPhi) => + case SuslikProofStep.CheckPost(prePhi, postPhi) => IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(node.children.head, ctx)), ctx) case SuslikProofStep.Inconsistency(_) => IR.Inconsistency(ctx) // unused rules: - case SuslikProofStep.HeapUnify(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.NilNotLval(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.WeakenPre(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.StarPartial(_, _, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.PickCard(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.FrameUnfold(_, h_pre, h_post) => fromRule(node.children.head, ctx) + case SuslikProofStep.HeapUnify(_) => fromRule(node.children.head, ctx) + case SuslikProofStep.NilNotLval(_) => fromRule(node.children.head, ctx) + case SuslikProofStep.WeakenPre(_) => fromRule(node.children.head, ctx) + case SuslikProofStep.StarPartial(_, _) => fromRule(node.children.head, ctx) + case SuslikProofStep.PickCard(_) => fromRule(node.children.head, ctx) + case SuslikProofStep.FrameUnfold(h_pre, h_post) => fromRule(node.children.head, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index cf60ec5bc..2a682dca1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -2,11 +2,12 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar import org.tygus.suslik.language.Expressions.Expr -import org.tygus.suslik.certification.{CertTree, ProofTree} +import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.vst.{Debug, State} import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} import org.tygus.suslik.language.PrettyPrinting import org.tygus.suslik.language.Statements.{Skip, Store} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index e9b68139a..09a655a5f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,19 +1,20 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.CardConstructor import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType +import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.{Ident, PrettyPrinting} -sealed abstract class VSTProofStep extends PrettyPrinting {} +sealed abstract class VSTProofStep extends DestStep {} object VSTProofStep { implicit object ProofTreePrinter extends ProofTreePrinter[VSTProofStep] { - override def pp(tree: ProofTree[VSTProofStep]): String = tree.rule match { + override def pp(tree: ProofTree[VSTProofStep]): String = tree.step match { case rule@ForwardIf => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) - case rule@ForwardIfConstructor(_,_,_) => tree.rule.pp ++ "\n" ++ rule.branch_strings(tree.children) - case _ => tree.rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + case rule@ForwardIfConstructor(_,_,_) => tree.step.pp ++ "\n" ++ rule.branch_strings(tree.children) + case _ => tree.step.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 3ad4f21ad..aae388fe4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -9,7 +9,8 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, ProofTypes, VSTProofStep} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with -import org.tygus.suslik.certification.{CertTree, SuslikProofStep, ProofTree} +import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.certification.{CertTree, SuslikProofStep} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc} import org.tygus.suslik.language.{Expressions, Ident, Statements} @@ -245,7 +246,7 @@ object ProofTranslation { * - emit a forward tactic to move over the operation */ def handle_read_rule(rule: SuslikProofStep.Read, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Read(_, subst, option) => + case SuslikProofStep.Read(subst, option) => subst.toList match { case ::((Var(old_var), Var(new_var)), _) => def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { @@ -266,49 +267,49 @@ object ProofTranslation { map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) rule match { - case ProofTree(SuslikProofStep.NilNotLval(_, vars), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.CheckPost(_, _, _), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickCard(_, _), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickArg(_, _), List(next)) => + case ProofTree(SuslikProofStep.NilNotLval(vars), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.CheckPost(_, _), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.PickCard(_), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.PickArg(_), List(next), _) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Pick(_, subst), List(next)) => + case ProofTree(SuslikProofStep.Pick(subst), List(next), _) => val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Branch(_, cond, _), List(ifTrue, ifFalse)) => + case ProofTree(SuslikProofStep.Branch(cond, _), List(ifTrue, ifFalse), _) => is_variable_used_in_exp(variable)(cond) || is_variable_used_in_proof(variable)(ifTrue) || is_variable_used_in_proof(variable)(ifFalse) - case ProofTree(SuslikProofStep.Write(_, Statements.Store(Var(tov), offset, e)), List(next)) => + case ProofTree(SuslikProofStep.Write(Statements.Store(Var(tov), offset, e)), List(next), _) => (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.WeakenPre(_, unused), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.EmpRule(_), List()) => false - case ProofTree(SuslikProofStep.PureSynthesis(_, is_final, assignments), List(next)) => + case ProofTree(SuslikProofStep.WeakenPre(unused), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.EmpRule(_), List(), _) => false + case ProofTree(SuslikProofStep.PureSynthesis(is_final, assignments), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Open(_, pred, _, heaplet,cases), children) => + case ProofTree(SuslikProofStep.Open(pred, _, heaplet,cases), children, _) => cases.zip(children).exists({ case (expr, rule) => is_variable_used_in_exp(variable)(expr) || is_variable_used_in_proof(variable)(rule) }) - case ProofTree(SuslikProofStep.SubstL(_, map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofTree(SuslikProofStep.SubstR(_, map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofTree(SuslikProofStep.AbduceCall(_, new_vars, f_pre, callePost, call, freshSub, _, _, _), List(next)) => + case ProofTree(SuslikProofStep.SubstL(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.SubstR(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.HeapUnify(_, _), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.HeapUnifyPointer(_, map), List(next)) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofTree(SuslikProofStep.FrameUnfold(_, h_pre, h_post), List(next)) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Close(_, app, selector, asn, fresh_exist), List(next)) => + case ProofTree(SuslikProofStep.HeapUnify(_), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.HeapUnifyPointer(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.Close(app, selector, asn, fresh_exist), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.StarPartial(_, new_pre_phi, new_post_phi), List(next)) => + case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Read(_, map, Load(Var(toe), _, Var(frome), offset)), List(next)) => + case ProofTree(SuslikProofStep.Read(map, Load(Var(toe), _, Var(frome), offset)), List(next), _) => (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) - case ProofTree(SuslikProofStep.Call(_, _, Call(_, args, _)), List(next)) => + case ProofTree(SuslikProofStep.Call(_, Call(_, args, _)), List(next), _) => args.exists(is_variable_used_in_exp(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Free(_, Free(Var(v)), _), List(next)) => + case ProofTree(SuslikProofStep.Free(Free(Var(v)), _), List(next), _) => (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Malloc(_, map, Malloc(Var(toe), tpe, sz)), List(next)) => + case ProofTree(SuslikProofStep.Malloc(map, Malloc(Var(toe), tpe, sz)), List(next), _) => (toe != variable) && is_variable_used_in_proof(variable)(next) } } @@ -334,7 +335,7 @@ object ProofTranslation { * and then for each branch introducing the variables that it uses. */ def handle_open_rule(rule: SuslikProofStep.Open, children: List[ProofTree[SuslikProofStep]], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Open(_, SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => + case SuslikProofStep.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => val pred = pred_map(predicate_name) val context_branches = pred.clauses.zip(cases.zip(children)).map({ case ((constructor, clause), (expr, rule)) => @@ -376,7 +377,7 @@ object ProofTranslation { } def handle_pick_rule(rule: SuslikProofStep.Pick, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Pick(_, subst) => + case SuslikProofStep.Pick(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) @@ -384,7 +385,7 @@ object ProofTranslation { } def handle_pick_card_rule(rule: SuslikProofStep.PickCard, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickCard(_, subst) => + case SuslikProofStep.PickCard(subst) => /** Given an expression representing a pick of a cardinality variable * returns the corresponding cardinality constructor @@ -424,7 +425,7 @@ object ProofTranslation { } def handle_pick_arg_rule(rule: SuslikProofStep.PickArg, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickArg(_, subst) => + case SuslikProofStep.PickArg(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) @@ -490,7 +491,7 @@ object ProofTranslation { } def handle_pure_synthesis_rule(rule: SuslikProofStep.PureSynthesis, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PureSynthesis(_, is_final, subst) => + case SuslikProofStep.PureSynthesis(is_final, subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) @@ -498,7 +499,7 @@ object ProofTranslation { } def handle_heap_unify(rule: SuslikProofStep.HeapUnify, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.HeapUnify(_, _) => + case SuslikProofStep.HeapUnify(_) => // val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ // case ((name,expr), context) => record_variable_assignment(name,expr)(context) // }) @@ -506,7 +507,7 @@ object ProofTranslation { } def handle_heap_unify_pointer(rule: SuslikProofStep.HeapUnifyPointer, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.HeapUnifyPointer(_, subst) => + case SuslikProofStep.HeapUnifyPointer(subst) => val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ case ((name, expr), context) => record_variable_assignment(name, expr)(context) }) @@ -514,7 +515,7 @@ object ProofTranslation { } def handle_substl_rule(rule: SuslikProofStep.SubstL, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstL(_, map) => + case SuslikProofStep.SubstL(map) => map.toList.foldRight(translate_proof_rules(next)(context))({ case ((Var(name), expr), next) => ProofTree(VSTProofStep.AssertPropSubst( @@ -525,7 +526,7 @@ object ProofTranslation { } def handle_substr_rule(rule: SuslikProofStep.SubstR, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstR(_, map) => + case SuslikProofStep.SubstR(map) => def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofTree[VSTProofStep] = map match { case Nil => translate_proof_rules(next)(context) @@ -543,7 +544,7 @@ object ProofTranslation { } def handle_abduce_call(rule: SuslikProofStep.AbduceCall, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.AbduceCall(_, new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _) => + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _) => var typing_context = retrieve_typing_context(context) f_pre.vars.foreach({ case Var(name) => if (!typing_context.contains(name)) { @@ -558,7 +559,7 @@ object ProofTranslation { } def handle_nilnotnval_rule(rule: SuslikProofStep.NilNotLval, next: ProofTree[SuslikProofStep], context: Context) = rule match { - case SuslikProofStep.NilNotLval(_, vars) => + case SuslikProofStep.NilNotLval(vars) => vars.foldRight(translate_proof_rules(next)(context))({ case (_@Var(name), rest) => ProofTree(VSTProofStep.ValidPointer( @@ -568,7 +569,7 @@ object ProofTranslation { } def handle_abduce_branch_rule(rule: SuslikProofStep.Branch, ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Branch(_, cond, _) => + case SuslikProofStep.Branch(cond, _) => ProofTree(VSTProofStep.ForwardIf, List( translate_proof_rules(ifTrue)(context), translate_proof_rules(ifFalse)(context) @@ -576,7 +577,7 @@ object ProofTranslation { } def handle_call_rule(rule: SuslikProofStep.Call, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Call(_, _, call) => + case SuslikProofStep.Call(_, call) => val ((fun, args, existentials), new_context_1) = pop_function(context) ProofTree(VSTProofStep.ForwardCall(args), existentials match { @@ -588,15 +589,15 @@ object ProofTranslation { } def handle_write_rule(rule: SuslikProofStep.Write, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Write(_, stmt) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) + case SuslikProofStep.Write(stmt) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) } def handle_free_rule(rule: SuslikProofStep.Free, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Free(_, Free(Var(name)), size) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) + case SuslikProofStep.Free(Free(Var(name)), size) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) } def handle_close_rule(rule: SuslikProofStep.Close, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Close(_, app, o_selector, asn, fresh_exist) => + case SuslikProofStep.Close(app, o_selector, asn, fresh_exist) => // Use application of of constructor to infer mapping of variables val predicate = pred_map(app.pred) @@ -660,7 +661,7 @@ object ProofTranslation { } def handle_malloc_rule(rule: SuslikProofStep.Malloc, next: ProofTree[SuslikProofStep], context: Context) = rule match { - case SuslikProofStep.Malloc(_, map, Malloc(Var(to_var), _, sz)) => + case SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)) => val new_context_1 = map.foldRight( add_new_variables(map.map({ case (Var(original), Var(name)) => (name, CoqPtrType) }))(context) @@ -676,45 +677,45 @@ object ProofTranslation { def translate_proof_rules(rule: ProofTree[SuslikProofStep])(context: Context): ProofTree[VSTProofStep] = { rule match { // Branching rules - case ProofTree(rule@SuslikProofStep.Open(_, SApp(_, _, _, Var(_)), _, _, _), children) => handle_open_rule(rule, children, context) - case ProofTree(rule@SuslikProofStep.Branch(_, cond, _), List(ifTrue, ifFalse) ) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) + case ProofTree(rule@SuslikProofStep.Open(SApp(_, _, _, Var(_)), _, _, _), children, _) => handle_open_rule(rule, children, context) + case ProofTree(rule@SuslikProofStep.Branch(cond, _), List(ifTrue, ifFalse), _) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) // Read and write Operations - case ProofTree(rule@SuslikProofStep.Write(_, _), List(next)) => handle_write_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Read(_, subst, option), List(next)) => handle_read_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Write(_), List(next), _) => handle_write_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Read(subst, option), List(next), _) => handle_read_rule(rule, next, context) // Memory management rules - case ProofTree(rule@SuslikProofStep.Free(_, Free(Var(_)), _), List(next)) => handle_free_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Malloc(_, map, Malloc(Var(to_var), _, sz)), List(next)) => handle_malloc_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Free(Free(Var(_)), _), List(next), _) => handle_free_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)), List(next), _) => handle_malloc_rule(rule, next, context) // Abduce call & Existentials - case ProofTree(rule@SuslikProofStep.AbduceCall(_, _, _, _, Call(Var(_), _, _), _, _, _, _), List(next)) => handle_abduce_call(rule, next, context) - case ProofTree(rule@SuslikProofStep.Pick(_, _), List(next)) => handle_pick_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PureSynthesis(_, _, _), List(next)) => handle_pure_synthesis_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PickCard(_, _), List(next)) => handle_pick_card_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PickArg(_, _), List(next)) => handle_pick_arg_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Call(_, _, _), List(next)) => handle_call_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Close(_, _, _, _, _), List(next)) => handle_close_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.HeapUnify(_, _), List(next)) => handle_heap_unify(rule, next, context) - case ProofTree(rule@SuslikProofStep.HeapUnifyPointer(_, _), List(next)) => handle_heap_unify_pointer(rule, next, context) + case ProofTree(rule@SuslikProofStep.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _), List(next), _) => handle_abduce_call(rule, next, context) + case ProofTree(rule@SuslikProofStep.Pick(_), List(next), _) => handle_pick_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PureSynthesis(_, _), List(next), _) => handle_pure_synthesis_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PickCard(_), List(next), _) => handle_pick_card_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.PickArg(_), List(next), _) => handle_pick_arg_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Call(_, _), List(next), _) => handle_call_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.Close(_, _, _, _), List(next), _) => handle_close_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.HeapUnify(_), List(next), _) => handle_heap_unify(rule, next, context) + case ProofTree(rule@SuslikProofStep.HeapUnifyPointer(_), List(next), _) => handle_heap_unify_pointer(rule, next, context) // Completion rule - case ProofTree(SuslikProofStep.EmpRule(_), List()) => handle_emp_rule(context) + case ProofTree(SuslikProofStep.EmpRule(_), List(), _) => handle_emp_rule(context) // Context changing rules - case ProofTree(rule@SuslikProofStep.NilNotLval(_, _), List(next)) => handle_nilnotnval_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.SubstL(_, _), List(next)) => handle_substl_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.SubstR(_, _), List(next)) => handle_substr_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.NilNotLval(_), List(next), _) => handle_nilnotnval_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.SubstL(_), List(next), _) => handle_substl_rule(rule, next, context) + case ProofTree(rule@SuslikProofStep.SubstR(_), List(next), _) => handle_substr_rule(rule, next, context) // Ignored rules - case ProofTree(SuslikProofStep.WeakenPre(_, unused), List(next)) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.CheckPost(_, _, _), List(next)) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.WeakenPre(unused), List(next), _) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.CheckPost(_, _), List(next), _) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.FrameUnfold(_, h_pre, h_post), List(next)) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next), _) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.StarPartial(_, new_pre_phi, new_post_phi), List(next)) => translate_proof_rules(next)(context) + case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next), _) => translate_proof_rules(next)(context) } } @@ -726,13 +727,13 @@ object ProofTranslation { Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) } - def contains_free(proof: ProofTree[SuslikProofStep]): Boolean = proof.rule match { - case SuslikProofStep.Free(_, stmt, size) => true + def contains_free(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { + case SuslikProofStep.Free(stmt, size) => true case _ => proof.children.exists(contains_free) } - def contains_malloc(proof: ProofTree[SuslikProofStep]): Boolean = proof.rule match { - case SuslikProofStep.Malloc(_, map, stmt) => true + def contains_malloc(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { + case SuslikProofStep.Malloc(map, stmt) => true case _ => proof.children.exists(contains_malloc) } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 2c974faf9..925fa9aec 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.certification.traversal.Step._ import scala.collection.immutable.Queue trait Evaluator[A <: SourceStep, B <: DestStep] { - def run(node: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B], initialClientContext: ClientContext[B]): Tree[B] + def run(node: ProofTree[A])(implicit translator: Translator[A,B], printer: ProofTreePrinter[B], initialClientContext: ClientContext[B]): ProofTree[B] } object Evaluator { diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala new file mode 100644 index 000000000..cc109e5b0 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala @@ -0,0 +1,16 @@ +package org.tygus.suslik.certification.traversal + +import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.logic.Specifications.GoalLabel + +/** + * Represents an abstract encoding of a proof tree + * + * @param step a proof step (rule application) + * @param children list of child nodes + * @param label the label of the Suslik goal to which the rule was applied + */ +sealed case class ProofTree[S <: Step](step: S, children: List[ProofTree[S]], label: Option[GoalLabel] = None)(implicit printer: ProofTreePrinter[S]) + extends PrettyPrinting { + override def pp : String = printer.pp(this) +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTreePrinter.scala b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTreePrinter.scala new file mode 100644 index 000000000..3db8cf057 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTreePrinter.scala @@ -0,0 +1,5 @@ +package org.tygus.suslik.certification.traversal + +trait ProofTreePrinter[S <: Step] { + def pp (tree: ProofTree[S]) : String +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index de73cfe34..6ef27318e 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -9,7 +9,7 @@ import scala.annotation.tailrec class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { // A pending evaluation task; tracks which children have and have not been evaluated - case class Task(values: List[B], label: GoalLabel, remainingBranches: List[(Tree[A], DeferredsStack, ClientContext[B])], resultsSoFar: List[Tree[B]]) + case class Task(values: List[B], label: Option[GoalLabel], remainingBranches: List[(ProofTree[A], DeferredsStack, ClientContext[B])], resultsSoFar: List[ProofTree[B]]) // A stack of pending evaluation tasks type TaskStack = List[Task] @@ -17,10 +17,10 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { // A stack of queued deferreds type DeferredsStack = List[Deferreds[B]] - def run(tree: Tree[A])(implicit translator: Translator[A,B], printer: TreePrinter[B], initialClientContext: ClientContext[B]): Tree[B] = { + def run(tree: ProofTree[A])(implicit translator: Translator[A,B], printer: ProofTreePrinter[B], initialClientContext: ClientContext[B]): ProofTree[B] = { // Use a child result to fulfill the evaluation task for a parent @tailrec - def backward(taskStack: TaskStack, childResult: Tree[B]): Tree[B] = + def backward(taskStack: TaskStack, childResult: ProofTree[B]): ProofTree[B] = taskStack match { case Nil => // no more tasks; return the result @@ -30,7 +30,7 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { case Nil => // received results for all children so topmost task is done; remove from stack and evaluate parent task val results = childResult :: currTask.resultsSoFar - val translatedTree = foldStepsIntoTree(currTask.values, currTask.label, results.reverse) + val translatedTree = foldStepsIntoTree(currTask.values, results.reverse, currTask.label) backward(remainingStack, translatedTree) case (nextChild, nextDeferreds, nextTricks) :: remainingBranches => // some siblings remain unvisited; update topmost task with child result and explore next sibling @@ -41,7 +41,7 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { // Do step-wise translation of current tree node and explore next child @tailrec - def forward(tree: Tree[A], deferredsStack: DeferredsStack, clientContext: ClientContext[B], taskStack: TaskStack): Tree[B] = { + def forward(tree: ProofTree[A], deferredsStack: DeferredsStack, clientContext: ClientContext[B], taskStack: TaskStack): ProofTree[B] = { val res = tree.step.translate[B](clientContext) if (tree.children.length != res.childrenMeta.length) { throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childrenMeta.length} children") @@ -91,7 +91,7 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { (tree.children, childDeferredsStacks, childClientContexts).zipped.toList match { case Nil => // terminal; evaluate the converted node in the context of the current stack - val result = foldStepsIntoTree(steps, tree.label, Nil) + val result = foldStepsIntoTree(steps, Nil, tree.label) backward(taskStack, result) case (nextChild, nextDeferredsStack, nextClientCtx) :: remainingBranches => // non-terminal; create evaluation task for current tree and visit the first child @@ -100,9 +100,9 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { } } // Create a tree from a list of values - def foldStepsIntoTree(values: List[B], label: GoalLabel, children: List[Tree[B]]): Tree[B] = + def foldStepsIntoTree(values: List[B], children: List[ProofTree[B]], label: Option[GoalLabel]): ProofTree[B] = values.reverse match { - case last :: rest => rest.foldLeft(Tree(last, label, children)){ case (child, v) => Tree(v, label, List(child)) } + case last :: rest => rest.foldLeft(ProofTree(last, children, label)){ case (child, v) => ProofTree(v, List(child), label) } case Nil => throw EvaluatorException("expected at least one translated value for this task") } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala deleted file mode 100644 index 5f97d87a7..000000000 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Tree.scala +++ /dev/null @@ -1,14 +0,0 @@ -package org.tygus.suslik.certification.traversal - -import org.tygus.suslik.language.PrettyPrinting -import org.tygus.suslik.logic.Specifications.GoalLabel - -case class Tree[S <: Step](step: S, label: GoalLabel, children: List[Tree[S]]) - (implicit printer: TreePrinter[S]) - extends PrettyPrinting { - override def pp : String = printer.pp(this) -} - -trait TreePrinter[S <: Step] { - def pp (tree: Tree[S]) : String -} From dbcdbd36a6cc4412e72046f8d1212ebebe09d1b8 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 8 Feb 2021 11:38:37 +0800 Subject: [PATCH 088/211] Handle trivial branch abduction case --- .../certification/SuslikProofStep.scala | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index daf15531a..b64a44d16 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -547,7 +547,11 @@ case class AbduceCall( case next :: remaining => val item = Item(step, label, remaining, Nil) val nextStack = step match { + // branch statement abduced to the current position; push on top of stack + case ab: Branch if label.contains(ab.bLabel) => item :: stack + // branch statement abduced to an ancestor; find and insert at correct position case ab: Branch => insertBranchPoint(item, ab.bLabel, stack, identity) + // non-branch statement; push on top of stack case _ => item :: stack } forward(next, nextStack) @@ -558,12 +562,13 @@ case class AbduceCall( * Look through tree traversal continuation stack to insert correct branching point for a branch abduction step. * * Consider in the left diagram, an AbduceBranch node B with children C (true case) and D (false case), and - * intended branch destination A. This procedure inserts a new Branch node E with A as its true case child; the - * inserted node E is meant to denote a finalized branching point. + * intended branch destination A. This procedure modifies the traversal continuation so that B is the direct + * parent of A. * - * D--- D--- D--- - * / => / / - * --A-----B-C--- --E-A-----B-C--- + * + * D--- D--- + * / => / + * --A-----B-C--- --B-A-----C--- * * @param item the branch abduction step * @param label the target goal label @@ -588,6 +593,8 @@ case class AbduceCall( val initStep = Init(node.goal) val initItem = Item(initStep, None, Nil, Nil) - forward(node, List(initItem)) + val res = forward(node, List(initItem)) + Console.println(res.pp) + res } } \ No newline at end of file From eb9db07fa25ee8157c7041b1c2d169169c748753 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 8 Feb 2021 14:01:34 +0800 Subject: [PATCH 089/211] Make evaluator parameterize over client ctx --- .../suslik/certification/traversal/Evaluator.scala | 10 +++++----- .../certification/traversal/StackEvaluator.scala | 12 ++++++------ .../certification/traversal/TranslatableOps.scala | 2 +- .../suslik/certification/traversal/Translator.scala | 6 +++--- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 925fa9aec..86a2ae5d5 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -5,16 +5,16 @@ import org.tygus.suslik.certification.traversal.Step._ import scala.collection.immutable.Queue -trait Evaluator[A <: SourceStep, B <: DestStep] { - def run(node: ProofTree[A])(implicit translator: Translator[A,B], printer: ProofTreePrinter[B], initialClientContext: ClientContext[B]): ProofTree[B] +trait Evaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] { + def run(node: ProofTree[A])(implicit translator: Translator[A,B,C], printer: ProofTreePrinter[B], initialClientContext: C): ProofTree[B] } object Evaluator { case class EvaluatorException(private val message: String) extends Exception(message) - type ClientContext[S <: DestStep] - type Deferred[S <: DestStep] = ClientContext[S] => (S, ClientContext[S]) - type Deferreds[S <: DestStep] = Queue[Deferred[S]] + trait ClientContext[S <: DestStep] + type Deferred[S <: DestStep, C <: ClientContext[S]] = C => (S, C) + type Deferreds[S <: DestStep, C <: ClientContext[S]] = Queue[Deferred[S,C]] abstract class EnvAction object EnvAction { diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index 6ef27318e..ff5354a38 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -7,17 +7,17 @@ import org.tygus.suslik.logic.Specifications.GoalLabel import scala.annotation.tailrec -class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { +class StackEvaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] extends Evaluator[A,B,C] { // A pending evaluation task; tracks which children have and have not been evaluated - case class Task(values: List[B], label: Option[GoalLabel], remainingBranches: List[(ProofTree[A], DeferredsStack, ClientContext[B])], resultsSoFar: List[ProofTree[B]]) + case class Task(values: List[B], label: Option[GoalLabel], remainingBranches: List[(ProofTree[A], DeferredsStack, C)], resultsSoFar: List[ProofTree[B]]) // A stack of pending evaluation tasks type TaskStack = List[Task] // A stack of queued deferreds - type DeferredsStack = List[Deferreds[B]] + type DeferredsStack = List[Deferreds[B,C]] - def run(tree: ProofTree[A])(implicit translator: Translator[A,B], printer: ProofTreePrinter[B], initialClientContext: ClientContext[B]): ProofTree[B] = { + def run(tree: ProofTree[A])(implicit translator: Translator[A,B,C], printer: ProofTreePrinter[B], initialClientContext: C): ProofTree[B] = { // Use a child result to fulfill the evaluation task for a parent @tailrec def backward(taskStack: TaskStack, childResult: ProofTree[B]): ProofTree[B] = @@ -41,8 +41,8 @@ class StackEvaluator[A <: SourceStep, B <: DestStep] extends Evaluator[A,B] { // Do step-wise translation of current tree node and explore next child @tailrec - def forward(tree: ProofTree[A], deferredsStack: DeferredsStack, clientContext: ClientContext[B], taskStack: TaskStack): ProofTree[B] = { - val res = tree.step.translate[B](clientContext) + def forward(tree: ProofTree[A], deferredsStack: DeferredsStack, clientContext: C, taskStack: TaskStack): ProofTree[B] = { + val res = tree.step.translate[B,C](clientContext) if (tree.children.length != res.childrenMeta.length) { throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childrenMeta.length} children") } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala index f0eca95be..0fc95cdbc 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.traversal.Step._ object TranslatableOps { implicit class Translatable[A <: SourceStep](step: A) { - def translate[B <: DestStep](clientContext: ClientContext[B])(implicit translator: Translator[A, B]): Translator.Result[B] = { + def translate[B <: DestStep, C <: ClientContext[B]](clientContext: C)(implicit translator: Translator[A, B, C]): Translator.Result[B,C] = { translator.translate(step, clientContext) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala index 74425b7fe..df14bcd96 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala @@ -3,10 +3,10 @@ package org.tygus.suslik.certification.traversal import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ -trait Translator[A <: SourceStep, B <: DestStep] { - def translate(value: A, clientContext: ClientContext[B]): Translator.Result[B] +trait Translator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] { + def translate(value: A, clientContext: C): Translator.Result[B,C] } object Translator { - case class Result[S <: DestStep](steps: List[S], childrenMeta: List[(Deferreds[S], ClientContext[S])]) + case class Result[S <: DestStep, C <: ClientContext[S]](steps: List[S], childrenMeta: List[(Deferreds[S,C], C)]) } \ No newline at end of file From 756421c67b991a9f07b737856fb0fcff09788254 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 10 Feb 2021 19:53:25 +0800 Subject: [PATCH 090/211] Define producer to capture single substitutions --- .../tygus/suslik/certification/CertTree.scala | 3 +- .../certification/SuslikProofStep.scala | 76 ++--- .../targets/htt/translation/IR.scala | 30 +- .../certification/targets/vst/Debug.scala | 7 +- .../targets/vst/logic/Proof.scala | 11 - .../vst/translation/ProofTranslation.scala | 164 ++++------ .../targets/vst/translation/Translation.scala | 9 - .../tygus/suslik/synthesis/SearchTree.scala | 1 + .../tygus/suslik/synthesis/StmtProducer.scala | 306 +++++++++--------- .../rules/DelegatePureSynthesis.scala | 4 +- .../suslik/synthesis/rules/FailRules.scala | 2 +- .../suslik/synthesis/rules/LogicalRules.scala | 4 +- .../synthesis/rules/OperationalRules.scala | 6 +- .../tygus/suslik/synthesis/rules/Rules.scala | 2 +- .../rules/SymbolicExecutionRules.scala | 1 + .../synthesis/rules/UnfoldingRules.scala | 3 +- .../synthesis/rules/UnificationRules.scala | 16 +- 17 files changed, 305 insertions(+), 340 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index a633072f0..46b0a92f2 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -3,7 +3,8 @@ package org.tygus.suslik.certification import org.tygus.suslik.logic.Specifications.{Footprint, Goal} import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.synthesis.SearchTree.{NodeId, OrNode} -import org.tygus.suslik.synthesis.{StmtProducer, SynthesisException} +import org.tygus.suslik.synthesis.SynthesisException +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules.SynthesisRule import scala.collection.mutable diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index b64a44d16..72c61af60 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -12,7 +12,7 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal, GoalLabel, Suspen import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules._ -import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import scala.annotation.tailrec import scala.collection.immutable.Map @@ -58,8 +58,8 @@ object SuslikProofStep { } /** picks an arbitrary instantiation of the proof rules */ - case class Pick(subst: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}Pick(${subst.mkString(", ")});" + case class Pick(from: Var, to: Expr) extends SuslikProofStep { + override def pp: String = s"${ind}Pick(${from.pp} -> ${to.pp});" } /** branches on a condition */ @@ -100,19 +100,19 @@ object SuslikProofStep { } /** subst L */ - case class SubstL(map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}SubstL(${map.mkString(",")});" + case class SubstL(from: Var, to: Expr) extends SuslikProofStep { + override def pp: String = s"${ind}SubstL(${from.pp} -> ${to.pp});" } /** subst R */ - case class SubstR(map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}SubstR(${map.mkString(",")});" + case class SubstR(from: Var, to: Expr) extends SuslikProofStep { + override def pp: String = s"${ind}SubstR(${from.pp} -> ${to.pp});" } /** read rule */ - case class Read(map: Map[Var,Var], operation: Load) extends SuslikProofStep { - override def pp: String = s"${ind}Read(${map.mkString(",")}, ${sanitize(operation.pp)});" + case class Read(ghostFrom: Var, ghostTo: Var, operation: Load) extends SuslikProofStep { + override def pp: String = s"${ind}Read(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(operation.pp)});" } // /** abduce a call */ @@ -137,8 +137,8 @@ case class AbduceCall( } /** unification of pointers */ - case class HeapUnifyPointer(map: Map[Var,Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}HeapUnifyPointer(${map.mkString(",")});" + case class HeapUnifyPointer(from: Var, to: Var) extends SuslikProofStep { + override def pp: String = s"${ind}HeapUnifyPointer(${from.pp} -> ${to.pp});" } /** unfolds frame */ @@ -158,8 +158,8 @@ case class AbduceCall( } /** malloc rule */ - case class Malloc(map: SubstVar, stmt: Statements.Malloc) extends SuslikProofStep { - override def pp: String = s"${ind}Malloc(${map.mkString(",")}, ${sanitize(stmt.pp)});" + case class Malloc(ghostFrom: Var, ghostTo: Var, stmt: Statements.Malloc) extends SuslikProofStep { + override def pp: String = s"${ind}Malloc(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(stmt.pp)});" } /** close rule */ @@ -172,13 +172,13 @@ case class AbduceCall( override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } - case class PickCard(map: Map[Var,Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}PickCard(${map.mkString(",")});" + case class PickCard(from: Var, to: Expr) extends SuslikProofStep { + override def pp: String = s"${ind}PickCard(${from.pp} -> ${to.pp});" } - case class PickArg(map: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}PickArg(${map.mkString(",")});" + case class PickArg(from: Var, to: Var) extends SuslikProofStep { + override def pp: String = s"${ind}PickArg(${from.pp} -> ${to.pp});" } case class Init(goal: Goal) extends SuslikProofStep { @@ -230,9 +230,9 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.Pick => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Pick(map) + case ::(head, Nil) => SuslikProofStep.Pick(from, to) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -279,7 +279,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case DelegatePureSynthesis.PureSynthesisFinal => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(assignments), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.PureSynthesis(is_final = true, assignments) @@ -293,25 +293,25 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case LogicalRules.SubstLeft => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.SubstL(map) + case ::(head, Nil) => SuslikProofStep.SubstL(from, to) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.SubstRight => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.SubstR(map) + case ::(head, Nil) => SuslikProofStep.SubstR(from, to) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case OperationalRules.ReadRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstVarProducer(from, to), PrependProducer(stmt@Load(_, _, _, _))), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.Read(map, stmt) + case ::(head, Nil) => SuslikProofStep.Read(from, to, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -331,7 +331,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) @@ -339,7 +339,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) @@ -347,7 +347,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) @@ -355,9 +355,9 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyPointer => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstVarProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(subst) + case ::(head, Nil) => SuslikProofStep.HeapUnifyPointer(from, to) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -435,7 +435,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnfoldingRules.CallRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst),PrependProducer(call: Statements.Call)), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.Call(subst, call) case ls => fail_with_bad_children(ls, 1) @@ -455,11 +455,11 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case OperationalRules.AllocRule => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(GhostSubstProducer(map), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstVarProducer(from, to), PrependProducer(stmt@Statements.Malloc(_, _, _))), HandleGuard(_)), ExtractHelper(goal)) => node.children match { case ::(head, Nil) => SuslikProofStep. - Malloc(map, stmt) + Malloc(from, to, stmt) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() @@ -486,16 +486,16 @@ case class AbduceCall( } case UnificationRules.PickCard => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.PickCard(map) + case ::(head, Nil) => SuslikProofStep.PickCard(from, to) case ls => fail_with_bad_children(ls, 1) } } case UnificationRules.PickArg => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstProducer(map), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => + case ChainedProducer(ChainedProducer(ChainedProducer(SubstVarProducer(from, to), IdProducer), HandleGuard(_)), ExtractHelper(goal)) => node.children match { - case ::(head, Nil) => SuslikProofStep.PickArg(map) + case ::(head, Nil) => SuslikProofStep.PickArg(from, to) case ls => fail_with_bad_children(ls, 1) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index e7ea4e884..31db4ce81 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -235,38 +235,38 @@ object IR { val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) IR.PureSynthesis(is_final, Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.SubstL(sbst) => - val csbst = translateSbst(sbst) + case SuslikProofStep.SubstL(from, to) => + val csbst = translateSbst(Map(from -> to)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) fromRule(node.children.head, ctx.copy(subst = ctx.subst ++ csbst, sappNames = sappNames)) - case SuslikProofStep.SubstR(sbst) => - val csbst = translateSbst(sbst) + case SuslikProofStep.SubstR(from, to) => + val csbst = translateSbst(Map(from -> to)) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) - case SuslikProofStep.Pick(sbst) => - val csbst = translateSbst(sbst) + case SuslikProofStep.Pick(from, to) => + val csbst = translateSbst(Map(from -> to)) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) fromRule(node.children.head, ctx1) - case SuslikProofStep.Read(ghosts, Load(to, tpe, from, offset)) => - val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) + case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, tpe, from, offset)) => + val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(Map(ghostFrom -> ghostTo))) IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(node.children.head, ctx1)), ctx1) case SuslikProofStep.Write(Store(to, offset, e)) => IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(node.children.head, ctx)), ctx) case SuslikProofStep.Free(Statements.Free(v), size) => IR.Free(CFree(CVar(v.name), size), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Malloc(ghosts, Statements.Malloc(to, tpe, sz)) => - val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(ghosts)) + case SuslikProofStep.Malloc(ghostFrom, ghostTo, Statements.Malloc(to, tpe, sz)) => + val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(Map(ghostFrom -> ghostTo))) IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(node.children.head, ctx1)), ctx1) case SuslikProofStep.Call(_, Statements.Call(fun, args, _)) => val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) val ctx2 = ctx1.copy(nestedContext = None) IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(node.children.head, ctx2)), ctx1) - case SuslikProofStep.PickArg(sbst) => - val csbst = translateSbst(sbst) + case SuslikProofStep.PickArg(from, to) => + val csbst = translateSbst(Map(from -> to)) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) @@ -277,8 +277,8 @@ object IR { val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) fromRule(node.children.head, ctx1) - case SuslikProofStep.HeapUnifyPointer(sbst) => - val csbst = translateSbst(sbst) + case SuslikProofStep.HeapUnifyPointer(from, to) => + val csbst = translateSbst(Map(from -> to)) val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) val sappNames = updateSAppAliases(ctx.sappNames, csbst) val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) @@ -292,7 +292,7 @@ object IR { case SuslikProofStep.NilNotLval(_) => fromRule(node.children.head, ctx) case SuslikProofStep.WeakenPre(_) => fromRule(node.children.head, ctx) case SuslikProofStep.StarPartial(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.PickCard(_) => fromRule(node.children.head, ctx) + case SuslikProofStep.PickCard(_, _) => fromRule(node.children.head, ctx) case SuslikProofStep.FrameUnfold(h_pre, h_post) => fromRule(node.children.head, ctx) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala index d219975ec..16effc1a5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.vst import java.io.{BufferedOutputStream, BufferedReader, ByteArrayInputStream, ByteArrayOutputStream, File, FileOutputStream, FileWriter, InputStreamReader, StringWriter} import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} +import org.tygus.suslik.synthesis.StmtProducer._ import scalaz.Scalaz.{ToTraverseOps, ToTraverseOpsUnapply} import scala.io._ @@ -39,8 +39,9 @@ object Debug { case BranchProducer(_, _, _, selectors) => s"BranchProducer[${stmtProducer.arity}] {\n${selectors.map(_.pp).mkString("\n")}\n}" case GuardedProducer(cond, goal) => s"GuardedProducer[${stmtProducer.arity}] {\n cond=${cond.pp}\n goal=${goal.pp}\n}" - case SubstProducer(subst) => s"SubstProducer[${stmtProducer.arity}](${subst.toString})" - case GhostSubstProducer(subst) => s"GhostSubstProducer[${stmtProducer.arity}](${subst.toString})" + case SubstMapProducer(subst) => s"SubstMapProducer[${stmtProducer.arity}](${subst.toString})" + case SubstProducer(from, to) => s"SubstProducer[${stmtProducer.arity}](${from.pp} -> ${to.pp})" + case SubstVarProducer(from, to) => s"SubstVarProducer[${stmtProducer.arity}](${from.pp} -> ${to.pp})" case UnfoldProducer(app, selector, asn, substEx) => s"UnfoldProducer[${stmtProducer.arity}] {\n app=${app.pp}\n selector=${selector.pp}\n asn=${asn.toString}\n substEx=${substEx.toString}\n}" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 2a682dca1..4fb06dd0e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,19 +1,8 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar -import org.tygus.suslik.language.Expressions.Expr -import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.targets.vst.{Debug, State} -import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Var} import org.tygus.suslik.language.PrettyPrinting -import org.tygus.suslik.language.Statements.{Skip, Store} -import org.tygus.suslik.logic.{PFormula, PointsTo, SFormula} -import org.tygus.suslik.synthesis.rules.{DelegatePureSynthesis, FailRules, LogicalRules, OperationalRules, UnificationRules} -import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], uses_free: Boolean = false, uses_malloc: Boolean = false) extends PrettyPrinting { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index aae388fe4..2ee9547ee 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -246,9 +246,7 @@ object ProofTranslation { * - emit a forward tactic to move over the operation */ def handle_read_rule(rule: SuslikProofStep.Read, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Read(subst, option) => - subst.toList match { - case ::((Var(old_var), Var(new_var)), _) => + case SuslikProofStep.Read(old_var, new_var, option) => def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { case Var(name) => (name == variable) case const: Expressions.Const => false @@ -269,12 +267,11 @@ object ProofTranslation { rule match { case ProofTree(SuslikProofStep.NilNotLval(vars), List(next), _) => is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.CheckPost(_, _), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickCard(_), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickArg(_), List(next), _) => - val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Pick(subst), List(next), _) => - val picked_variables = subst.toList.flatMap({ case (Var(froe), Var(toe)) => Some(toe) case _ => None }).toSet + case ProofTree(SuslikProofStep.PickCard(_, _), List(next), _) => is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.PickArg(from, to), List(next), _) => + to.name == variable || is_variable_used_in_proof(variable)(next) + case ProofTree(SuslikProofStep.Pick(from, to), List(next), _) => + val picked_variables = to match { case Var(v) => Some(v) case _ => None } (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.Branch(cond, _), List(ifTrue, ifFalse), _) => is_variable_used_in_exp(variable)(cond) || @@ -291,41 +288,40 @@ object ProofTranslation { is_variable_used_in_exp(variable)(expr) || is_variable_used_in_proof(variable)(rule) }) - case ProofTree(SuslikProofStep.SubstL(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) - case ProofTree(SuslikProofStep.SubstR(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.SubstL(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) + case ProofTree(SuslikProofStep.SubstR(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) case ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _), List(next), _) => is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.HeapUnify(_), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.HeapUnifyPointer(map), List(next), _) => is_variable_used_in_proof(map_varaible(map))(next) + case ProofTree(SuslikProofStep.HeapUnifyPointer(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next), _) => is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.Close(app, selector, asn, fresh_exist), List(next), _) => is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Read(map, Load(Var(toe), _, Var(frome), offset)), List(next), _) => + case ProofTree(SuslikProofStep.Read(_, _, Load(Var(toe), _, Var(frome), offset)), List(next), _) => (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) case ProofTree(SuslikProofStep.Call(_, Call(_, args, _)), List(next), _) => args.exists(is_variable_used_in_exp(variable)) || is_variable_used_in_proof(variable)(next) case ProofTree(SuslikProofStep.Free(Free(Var(v)), _), List(next), _) => (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Malloc(map, Malloc(Var(toe), tpe, sz)), List(next), _) => + case ProofTree(SuslikProofStep.Malloc(_, _, Malloc(Var(toe), tpe, sz)), List(next), _) => (toe != variable) && is_variable_used_in_proof(variable)(next) } } - val new_context = record_variable_mapping(subst)(context) - val rest = (retrieve_typing_context(context).get(old_var)) match { + val new_context = record_variable_mapping(Map(old_var -> new_var))(context) + val rest = (retrieve_typing_context(context).get(old_var.name)) match { case Some(CoqPtrType) => - ProofTree(VSTProofStep.ValidPointerOrNull(new_var), List(translate_proof_rules(next)(new_context))) + ProofTree(VSTProofStep.ValidPointerOrNull(new_var.name), List(translate_proof_rules(next)(new_context))) case _ => translate_proof_rules(next)(new_context) } - if (is_variable_used_in_proof(new_var)(next)) { - ProofTree(VSTProofStep.Rename(old_var, new_var), List(ProofTree(VSTProofStep.Forward, List(rest)))) + if (is_variable_used_in_proof(new_var.name)(next)) { + ProofTree(VSTProofStep.Rename(old_var.name, new_var.name), List(ProofTree(VSTProofStep.Forward, List(rest)))) } else { - ProofTree(VSTProofStep.Rename(old_var, new_var), List(rest)) + ProofTree(VSTProofStep.Rename(old_var.name, new_var.name), List(rest)) } - } } /** @@ -377,15 +373,13 @@ object ProofTranslation { } def handle_pick_rule(rule: SuslikProofStep.Pick, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Pick(subst) => - val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ - case ((name, expr), context) => record_variable_assignment(name, expr)(context) - }) + case SuslikProofStep.Pick(Var(name), expr) => + val new_context = record_variable_assignment(name, expr)(context) translate_proof_rules(next)(new_context) } def handle_pick_card_rule(rule: SuslikProofStep.PickCard, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickCard(subst) => + case SuslikProofStep.PickCard(Var(name), expr) => /** Given an expression representing a pick of a cardinality variable * returns the corresponding cardinality constructor @@ -414,21 +408,16 @@ object ProofTranslation { (ProofCCardinalityConstructor(predicate.name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) } - val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ - case ((name, expr), context) => - val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(name)(expr) - record_variable_assignment_card(name, translated_expr)( - add_new_variables(new_vars.toMap)(context) - ) - }) + val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(name)(expr) + val new_context = record_variable_assignment_card(name, translated_expr)( + add_new_variables(new_vars.toMap)(context) + ) translate_proof_rules(next)(new_context) } def handle_pick_arg_rule(rule: SuslikProofStep.PickArg, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickArg(subst) => - val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ - case ((name, expr), context) => record_variable_assignment(name, expr)(context) - }) + case SuslikProofStep.PickArg(Var(name), expr) => + val new_context = record_variable_assignment(name, expr)(context) translate_proof_rules(next)(new_context) } @@ -507,40 +496,29 @@ object ProofTranslation { } def handle_heap_unify_pointer(rule: SuslikProofStep.HeapUnifyPointer, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.HeapUnifyPointer(subst) => - val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ - case ((name, expr), context) => record_variable_assignment(name, expr)(context) - }) + case SuslikProofStep.HeapUnifyPointer(Var(name), expr) => + val new_context = record_variable_assignment(name, expr)(context) translate_proof_rules(next)(new_context) } def handle_substl_rule(rule: SuslikProofStep.SubstL, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstL(map) => - map.toList.foldRight(translate_proof_rules(next)(context))({ - case ((Var(name), expr), next) => - ProofTree(VSTProofStep.AssertPropSubst( - name, - ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr)), - List(next)) - }) + case SuslikProofStep.SubstL(Var(name), expr) => + ProofTree( + VSTProofStep.AssertPropSubst(name, ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr)), + List(translate_proof_rules(next)(context)) + ) } def handle_substr_rule(rule: SuslikProofStep.SubstR, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstR(map) => - def apply_subst(context: Context)(map: List[(Var, Expr)]): ProofTree[VSTProofStep] = - map match { - case Nil => translate_proof_rules(next)(context) - case ::((Var(old_name), Var(new_name)), rest) => - val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) - ProofTree(VSTProofStep.Rename(old_name, new_name, - - ), List(apply_subst(new_context)(rest))) - case ::((Var(name), expr), rest) => - val new_context = record_variable_assignment(name, expr)(context) - apply_subst(new_context)(rest) - } - - apply_subst(context)(map.toList) + case SuslikProofStep.SubstR(Var(old_name), expr) => + expr match { + case Var(new_name) => + val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) + ProofTree(VSTProofStep.Rename(old_name, new_name), List(translate_proof_rules(next)(new_context))) + case _ => + val new_context = record_variable_assignment(old_name, expr)(context) + translate_proof_rules(next)(new_context) + } } def handle_abduce_call(rule: SuslikProofStep.AbduceCall, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { @@ -661,11 +639,9 @@ object ProofTranslation { } def handle_malloc_rule(rule: SuslikProofStep.Malloc, next: ProofTree[SuslikProofStep], context: Context) = rule match { - case SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)) => - val new_context_1 = - map.foldRight( - add_new_variables(map.map({ case (Var(original), Var(name)) => (name, CoqPtrType) }))(context) - )({ case ((Var(old_name), new_expr), context) => record_variable_assignment(old_name, new_expr)(context) }) + case SuslikProofStep.Malloc(Var(original), Var(name), Malloc(Var(to_var), _, sz)) => + val new_context_0 = add_new_variables(Map(name -> CoqPtrType))(context) + val new_context_1 = record_variable_assignment(original, Var(name))(new_context_0) val new_context = add_new_coq_variables(Map(to_var -> CoqPtrType))(new_context_1) ProofTree(VSTProofStep.Malloc(sz), List(ProofTree(VSTProofStep.Intros( @@ -677,45 +653,45 @@ object ProofTranslation { def translate_proof_rules(rule: ProofTree[SuslikProofStep])(context: Context): ProofTree[VSTProofStep] = { rule match { // Branching rules - case ProofTree(rule@SuslikProofStep.Open(SApp(_, _, _, Var(_)), _, _, _), children, _) => handle_open_rule(rule, children, context) - case ProofTree(rule@SuslikProofStep.Branch(cond, _), List(ifTrue, ifFalse), _) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) + case ProofTree(rule:SuslikProofStep.Open, children, _) => handle_open_rule(rule, children, context) + case ProofTree(rule:SuslikProofStep.Branch, List(ifTrue, ifFalse), _) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) // Read and write Operations - case ProofTree(rule@SuslikProofStep.Write(_), List(next), _) => handle_write_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Read(subst, option), List(next), _) => handle_read_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Write, List(next), _) => handle_write_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Read, List(next), _) => handle_read_rule(rule, next, context) // Memory management rules - case ProofTree(rule@SuslikProofStep.Free(Free(Var(_)), _), List(next), _) => handle_free_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Malloc(map, Malloc(Var(to_var), _, sz)), List(next), _) => handle_malloc_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Free, List(next), _) => handle_free_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Malloc, List(next), _) => handle_malloc_rule(rule, next, context) // Abduce call & Existentials - case ProofTree(rule@SuslikProofStep.AbduceCall(_, _, _, Call(Var(_), _, _), _, _, _, _), List(next), _) => handle_abduce_call(rule, next, context) - case ProofTree(rule@SuslikProofStep.Pick(_), List(next), _) => handle_pick_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PureSynthesis(_, _), List(next), _) => handle_pure_synthesis_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PickCard(_), List(next), _) => handle_pick_card_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.PickArg(_), List(next), _) => handle_pick_arg_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Call(_, _), List(next), _) => handle_call_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.Close(_, _, _, _), List(next), _) => handle_close_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.HeapUnify(_), List(next), _) => handle_heap_unify(rule, next, context) - case ProofTree(rule@SuslikProofStep.HeapUnifyPointer(_), List(next), _) => handle_heap_unify_pointer(rule, next, context) + case ProofTree(rule:SuslikProofStep.AbduceCall, List(next), _) => handle_abduce_call(rule, next, context) + case ProofTree(rule:SuslikProofStep.Pick, List(next), _) => handle_pick_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.PureSynthesis, List(next), _) => handle_pure_synthesis_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.PickCard, List(next), _) => handle_pick_card_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.PickArg, List(next), _) => handle_pick_arg_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Call, List(next), _) => handle_call_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.Close, List(next), _) => handle_close_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.HeapUnify, List(next), _) => handle_heap_unify(rule, next, context) + case ProofTree(rule:SuslikProofStep.HeapUnifyPointer, List(next), _) => handle_heap_unify_pointer(rule, next, context) // Completion rule - case ProofTree(SuslikProofStep.EmpRule(_), List(), _) => handle_emp_rule(context) + case ProofTree(_:SuslikProofStep.EmpRule, List(), _) => handle_emp_rule(context) // Context changing rules - case ProofTree(rule@SuslikProofStep.NilNotLval(_), List(next), _) => handle_nilnotnval_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.SubstL(_), List(next), _) => handle_substl_rule(rule, next, context) - case ProofTree(rule@SuslikProofStep.SubstR(_), List(next), _) => handle_substr_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.NilNotLval, List(next), _) => handle_nilnotnval_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.SubstL, List(next), _) => handle_substl_rule(rule, next, context) + case ProofTree(rule:SuslikProofStep.SubstR, List(next), _) => handle_substr_rule(rule, next, context) // Ignored rules - case ProofTree(SuslikProofStep.WeakenPre(unused), List(next), _) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.CheckPost(_, _), List(next), _) => translate_proof_rules(next)(context) + case ProofTree(_:SuslikProofStep.WeakenPre, List(next), _) => translate_proof_rules(next)(context) + case ProofTree(_:SuslikProofStep.CheckPost, List(next), _) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next), _) => translate_proof_rules(next)(context) + case ProofTree(_:SuslikProofStep.FrameUnfold, List(next), _) => translate_proof_rules(next)(context) - case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next), _) => translate_proof_rules(next)(context) + case ProofTree(_:SuslikProofStep.StarPartial, List(next), _) => translate_proof_rules(next)(context) } } @@ -728,12 +704,12 @@ object ProofTranslation { } def contains_free(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { - case SuslikProofStep.Free(stmt, size) => true + case _:SuslikProofStep.Free => true case _ => proof.children.exists(contains_free) } def contains_malloc(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { - case SuslikProofStep.Malloc(map, stmt) => true + case _:SuslikProofStep.Malloc => true case _ => proof.children.exists(contains_malloc) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 32e7d67d0..6be40755e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -3,18 +3,9 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.targets.vst.VSTCertificate -import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation -import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment -import org.tygus.suslik.synthesis.IdProducer.ruleAssert -import org.tygus.suslik.synthesis.{AppendProducer, BranchProducer, ChainedProducer, ConstProducer, ExtractHelper, GhostSubstProducer, GuardedProducer, HandleGuard, IdProducer, PartiallyAppliedProducer, PrependFromSketchProducer, PrependProducer, SeqCompProducer, StmtProducer, SubstProducer, UnfoldProducer} - -import scala.annotation.tailrec -import scala.collection.immutable object Translation { diff --git a/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala b/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala index 7b6e39ad2..b89533fcf 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SearchTree.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.synthesis.Memoization._ import org.tygus.suslik.synthesis.Termination.Transition import org.tygus.suslik.synthesis.rules.Rules.{RuleResult, SynthesisRule} +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.util.SynStats /** diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index da8462a91..803943d32 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -6,178 +6,180 @@ import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, PFormula, SApp import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils -/** - * A continuation that builds a solution for a synthesis problem - * from solutions of its sub-problems - */ -sealed abstract class StmtProducer extends RuleUtils { - val exceptionQualifier: String = "producer" - - val arity: Int - val fn: Kont - - def apply(children: Seq[Solution]): Solution = { - ruleAssert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") - fn(children) +object StmtProducer { + + /** + * A continuation that builds a solution for a synthesis problem + * from solutions of its sub-problems + */ + sealed abstract class StmtProducer extends RuleUtils { + val exceptionQualifier: String = "producer" + + val arity: Int + val fn: Kont + + def apply(children: Seq[Solution]): Solution = { + ruleAssert(children.lengthCompare(arity) == 0, s"Producer expects $arity children and got ${children.length}") + fn(children) + } + + /** + * Producer transformer that sequences two producers: + * the resulting producer first applies p1 to a prefix of child solutions, + * then applies p2 to the result of p1 and a suffix of child solutions + */ + def >>(p: StmtProducer): StmtProducer = ChainedProducer(this, p) + + /** + * Producer that results form applying this producer to s as its idx argument + */ + def partApply(s: Solution): StmtProducer = PartiallyAppliedProducer(this, s) + + def liftToSolutions(f: Seq[Statement] => Statement)(arg: Seq[Solution]): Solution = { + val (stmts, helpers) = arg.unzip + val stmt = f(stmts) + val allHelpers = helpers.toList.flatten + (stmt, allHelpers) + } + } + + case class ChainedProducer(p1: StmtProducer, p2: StmtProducer) extends StmtProducer { + val arity: Int = p1.arity + p2.arity - 1 + val fn: Kont = sols => { + val (sols1, sols2) = sols.splitAt(p1.arity) + val sol = p1.fn(sols1) + p2.fn(sol +: sols2) + } + } + + case class PartiallyAppliedProducer(p: StmtProducer, s: Solution) extends StmtProducer { + val arity: Int = p.arity - 1 + val fn: Kont = sols => { + p.apply(s +: sols) + } + } + + /** + * Identity producer: returns the first child solution unchanged + */ + case object IdProducer extends StmtProducer { + val arity: Int = 1 + val fn: Kont = _.head } /** - * Producer transformer that sequences two producers: - * the resulting producer first applies p1 to a prefix of child solutions, - * then applies p2 to the result of p1 and a suffix of child solutions + * Constant producer: ignored child solutions and returns s */ - def >>(p: StmtProducer): StmtProducer = ChainedProducer(this, p) + case class ConstProducer(s: Statement) extends StmtProducer { + val arity: Int = 0 + val fn: Kont = liftToSolutions(_ => s) + } /** - * Producer that results form applying this producer to s as its idx argument + * Producer that prepends s to the first child solution */ - def partApply(s: Solution): StmtProducer = PartiallyAppliedProducer(this, s) + case class PrependProducer(s: Statement) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = liftToSolutions(stmts => { + SeqComp(s, stmts.head).simplify + }) + } - def liftToSolutions(f: Seq[Statement] => Statement)(arg: Seq[Solution]): Solution = { - val (stmts, helpers) = arg.unzip - val stmt = f(stmts) - val allHelpers = helpers.toList.flatten - (stmt, allHelpers) + /** + * Same as prepend but do not simplify s away, because it comes from the sketch + */ + case class PrependFromSketchProducer(s: Statement) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = liftToSolutions(stmts => { + SeqComp(s, stmts.head) + }) } -} + /** + * Producer that appends s to the first child solution + */ + case class AppendProducer(s: Statement) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = liftToSolutions(stmts => { + SeqComp(stmts.head, s).simplify + }) + } + /** + * Producer that sequentially composes results of two goals + */ + case object SeqCompProducer extends StmtProducer { + val arity: Int = 2 + val fn: Kont = liftToSolutions(stmts => { + SeqComp(stmts.head, stmts.last).simplify + }) + } -case class ChainedProducer(p1: StmtProducer, p2: StmtProducer) extends StmtProducer { - val arity: Int = p1.arity + p2.arity - 1 - val fn: Kont = sols => { - val (sols1, sols2) = sols.splitAt(p1.arity) - val sol = p1.fn(sols1) - p2.fn(sol +: sols2) + /** + * Producer that checks if the child solution has backlinks to its goal, + * and if so produces a helper call and a new helper + */ + case class ExtractHelper(goal: Goal) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = sols => { + val (stmt, helpers) = sols.head + if (stmt.companions.contains(goal.label) && !goal.isTopLevel) { + val f = goal.toFunSpec + val newHelper = Procedure(f, stmt) + (goal.toCall, newHelper :: helpers) + } else + (stmt, helpers) + } } -} -case class PartiallyAppliedProducer(p: StmtProducer, s: Solution) extends StmtProducer { - val arity: Int = p.arity - 1 - val fn: Kont = sols => { - p.apply(s +: sols) + case class HandleGuard(goal: Goal) extends StmtProducer { + val arity: Int = 1 + val fn: Kont = liftToSolutions(stmts => { + stmts.head match { + case g@Guarded(cond, body, els, l) => + if (goal.label == l) If(cond, body, els).simplify // Current goal is the branching point: create conditional + else g // Haven't reached the branching point yet: propagate guarded statement + case stmt => stmt + } + }) } -} - -/** - * Identity producer: returns the first child solution unchanged - */ -case object IdProducer extends StmtProducer { - val arity: Int = 1 - val fn: Kont = _.head -} - -/** - * Constant producer: ignored child solutions and returns s - */ -case class ConstProducer(s: Statement) extends StmtProducer { - val arity: Int = 0 - val fn: Kont = liftToSolutions(_ => s) -} - -/** - * Producer that prepends s to the first child solution - */ -case class PrependProducer(s: Statement) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => { - SeqComp(s, stmts.head).simplify - }) -} - -/** - * Same as prepend but do not simplify s away, because it comes from the sketch - */ -case class PrependFromSketchProducer(s: Statement) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => { - SeqComp(s, stmts.head) - }) -} - -/** - * Producer that appends s to the first child solution - */ -case class AppendProducer(s: Statement) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => { - SeqComp(stmts.head, s).simplify - }) -} - -/** - * Producer that sequentially composes results of two goals - */ -case object SeqCompProducer extends StmtProducer { - val arity: Int = 2 - val fn: Kont = liftToSolutions(stmts => { - SeqComp(stmts.head, stmts.last).simplify - }) -} - -/** - * Producer that checks if the child solution has backlinks to its goal, - * and if so produces a helper call and a new helper - */ -case class ExtractHelper(goal: Goal) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = sols => { - val (stmt, helpers) = sols.head - if (stmt.companions.contains(goal.label) && !goal.isTopLevel) { - val f = goal.toFunSpec - val newHelper = Procedure(f, stmt) - (goal.toCall, newHelper :: helpers) - } else - (stmt, helpers) + + // Produces a conditional that branches on the selectors + case class BranchProducer(pred: Option[SApp], freshVars: SubstVar, sbst: Subst, selectors: Seq[Expr]) extends StmtProducer { + val arity: Int = selectors.length + val fn: Kont = liftToSolutions(stmts => { + if (stmts.length == 1) stmts.head else { + val cond_branches = selectors.zip(stmts).reverse + val ctail = cond_branches.tail + val finalBranch = cond_branches.head._2 + ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => If(c, tb, eb).simplify } + } + }) } -} - -case class HandleGuard(goal: Goal) extends StmtProducer { - val arity: Int = 1 - val fn: Kont = liftToSolutions(stmts => { - stmts.head match { - case g@Guarded(cond, body, els, l) => - if (goal.label == l) If(cond, body, els).simplify // Current goal is the branching point: create conditional - else g // Haven't reached the branching point yet: propagate guarded statement - case stmt => stmt - } - }) -} - -// Produces a conditional that branches on the selectors -case class BranchProducer(pred: Option[SApp], freshVars: SubstVar, sbst: Subst, selectors: Seq[Expr]) extends StmtProducer { - val arity: Int = selectors.length - val fn: Kont = liftToSolutions(stmts => { - if (stmts.length == 1) stmts.head else { - val cond_branches = selectors.zip(stmts).reverse - val ctail = cond_branches.tail - val finalBranch = cond_branches.head._2 - ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => If(c, tb, eb).simplify } - } - }) -} -case class GuardedProducer(cond: Expr, goal: Goal) extends StmtProducer { - val arity: Int = 2 - val fn: Kont = liftToSolutions(stmts => Guarded(cond, stmts.head, stmts.last, goal.label)) -} + case class GuardedProducer(cond: Expr, goal: Goal) extends StmtProducer { + val arity: Int = 2 + val fn: Kont = liftToSolutions(stmts => Guarded(cond, stmts.head, stmts.last, goal.label)) + } -trait Noop { - val arity: Int = 1 - val fn: Kont = _.head -} + trait Noop { + val arity: Int = 1 + val fn: Kont = _.head + } -// Captures variable substitutions -case class SubstProducer(subst: Subst) extends StmtProducer with Noop + // Captures variable to expression substitutions + case class SubstProducer(from: Var, to: Expr) extends StmtProducer with Noop + case class SubstMapProducer(subst: Subst) extends StmtProducer with Noop -// Captures ghost variable instantiations -case class GhostSubstProducer(subst: SubstVar) extends StmtProducer with Noop + // Captures variable to variable substitutions + case class SubstVarProducer(from: Var, to: Var) extends StmtProducer with Noop -// Captures an unfolded predicate application -case class UnfoldProducer(app: SApp, selector: Expr, asn: Assertion, substEx: SubstVar) extends StmtProducer with Noop + // Captures an unfolded predicate application + case class UnfoldProducer(app: SApp, selector: Expr, asn: Assertion, substEx: SubstVar) extends StmtProducer with Noop -// Abduce Call -case class AbduceCallProducer(f: FunSpec) extends StmtProducer with Noop + // Abduce Call + case class AbduceCallProducer(f: FunSpec) extends StmtProducer with Noop -// Captures entailments emitted by SMT -case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula) extends StmtProducer with Noop \ No newline at end of file + // Captures entailments emitted by SMT + case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula) extends StmtProducer with Noop +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala index 5559e7dd6..3e9913d38 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala @@ -9,7 +9,7 @@ import org.tygus.suslik.language._ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic.{PFormula, Specifications} import org.tygus.suslik.synthesis.rules.Rules.{InvertibleRule, RuleResult, SynthesisRule} -import org.tygus.suslik.synthesis.{SubstProducer, ExtractHelper, HandleGuard, IdProducer} +import org.tygus.suslik.synthesis.StmtProducer.{SubstMapProducer, ExtractHelper, HandleGuard, IdProducer} import scala.sys.process._ import scala.util.{Failure, Success} @@ -219,7 +219,7 @@ object DelegatePureSynthesis { val newCallGoal = goal.callGoal.map(_.updateSubstitution(assignments)) val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) if (isFinal || !DelegatePureSynthesis.hasSecondResult(goal,assignments)) { - val kont = SubstProducer(assignments) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstMapProducer(assignments) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) val alternatives = RuleResult(List(newGoal), kont, this, goal) :: Nil nubBy[RuleResult, Assertion](alternatives, res => res.subgoals.head.post) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala index bc13d5566..43bd1d7f4 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic.smt.SMTSolving import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis.Termination.Transition -import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules._ /** diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala index fe0581938..31a168adf 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/LogicalRules.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.language.Statements._ import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic._ import org.tygus.suslik.logic.smt.SMTSolving -import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules._ /** @@ -264,7 +264,7 @@ object LogicalRules extends PureLogicUtils with SepLogicUtils with RuleUtils { val _p1 = rest1.subst(x, e) val _s1 = s1.subst(x, e) val newGoal = goal.spawnChild(Assertion(_p1, _s1), goal.post.subst(x, e)) - val kont = SubstProducer(Map(x -> e)) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(x, e) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) assert(goal.callGoal.isEmpty) List(RuleResult(List(newGoal), kont, this, goal)) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala index 0fd11b23f..a0fc00f7b 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/OperationalRules.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.{Statements, _} import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic._ -import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules._ /** @@ -96,7 +96,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post = post.subst(a, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = GhostSubstProducer(Map(a -> y)) >> PrependProducer(Load(y, tpy, x, offset)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstVarProducer(a, y) >> PrependProducer(Load(y, tpy, x, offset)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, this, goal)) case Some(h) => ruleAssert(false, s"Read rule matched unexpected heaplet ${h.pp}") @@ -148,7 +148,7 @@ object OperationalRules extends SepLogicUtils with RuleUtils { post.subst(x, y), gamma = goal.gamma + (y -> tpy), programVars = y :: goal.programVars) - val kont: StmtProducer = GhostSubstProducer(Map(x -> y)) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstVarProducer(x, y) >> PrependProducer(Malloc(y, tpy, sz)) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(subGoal), kont, this, goal)) case _ => Nil } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/Rules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/Rules.scala index 2d2012d5b..5fa141608 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/Rules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/Rules.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis.rules import org.tygus.suslik.logic.Specifications.Goal import org.tygus.suslik.logic._ -import org.tygus.suslik.synthesis.StmtProducer +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.Termination.Transition object Rules { diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala index e9e1f7a9c..5c4c5366a 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/SymbolicExecutionRules.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.language.{Statements, _} import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic._ import org.tygus.suslik.logic.smt.SMTSolving +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis._ import org.tygus.suslik.synthesis.rules.Rules._ diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala index 035a702e9..30702fa45 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnfoldingRules.scala @@ -8,6 +8,7 @@ import org.tygus.suslik.logic._ import org.tygus.suslik.logic.smt.SMTSolving import org.tygus.suslik.synthesis.Termination.Transition import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules._ /** @@ -126,7 +127,7 @@ object UnfoldingRules extends SepLogicUtils with RuleUtils { val newPost = callGoal.callerPost val newGoal = goal.spawnChild(pre = newPre, post = newPost, callGoal = None) val postCallTransition = Transition(goal, newGoal) - val kont: StmtProducer = SubstProducer(callGoal.freshToActual) >> PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) + val kont: StmtProducer = SubstMapProducer(callGoal.freshToActual) >> PrependProducer(call) >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, List(postCallTransition) ++ companionTransition(callGoal, goal))) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 128dbf198..3b763cd7b 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -5,6 +5,7 @@ import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.logic.Specifications._ import org.tygus.suslik.logic._ import org.tygus.suslik.synthesis._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.Rules._ /** @@ -48,7 +49,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils // val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) val newPost = Assertion(post.phi && subExpr, newPostSigma) val newGoal = goal.spawnChild(post = newPost) - val kont = SubstProducer(sub.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstMapProducer(sub.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } nubBy[RuleResult, Assertion](alternatives, sub => sub.subgoals.head.post) @@ -92,7 +93,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils val subExpr = goal.substToFormula(subst) val newPost = Assertion(post.phi && subExpr, post.sigma) val newGoal = goal.spawnChild(post = newPost) - val kont = SubstProducer(subst.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstVarProducer(y.asInstanceOf[Var], x.asInstanceOf[Var]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) } } @@ -135,7 +136,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils val _s2 = s2.subst(sigma) val newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) val newGoal = goal.spawnChild(post = Assertion(_p2, _s2), callGoal = newCallGoal) - val kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstProducer(x, e) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) case _ => Nil } @@ -171,7 +172,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils newPost = goal.post.subst(sigma) newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) - kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = SubstProducer(ex, v) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, this, goal) } } @@ -208,7 +209,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils newPost = goal.post.subst(sigma) newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) - kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + kont = SubstProducer(ex, sol) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) } yield RuleResult(List(newGoal), kont, this, goal) } } @@ -234,11 +235,12 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils if (i < 0) return Nil // no unsubstituted arguments remain val arg = companion.params(i)._1 - val sigma = Map(callGoal.call.args(i).asInstanceOf[Var] -> arg) + val v = callGoal.call.args(i).asInstanceOf[Var] + val sigma = Map(v -> arg) val newPost = goal.post.subst(sigma) val newCallGoal = goal.callGoal.map(_.updateSubstitution(sigma)) val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) - val kont = SubstProducer(sigma) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = SubstVarProducer(v, arg) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) List(RuleResult(List(newGoal), kont, this, goal)) } } From 311092cc7e60a965a2c38161f9fb308035d783e0 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 11 Feb 2021 18:16:37 +0800 Subject: [PATCH 091/211] HTT: implement evaluator-based traversal except call abduction --- .../targets/htt/language/package.scala | 2 +- .../targets/htt/logic/Hint.scala | 6 +- .../targets/htt/logic/Proof.scala | 81 ++++---- .../targets/htt/logic/Sentences.scala | 18 +- .../targets/htt/program/Statements.scala | 6 +- .../targets/htt/translation/Context.scala | 75 ++++++++ .../htt/translation/HTTEvaluator.scala | 7 + .../htt/translation/HTTTranslator.scala | 107 +++++++++++ .../targets/htt/translation/IR.scala | 6 +- .../htt/translation/ProofPrinter.scala | 39 ++++ .../htt/translation/ProofTranslator.scala | 176 ++++++++++++++++++ .../htt/translation/TranslatableOps.scala | 9 + .../targets/htt/translation/Translation.scala | 11 +- 13 files changed, 485 insertions(+), 58 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala index 885e32466..6ba38c0d8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/package.scala @@ -5,5 +5,5 @@ import org.tygus.suslik.certification.targets.htt.language.Types.HTTType package object language { type CGamma = Map[CVar, HTTType] - type CFormals = Seq[(HTTType, CVar)] + type CFormals = Seq[(CVar, HTTType)] } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 3570d6be8..895d9f6b8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -23,7 +23,7 @@ object Hint { case class Hypothesis(params: Seq[CVar], idx: Int) { val name = s"${pred.name}_perm_eq_trans$freshHintId" - val (before, after) = pred.params.map(_._2).splitAt(idx) + val (before, after) = pred.params.map(_._1).splitAt(idx) val s1: CVar = CVar("s_1") val s2: CVar = CVar("s_2") val params1: Seq[CVar] = before ++ Seq(s1) ++ after.tail @@ -37,8 +37,8 @@ object Hint { } private val hypotheses: Seq[Hypothesis] = { - val paramVars = pred.params.map(_._2) - pred.params.zipWithIndex.filter(_._1._1 == CNatSeqType).map(_._2).map(i => Hypothesis(paramVars, i)) + val paramVars = pred.params.map(_._1) + pred.params.zipWithIndex.filter(_._1._2 == CNatSeqType).map(_._2).map(i => Hypothesis(paramVars, i)) } val numHypotheses: Int = hypotheses.length diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index c42512fa4..f3d56d71e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -2,11 +2,11 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} import org.tygus.suslik.certification.targets.htt.language.Types.HTTType +import org.tygus.suslik.certification.traversal.Step.DestStep object Proof { - abstract class Step { + abstract class Step extends DestStep { val isNoop: Boolean = false - def pp: String def >>(that: Step): Step = SeqComp(this, that) def >>>(that: Step): Step = SeqCompAlt(this, that) def simplify: Step = this match { @@ -16,130 +16,143 @@ object Proof { case Solve(steps) => Solve(steps.filterNot(_.isNoop).map(_.simplify)) case _ => this } + def andThen: Step = Then(this) } + case class Then(step: Step) extends Step { + override def pp: String = step.pp + } + case class Rename(from: CVar, to: CVar) extends Step { + override def pp: String = s"rename ${from.pp} into ${to.pp}" + } case class SubProof(branches: Seq[Step]) extends Step { override val isNoop: Boolean = branches.forall(_.isNoop) - def pp: String = branches.map(_.pp).mkString(".\n") + override def pp: String = branches.map(_.pp).mkString(".\n") } case class SeqComp(s1: Step, s2: Step) extends Step { override val isNoop: Boolean = s1.isNoop && s2.isNoop - def pp: String = s"${s1.pp}.\n${s2.pp}" + override def pp: String = s"${s1.pp}.\n${s2.pp}" } case class SeqCompAlt(s1: Step, s2: Step) extends Step { override val isNoop: Boolean = s1.isNoop && s2.isNoop - def pp: String = s"${s1.pp};\n${s2.pp}" + override def pp: String = s"${s1.pp};\n${s2.pp}" } case class Solve(steps: Seq[Step]) extends Step { override val isNoop: Boolean = steps.forall(_.isNoop) - def pp: String = s"solve [\n${steps.map(_.pp).mkString(" |\n")} ]" + override def pp: String = s"solve [\n${steps.map(_.pp).mkString(" |\n")} ]" } case class MoveToCtx(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"move=>${items.map(_.pp).mkString(" ")}" + override def pp: String = s"move=>${items.map(_.pp).mkString(" ")}" } case class MoveToCtxDestruct(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"move=>${items.map(i => s"[${i.pp}]").mkString(" ")}" + override def pp: String = s"move=>${items.map(i => s"[${i.pp}]").mkString(" ")}" } case class MoveToCtxDestructFoldLeft(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"move=>${items.map(_.pp).reduceLeft[String]{ case (acc, el) => s"[$acc $el]"}}" + override def pp: String = s"move=>${items.map(_.pp).reduceLeft[String]{ case (acc, el) => s"[$acc $el]"}}" } case class MoveToCtxDestructFoldRight(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"move=>${items.map(_.pp).reduceRight[String]{ case (el, acc) => s"[$el $acc]"}}" + override def pp: String = s"move=>${items.map(_.pp).reduceRight[String]{ case (el, acc) => s"[$el $acc]"}}" } case class MoveToGoal(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"move: ${items.map(_.pp).mkString(" ")}" + override def pp: String = s"move: ${items.map(_.pp).mkString(" ")}" } case class ElimExistential(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString(".\n") + override def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString(".\n") } case class Sbst(vars: Seq[CVar]) extends Step { - def pp: String = if (vars.isEmpty) s"subst" else s"subst ${vars.map(_.pp).mkString(" ")}" + override def pp: String = if (vars.isEmpty) s"subst" else s"subst ${vars.map(_.pp).mkString(" ")}" } case class Exists(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty - def pp: String = s"exists ${items.map { + override def pp: String = s"exists ${items.map { case h:CSFormula => s"(${h.ppHeap})" case i => s"(${i.pp})" }.mkString(", ")}" } case object Auto extends Step { - def pp: String = "sslauto" + override def pp: String = "sslauto" } case class UnfoldConstructor(idx: Int) extends Step { - def pp: String = s"unfold_constructor $idx" + override def pp: String = s"unfold_constructor $idx" } case class Write(to: CVar, offset: Int = 0, e: CExpr) extends Step { - def pp: String = { + override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" s"ssl_write $ptr" } } case class WritePost(to: CVar, offset: Int = 0) extends Step { - def pp: String = { + override def pp: String = { val ptr = if (offset == 0) to.pp else s"(${to.pp} .+ $offset)" s"ssl_write_post $ptr" } } case class Read(to: CVar, from: CVar, offset: Int = 0) extends Step { - def pp: String = { + override def pp: String = { val ptr = if (offset == 0) from.pp else s"(${from.pp} .+ $offset)" s"ssl_read $ptr" } } case class Alloc(to: CVar, tpe: HTTType, sz: Int) extends Step { - def pp: String = s"ssl_alloc ${to.pp}" + override def pp: String = s"ssl_alloc ${to.pp}" } case class Dealloc(v: CVar, offset: Int) extends Step { - def pp: String = { + override def pp: String = { val ptr = if (offset == 0) v.pp else s"(${v.pp} .+ $offset)" s"ssl_dealloc $ptr" } } case class Open(selectors: Seq[CExpr]) extends Step { - def pp: String = s"ssl_open (${selectors.head.pp})" + override def pp: String = s"ssl_open (${selectors.head.pp})" } case class OpenPost(app: CSApp) extends Step { - def pp: String = s"ssl_open_post ${app.hypName}" + override def pp: String = s"ssl_open_post ${app.hypName}" } case class CallPre(heap: CSFormula) extends Step { - def pp: String = s"ssl_call_pre (${heap.ppHeap})" + override def pp: String = s"ssl_call_pre (${heap.ppHeap})" } case class Call(args: Seq[CExpr], ghosts: Seq[CVar]) extends Step { - def pp: String = s"ssl_call (${ghosts.map(_.pp).mkString(", ")})" + override def pp: String = s"ssl_call (${ghosts.map(_.pp).mkString(", ")})" } case object StoreValid extends Step { - def pp: String = "store_valid" + override def pp: String = "store_valid" } case object GhostElimPre extends Step { - def pp: String = "ssl_ghostelim_pre" + override def pp: String = "ssl_ghostelim_pre" } case object GhostElimPost extends Step { - def pp: String = "ssl_ghostelim_post" + override def pp: String = "ssl_ghostelim_post" } case class Branch(cond: CExpr) extends Step { - def pp: String = s"ssl_abduce_branch (${cond.pp})" + override def pp: String = s"ssl_abduce_branch (${cond.pp})" } case object Emp extends Step { - def pp: String = "ssl_emp" + override def pp: String = "ssl_emp" } case object Error extends Step { override val isNoop: Boolean = true - def pp: String = "" + override def pp: String = "" } case object Noop extends Step { override val isNoop: Boolean = true - def pp: String = "" + override def pp: String = "" } case class StartProof(params: Seq[CVar]) extends Step { - def pp: String = s"Obligation Tactic := intro; ${MoveToCtxDestructFoldLeft(params).pp}; ssl_program_simpl.\nNext Obligation" + override def pp: String = s"Obligation Tactic := intro; ${MoveToCtxDestructFoldLeft(params).pp}; ssl_program_simpl.\nNext Obligation" } case object EndProof extends Step { - def pp: String = "Qed.\n" + override def pp: String = "Qed.\n" + } + case object Shelve extends Step { + override def pp: String = "shelve" + } + case object Unshelve extends Step { + override def pp: String = "Unshelve" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 6d1b8364b..072ce06b7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -55,19 +55,19 @@ object Sentences { } case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends PrettyPrinting { - val paramVars: Seq[CVar] = params.map(_._2) + val paramVars: Seq[CVar] = params.map(_._1) def subst(sub: CSubst): CInductivePredicate = CInductivePredicate( name, - params.map(p => (p._1, p._2.substVar(sub))), + params.map(p => (p._1.substVar(sub), p._2)), clauses.map(_.subst(sub)), gamma.map(g => (g._1.substVar(sub), g._2)) ) override def pp: String = { val builder = new StringBuilder() - builder.append(s"Inductive $name ${params.map{ case (t, v) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") + builder.append(s"Inductive $name ${params.map{ case (v, t) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") // clauses val clausesStr = for { @@ -80,8 +80,8 @@ object Sentences { } case class CFunSpec(name: String, rType: HTTType, params: CFormals, ghostParams: CFormals, pre: CAssertion, post: CAssertion) extends PrettyPrinting { - val paramVars: Seq[CVar] = params.map(_._2) - val ghostParamVars: Seq[CVar] = ghostParams.map(_._2) + val paramVars: Seq[CVar] = params.map(_._1) + val ghostParamVars: Seq[CVar] = ghostParams.map(_._1) val progVarsAlias = "vprogs" val ghostVarsAlias = "vghosts" @@ -89,10 +89,10 @@ object Sentences { val existentials: Seq[CVar] = post.valueVars.diff(paramVars ++ ghostParamVars) def subst(subst: CSubst): CFunSpec = - CFunSpec(name, rType, params.map(p => (p._1, p._2.substVar(subst))), ghostParams.map(p => (p._1, p._2.substVar(subst))), pre.subst(subst), post.subst(subst)) + CFunSpec(name, rType, params.map(p => (p._1.substVar(subst), p._2)), ghostParams.map(p => (p._1.substVar(subst), p._2)), pre.subst(subst), post.subst(subst)) private def ppFormals(formals: CFormals): (String, String) = { - val (typs, names) = formals.unzip + val (names, typs) = formals.unzip val typsStr = typs.map(_.pp).mkString(" * ") val namesStr = names.map(_.pp).mkString(", ") (typsStr, namesStr) @@ -101,8 +101,8 @@ object Sentences { override def pp: String = { val builder = new StringBuilder() - val (typsStr, progVarsStr) = ppFormals(params) - val (ghostTypsStr, ghostVarsStr) = ppFormals(ghostParams) + val (progVarsStr, typsStr) = ppFormals(params) + val (ghostVarsStr, ghostTypsStr) = ppFormals(ghostParams) val destructuredAliases = { val destructuredParams = if (params.nonEmpty) s"${getIndent(3)}let: ($progVarsStr) := $progVarsAlias in\n" else "" val destructuredGhostParams = if (ghostParams.nonEmpty) s"${getIndent(3)}let: ($ghostVarsStr) := $ghostVarsAlias in\n" else "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 9e165070a..1d4a235c4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.htt.program -import org.tygus.suslik.certification.targets.htt.language.PrettyPrinting +import org.tygus.suslik.certification.targets.htt.language.{CFormals, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ @@ -129,14 +129,14 @@ object Statements { case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement - case class CProcedure(name: String, tp: HTTType, formals: Seq[(HTTType, CVar)], body: CStatement) extends CStatement { + case class CProcedure(name: String, tp: HTTType, formals: CFormals, body: CStatement) extends CStatement { override def pp: String = { val vprogs = "vprogs" val builder = new StringBuilder builder.append(s"Program Definition $name : ${name}_type :=\n") builder.append(s"${getIndent(1)}Fix (fun ($name : ${name}_type) $vprogs =>\n") - builder.append(s"${getIndent(2)}let: (${formals.map(_._2.pp).mkString(", ")}) := $vprogs in\n") + builder.append(s"${getIndent(2)}let: (${formals.map(_._1.pp).mkString(", ")}) := $vprogs in\n") builder.append(s"${getIndent(2)}Do (\n") builder.append(body.ppIndent(3)) builder.append(s"\n${getIndent(2)})).") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala new file mode 100644 index 000000000..78311e1a1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala @@ -0,0 +1,75 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} +import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductiveClause +import org.tygus.suslik.certification.targets.htt.translation.IR.PredicateEnv +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext + +case class Context(predicates: PredicateEnv = Map.empty, + postEx: Seq[CExpr] = Seq.empty, + appAliases: Map[CSApp, CSApp] = Map.empty, + unfoldings: Map[CSApp, CInductiveClause] = Map.empty) + extends ClientContext[Proof.Step] { + + /** + * Get existentials that result from unfolding an app; heap existentials are provided in + * their maximally unfolded form (i.e., accounts for nested unfolds) + */ + def unfoldedAppExistentials(app: CSApp): (Seq[CExpr], Seq[CSFormula]) = { + unfoldings.get(app) match { + case Some(clause) => + val heapEx = clause.asn.sigma.apps.map(unfoldedApp) + (clause.existentials, heapEx) + case _ => (Seq.empty, Seq.empty) + } + } + + /** + * Get the latest version of an app, which may change if one of its argument variables is substituted + */ + def currentAppAlias(app: CSApp): CSApp = appAliases.get(app) match { + case None => app + case Some(app1) => if (app == app1) app else currentAppAlias(app1) + } + + /** + * Get the maximally unfolded heap equivalent of an app + */ + def unfoldedApp(app: CSApp): CSFormula = unfoldings.get(app) match { + case None => CSFormula(app.heapName, Seq(app), Seq.empty) + case Some(clause) => + val sigma = clause.asn.sigma + val (apps, ptss) = sigma.apps.map(unfoldedApp).map(h => (h.apps, h.ptss)).unzip + CSFormula(app.heapName, apps.flatten, sigma.ptss ++ ptss.flatten) + } + + /** + * Update the current context with new substitutions + */ + def withSubst(m: Map[CVar, CExpr]): Context = { + val postEx1 = postEx.map(_.subst(m)) + val appAliases1 = appAliases.foldLeft(appAliases) { case (appAliases, (app, alias)) => + if (app == alias) { + val app1 = app.subst(m) + if (app != app1) { + appAliases + (app -> app1) + (app1 -> app1) + } else appAliases + } else appAliases + } + val unfoldings1 = unfoldings.map { case (app, clause) => app.subst(m) -> clause.subst(m) } + this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) + } + + /** + * Update the current context with a new variable rename + */ + def withRename(from: CVar, to: CVar): Context = { + val m = Map(from -> to) + withSubst(m) + } +} + +object Context { + val empty: Context = Context() +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala new file mode 100644 index 000000000..b46654719 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.traversal.StackEvaluator + +object HTTEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, Context] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala new file mode 100644 index 000000000..b4639a00b --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -0,0 +1,107 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.targets.htt.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CAssertion +import org.tygus.suslik.certification.targets.htt.translation.IR.CGoal +import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateExpr, translatePointsTo, translateSApp, translateSFormula} +import org.tygus.suslik.language.Expressions._ +import org.tygus.suslik.language._ +import org.tygus.suslik.logic.Specifications.{Assertion, Goal} +import org.tygus.suslik.logic._ + +trait HTTTranslator[A,B] { + def translate(value: A): B +} + +object HTTTranslator { + case class TranslatorException(msg: String) extends Exception(msg) + + implicit val binOpTranslator: HTTTranslator[BinOp, CBinOp] = { + case OpImplication => COpImplication + case OpPlus => COpPlus + case OpMinus => COpMinus + case OpMultiply => COpMultiply + case OpEq => COpEq + case OpBoolEq => COpBoolEq + case OpLeq => COpLeq + case OpLt => COpLt + case OpAnd => COpAnd + case OpOr => COpOr + case OpUnion => COpUnion + case OpDiff => COpDiff + case OpIn => COpIn + case OpSetEq => COpSetEq + case OpSubset => COpSubset + case OpIntersect => COpIntersect + } + implicit val unopTranslator: HTTTranslator[UnOp, CUnOp] = { + case OpNot => COpNot + case OpUnaryMinus => COpUnaryMinus + } + + implicit val exprTranslator: HTTTranslator[Expr, CExpr] = (value: Expr) => { + def visit(value: Expr): CExpr = value match { + case Var(name) => CVar(name) + case BoolConst(value) => CBoolConst(value) + case LocConst(value) => CPtrConst(value) + case IntConst(value) => CNatConst(value) + case UnaryExpr(op, arg) => CUnaryExpr(op.translate, visit(arg)) + case BinaryExpr(op, left, right) => CBinaryExpr(op.translate, visit(left), visit(right)) + case SetLiteral(elems) => CSetLiteral(elems.map(visit)) + case IfThenElse(c, t, e) => CIfThenElse(visit(c), visit(t), visit(e)) + case _ => throw TranslatorException("Operation not supported") + } + visit(value) + } + + implicit val varTranslator: HTTTranslator[Var, CVar] = (value: Var) => CVar(value.name) + + implicit val typeTranslator: HTTTranslator[SSLType, HTTType] = { + case BoolType => CBoolType + case IntType => CNatType + case LocType => CPtrType + case IntSetType => CNatSeqType + case VoidType => CUnitType + case CardType => CCardType + } + + implicit val paramTranslator: HTTTranslator[(Var, SSLType), (CVar, HTTType)] = + el => (el._1.translate, el._2.translate) + + implicit val sappTranslator: HTTTranslator[SApp, CSApp] = el => CSApp(el.pred, el.args.map(_.translate), el.card.translate) + + implicit val pointsToTranslator: HTTTranslator[PointsTo, CPointsTo] = el => CPointsTo(el.loc.translate, el.offset, el.value.translate) + + implicit val asnTranslator: HTTTranslator[Assertion, CAssertion] = el => { + val conjuncts = el.phi.conjuncts.toSeq.map(c => translateExpr(c).simplify).filterNot(_.isCard) + val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) + val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) + val sigma = translateSFormula(el.sigma) + CAssertion(phi, sigma).removeCardConstraints + } + + implicit val sFormulaTranslator: HTTTranslator[SFormula, CSFormula] = el => { + val ptss = el.ptss.map(translatePointsTo) + val apps = el.apps.map(translateSApp) + CSFormula("h", apps, ptss) + } + + implicit val goalTranslator: HTTTranslator[Goal, CGoal] = goal => { + val pre = goal.pre.translate + val post = goal.post.translate + val gamma = goal.gamma.map(_.translate) + val programVars = goal.programVars.map(_.translate) + val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) + CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) + } + + implicit val substTranslator: HTTTranslator[Map[Var, Expr], Map[CVar, CExpr]] = _.map { + case (k, v) => k.translate -> v.translate + } + + implicit val substVarTranslator: HTTTranslator[Map[Var, Var], Map[CVar, CVar]] = _.map { + case (k, v) => k.translate -> v.translate + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala index 31db4ce81..682851a89 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala @@ -38,8 +38,8 @@ object IR { ) def toFunspec: CFunSpec = { - val params = programVars.map(v => (gamma(v), v)) - val ghosts = universalGhosts.map(v => (gamma(v), v)) + val params = programVars.map(v => (v, gamma(v))) + val ghosts = universalGhosts.map(v => (v, gamma(v))) CFunSpec(fname, CUnitType, params, ghosts, pre, post) } } @@ -196,7 +196,7 @@ object IR { val pre = translateAsn(f.pre) val post = translateAsn(f.post) val params = f.params.map(translateParam) - val ghosts = pre.valueVars.diff(params.map(_._2)).map(v => (translateType(gamma(Var(v.name))), v)) + val ghosts = pre.valueVars.diff(params.map(_._2)).map(v => (v, translateType(gamma(Var(v.name))))) CFunSpec(f.name, rType, params, ghosts, pre, post) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala new file mode 100644 index 000000000..4243277c0 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala @@ -0,0 +1,39 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} + +import scala.annotation.tailrec + +object ProofPrinter extends ProofTreePrinter[Proof.Step] { + override def pp(tree: ProofTree[Proof.Step]): String = { + case class Item(res: String, todo: List[ProofTree[Proof.Step]]) + @tailrec + def backward(k: List[Item], res: String): String = k match { + case Nil => res + case curr :: rest => + curr.todo match { + case Nil => backward(rest, curr.res ++ res) + case next :: todo => + val item = Item(curr.res ++ res, todo) + forward(next, item :: rest) + } + } + @tailrec + def forward(tree: ProofTree[Proof.Step], k: List[Item]): String = { + val res = tree.step match { + case s if s.isNoop => "" + case s:Proof.Then => s"${s.pp};\n" + case s => s"${s.pp}.\n" + } + tree.children match { + case Nil => + backward(k, res) + case next :: rest => + val item = Item(res, rest) + forward(next, item :: k) + } + } + forward(tree, Nil) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala new file mode 100644 index 000000000..2f955c8c1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -0,0 +1,176 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar +import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} +import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.traversal.Evaluator.Deferreds +import org.tygus.suslik.certification.traversal.Translator +import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.language.Statements._ + +import scala.collection.immutable.Queue + +object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] { + private val noDeferreds: Deferreds[Proof.Step, Context] = Queue.empty + private def withNoDeferreds(ctx: Context): (Deferreds[Proof.Step, Context], Context) = (noDeferreds, ctx) + + private def initPre(asn: CAssertion, uniqueName: String): List[Proof.Step] = { + val phi = asn.phi + val sigma = asn.sigma + + // move pure part to context + val pureToCtx = if (!phi.isTrivial) { + val hyps = if (phi.conjuncts.isEmpty) Seq(CVar(s"phi_$uniqueName")) else phi.conjuncts.indices.map(i => CVar(s"phi_$uniqueName$i")) + List(Proof.MoveToCtxDestruct(hyps)) + } else Nil + + // move spatial part to context, and then substitute where appropriate + val spatialToCtx = List( + Proof.MoveToCtxDestruct(Seq(CVar(s"sigma_$uniqueName"))), + Proof.Sbst(Seq(CVar(s"h_$uniqueName"))) + ) + + // move predicate apps to context, if any + val sappToCtx = if (sigma.apps.nonEmpty) { + val appNames = sigma.apps.map(app => CVar(app.hypName)) + List(Proof.MoveToCtxDestructFoldRight(appNames)) + } else Nil + + pureToCtx ++ spatialToCtx ++ sappToCtx + } + + override def translate(value: SuslikProofStep, ctx: Context): Translator.Result[Proof.Step, Context] = { + value match { + /** Initialization */ + case SuslikProofStep.Init(goal) => + val programVars = goal.programVars.map(_.translate) + val universalGhosts = goal.universalGhosts.map(_.translate).toSeq + + // handle pre-condition immediately + val pre = goal.pre.translate + val steps = List( + Proof.StartProof(programVars), + Proof.GhostElimPre, + Proof.MoveToCtxDestructFoldLeft(universalGhosts), + Proof.ElimExistential(pre.heapVars) + ) ++ initPre(pre, "self") ++ List(Proof.GhostElimPost) + + // defer post-condition handling + val post = goal.post.translate + val sapps = post.sigma.apps + val d1 = (ctx: Context) => (Proof.Exists(ctx.postEx) andThen, ctx) + val q0 = sapps.foldLeft(Queue(d1)) { case (q, app) => + val d = (ctx: Context) => { + val app1 = ctx.currentAppAlias(app) + val heap = ctx.unfoldedApp(app1) + (Proof.Exists(List(heap)) andThen, ctx) + } + q.enqueue(d) + } + val d2 = (ctx: Context) => (Proof.Auto, ctx) + val q = q0.enqueue(d2) + + // initialize context with post-condition info + val postEx = post.valueVars.diff(programVars ++ universalGhosts) + val appAliases = sapps.map(a => a -> a).toMap + val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) + + Result(steps, List((q, ctx1))) + + /** Control flow */ + case SuslikProofStep.Branch(cond, _) => + val meta = withNoDeferreds(ctx) + Result(List(Proof.Branch(cond.translate)), List(meta, meta)) + case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => + val pred = ctx.predicates(sapp.pred).subst(fresh_vars.translate).subst(sbst.translate) + val csapp = sapp.translate + val cselectors = selectors.map(_.translate) + val branchSteps = pred.clauses.flatMap { clause => + List( + Proof.ElimExistential(clause.existentials), + Proof.ElimExistential(clause.asn.heapVars) + ) ++ initPre(clause.asn, csapp.uniqueName) ++ List(Proof.Shelve) + } + val steps = List(Proof.Open(cselectors), Proof.OpenPost(csapp)) ++ branchSteps ++ List(Proof.Unshelve) + Result(steps, selectors.map(_ => withNoDeferreds(ctx))) + + /** Program statements */ + case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, _, from, offset)) => + val readStep = Proof.Read(to.translate, from.translate, offset) + val renameStep = Proof.Rename(ghostFrom.translate, ghostTo.translate) + val ctx1 = ctx.withRename(ghostFrom.translate, ghostTo.translate) + Result(List(readStep, renameStep), List(withNoDeferreds(ctx1))) + case SuslikProofStep.Write(Store(to, offset, e)) => + val writeStep = Proof.Write(to.translate, offset, e.translate) + val writePostStep = Proof.WritePost(to.translate, offset) + Result(List(writeStep, writePostStep), List(withNoDeferreds(ctx))) + case SuslikProofStep.Malloc(ghostFrom, ghostTo, Malloc(to, tpe, sz)) => + val allocStep = Proof.Alloc(to.translate, tpe.translate, sz) + val renameStep = Proof.Rename(ghostFrom.translate, ghostTo.translate) + val ctx1 = ctx.withRename(ghostFrom.translate, ghostTo.translate) + Result(List(allocStep, renameStep), List(withNoDeferreds(ctx1))) + case SuslikProofStep.Free(Free(v), size) => + val steps = (0 until size).map(i => Proof.Dealloc(v.translate, i)).toList + Result(steps, List(withNoDeferreds(ctx))) + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => + ??? // TODO + case SuslikProofStep.Call(subst, call) => + ??? // TODO + + // case SuslikProofStep.CheckPost(prePhi, postPhi) => ??? TODO: collect pure entailments + + /** Substitutions and unfoldings */ + case SuslikProofStep.Pick(from, to) => + val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) + Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + case SuslikProofStep.PickArg(from, to) => + val ctx1 = ctx.withRename(from.translate, to.translate) + Result(List(Proof.Rename(from.translate, to.translate)), List(withNoDeferreds(ctx1))) + case SuslikProofStep.PureSynthesis(_, assignments) => + val ctx1 = ctx.withSubst(assignments.translate) + Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + case SuslikProofStep.SubstL(from, to) => + val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) + Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + case SuslikProofStep.SubstR(from, to) => + val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) + Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => + // construct an up-to-date inductive clause with synthesis info + val csapp = app.translate + val cselector = selector.translate + val m = fresh_exist.translate + val clause0 = ctx.predicates(csapp.pred).clauses.find(_.selector == cselector).get + val clause = CInductiveClause(csapp.pred, clause0.idx, cselector, asn.translate, clause0.existentials.map(_.subst(m))) + + // create deferreds to perform unfolding + val deferreds = Queue( + (ctx: Context) => (Proof.UnfoldConstructor(clause.idx), ctx), + (ctx: Context) => { + val csapp1 = ctx.currentAppAlias(csapp) + val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) + (Proof.Exists(valueEx ++ heapEx), ctx) + }, + (ctx: Context) => (Proof.Auto, ctx) + ) + + // update context with unfolding info + val appAliases = ctx.appAliases ++ clause.asn.sigma.apps.map(a => a -> a) + val unfoldings = ctx.unfoldings + (csapp -> clause) + val ctx1 = ctx.copy(appAliases = appAliases, unfoldings = unfoldings) + + Result(List(Proof.Noop), List((deferreds, ctx1))) + + /** Terminals */ + case SuslikProofStep.Inconsistency(label) => + Result(List(Proof.Error), Nil) + case SuslikProofStep.EmpRule(label) => + Result(List(Proof.Emp andThen), Nil) + + /** Ignored */ + case _ => Result(List(Proof.Noop), List(withNoDeferreds(ctx))) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala new file mode 100644 index 000000000..75eab60e1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala @@ -0,0 +1,9 @@ +package org.tygus.suslik.certification.targets.htt.translation + +object TranslatableOps { + implicit class Translatable[A](value: A) { + def translate[B](implicit translator: HTTTranslator[A,B]): B = { + translator.translate(value) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 0cd08734b..466cdefa1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -32,7 +32,8 @@ object Translation { }) val goal = translateGoal(node.goal) val ctx = IR.emptyContext.copy(predicateEnv = cpreds) - val ir = IR.fromRule(SuslikProofStep.of_certtree(node), ctx).propagateContext + val suslikTree = SuslikProofStep.of_certtree(node) + val ir = IR.fromRule(suslikTree, ctx).propagateContext val proof = ProofTranslation.irToProofSteps(ir) val hints = ProofTranslation.irToHints(ir) val progBody = ProgramTranslation.translate(ir) @@ -41,7 +42,7 @@ object Translation { } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { - val cParams = el.params.map(translateParam) :+ (CHeapType, CVar("h")) + val cParams = el.params.map(translateParam) :+ (CVar("h"), CHeapType) val cGamma = gamma.map { case (v, t) => (CVar(v.name), translateType(t))} val cClauses = el.clauses.zipWithIndex.map { case (c, idx) => @@ -49,13 +50,13 @@ object Translation { val asn = translateAsn(c.asn) // Include the clause number so that we can use Coq's `constructor n` tactic - CInductiveClause(el.name, idx + 1, selector, asn, asn.existentials(cParams.map(_._2))) + CInductiveClause(el.name, idx + 1, selector, asn, asn.existentials(cParams.map(_._1))) } CInductivePredicate(el.name, cParams, cClauses, cGamma) } - def translateParam(el: (Var, SSLType)): (HTTType, CVar) = - (translateType(el._2), translateVar(el._1)) + def translateParam(el: (Var, SSLType)): (CVar, HTTType) = + (translateVar(el._1), translateType(el._2)) def translateType(el: SSLType): HTTType = el match { case BoolType => CBoolType From 93c7de88a99892cd8cbe227d39bfbda1a11f69b0 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 13 Feb 2021 11:29:38 +0800 Subject: [PATCH 092/211] HTT: Finish evaluator-based traversal for proofs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Handle call abduction - Emit renames for heap/hyp names affected by variable substitutions - Collect proof hints using a mutable list buffer - Open tactic: do per-branch cleanup using shelves - Open tactic: don’t eagerly discharge inconsistencies; use a separate Inconsistency tactic instead, to maintain alignment with suslik context --- .../targets/htt/logic/Proof.scala | 12 +- .../targets/htt/translation/Context.scala | 39 ++--- .../htt/translation/HTTTranslator.scala | 4 + .../htt/translation/ProofTranslation.scala | 4 +- .../htt/translation/ProofTranslator.scala | 143 ++++++++++++------ 5 files changed, 131 insertions(+), 71 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index f3d56d71e..5c41ddcc6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -20,10 +20,11 @@ object Proof { } case class Then(step: Step) extends Step { + override val isNoop: Boolean = step.isNoop override def pp: String = step.pp } - case class Rename(from: CVar, to: CVar) extends Step { - override def pp: String = s"rename ${from.pp} into ${to.pp}" + case class Rename(from: String, to: String) extends Step { + override def pp: String = s"try rename $from into $to" } case class SubProof(branches: Seq[Step]) extends Step { override val isNoop: Boolean = branches.forall(_.isNoop) @@ -108,11 +109,8 @@ object Proof { s"ssl_dealloc $ptr" } } - case class Open(selectors: Seq[CExpr]) extends Step { - override def pp: String = s"ssl_open (${selectors.head.pp})" - } - case class OpenPost(app: CSApp) extends Step { - override def pp: String = s"ssl_open_post ${app.hypName}" + case class Open(selectors: Seq[CExpr], app: CSApp) extends Step { + override def pp: String = s"ssl_open (${selectors.head.pp}) ${app.hypName}" } case class CallPre(heap: CSFormula) extends Step { override def pp: String = s"ssl_call_pre (${heap.ppHeap})" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala index 78311e1a1..855eab763 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala @@ -1,15 +1,21 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} -import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductiveClause -import org.tygus.suslik.certification.targets.htt.translation.IR.PredicateEnv +import org.tygus.suslik.certification.targets.htt.translation.IR.{CGoal, PredicateEnv} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import scala.collection.mutable.ListBuffer + case class Context(predicates: PredicateEnv = Map.empty, postEx: Seq[CExpr] = Seq.empty, appAliases: Map[CSApp, CSApp] = Map.empty, - unfoldings: Map[CSApp, CInductiveClause] = Map.empty) + unfoldings: Map[CSApp, CInductiveClause] = Map.empty, + callGoal: Option[CGoal] = None, + nextCallId: Int = 0, + hints: ListBuffer[Hint] = ListBuffer.empty, + numSubgoals: Int = 0) extends ClientContext[Proof.Step] { /** @@ -47,27 +53,22 @@ case class Context(predicates: PredicateEnv = Map.empty, /** * Update the current context with new substitutions */ - def withSubst(m: Map[CVar, CExpr]): Context = { + def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): Context = { val postEx1 = postEx.map(_.subst(m)) - val appAliases1 = appAliases.foldLeft(appAliases) { case (appAliases, (app, alias)) => - if (app == alias) { - val app1 = app.subst(m) - if (app != app1) { - appAliases + (app -> app1) + (app1 -> app1) - } else appAliases - } else appAliases - } + val appAliases1 = affectedApps.foldLeft(appAliases) { case (appAliases, (app, alias)) => appAliases + (app -> alias) + (alias -> alias) } val unfoldings1 = unfoldings.map { case (app, clause) => app.subst(m) -> clause.subst(m) } this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) } - /** - * Update the current context with a new variable rename - */ - def withRename(from: CVar, to: CVar): Context = { - val m = Map(from -> to) - withSubst(m) - } + def appsAffectedBySubst(m: Map[CVar, CExpr]): Map[CSApp, CSApp] = + appAliases.foldLeft(Map[CSApp, CSApp]()) { case (affectedApps, (app, alias)) => + if (app == alias) { + val app1 = app.subst(m) + if (app != app1) { + affectedApps + (app -> app1) + } else affectedApps + } else affectedApps + } } object Context { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala index b4639a00b..f59f9c0ce 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -104,4 +104,8 @@ object HTTTranslator { implicit val substVarTranslator: HTTTranslator[Map[Var, Var], Map[CVar, CVar]] = _.map { case (k, v) => k.translate -> v.translate } + + implicit val gammaTranslator: HTTTranslator[Map[Var, SSLType], Map[CVar, HTTType]] = _.map { + case (k, v) => k.translate -> v.translate + } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala index ca3942038..9f0c8ce3e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala @@ -144,7 +144,7 @@ object ProofTranslation { res } } - Proof.Open(selectors.map(_.subst(ctx.substVar))) >>> Proof.OpenPost(app1) >> Proof.SubProof(branchSteps) + Proof.Open(selectors.map(_.subst(ctx.substVar)), app1) >> Proof.SubProof(branchSteps) case IR.Inconsistency(_) => Proof.Error case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) } @@ -194,7 +194,7 @@ object ProofTranslation { case Proof.Alloc(to, tpe, sz) => (step, Set(to)) case Proof.Dealloc(v, offset) => (step, Set(v)) case Proof.Call(args, _) => (step, args.flatMap(_.vars).toSet) - case Proof.Open(selectors) => (step, selectors.flatMap(_.vars).toSet) + case Proof.Open(selectors, _) => (step, selectors.flatMap(_.vars).toSet) case Proof.Branch(cond) => (step, cond.vars.toSet) case _ => (step, Set.empty) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 2f955c8c1..17cd12abf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -1,9 +1,10 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.SuslikProofStep -import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar -import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CVar} +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} +import org.tygus.suslik.certification.targets.htt.translation.IR.CGoal import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.Deferreds import org.tygus.suslik.certification.traversal.Translator @@ -41,25 +42,36 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] pureToCtx ++ spatialToCtx ++ sappToCtx } + def renameAppsStep(affectedApps: Map[CSApp, CSApp]): List[Proof.Rename] = { + for { + (appBefore, appAfter) <- affectedApps.toList + (from, to) <- Map(appBefore.heapName -> appAfter.heapName, appBefore.hypName -> appAfter.hypName) + } yield Proof.Rename(from, to) + } + + def handleSubstitution(m: Map[CVar, CExpr], ctx: Context): Translator.Result[Proof.Step, Context] = { + val affectedApps = ctx.appsAffectedBySubst(m) + val ctx1 = ctx.withSubst(m, affectedApps) + val steps = Proof.Noop :: renameAppsStep(affectedApps) + Result(steps, List(withNoDeferreds(ctx1))) + } + override def translate(value: SuslikProofStep, ctx: Context): Translator.Result[Proof.Step, Context] = { value match { /** Initialization */ case SuslikProofStep.Init(goal) => - val programVars = goal.programVars.map(_.translate) - val universalGhosts = goal.universalGhosts.map(_.translate).toSeq + val cgoal = goal.translate // handle pre-condition immediately - val pre = goal.pre.translate val steps = List( - Proof.StartProof(programVars), + Proof.StartProof(cgoal.programVars), Proof.GhostElimPre, - Proof.MoveToCtxDestructFoldLeft(universalGhosts), - Proof.ElimExistential(pre.heapVars) - ) ++ initPre(pre, "self") ++ List(Proof.GhostElimPost) + Proof.MoveToCtxDestructFoldLeft(cgoal.universalGhosts), + Proof.ElimExistential(cgoal.pre.heapVars) + ) ++ initPre(cgoal.pre, "self") ++ List(Proof.GhostElimPost) // defer post-condition handling - val post = goal.post.translate - val sapps = post.sigma.apps + val sapps = cgoal.post.sigma.apps val d1 = (ctx: Context) => (Proof.Exists(ctx.postEx) andThen, ctx) val q0 = sapps.foldLeft(Queue(d1)) { case (q, app) => val d = (ctx: Context) => { @@ -72,17 +84,25 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] val d2 = (ctx: Context) => (Proof.Auto, ctx) val q = q0.enqueue(d2) - // initialize context with post-condition info - val postEx = post.valueVars.diff(programVars ++ universalGhosts) - val appAliases = sapps.map(a => a -> a).toMap - val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) + // initialize context with post-condition info and predicate hints, if any + val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap + ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) + val ctx1 = ctx.copy(postEx = cgoal.existentials, appAliases = appAliases) Result(steps, List((q, ctx1))) + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => + val pre = f.pre.translate + val post = f.post.translate + val programVars = f.params.map(_._1.translate) + val universalGhosts = pre.valueVars.diff(programVars) + val goal = CGoal(pre, post, gamma.translate, programVars, universalGhosts, f.name) + val ctx1 = ctx.copy(callGoal = Some(goal)) + Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) /** Control flow */ case SuslikProofStep.Branch(cond, _) => - val meta = withNoDeferreds(ctx) - Result(List(Proof.Branch(cond.translate)), List(meta, meta)) + val childContexts = List(ctx.copy(numSubgoals = ctx.numSubgoals + 1), ctx) + Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferreds)) case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => val pred = ctx.predicates(sapp.pred).subst(fresh_vars.translate).subst(sbst.translate) val csapp = sapp.translate @@ -93,50 +113,82 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] Proof.ElimExistential(clause.asn.heapVars) ) ++ initPre(clause.asn, csapp.uniqueName) ++ List(Proof.Shelve) } - val steps = List(Proof.Open(cselectors), Proof.OpenPost(csapp)) ++ branchSteps ++ List(Proof.Unshelve) - Result(steps, selectors.map(_ => withNoDeferreds(ctx))) + val parentGoalShelves = (1 to ctx.numSubgoals).map(_ => Proof.Shelve).toList + val steps = List(Proof.Open(cselectors, csapp)) ++ branchSteps ++ parentGoalShelves ++ List(Proof.Unshelve) + + val numClauses = pred.clauses.length + val childCtxs = pred.clauses.toList.map(clause => { + val newApps = clause.asn.sigma.apps.map(a => a -> a).toMap + val numSubgoals = numClauses - clause.idx + ctx.numSubgoals + ctx.copy(appAliases = ctx.appAliases ++ newApps, numSubgoals = numSubgoals) + }) + Result(steps, childCtxs.map(withNoDeferreds)) /** Program statements */ case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, _, from, offset)) => val readStep = Proof.Read(to.translate, from.translate, offset) - val renameStep = Proof.Rename(ghostFrom.translate, ghostTo.translate) - val ctx1 = ctx.withRename(ghostFrom.translate, ghostTo.translate) - Result(List(readStep, renameStep), List(withNoDeferreds(ctx1))) + val renameStep = Proof.Rename(ghostFrom.name, ghostTo.name) + val m = Map(ghostFrom.translate -> ghostTo.translate) + val affectedApps = ctx.appsAffectedBySubst(m) + val ctx1 = ctx.withSubst(m, affectedApps) + val steps = readStep :: renameStep :: renameAppsStep(affectedApps) + Result(steps, List(withNoDeferreds(ctx1))) case SuslikProofStep.Write(Store(to, offset, e)) => val writeStep = Proof.Write(to.translate, offset, e.translate) val writePostStep = Proof.WritePost(to.translate, offset) - Result(List(writeStep, writePostStep), List(withNoDeferreds(ctx))) + val steps = if (ctx.callGoal.isDefined) List(writeStep) else List(writeStep, writePostStep) + Result(steps, List(withNoDeferreds(ctx))) case SuslikProofStep.Malloc(ghostFrom, ghostTo, Malloc(to, tpe, sz)) => val allocStep = Proof.Alloc(to.translate, tpe.translate, sz) - val renameStep = Proof.Rename(ghostFrom.translate, ghostTo.translate) - val ctx1 = ctx.withRename(ghostFrom.translate, ghostTo.translate) - Result(List(allocStep, renameStep), List(withNoDeferreds(ctx1))) + val renameStep = Proof.Rename(ghostFrom.name, ghostTo.name) + val m = Map(ghostFrom.translate -> ghostTo.translate) + val affectedApps = ctx.appsAffectedBySubst(m) + val ctx1 = ctx.withSubst(m, affectedApps) + val steps = allocStep :: renameStep :: renameAppsStep(affectedApps) + Result(steps, List(withNoDeferreds(ctx1))) case SuslikProofStep.Free(Free(v), size) => val steps = (0 until size).map(i => Proof.Dealloc(v.translate, i)).toList Result(steps, List(withNoDeferreds(ctx))) - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => - ??? // TODO - case SuslikProofStep.Call(subst, call) => - ??? // TODO + case SuslikProofStep.Call(freshToActual, Call(_, args, _)) => + val callId = ctx.nextCallId + val callGoal = ctx.callGoal.get + val goal = callGoal.subst(freshToActual.translate) - // case SuslikProofStep.CheckPost(prePhi, postPhi) => ??? TODO: collect pure entailments + val steps = List( + // Move the part of the heap relevant to the call abduction to the beginning + Proof.CallPre(goal.pre.sigma), + // Provide the necessary existentials so that the precondition of the call goal is met, + // and then execute the call + Proof.Call(args.map(_.translate), goal.universalGhosts.filterNot(_.isCard)), + Proof.Exists(goal.pre.sigma.apps.map(ctx.currentAppAlias).map(ctx.unfoldedApp)) andThen, + Proof.Auto, + Proof.MoveToCtx(List(CVar(s"h_call$callId"))), + // The postcondition of the call abduction becomes the precondition of the companion + Proof.ElimExistential(goal.existentials), + Proof.ElimExistential(goal.post.heapVars), + ) ++ initPre(goal.post, s"call$callId") ++ List(Proof.StoreValid) + + val newApps = goal.post.sigma.apps.map(a => a -> a).toMap + val ctx1 = ctx.copy(callGoal = None, nextCallId = callId + 1, appAliases = ctx.appAliases ++ newApps) + + Result(steps, List(withNoDeferreds(ctx1))) /** Substitutions and unfoldings */ case SuslikProofStep.Pick(from, to) => - val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) - Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + val m = Map(from.translate -> to.translate) + handleSubstitution(m, ctx) case SuslikProofStep.PickArg(from, to) => - val ctx1 = ctx.withRename(from.translate, to.translate) - Result(List(Proof.Rename(from.translate, to.translate)), List(withNoDeferreds(ctx1))) + val m = Map(from.translate -> to.translate) + handleSubstitution(m, ctx) case SuslikProofStep.PureSynthesis(_, assignments) => - val ctx1 = ctx.withSubst(assignments.translate) - Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + val m = assignments.translate + handleSubstitution(m, ctx) case SuslikProofStep.SubstL(from, to) => - val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) - Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + val m = Map(from.translate -> to.translate) + handleSubstitution(m, ctx) case SuslikProofStep.SubstR(from, to) => - val ctx1 = ctx.withSubst(Map(from.translate -> to.translate)) - Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + val m = Map(from.translate -> to.translate) + handleSubstitution(m, ctx) case SuslikProofStep.Close(app, selector, asn, fresh_exist) => // construct an up-to-date inductive clause with synthesis info val csapp = app.translate @@ -147,11 +199,11 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] // create deferreds to perform unfolding val deferreds = Queue( - (ctx: Context) => (Proof.UnfoldConstructor(clause.idx), ctx), + (ctx: Context) => (Proof.UnfoldConstructor(clause.idx) andThen, ctx), (ctx: Context) => { val csapp1 = ctx.currentAppAlias(csapp) val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) - (Proof.Exists(valueEx ++ heapEx), ctx) + (Proof.Exists(valueEx ++ heapEx) andThen, ctx) }, (ctx: Context) => (Proof.Auto, ctx) ) @@ -169,6 +221,11 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] case SuslikProofStep.EmpRule(label) => Result(List(Proof.Emp andThen), Nil) + /** Pure entailments */ + case SuslikProofStep.CheckPost(prePhi, postPhi) => + ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate)) + Result(List(Proof.Noop), List(withNoDeferreds(ctx))) + /** Ignored */ case _ => Result(List(Proof.Noop), List(withNoDeferreds(ctx))) } From c8f01d94cebec869a6e68ca5feb90a34ffe68a74 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 13 Feb 2021 20:24:52 +0800 Subject: [PATCH 093/211] HTT: Switch program generation to evaluator and cleanup --- .../targets/htt/HTTCertificate.scala | 28 +- .../certification/targets/htt/Printer.scala | 44 +++ .../targets/htt/language/Expressions.scala | 3 +- .../targets/htt/language/PrettyPrinting.scala | 1 - .../targets/htt/logic/Proof.scala | 49 ++- .../targets/htt/logic/ProofPrinter.scala | 22 ++ .../targets/htt/logic/Sentences.scala | 3 +- .../targets/htt/program/Program.scala | 17 + .../targets/htt/program/ProgramPrinter.scala | 31 ++ .../targets/htt/program/Statements.scala | 158 ++-------- .../htt/translation/HTTTranslator.scala | 21 +- .../targets/htt/translation/IR.scala | 298 ------------------ .../htt/translation/ProgramContext.scala | 6 + .../htt/translation/ProgramEvaluator.scala | 7 + .../htt/translation/ProgramTranslation.scala | 51 --- .../htt/translation/ProgramTranslator.scala | 41 +++ .../{Context.scala => ProofContext.scala} | 26 +- ...TTEvaluator.scala => ProofEvaluator.scala} | 2 +- .../htt/translation/ProofPrinter.scala | 39 --- .../htt/translation/ProofTranslation.scala | 203 ------------ .../htt/translation/ProofTranslator.scala | 29 +- .../targets/htt/translation/Translation.scala | 109 ++----- 22 files changed, 313 insertions(+), 875 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala rename src/main/scala/org/tygus/suslik/certification/targets/htt/translation/{Context.scala => ProofContext.scala} (78%) rename src/main/scala/org/tygus/suslik/certification/targets/htt/translation/{HTTEvaluator.scala => ProofEvaluator.scala} (73%) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index a5c953897..2742d5661 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -1,29 +1,29 @@ package org.tygus.suslik.certification.targets.htt -import org.tygus.suslik.certification.targets.htt.language.Types.CNatSeqType import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.CFunSpec -import org.tygus.suslik.certification.targets.htt.program.Statements.CProcedure -import org.tygus.suslik.certification.targets.htt.translation.IR.PredicateEnv +import org.tygus.suslik.certification.targets.htt.program.Program +import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof.Step, proc: CProcedure, hints: Seq[Hint] = Seq.empty) extends Certificate { +case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') // Import Coq dependencies - private val prelude = s"""From mathcomp -Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. -From fcsl -Require Import prelude pred pcm unionmap heap. -From HTT -Require Import stmod stsep stlog stlogR. -From SSL -Require Import core. - -""" + private val prelude = + """From mathcomp + |Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. + |From fcsl + |Require Import prelude pred pcm unionmap heap. + |From HTT + |Require Import stmod stsep stlog stlogR. + |From SSL + |Require Import core. + | + |""".stripMargin def pp: String = { val builder = new StringBuilder diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala new file mode 100644 index 000000000..7fdaa31bb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala @@ -0,0 +1,44 @@ +package org.tygus.suslik.certification.targets.htt + +import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} + +import scala.annotation.tailrec + +trait Printer[S <: DestStep] extends ProofTreePrinter[S] { + type Item + + case class Task(k: List[String] => String, todo: List[Item], done: List[String]) + + def initialize(tree: ProofTree[S]): Item + + def process(item: Item): (List[String] => String, List[Item]) + + @tailrec + private def backward(stack: List[Task], res: String): String = stack match { + case Nil => res + case curr :: rest => + curr.todo match { + case Nil => backward(rest, curr.k((res :: curr.done).reverse)) + case next :: todo => + val task = curr.copy(todo = todo, done = res :: curr.done) + forward(next, task :: rest) + } + } + + @tailrec + private def forward(item: Item, stack: List[Task]): String = { + val (k, items) = process(item) + items match { + case Nil => + backward(stack, k(Nil)) + case next :: rest => + val task = Task(k, rest, Nil) + forward(next, task :: stack) + } + } + + override def pp(tree: ProofTree[S]): String = { + forward(initialize(tree), Nil) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index f600aca84..83ea23508 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -1,10 +1,11 @@ package org.tygus.suslik.certification.targets.htt.language import org.tygus.suslik.LanguageUtils.cardinalityPrefix -import org.tygus.suslik.certification.targets.htt.translation.IR.{CSubst, CSubstVar} import org.tygus.suslik.logic.Specifications.selfCardVar object Expressions { + type CSubst = Map[CVar, CExpr] + type CSubstVar = Map[CVar, CVar] sealed abstract class CExpr extends PrettyPrinting { def isTrivial: Boolean = this == CBoolConst(true) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala index f86523a78..364ec9b33 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/PrettyPrinting.scala @@ -2,6 +2,5 @@ package org.tygus.suslik.certification.targets.htt.language trait PrettyPrinting { def pp: String = toString - def ppIndent(depth: Int) = s"${getIndent(depth)}${pp.replaceAll("\n", s"\n${getIndent(depth)}")}" def getIndent(depth: Int): String = " " * depth } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 5c41ddcc6..7aa163c1c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -1,21 +1,43 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} -import org.tygus.suslik.certification.targets.htt.language.Types.HTTType +import org.tygus.suslik.certification.targets.htt.language.CGamma +import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CSubst, CVar} +import org.tygus.suslik.certification.targets.htt.language.Types.{CUnitType, HTTType} +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CFunSpec} +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.language.PrettyPrinting + +case class Proof(body: ProofTree[Proof.Step]) extends PrettyPrinting { + override def pp: String = s"${body.pp}Qed." +} object Proof { + case class Goal(pre: CAssertion, + post: CAssertion, + gamma: CGamma, + programVars: Seq[CVar], + universalGhosts: Seq[CVar], + fname: String) { + val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) + def subst(sub: CSubst): Goal = Goal( + pre.subst(sub), + post.subst(sub), + gamma.map { case (v, t) => v.substVar(sub) -> t }, + programVars.map(_.substVar(sub)), + universalGhosts.map(_.substVar(sub)), + fname + ) + + def toFunspec: CFunSpec = { + val params = programVars.map(v => (v, gamma(v))) + val ghosts = universalGhosts.map(v => (v, gamma(v))) + CFunSpec(fname, CUnitType, params, ghosts, pre, post) + } + } + abstract class Step extends DestStep { val isNoop: Boolean = false - def >>(that: Step): Step = SeqComp(this, that) - def >>>(that: Step): Step = SeqCompAlt(this, that) - def simplify: Step = this match { - case SeqComp(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqComp(s1.simplify, s2.simplify) - case SeqCompAlt(s1, s2) => if (s1.isNoop) s2.simplify else if (s2.isNoop) s1.simplify else SeqCompAlt(s1.simplify, s2.simplify) - case SubProof(branches) => SubProof(branches.filterNot(_.isNoop).map(_.simplify)) - case Solve(steps) => Solve(steps.filterNot(_.isNoop).map(_.simplify)) - case _ => this - } def andThen: Step = Then(this) } @@ -133,9 +155,8 @@ object Proof { case object Emp extends Step { override def pp: String = "ssl_emp" } - case object Error extends Step { - override val isNoop: Boolean = true - override def pp: String = "" + case object Inconsistency extends Step { + override def pp: String = "ssl_inconsistency" } case object Noop extends Step { override val isNoop: Boolean = true diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala new file mode 100644 index 000000000..ddfa155c3 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala @@ -0,0 +1,22 @@ +package org.tygus.suslik.certification.targets.htt.logic + +import org.tygus.suslik.certification.targets.htt.Printer +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} + +import scala.annotation.tailrec + +object ProofPrinter extends Printer[Proof.Step] { + type Item = ProofTree[Proof.Step] + def initialize(tree: ProofTree[Proof.Step]): Item = tree + def process(item: Item): (List[String] => String, List[Item]) = { + val k = (children: List[String]) => { + val res = item.step match { + case s if s.isNoop => "" + case s: Proof.Then => s"${s.pp};\n" + case s => s"${s.pp}.\n" + } + s"$res${children.mkString("")}" + } + (k, item.children) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 072ce06b7..c73743818 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -3,7 +3,6 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.{CFormals, CGamma, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.translation.IR.CSubst object Sentences { case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { @@ -95,7 +94,7 @@ object Sentences { val (names, typs) = formals.unzip val typsStr = typs.map(_.pp).mkString(" * ") val namesStr = names.map(_.pp).mkString(", ") - (typsStr, namesStr) + (namesStr, typsStr) } override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala new file mode 100644 index 000000000..8f763ffd7 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala @@ -0,0 +1,17 @@ +package org.tygus.suslik.certification.targets.htt.program + +import org.tygus.suslik.certification.targets.htt.language.CFormals +import org.tygus.suslik.certification.targets.htt.language.Types.HTTType +import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement +import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.language.PrettyPrinting + +case class Program(name: String, tp: HTTType, formals: CFormals, body: ProofTree[CStatement]) extends PrettyPrinting { + override def pp: String = + s"""Program Definition $name : ${name}_type := + | Fix (fun ($name : ${name}_type) vprogs => + | let: (${formals.map(_._1.pp).mkString(", ")}) := vprogs in + | Do ( + | ${body.pp.replace("\n", "\n ")} + | )).""".stripMargin +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala new file mode 100644 index 000000000..a0953df60 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala @@ -0,0 +1,31 @@ +package org.tygus.suslik.certification.targets.htt.program + +import org.tygus.suslik.certification.targets.htt.Printer +import org.tygus.suslik.certification.targets.htt.program.Statements.{CGuarded, CIf, CStatement, Noop} +import org.tygus.suslik.certification.traversal.ProofTree + +object ProgramPrinter extends Printer[CStatement] { + + case class Item(tree: ProofTree[CStatement], pre: String) + + override def initialize(tree: ProofTree[CStatement]): Item = Item(tree, "") + + override def process(item: Item): (List[String] => String, List[Item]) = { + val pre = item.pre + val tree = item.tree + val k = tree.step match { + case CIf(selectors) => (children: List[String]) => { + val branches = selectors.zip(children).reverse + branches.tail.foldLeft(branches.head._2) { case (eb, (c, tb)) => s"${pre}if ${c.pp}\n${pre}then\n$tb\n${pre}else\n$eb" } + } + case CGuarded(c) => (children: List[String]) => s"${pre}if ${c.pp}\n${pre}then\n${children.head}\n${pre}else\n${children(1)}" + case Noop => (children: List[String]) => children.head + case _ => (children: List[String]) => s"$pre${tree.step.pp}${children.headOption.map("\n" + _).getOrElse("")}" + } + val pre1 = tree.step match { + case _: CIf | _: CGuarded => pre + " " + case _ => pre + } + (k, tree.children.map(Item(_, pre1))) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala index 1d4a235c4..807e71725 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Statements.scala @@ -1,146 +1,54 @@ package org.tygus.suslik.certification.targets.htt.program -import org.tygus.suslik.certification.targets.htt.language.{CFormals, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.traversal.Step.DestStep object Statements { - sealed abstract class CStatement extends PrettyPrinting { - // Expression-collector - def collectE[R <: CExpr](p: CExpr => Boolean): Seq[R] = { + sealed abstract class CStatement extends DestStep - def collector(acc: Seq[R])(st: CStatement): Seq[R] = st match { - case CSkip => acc - case CStore(to, off, e) => - acc ++ to.collect(p) ++ e.collect(p) - case CLoad(_, _, from, off) => - acc ++ from.collect(p) - case CMalloc(_, _, _) => - acc - case CFree(x, _) => - acc ++ x.collect(p) - case CCall(fun, args) => - acc ++ fun.collect(p) ++ args.flatMap(_.collect(p)) - case CSeqComp(s1,s2) => - val acc1 = collector(acc)(s1) - collector(acc1)(s2) - case CIf(cond, tb, eb) => - val acc1 = collector(acc ++ cond.collect(p))(tb) - collector(acc1)(eb) - case CGuarded(cond, b, eb) => - val acc1 = collector(acc ++ cond.collect(p))(b) - collector(acc1)(eb) - case _ => - acc - } - - collector(Seq.empty)(this) - } + case object CError extends CStatement { + override def pp: String = "ret tt" + } - def usedVars: Seq[CVar] = this match { - case _ => collectE(_.isInstanceOf[CVar]) - } + case object CSkip extends CStatement { + override def pp: String = "ret tt" + } - override def pp: String = { - val builder = new StringBuilder + case class CMalloc(to: CVar, tpe: HTTType, sz: Int = 1) extends CStatement { + override def pp: String = s"${to.pp} <-- allocb null $sz;" + } - def build(s: CStatement, depth: Int = 0) : Unit = { - val indent = getIndent(depth) - s match { - case CSkip | CError => - builder.append(s"${indent}ret tt") - case CMalloc(to, _, sz) => - builder.append(s"$indent${to.pp} <-- allocb null $sz") - case CFree(v, sz) => - val deallocs = for (off <- 0 until sz) yield { - if (off > 0) { - s"dealloc (${v.pp} .+ $off)" - } else { - s"dealloc ${v.pp}" - } - } - builder.append(s"$indent${deallocs.mkString(s";;\n$indent")}") - case CStore(to, off, e) => - val t = if (off <= 0) to.pp else s"(${to.pp} .+ $off)" - builder.append(s"$indent$t ::= ${e.pp}") - case CLoad(to, tpe, from, off) => - val f = if (off <= 0) from.pp else s"(${from.pp} .+ $off)" - builder.append(s"$indent${to.pp} <-- @read ${tpe.pp} $f") - case CCall(fun, args) => - val function_call = s"$indent${fun.pp} (${args.map(_.pp).mkString(", ")})" - // TODO: handle return types - builder.append(function_call) - case CSeqComp(s1, s2) => - build(s1, depth) - s1 match { - case _: ReturnsValue => builder.append(";\n") - case _ => builder.append(";;\n") - } - build(s2, depth) - case CIf(cond, tb, eb) => - builder.append(s"${indent}if ${cond.pp}\n") - builder.append(s"${indent}then\n") - build(tb, depth + 1) - builder.append(s"\n${indent}else\n") - build(eb, depth + 1) - case CGuarded(cond, body, els) => - builder.append(s"${indent}if ${cond.pp}\n") - builder.append(s"${indent}then\n") - build(body, depth + 1) - builder.append(s"\n${indent}else\n") - build(els, depth + 1) - case _ => - } + case class CFree(v: CVar, offset: Int) extends CStatement { + override def pp: String = + if (offset > 0) { + s"dealloc (${v.pp} .+ $offset);;" + } else { + s"dealloc ${v.pp};;" } + } - build(this) - builder.toString() + case class CLoad(to: CVar, tpe: HTTType, from: CVar, offset: Int = 0) extends CStatement { + override def pp: String = { + val f = if (offset <= 0) from.pp else s"(${from.pp} .+ $offset)" + s"${to.pp} <-- @read ${tpe.pp} $f;" } + } - def >>(that: CStatement): CStatement = CSeqComp(this, that) - - def simplify: CStatement = this match { - case CIf(_, CSkip, CSkip) => CSkip - case CSeqComp(CGuarded(cond, b, eb), s2) => CGuarded(cond, CSeqComp(b, s2).simplify, eb) - case CSeqComp(s1, CGuarded(cond, b, eb)) => CGuarded(cond, CSeqComp(s1, b).simplify, eb) - case _ => this + case class CStore(to: CVar, offset: Int, e: CExpr) extends CStatement { + override def pp: String = { + val t = if (offset <= 0) to.pp else s"(${to.pp} .+ $offset)" + s"$t ::= ${e.pp};;" } } - trait ReturnsValue - - case object CError extends CStatement - - case object CSkip extends CStatement - - case class CMalloc(to: CVar, tpe: HTTType, sz: Int = 1) extends CStatement with ReturnsValue - - case class CFree(v: CVar, sz: Int = 1) extends CStatement - - case class CLoad(to: CVar, tpe: HTTType, from: CVar, offset: Int = 0) extends CStatement with ReturnsValue - - case class CStore(to: CVar, offset: Int, e: CExpr) extends CStatement - - case class CCall(fun: CVar, args: Seq[CExpr]) extends CStatement - - case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement - - case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement + case class CCall(fun: CVar, args: Seq[CExpr]) extends CStatement { + override def pp: String = s"${fun.pp} (${args.map(_.pp).mkString(", ")});;" + } - case class CGuarded(cond: CExpr, body: CStatement, els: CStatement) extends CStatement + case class CIf(selectors: Seq[CExpr]) extends CStatement - case class CProcedure(name: String, tp: HTTType, formals: CFormals, body: CStatement) extends CStatement { - override def pp: String = { - val vprogs = "vprogs" - val builder = new StringBuilder - builder.append(s"Program Definition $name : ${name}_type :=\n") - builder.append(s"${getIndent(1)}Fix (fun ($name : ${name}_type) $vprogs =>\n") + case class CGuarded(cond: CExpr) extends CStatement - builder.append(s"${getIndent(2)}let: (${formals.map(_._1.pp).mkString(", ")}) := $vprogs in\n") - builder.append(s"${getIndent(2)}Do (\n") - builder.append(body.ppIndent(3)) - builder.append(s"\n${getIndent(2)})).") - builder.toString() - } - } + case object Noop extends CStatement } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala index f59f9c0ce..9cef21f51 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -2,10 +2,10 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ +import org.tygus.suslik.certification.targets.htt.logic.Proof import org.tygus.suslik.certification.targets.htt.logic.Sentences.CAssertion -import org.tygus.suslik.certification.targets.htt.translation.IR.CGoal +import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateExpr, translatePointsTo, translateSApp, translateSFormula} import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language._ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} @@ -58,6 +58,11 @@ object HTTTranslator { implicit val varTranslator: HTTTranslator[Var, CVar] = (value: Var) => CVar(value.name) + implicit val mallocTranslator: HTTTranslator[Statements.Malloc, CMalloc] = stmt => CMalloc(stmt.to.translate, stmt.tpe.translate, stmt.sz) + implicit val loadTranslator: HTTTranslator[Statements.Load, CLoad] = stmt => CLoad(stmt.to.translate, stmt.tpe.translate, stmt.from.translate, stmt.offset) + implicit val storeTranslator: HTTTranslator[Statements.Store, CStore] = stmt => CStore(stmt.to.translate, stmt.offset, stmt.e.translate) + implicit val callTranslator: HTTTranslator[Statements.Call, CCall] = stmt => CCall(stmt.fun.translate, stmt.args.map(_.translate)) + implicit val typeTranslator: HTTTranslator[SSLType, HTTType] = { case BoolType => CBoolType case IntType => CNatType @@ -75,26 +80,26 @@ object HTTTranslator { implicit val pointsToTranslator: HTTTranslator[PointsTo, CPointsTo] = el => CPointsTo(el.loc.translate, el.offset, el.value.translate) implicit val asnTranslator: HTTTranslator[Assertion, CAssertion] = el => { - val conjuncts = el.phi.conjuncts.toSeq.map(c => translateExpr(c).simplify).filterNot(_.isCard) + val conjuncts = el.phi.conjuncts.toSeq.map(c => c.translate.simplify).filterNot(_.isCard) val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) - val sigma = translateSFormula(el.sigma) + val sigma = el.sigma.translate CAssertion(phi, sigma).removeCardConstraints } implicit val sFormulaTranslator: HTTTranslator[SFormula, CSFormula] = el => { - val ptss = el.ptss.map(translatePointsTo) - val apps = el.apps.map(translateSApp) + val ptss = el.ptss.map(_.translate) + val apps = el.apps.map(_.translate) CSFormula("h", apps, ptss) } - implicit val goalTranslator: HTTTranslator[Goal, CGoal] = goal => { + implicit val goalTranslator: HTTTranslator[Goal, Proof.Goal] = goal => { val pre = goal.pre.translate val post = goal.post.translate val gamma = goal.gamma.map(_.translate) val programVars = goal.programVars.map(_.translate) val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) - CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) + Proof.Goal(pre, post, gamma, programVars, universalGhosts, goal.fname) } implicit val substTranslator: HTTTranslator[Map[Var, Expr], Map[CVar, CExpr]] = _.map { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala deleted file mode 100644 index 682851a89..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/IR.scala +++ /dev/null @@ -1,298 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.translation - -import org.tygus.suslik.certification.SuslikProofStep -import org.tygus.suslik.certification.targets.htt.language.CGamma -import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CFunSpec, CInductiveClause, CInductivePredicate} -import org.tygus.suslik.certification.targets.htt.program.Statements._ -import org.tygus.suslik.certification.targets.htt.translation.Translation.{translateAsn, translateExpr, translateParam, translateSApp, translateType, translateVar} -import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.language.Expressions.{Subst, SubstVar, Var} -import org.tygus.suslik.language.Statements -import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} -import org.tygus.suslik.logic.{FunSpec, Gamma} -import org.tygus.suslik.logic.Specifications.Goal - -object IR { - type Unfoldings = Map[CSApp, CInductiveClause] - type SAppNames = Map[CSApp, CSApp] - type CSubst = Map[CVar, CExpr] - type CSubstVar = Map[CVar, CVar] - type PredicateEnv = Map[String, CInductivePredicate] - - case class CGoal(pre: CAssertion, - post: CAssertion, - gamma: CGamma, - programVars: Seq[CVar], - universalGhosts: Seq[CVar], - fname: String) { - val existentials: Seq[CVar] = post.valueVars.diff(programVars ++ universalGhosts) - def subst(sub: CSubst): CGoal = CGoal( - pre.subst(sub), - post.subst(sub), - gamma.map { case (v, t) => v.substVar(sub) -> t }, - programVars.map(_.substVar(sub)), - universalGhosts.map(_.substVar(sub)), - fname - ) - - def toFunspec: CFunSpec = { - val params = programVars.map(v => (v, gamma(v))) - val ghosts = universalGhosts.map(v => (v, gamma(v))) - CFunSpec(fname, CUnitType, params, ghosts, pre, post) - } - } - - case class Context( - unfoldings: Unfoldings, - sappNames: SAppNames, - subst: CSubst, - substVar: CSubstVar, - predicateEnv: PredicateEnv, - nestedContext: Option[NestedContext], - topLevelGoal: Option[CGoal], - ) - - val emptyContext: Context = Context(Map.empty, Map.empty, Map.empty, Map.empty, Map.empty, None, None) - - case class NestedContext(funspec: CFunSpec, call: CCall, freshToActual: CSubst = Map.empty, companionToFresh: CSubstVar) { - def updateSubstitution(sigma: CSubst): NestedContext = { - this.copy(freshToActual = freshToActual.map { case (k, e) => k -> e.subst(sigma) } ++ sigma) - } - - def applySubstitution: NestedContext = { - val newPost = funspec.post.subst(freshToActual) - val newFunSpec = funspec.copy(post = newPost) - val newCall = call.copy(args = call.args.map(_.subst(freshToActual))) - this.copy(funspec = newFunSpec, call = newCall) - } - } - - abstract class Node { - val ctx : Context - val next : Seq[Node] - - private def mapJoin[K, V](m1: Map[K, V], m2: Map[K, V]) : Map[K, V] = { - m1.toSeq.intersect(m2.toSeq).toMap - } - - def propagateContext: IR.Node = this match { - case n:IR.EmpRule => n - case n:IR.Open => - val children = n.next.map(_.propagateContext) - val childCtxs = children.map(_.ctx) - val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) - val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) - val childSAppNames = childCtxs.map(_.sappNames).reduceOption[SAppNames](mapJoin).getOrElse(Map.empty) - n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar, sappNames = childSAppNames)) - case n:IR.Branch => - val children = n.next.map(_.propagateContext) - val childCtxs = children.map(_.ctx) - val childSubst = childCtxs.map(_.subst).reduceOption[CSubst](mapJoin).getOrElse(Map.empty) - val childSubstVar = childCtxs.map(_.substVar).reduceOption[CSubstVar](mapJoin).getOrElse(Map.empty) - val childSAppNames = childCtxs.map(_.sappNames).reduceOption[SAppNames](mapJoin).getOrElse(Map.empty) - n.copy(next = children, ctx = n.ctx.copy(subst = childSubst, substVar = childSubstVar, sappNames = childSAppNames)) - case n:IR.Read => - val next1 = n.next.head.propagateContext - n.copy(ctx = next1.ctx, next = Seq(next1)) - case n:IR.Call => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Free => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Write => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Malloc => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.PureSynthesis => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Close => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.AbduceCall => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Init => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.CheckPost => - val next1 = n.next.head.propagateContext - val ctx1 = next1.ctx.copy(nestedContext = n.ctx.nestedContext) - n.copy(ctx = ctx1, next = Seq(next1)) - case n:IR.Inconsistency => n - } - } - - case class Open(app: CSApp, clauses: Seq[CInductiveClause], selectors: Seq[CExpr], next: Seq[Node], ctx: Context) extends Node - - case class Close(app: CSApp, selector: CExpr, asn: CAssertion, fresh_exist: CSubstVar, next: Seq[Node], ctx: Context) extends Node - - case class Branch(cond: CExpr, next: Seq[Node], ctx: Context) extends Node - - case class PureSynthesis(is_final: Boolean, next: Seq[Node], ctx: Context) extends Node - - case class Read(op: CLoad, next: Seq[Node], ctx: Context) extends Node - - case class Write(stmt: CStore, next: Seq[Node], ctx: Context) extends Node - - case class Free(stmt: CFree, next: Seq[Node], ctx: Context) extends Node - - case class Malloc(stmt: CMalloc, next: Seq[Node], ctx: Context) extends Node - - case class AbduceCall(new_vars: Map[CVar, HTTType], - f_pre: CAssertion, - calleePost: CAssertion, - call: CCall, - freshSub: CSubstVar, - freshToActual: CSubst, - next: Seq[Node], - ctx: Context - ) extends Node - - case class Call(call: CCall, next: Seq[Node], ctx: Context) extends Node - - case class EmpRule(ctx: Context) extends Node { - val next: Seq[Node] = Seq.empty - } - - case class Init(ctx: Context, next: Seq[Node]) extends Node - - case class Inconsistency(ctx: Context) extends Node { - val next: Seq[Node] = Seq.empty - } - - case class CheckPost(prePhi: Set[CExpr], postPhi: Set[CExpr], next: Seq[Node], ctx: Context) extends Node - - private def translateSbst(sbst: Subst) = sbst.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} - private def translateSbstVar(sbst: SubstVar) = sbst.map{ case (k,v) => CVar(k.name) -> CVar(v.name)} - private def updateSAppAliases(m: SAppNames, sub: CSubst): SAppNames = { - val affected = m.map { case (app, alias) => app -> (app.subst(sub) -> alias) }.filter { case (app, (app1, _)) => app != app1 } - m ++ affected.values.toMap - } - - def translateGoal(goal: Goal): CGoal = { - val pre = translateAsn(goal.pre) - val post = translateAsn(goal.post) - val gamma = goal.gamma.map { case (value, lType) => (translateVar(value), translateType(lType)) } - val programVars = goal.programVars.map(translateVar) - val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) - CGoal(pre, post, gamma, programVars, universalGhosts, goal.fname) - } - def translateFunSpec(f: FunSpec, gamma: Gamma): CFunSpec = { - val rType = translateType(f.rType) - val pre = translateAsn(f.pre) - val post = translateAsn(f.post) - val params = f.params.map(translateParam) - val ghosts = pre.valueVars.diff(params.map(_._2)).map(v => (v, translateType(gamma(Var(v.name))))) - CFunSpec(f.name, rType, params, ghosts, pre, post) - } - - def fromRule(node: ProofTree[SuslikProofStep], ctx: IR.Context) : IR.Node = node.step match { - case SuslikProofStep.Init(goal) => - val cgoal = translateGoal(goal) - val sappNames = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap - val ctx1 = ctx.copy(topLevelGoal = Some(cgoal), sappNames = sappNames) - IR.Init(ctx1, Seq(fromRule(node.children.head, ctx1))) - case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => - val csapp = translateSApp(sapp) - val freshCVars = fresh_vars.map{ case (k,v) => CVar(k.name) -> translateExpr(v)} - val csbst = translateSbst(sbst) - val pred = ctx.predicateEnv(sapp.pred).subst(freshCVars).subst(csbst) - val next = node.children.map(n => fromRule(n, ctx)) - val cselectors = selectors.map(translateExpr) - IR.Open(csapp, pred.clauses, cselectors, next, ctx) - case SuslikProofStep.Close(sapp, selector, asn, sbst) => - val csapp = translateSApp(sapp) - val cselector = translateExpr(selector) - val casn = translateAsn(asn) - val csbst = translateSbst(sbst) - val pred = ctx.predicateEnv(csapp.pred) - val cclause = pred.clauses.find(_.selector == cselector).get - val ex = cclause.existentials.map(_.subst(csbst)) - val actualClause = CInductiveClause(csapp.pred, cclause.idx, cselector, casn, ex) - val unfoldings = ctx.unfoldings + (csapp -> actualClause) - val sappNames = ctx.sappNames ++ casn.sigma.apps.map(a => a -> a) - fromRule(node.children.head, ctx.copy(unfoldings = unfoldings, sappNames = sappNames)) - case SuslikProofStep.Branch(cond, _) => - val Seq(ifTrue, ifFalse) = node.children - IR.Branch(translateExpr(cond), Seq(fromRule(ifTrue, ctx), fromRule(ifFalse, ctx)), ctx) - case SuslikProofStep.PureSynthesis(is_final, sbst) => - val csbst = translateSbst(sbst) - val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) - IR.PureSynthesis(is_final, Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.SubstL(from, to) => - val csbst = translateSbst(Map(from -> to)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - fromRule(node.children.head, ctx.copy(subst = ctx.subst ++ csbst, sappNames = sappNames)) - case SuslikProofStep.SubstR(from, to) => - val csbst = translateSbst(Map(from -> to)) - val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) - fromRule(node.children.head, ctx1) - case SuslikProofStep.Pick(from, to) => - val csbst = translateSbst(Map(from -> to)) - val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) - fromRule(node.children.head, ctx1) - case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, tpe, from, offset)) => - val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(Map(ghostFrom -> ghostTo))) - IR.Read(CLoad(CVar(to.name), translateType(tpe), CVar(from.name), offset), Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.Write(Store(to, offset, e)) => - IR.Write(CStore(CVar(to.name), offset, translateExpr(e)), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Free(Statements.Free(v), size) => - IR.Free(CFree(CVar(v.name), size), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Malloc(ghostFrom, ghostTo, Statements.Malloc(to, tpe, sz)) => - val ctx1 = ctx.copy(substVar = ctx.substVar ++ translateSbstVar(Map(ghostFrom -> ghostTo))) - IR.Malloc(CMalloc(CVar(to.name), translateType(tpe), sz), Seq(fromRule(node.children.head, ctx1)), ctx1) - case SuslikProofStep.Call(_, Statements.Call(fun, args, _)) => - val ctx1 = ctx.copy(nestedContext = ctx.nestedContext.map(_.applySubstitution)) - val ctx2 = ctx1.copy(nestedContext = None) - IR.Call(CCall(CVar(fun.name), args.map(translateExpr)), Seq(fromRule(node.children.head, ctx2)), ctx1) - case SuslikProofStep.PickArg(from, to) => - val csbst = translateSbst(Map(from -> to)) - val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) - fromRule(node.children.head, ctx1) - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, companionToFresh, freshToActual, f, gamma) => - val cfunspec = translateFunSpec(f, gamma) - val ccall = CCall(translateVar(call.fun), call.args.map(translateExpr)) - val nestedContext = NestedContext(funspec = cfunspec, call = ccall, freshToActual = translateSbst(freshToActual), companionToFresh = translateSbstVar(companionToFresh)) - val ctx1 = ctx.copy(nestedContext = Some(nestedContext)) - fromRule(node.children.head, ctx1) - case SuslikProofStep.HeapUnifyPointer(from, to) => - val csbst = translateSbst(Map(from -> to)) - val nestedContext = ctx.nestedContext.map(_.updateSubstitution(csbst)) - val sappNames = updateSAppAliases(ctx.sappNames, csbst) - val ctx1 = ctx.copy(subst = ctx.subst ++ csbst, nestedContext = nestedContext, sappNames = sappNames) - fromRule(node.children.head, ctx1) - case SuslikProofStep.EmpRule(_) => IR.EmpRule(ctx) - case SuslikProofStep.CheckPost(prePhi, postPhi) => - IR.CheckPost(prePhi.conjuncts.map(translateExpr), postPhi.conjuncts.map(translateExpr), Seq(fromRule(node.children.head, ctx)), ctx) - case SuslikProofStep.Inconsistency(_) => IR.Inconsistency(ctx) - // unused rules: - case SuslikProofStep.HeapUnify(_) => fromRule(node.children.head, ctx) - case SuslikProofStep.NilNotLval(_) => fromRule(node.children.head, ctx) - case SuslikProofStep.WeakenPre(_) => fromRule(node.children.head, ctx) - case SuslikProofStep.StarPartial(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.PickCard(_, _) => fromRule(node.children.head, ctx) - case SuslikProofStep.FrameUnfold(h_pre, h_post) => fromRule(node.children.head, ctx) - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala new file mode 100644 index 000000000..cc0c868a1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala @@ -0,0 +1,6 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext + +case class ProgramContext() extends ClientContext[CStatement] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala new file mode 100644 index 000000000..f32bb4536 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement +import org.tygus.suslik.certification.traversal.StackEvaluator + +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, CStatement, ProgramContext] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala deleted file mode 100644 index 8a8323270..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslation.scala +++ /dev/null @@ -1,51 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.translation - -import org.tygus.suslik.certification.targets.htt.language.Expressions.CVar -import org.tygus.suslik.certification.targets.htt.program.Statements._ - -object ProgramTranslation { - def translate(ir: IR.Node): CStatement = irToProgramStatements(ir) - - def irToProgramStatements(node: IR.Node): CStatement = { - def visit(node: IR.Node): CStatement = node match { - case IR.EmpRule(_) => CSkip - case IR.Call(stmt, next, _) => stmt >> visit(next.head) - case IR.Free(stmt, next, _) => stmt >> visit(next.head) - case IR.Malloc(stmt, next, _) => stmt >> visit(next.head) - case IR.Read(stmt, next, _) => stmt >> visit(next.head) - case IR.Write(stmt, next, _) => stmt >> visit(next.head) - case IR.Branch(cond, Seq(tb, eb), _) => CIf(cond, visit(tb), visit(eb)) - case IR.Open(app, clauses, selectors, next, ctx) => - val cond_branches = selectors.zip(next.map(visit)).reverse - val ctail = cond_branches.tail - val finalBranch = cond_branches.head._2 - ctail.foldLeft(finalBranch) { case (eb, (c, tb)) => CIf(c, tb, eb) } - case IR.Inconsistency(_) => CSkip - case _ => visit(node.next.head) - } - pruneUnusedReads(visit(node).simplify) - } - - def pruneUnusedReads(stmts: CStatement): CStatement = { - def visit(stmt: CStatement): (CStatement, Set[CVar]) = stmt match { - case CSeqComp(s1, s2) => - val (s2New, s2Used) = visit(s2) - val (s1New, s1Used) = visit(s1) - s1 match { - case CLoad(to, _, _, _) if !s2Used.contains(to) => (s2New, s2Used) - case _ => (s1New >> s2New, s1Used ++ s2Used) - } - case CIf(cond, tb, eb) => - val (tbNew, tbUsed) = visit(tb) - val (ebNew, ebUsed) = visit(eb) - (CIf(cond, tbNew, ebNew), tbUsed ++ ebUsed ++ cond.vars) - case CLoad(to, tpe, from, offset) => (stmt, Set(to, from)) - case CStore(to, offset, e) => (stmt, Set(to) ++ e.vars.toSet) - case CMalloc(to, tpe, sz) => (stmt, Set(to)) - case CFree(v, sz) => (stmt, Set(v)) - case CCall(fun, args) => (stmt, args.flatMap(_.vars).toSet) - case _ => (stmt, Set.empty) - } - visit(stmts)._1 - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala new file mode 100644 index 000000000..c0684f488 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -0,0 +1,41 @@ +package org.tygus.suslik.certification.targets.htt.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.htt.program.Statements.{CFree, CGuarded, CIf, CSkip, CStatement, Noop} +import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.traversal.Evaluator.Deferreds +import org.tygus.suslik.certification.traversal.Translator +import org.tygus.suslik.certification.traversal.Translator.Result + +import scala.collection.immutable.Queue + +object ProgramTranslator extends Translator[SuslikProofStep, CStatement, ProgramContext] { + private val noDeferreds: Deferreds[CStatement, ProgramContext] = Queue.empty + + override def translate(value: SuslikProofStep, ctx: ProgramContext): Translator.Result[CStatement, ProgramContext] = { + val meta = (noDeferreds, ctx) + value match { + case SuslikProofStep.Open(_, _, _, selectors) => + val stmt = CIf(selectors.map(_.translate)) + val metas = selectors.map(_ => meta) + Result(List(stmt), metas) + case SuslikProofStep.Branch(cond, _) => + val stmt = CGuarded(cond.translate) + Result(List(stmt), List(meta, meta)) + case _:SuslikProofStep.EmpRule | _:SuslikProofStep.Inconsistency => + Result(List(CSkip), Nil) + case _ => + val stmts = value match { + case SuslikProofStep.Write(stmt) => List(stmt.translate) + case SuslikProofStep.Read(_, _, stmt) => List(stmt.translate) + case SuslikProofStep.Malloc(_, _, stmt) => List(stmt.translate) + case SuslikProofStep.Free(stmt, sz) => + val v = stmt.v.translate + (0 until sz).map(i => CFree(v, i)).toList + case SuslikProofStep.Call(_, stmt) => List(stmt.translate) + case _ => List(Noop) + } + Result(stmts, List(meta)) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala similarity index 78% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index 855eab763..580c3a4da 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Context.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -2,20 +2,20 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductiveClause -import org.tygus.suslik.certification.targets.htt.translation.IR.{CGoal, PredicateEnv} +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CInductiveClause, CInductivePredicate} +import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import scala.collection.mutable.ListBuffer -case class Context(predicates: PredicateEnv = Map.empty, - postEx: Seq[CExpr] = Seq.empty, - appAliases: Map[CSApp, CSApp] = Map.empty, - unfoldings: Map[CSApp, CInductiveClause] = Map.empty, - callGoal: Option[CGoal] = None, - nextCallId: Int = 0, - hints: ListBuffer[Hint] = ListBuffer.empty, - numSubgoals: Int = 0) +case class ProofContext(predicates: PredicateEnv = Map.empty, + postEx: Seq[CExpr] = Seq.empty, + appAliases: Map[CSApp, CSApp] = Map.empty, + unfoldings: Map[CSApp, CInductiveClause] = Map.empty, + callGoal: Option[Proof.Goal] = None, + nextCallId: Int = 0, + hints: ListBuffer[Hint], + numSubgoals: Int = 0) extends ClientContext[Proof.Step] { /** @@ -53,7 +53,7 @@ case class Context(predicates: PredicateEnv = Map.empty, /** * Update the current context with new substitutions */ - def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): Context = { + def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): ProofContext = { val postEx1 = postEx.map(_.subst(m)) val appAliases1 = affectedApps.foldLeft(appAliases) { case (appAliases, (app, alias)) => appAliases + (app -> alias) + (alias -> alias) } val unfoldings1 = unfoldings.map { case (app, clause) => app.subst(m) -> clause.subst(m) } @@ -71,6 +71,6 @@ case class Context(predicates: PredicateEnv = Map.empty, } } -object Context { - val empty: Context = Context() +object ProofContext { + type PredicateEnv = Map[String, CInductivePredicate] } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala similarity index 73% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala index b46654719..ffcd61bae 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala @@ -4,4 +4,4 @@ import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.htt.logic.Proof import org.tygus.suslik.certification.traversal.StackEvaluator -object HTTEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, Context] +object ProofEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, ProofContext] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala deleted file mode 100644 index 4243277c0..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofPrinter.scala +++ /dev/null @@ -1,39 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.translation - -import org.tygus.suslik.certification.targets.htt.logic.Proof -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} - -import scala.annotation.tailrec - -object ProofPrinter extends ProofTreePrinter[Proof.Step] { - override def pp(tree: ProofTree[Proof.Step]): String = { - case class Item(res: String, todo: List[ProofTree[Proof.Step]]) - @tailrec - def backward(k: List[Item], res: String): String = k match { - case Nil => res - case curr :: rest => - curr.todo match { - case Nil => backward(rest, curr.res ++ res) - case next :: todo => - val item = Item(curr.res ++ res, todo) - forward(next, item :: rest) - } - } - @tailrec - def forward(tree: ProofTree[Proof.Step], k: List[Item]): String = { - val res = tree.step match { - case s if s.isNoop => "" - case s:Proof.Then => s"${s.pp};\n" - case s => s"${s.pp}.\n" - } - tree.children match { - case Nil => - backward(k, res) - case next :: rest => - val item = Item(res, rest) - forward(next, item :: k) - } - } - forward(tree, Nil) - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala deleted file mode 100644 index 9f0c8ce3e..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslation.scala +++ /dev/null @@ -1,203 +0,0 @@ -package org.tygus.suslik.certification.targets.htt.translation - -import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences._ -import org.tygus.suslik.certification.targets.htt.program.Statements._ - -object ProofTranslation { - def irToProofSteps(node: IR.Node) : Proof.Step = { - def elimExistentials(ex: Seq[CExpr], nested: Boolean = false): Proof.Step = { - if (ex.nonEmpty) { - if (nested) Proof.MoveToCtxDestructFoldLeft(ex) else Proof.ElimExistential(ex) - } else Proof.Noop - } - - def initPre(asn: CAssertion, uniqueName: String): Proof.Step = { - val phi = asn.phi - val sigma = asn.sigma - - // move pure part to context - val pureToCtx = if (!phi.isTrivial) { - val hyps = if (phi.conjuncts.isEmpty) Seq(CVar(s"phi_$uniqueName")) else phi.conjuncts.indices.map(i => CVar(s"phi_$uniqueName$i")) - Proof.MoveToCtxDestruct(hyps) - } else Proof.Noop - - // move spatial part to context, and then substitute where appropriate - val spatialToCtx = Proof.MoveToCtxDestruct(Seq(CVar(s"sigma_$uniqueName"))) >> Proof.Sbst(Seq(CVar(s"h_$uniqueName"))) - - // move predicate apps to context, if any - val sappToCtx = if (sigma.apps.nonEmpty) { - val appNames = sigma.apps.map(app => CVar(app.hypName)) - Proof.MoveToCtxDestructFoldRight(appNames) - } else Proof.Noop - - pureToCtx >> spatialToCtx >> sappToCtx - } - - def solvePost(asn: CAssertion, ex: Seq[CExpr], unfoldings: IR.Unfoldings): Proof.Step = { - val valueExElim = Proof.Exists(ex) - - /** - * Existentials for the current assertion need to be provided eagerly, so look ahead to find out - * the maximally unfolded, "flattened" form of the heap - * - * @param app the predicate app to flatten - * @return the points-to's and pred apps in the unfolded heap; any pred apps that remain have already been - * established as facts earlier on, so there is no need to unfold further - */ - def toUnfoldedHeap(app: CSApp): (Seq[CPointsTo], Seq[CSApp]) = unfoldings.get(app) match { - case Some(a) => - val sigma = a.asn.sigma - val (ptss, apps) = sigma.apps.map(toUnfoldedHeap).unzip - (sigma.ptss ++ ptss.flatten, apps.flatten) - case None => - (Seq.empty, Seq(app)) - } - - // Eliminate heap existentials (one for each predicate application in the assertion) - val apps = asn.sigma.apps - val heapExElim = for (app <- apps) yield { - val (unfoldedPtss, unfoldedApps) = toUnfoldedHeap(app) - Proof.Exists(Seq(CSFormula("", unfoldedApps, unfoldedPtss))) - } - - // For each predicate application, unfold the correct constructor and recursively solve its expanded form - val rest = for { - app <- apps - // If `app` isn't found in the unfoldings, its truth was already established earlier on - c <- unfoldings.get(app) - } yield Proof.UnfoldConstructor(c.idx) >>> solvePost(c.asn, c.existentials, unfoldings) - - valueExElim >>> heapExElim.foldLeft[Proof.Step](Proof.Noop)(_ >>> _) >>> Proof.Auto >>> Proof.Solve(rest) - } - - var currCallId = 0 - def freshCallId: String = { currCallId += 1; s"call$currCallId" } - - def visit(node: IR.Node): Proof.Step = node match { - case IR.Init(ctx, next) => - val goal = ctx.topLevelGoal.get.subst(ctx.substVar) - Proof.StartProof(goal.programVars) >> - // Pull out any precondition ghosts and move precondition heap to the context - Proof.GhostElimPre >> - Proof.MoveToCtxDestructFoldLeft(goal.universalGhosts) >> - Proof.ElimExistential(goal.pre.heapVars) >> - initPre(goal.pre, "self") >> - // store heap validity assertions - Proof.GhostElimPost >> - visit(next.head) - case IR.EmpRule(ctx) => - val topLevelGoal = ctx.topLevelGoal.get - val post = topLevelGoal.post.subst(ctx.subst).subst(ctx.substVar) - val existentials = topLevelGoal.existentials.map(_.substVar(ctx.substVar).subst(ctx.subst).subst(ctx.substVar)) - val unfoldings = ctx.unfoldings.map { case (app, e) => app.subst(ctx.substVar).subst(ctx.subst) -> e.subst(ctx.substVar).subst(ctx.subst) } - Proof.Emp >>> solvePost(post, existentials, unfoldings) - case IR.AbduceCall(new_vars, f_pre, calleePost, call, freshSub, freshToActual, next, ctx) => - visit(next.head) - case IR.Call(CCall(_, args), next, ctx) => - val callId = freshCallId - val nestedContext = ctx.nestedContext.get - val f = ctx.nestedContext.get.funspec.subst(ctx.substVar).subst(nestedContext.freshToActual).subst(ctx.substVar) - // Move the part of the heap relevant to the call abduction to the beginning - Proof.CallPre(f.pre.sigma) >> - // Provide the necessary existentials so that the precondition of the call goal is met, - // and then execute the call - Proof.Call(args, f.ghostParamVars.filterNot(_.isCard)) >> - solvePost(f.pre, Seq.empty, Map.empty) >> - Proof.MoveToCtx(Seq(CVar(s"h_$callId"))) >> - // The postcondition of the call abduction becomes the precondition of the companion - elimExistentials(f.existentials) >> - elimExistentials(f.post.subst(ctx.subst).heapVars) >> - initPre(f.post, callId) >> - // Store validity hypotheses in context - Proof.StoreValid >> - visit(next.head) - case IR.Free(CFree(v, sz), next, _) => - (0 until sz).map(o => Proof.Dealloc(v, o)).reduceLeft[Proof.Step](_ >> _) >> visit(next.head) - case IR.Read(CLoad(to, _, from, offset), next, ctx) => - Proof.Read(to.substVar(ctx.substVar), from.substVar(ctx.substVar), offset) >> visit(next.head) - case IR.Write(CStore(to, offset, e), next, ctx) => - val step = Proof.Write(to.substVar(ctx.substVar), offset, e) - // SSL's `Write` rule does an implicit frame under normal circumstances, but not during a call synthesis - val step1 = if (ctx.nestedContext.isDefined) step else step >> Proof.WritePost(to, offset) - step1 >> visit(next.head) - case IR.Malloc(CMalloc(to, tpe, sz), next, ctx) => - Proof.Alloc(to.substVar(ctx.substVar), tpe, sz) >> visit(next.head) - case IR.PureSynthesis(_, next, _) => - visit(next.head) - case IR.Close(_, _, _, _, next, _) => - visit(next.head) - case IR.Branch(cond, next, ctx) => - Proof.Branch(cond.subst(ctx.substVar)) >> Proof.SubProof(next.map(visit)) - case IR.Open(app, clauses, selectors, next, ctx) => - val app1 = ctx.sappNames.getOrElse(app, app) - val branchSteps = next.zip(clauses).map { case (n, c) => - val c1 = c.subst(n.ctx.substVar) - val res = visit(n) - if (res == Proof.Error) { - Proof.Noop // Don't emit branch prelude if inconsistent - } else { - elimExistentials(c1.existentials) >> - elimExistentials(c1.asn.heapVars) >> - initPre(c1.asn, app1.uniqueName) >> - res - } - } - Proof.Open(selectors.map(_.subst(ctx.substVar)), app1) >> Proof.SubProof(branchSteps) - case IR.Inconsistency(_) => Proof.Error - case IR.CheckPost(prePhi, postPhi, next, _) => visit(next.head) - } - - pruneUnusedReads(visit(node).simplify) >> Proof.EndProof - } - - def irToHints(node: IR.Node) : Seq[Hint] = { - def visit(node: IR.Node, hints: Seq[Hint]) : Seq[Hint] = node match { - case IR.Init(ctx, next) => - visit(next.head, ctx.predicateEnv.values.map(p => Hint.PredicateSetTransitive(p)).toSeq) - case IR.CheckPost(prePhi, postPhi, next, _) => - visit(next.head, hints :+ Hint.PureEntailment(prePhi, postPhi)) - case _:IR.Inconsistency | _:IR.EmpRule => - hints - case _:IR.Open | _:IR.Branch => - node.next.foldLeft(hints){ case (hints, next) => visit(next, hints) } - case _ => - visit(node.next.head, hints) - } - - visit(node, Seq.empty).filter(_.numHypotheses > 0) - } - - def pruneUnusedReads(step: Proof.Step): Proof.Step = { - def visit(step: Proof.Step): (Proof.Step, Set[CVar]) = step match { - case Proof.SeqComp(s1, s2) => - val (s2New, s2Used) = visit(s2) - val (s1New, s1Used) = visit(s1) - s1 match { - case Proof.Read(to, _, _) if !s2Used.contains(to) => (s2New, s2Used) - case _ => (s1New >> s2New, s1Used ++ s2Used) - } - case Proof.SeqCompAlt(s1, s2) => - val (s2New, s2Used) = visit(s2) - val (s1New, s1Used) = visit(s1) - s1 match { - case Proof.Read(to, _, _) if !s2Used.contains(to) => (s2New, s2Used) - case _ => (s1New >>> s2New, s1Used ++ s2Used) - } - case Proof.SubProof(branches) => - val (branchesNew, branchesUsed) = branches.map(visit).unzip - (Proof.SubProof(branchesNew), branchesUsed.reduce(_ ++ _)) - case Proof.Read(to, from, offset) => (step, Set(to, from)) - case Proof.Write(to, offset, e) => (step, Set(to) ++ e.vars.toSet) - case Proof.WritePost(to, offset) => (step, Set(to)) - case Proof.Alloc(to, tpe, sz) => (step, Set(to)) - case Proof.Dealloc(v, offset) => (step, Set(v)) - case Proof.Call(args, _) => (step, args.flatMap(_.vars).toSet) - case Proof.Open(selectors, _) => (step, selectors.flatMap(_.vars).toSet) - case Proof.Branch(cond) => (step, cond.vars.toSet) - case _ => (step, Set.empty) - } - visit(step)._1 - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 17cd12abf..580b30e7e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -4,18 +4,17 @@ import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CVar} import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} -import org.tygus.suslik.certification.targets.htt.translation.IR.CGoal import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.Deferreds import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result -import org.tygus.suslik.language.Statements._ +import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} import scala.collection.immutable.Queue -object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] { - private val noDeferreds: Deferreds[Proof.Step, Context] = Queue.empty - private def withNoDeferreds(ctx: Context): (Deferreds[Proof.Step, Context], Context) = (noDeferreds, ctx) +object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofContext] { + private val noDeferreds: Deferreds[Proof.Step, ProofContext] = Queue.empty + private def withNoDeferreds(ctx: ProofContext): (Deferreds[Proof.Step, ProofContext], ProofContext) = (noDeferreds, ctx) private def initPre(asn: CAssertion, uniqueName: String): List[Proof.Step] = { val phi = asn.phi @@ -49,14 +48,14 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] } yield Proof.Rename(from, to) } - def handleSubstitution(m: Map[CVar, CExpr], ctx: Context): Translator.Result[Proof.Step, Context] = { + def handleSubstitution(m: Map[CVar, CExpr], ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) val steps = Proof.Noop :: renameAppsStep(affectedApps) Result(steps, List(withNoDeferreds(ctx1))) } - override def translate(value: SuslikProofStep, ctx: Context): Translator.Result[Proof.Step, Context] = { + override def translate(value: SuslikProofStep, ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { value match { /** Initialization */ case SuslikProofStep.Init(goal) => @@ -72,16 +71,16 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] // defer post-condition handling val sapps = cgoal.post.sigma.apps - val d1 = (ctx: Context) => (Proof.Exists(ctx.postEx) andThen, ctx) + val d1 = (ctx: ProofContext) => (Proof.Exists(ctx.postEx) andThen, ctx) val q0 = sapps.foldLeft(Queue(d1)) { case (q, app) => - val d = (ctx: Context) => { + val d = (ctx: ProofContext) => { val app1 = ctx.currentAppAlias(app) val heap = ctx.unfoldedApp(app1) (Proof.Exists(List(heap)) andThen, ctx) } q.enqueue(d) } - val d2 = (ctx: Context) => (Proof.Auto, ctx) + val d2 = (ctx: ProofContext) => (Proof.Auto, ctx) val q = q0.enqueue(d2) // initialize context with post-condition info and predicate hints, if any @@ -95,7 +94,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] val post = f.post.translate val programVars = f.params.map(_._1.translate) val universalGhosts = pre.valueVars.diff(programVars) - val goal = CGoal(pre, post, gamma.translate, programVars, universalGhosts, f.name) + val goal = Proof.Goal(pre, post, gamma.translate, programVars, universalGhosts, f.name) val ctx1 = ctx.copy(callGoal = Some(goal)) Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) @@ -199,13 +198,13 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] // create deferreds to perform unfolding val deferreds = Queue( - (ctx: Context) => (Proof.UnfoldConstructor(clause.idx) andThen, ctx), - (ctx: Context) => { + (ctx: ProofContext) => (Proof.UnfoldConstructor(clause.idx) andThen, ctx), + (ctx: ProofContext) => { val csapp1 = ctx.currentAppAlias(csapp) val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) (Proof.Exists(valueEx ++ heapEx) andThen, ctx) }, - (ctx: Context) => (Proof.Auto, ctx) + (ctx: ProofContext) => (Proof.Auto, ctx) ) // update context with unfolding info @@ -217,7 +216,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, Context] /** Terminals */ case SuslikProofStep.Inconsistency(label) => - Result(List(Proof.Error), Nil) + Result(List(Proof.Inconsistency), Nil) case SuslikProofStep.EmpRule(label) => Result(List(Proof.Emp andThen), Nil) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 466cdefa1..5925b8b7a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -5,14 +5,14 @@ import org.tygus.suslik.certification.{CertTree, SuslikProofStep} import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.logic.Proof +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof, ProofPrinter} import org.tygus.suslik.certification.targets.htt.logic.Sentences._ -import org.tygus.suslik.certification.targets.htt.translation.IR.translateGoal -import org.tygus.suslik.language.Expressions._ +import org.tygus.suslik.certification.targets.htt.program.{Program, ProgramPrinter} +import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.language._ -import org.tygus.suslik.logic.Specifications.Assertion -import org.tygus.suslik.logic._ +import org.tygus.suslik.logic.{Environment, Gamma, InductivePredicate} + +import scala.collection.mutable.ListBuffer object Translation { case class TranslationException(msg: String) extends Exception(msg) @@ -30,101 +30,30 @@ object Translation { val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) translateInductivePredicate(p1, gamma) }) - val goal = translateGoal(node.goal) - val ctx = IR.emptyContext.copy(predicateEnv = cpreds) + val goal = node.goal.translate val suslikTree = SuslikProofStep.of_certtree(node) - val ir = IR.fromRule(suslikTree, ctx).propagateContext - val proof = ProofTranslation.irToProofSteps(ir) - val hints = ProofTranslation.irToHints(ir) - val progBody = ProgramTranslation.translate(ir) - val cproc = CProcedure(proc.name, translateType(proc.tp), proc.formals.map(translateParam), progBody) + + val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint]) + val proofBody = ProofEvaluator.run(suslikTree)(ProofTranslator, ProofPrinter, ctx) + val proof = Proof(proofBody) + val hints = ctx.hints.filter(_.numHypotheses > 0) + val progBody = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramPrinter, ProgramContext()) + val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) + HTTCertificate(cproc.name, cpreds, goal.toFunspec, proof, cproc, hints) } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { - val cParams = el.params.map(translateParam) :+ (CVar("h"), CHeapType) - val cGamma = gamma.map { case (v, t) => (CVar(v.name), translateType(t))} + val cParams = el.params.map(_.translate) :+ (CVar("h"), CHeapType) + val cGamma = gamma.translate val cClauses = el.clauses.zipWithIndex.map { case (c, idx) => - val selector = translateExpr(c.selector) - val asn = translateAsn(c.asn) + val selector = c.selector.translate + val asn = c.asn.translate // Include the clause number so that we can use Coq's `constructor n` tactic CInductiveClause(el.name, idx + 1, selector, asn, asn.existentials(cParams.map(_._1))) } CInductivePredicate(el.name, cParams, cClauses, cGamma) } - - def translateParam(el: (Var, SSLType)): (CVar, HTTType) = - (translateVar(el._1), translateType(el._2)) - - def translateType(el: SSLType): HTTType = el match { - case BoolType => CBoolType - case IntType => CNatType - case LocType => CPtrType - case IntSetType => CNatSeqType - case VoidType => CUnitType - case CardType => CCardType - } - - def translateExpr(el: Expr): CExpr = el match { - case Var(name) => CVar(name) - case BoolConst(value) => CBoolConst(value) - case LocConst(value) => CPtrConst(value) - case IntConst(value) => CNatConst(value) - case el@UnaryExpr(_, _) => translateUnaryExpr(el) - case el@BinaryExpr(_, _, _) => translateBinaryExpr(el) - case SetLiteral(elems) => CSetLiteral(elems.map(e => translateExpr(e))) - case IfThenElse(c, t, e) => CIfThenElse(translateExpr(c), translateExpr(t), translateExpr(e)) - case _ => throw TranslationException("Operation not supported") - } - - def translateVar(el: Var): CVar = CVar(el.name) - - def translateSApp(el: SApp): CSApp = CSApp(el.pred, el.args.map(translateExpr), translateExpr(el.card)) - def translatePointsTo(el: PointsTo): CPointsTo = CPointsTo(translateExpr(el.loc), el.offset, translateExpr(el.value)) - - def translateAsn(el: Assertion): CAssertion = { - val conjuncts = el.phi.conjuncts.toSeq.map(c => translateExpr(c).simplify).filterNot(_.isCard) - val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) - val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) - val sigma = translateSFormula(el.sigma) - CAssertion(phi, sigma).removeCardConstraints - } - - def translateSFormula(el: SFormula): CSFormula = { - val ptss = el.ptss.map(translatePointsTo) - val apps = el.apps.map(translateSApp) - CSFormula("h", apps, ptss) - } - - private def translateUnOp(op: UnOp): CUnOp = op match { - case OpNot => COpNot - case OpUnaryMinus => COpUnaryMinus - } - - private def translateBinOp(op: BinOp): CBinOp = op match { - case OpImplication => COpImplication - case OpPlus => COpPlus - case OpMinus => COpMinus - case OpMultiply => COpMultiply - case OpEq => COpEq - case OpBoolEq => COpBoolEq - case OpLeq => COpLeq - case OpLt => COpLt - case OpAnd => COpAnd - case OpOr => COpOr - case OpUnion => COpUnion - case OpDiff => COpDiff - case OpIn => COpIn - case OpSetEq => COpSetEq - case OpSubset => COpSubset - case OpIntersect => COpIntersect - } - - private def translateUnaryExpr(el: UnaryExpr) : CExpr = - CUnaryExpr(translateUnOp(el.op), translateExpr(el.arg)) - - private def translateBinaryExpr(el: BinaryExpr) : CExpr = - CBinaryExpr(translateBinOp(el.op), translateExpr(el.left), translateExpr(el.right)) } From f126b8ddb9cf2bb42668242997737a337256b2df Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 16 Feb 2021 10:46:17 +0800 Subject: [PATCH 094/211] Working on evaluator implementation --- .../certification/targets/vst/Debug.scala | 206 ------------------ .../certification/targets/vst/State.scala | 63 ------ .../certification/targets/vst/VST.scala | 49 ----- .../vst/translation/CTranslation.scala | 17 ++ .../translation/ProofSpecTranslation.scala | 1 - .../targets/vst/translation/Translation.scala | 42 ++-- .../vst/translation/VSTTranslator.scala | 89 ++++++++ 7 files changed, 132 insertions(+), 335 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala deleted file mode 100644 index 16effc1a5..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/Debug.scala +++ /dev/null @@ -1,206 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import java.io.{BufferedOutputStream, BufferedReader, ByteArrayInputStream, ByteArrayOutputStream, File, FileOutputStream, FileWriter, InputStreamReader, StringWriter} - -import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.synthesis.StmtProducer._ -import scalaz.Scalaz.{ToTraverseOps, ToTraverseOpsUnapply} - -import scala.io._ -import scala.sys.process._ -import scala.sys.process.ProcessLogger - -object Debug { - - - def dotbin(format: String)(dot: String): Array[Byte] = { - val process = "dot -Gdpi=300 -T" + format - val bos = new ByteArrayOutputStream() - val exitCode = process #< new ByteArrayInputStream(dot.getBytes) #> bos !< ProcessLogger(s => ()) - if (exitCode == 0) { - bos.toByteArray() - } - else { - throw new RuntimeException("Nonzero exit value (" + exitCode + ") for '" + process + "' with: " + dot) - } - } - - def stmt_producer_node_to_text(stmtProducer: StmtProducer): String = stmtProducer match { - case ChainedProducer(p1, p2) => s"ChainedProducer [${stmtProducer.arity}]" - case PartiallyAppliedProducer(p, s) => s"PartiallyAppliedProducer [${stmtProducer.arity}] (${s.toString()})" - case IdProducer => s"IdProducer[${stmtProducer.arity}]" - case ConstProducer(s) => s"ConstProducer[${stmtProducer.arity}](${s.pp})" - case PrependProducer(s) => s"PrependProducer[${stmtProducer.arity}](${s.pp})" - case PrependFromSketchProducer(s) => s"PrependFromSketchProducer[${stmtProducer.arity}](${s.pp})" - case AppendProducer(s) => s"AppendProducer[${stmtProducer.arity}](${s.pp})" - case SeqCompProducer => "SeqCompProducer[${stmtProducer.arity}]" - case ExtractHelper(goal) => s"ExtractHelper[${stmtProducer.arity}](${""})" - case HandleGuard(goal) => s"HandleGuard[${stmtProducer.arity}](${""})" - case BranchProducer(_, _, _, selectors) => s"BranchProducer[${stmtProducer.arity}] {\n${selectors.map(_.pp).mkString("\n")}\n}" - case GuardedProducer(cond, goal) => - s"GuardedProducer[${stmtProducer.arity}] {\n cond=${cond.pp}\n goal=${goal.pp}\n}" - case SubstMapProducer(subst) => s"SubstMapProducer[${stmtProducer.arity}](${subst.toString})" - case SubstProducer(from, to) => s"SubstProducer[${stmtProducer.arity}](${from.pp} -> ${to.pp})" - case SubstVarProducer(from, to) => s"SubstVarProducer[${stmtProducer.arity}](${from.pp} -> ${to.pp})" - case UnfoldProducer(app, selector, asn, substEx) => - s"UnfoldProducer[${stmtProducer.arity}] {\n app=${app.pp}\n selector=${selector.pp}\n asn=${asn.toString}\n substEx=${substEx.toString}\n}" - } - - - def visualize_proof_tree(stmtProducer: StmtProducer): Unit = { - var nodes: List[(String, String)] = Nil - var edges: List[(String, String)] = Nil - var same_ranks: List[List[String]] = Nil - var count = 0 - - def next_id(x: Unit): String = { - count = count + 1 - s"node_${count}" - } - - def process_producer(root_node: Option[String])(stmtProducer: StmtProducer): String = { - val node_id = next_id() - val node_text = stmt_producer_node_to_text(stmtProducer) - nodes = (node_id, node_text) :: nodes - root_node match { - case Some(value) => edges = (value, node_id) :: edges - case None => () - } - stmtProducer match { - case ChainedProducer(p1, p2) => - val child1 = process_producer(Some(node_id))(p1) - val child2 = process_producer(Some(node_id))(p2) - same_ranks = List(child1, child2) :: same_ranks - case PartiallyAppliedProducer(p, s) => - process_producer(Some(node_id))(p) - case _ => () - } - node_id - } - - process_producer(None)(stmtProducer) - - val graph_dot_spec = - s""" - |digraph proof_tree { - | size="10,10"; - ${nodes.map({ case (node_id, node_text) => s" | ${node_id} [label=${"\"" ++ node_text ++ "\""}]" }).mkString("\n")} - | - ${edges.map({ case (fro_n, to_n) => s" | ${fro_n} -> ${to_n}" }).mkString("\n")} - | - ${same_ranks.map((ls => s" | { rank=same; ${ls.mkString(" ")} }")).mkString("\n")} - |} - |""".stripMargin - - visualize_dot_viz_spec(graph_dot_spec) - - } - - - def visualize_ctree(root: CertTree.Node): Unit = { - var nodes: List[(String, String)] = Nil - var edges: List[(String, String)] = Nil - var same_ranks: List[List[String]] = Nil - var count = 0 - - def next_id(x: Unit): String = { - count = count + 1 - s"node_${count}" - } - - def process_producer(root_node: Option[String])(stmtProducer: StmtProducer): State[CertTree.Node, String] = { - val node_id = next_id() - val node_text = stmt_producer_node_to_text(stmtProducer) - nodes = (node_id, node_text) :: nodes - root_node match { - case Some(value) => edges = (value, node_id) :: edges - case None => () - } - for { - _ <- (stmtProducer match { - case ChainedProducer(p1, p2) => - for { - child1 <- process_producer(Some(node_id))(p1) - child2 <- process_producer(Some(node_id))(p2) - _ <- State.ret(same_ranks = List(child1, child2) :: same_ranks) - } yield () - case PartiallyAppliedProducer(p, s) => for { - _ <- process_producer(Some(node_id)) (p) - } yield () - case BranchProducer(_, _, _, selectors) =>for { - _ <- State.mapM(selectors.toList)(_ => for { - child <- State.pop[CertTree.Node] - result <- process_root(Some(node_id))(child: CertTree.Node) - } yield result) - } yield () - - case GuardedProducer(cond, _) => for { - if_true <- State.pop[CertTree.Node] - if_false <- State.pop[CertTree.Node] - _ <- process_root(Some(node_id))(if_true) - _ <- process_root(Some(node_id))(if_false) - } yield () - case HandleGuard(_) => State.ret(()) - case ConstProducer(_) => State.ret(()) - case ExtractHelper(_) => State.ret(()) - case IdProducer => for { - child <- State.pop[CertTree.Node] - result <- process_root(Some(node_id))(child) - } yield () - - - case PrependProducer(_) => for { - child <- State.pop[CertTree.Node] - result <- process_root(Some(node_id))(child) - } yield () - - case v => for { - _ <- State.mapM(List.range(1, v.arity))(_ => for { - child <- State.pop[CertTree.Node] - result <- process_root(Some(node_id))(child: CertTree.Node) - } yield result) - } yield () - }) : State[CertTree.Node, Unit] - result <- State.ret(node_id) - } yield result - } - def process_root(parent:Option[String])(node: CertTree.Node) : State[CertTree.Node, String] = { - for { - _ <- State.push_multi(node.children) - result <- process_producer(parent)(node.kont) - } yield result - } - - process_root(None)(root).eval(Nil) - - val graph_dot_spec = - s""" - |digraph proof_tree { - | size="10,10"; - ${nodes.map({ case (node_id, node_text) => s" | ${node_id} [label=${"\"" ++ node_text ++ "\""}]" }).mkString("\n")} - | - ${edges.map({ case (fro_n, to_n) => s" | ${fro_n} -> ${to_n}" }).mkString("\n")} - | - ${same_ranks.map((ls => s" | { rank=same; ${ls.mkString(" ")} }")).mkString("\n")} - |} - |""".stripMargin - - visualize_dot_viz_spec(graph_dot_spec) - - } - - - private def visualize_dot_viz_spec(graph_dot_spec: String) = { - val dot_bin: Array[Byte] = dotbin("png")(graph_dot_spec) - - val image_file: File = { - val file = File.createTempFile("suslik_visualizer", ".png") - val out = new BufferedOutputStream(new FileOutputStream(file)) - out.write(dot_bin) - out.close() - file - } - val cmd = "feh " ++ image_file.toPath.toAbsolutePath.toString - cmd !! - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala deleted file mode 100644 index afb81013a..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/State.scala +++ /dev/null @@ -1,63 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with - -import scala.annotation.tailrec - -/** Implementation of a state monad where the state is a stack of elements */ -case class State[S, A](f: Seq[S] => (A, Seq[S])) { - - /** map = fmap */ - def map[B](g: A => B): State[S, B] = - State(s => { - val (a, t) = f(s) - (g(a), t) - }) - - /** flatMap = >>= */ - def flatMap[B](g: A => State[S, B]): State[S, B] = - State(s => { - val (a, t) = f(s) - g(a).f(t) - }) - - - /** run the state monad to get a value */ - def eval(s: Seq[S]): A = f(s)._1 -} - -object State { - /** return value */ - def ret[S, A] (v: A ) : State[S, A] = State(ls => (v ,ls)) - - /** push an element onto the state */ - def push[S] (s :S) : State[S, Unit] = { - State(ss => ((), s +: ss)) - } - - /** push multiple values onto the state, in such a way that is equivalent to iterating through the list and pushing them on individually */ - def push_multi[S] (s :Seq[S]) : State[S, Unit] = { - @tailrec - def rev_cat[C](ls: Seq[C])(acc: Seq[C]) : Seq[C] = ls match { - case Seq(head, tail@_*) => rev_cat(tail)(head +: acc) - case _ => acc - } - State(ss => ((), rev_cat(s)(ss))) - } - - /** pop a value off the stack */ - def pop[S] : State[S, S] = State { - case Seq(head, tl@_*) => (head, tl) - case _ => fail_with("State monad ran out of state") - } - - def mapM[B,S,A] (ls: Seq[B]) (f: B => State[S,A]) : State[S,Seq[A]] = - ls match { - case Seq(head0, tail0@_*) => for { - head <- f(head0) - tail <- mapM(tail0)(f) - } yield head +: tail - case _ => State.ret(Seq.empty) - } - -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index 4aec9459b..cb77c2b9b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -17,63 +17,14 @@ object VST extends CertificationTarget { override val name: String = "VST" override val suffix: String = ".v" - - override def certify(proc: Statements.Procedure, env: Environment): Certificate = { // retrieve the search tree val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - - val builder = new StringBuilder - // append the coq prelude - val fun_name : String = proc.f.name - //builder.append(coq_prelude(fun_name)) - - Translation.translate(root, proc, env) } - def main(args: Array[String]) : Unit = { - val certDest = "/tmp/cert-test" - val certTarget = VST - val parser = new SSLParser - - val res = parser.parseGoalSYN( - "{ x :-> a ** y :-> b } void swap(loc x, loc y) { x :-> b ** y :-> a }" - ) - val prog = res.get - - val (specs, predEnv, funcEnv, body) = - Preprocessor.preprocessProgram(prog, new SynConfig) - - val spec = specs.head - - val config = (new SynConfig).copy(certTarget = VST) - - val env = Environment(predEnv, funcEnv, config, new SynStats(120000)) - - val synthesizer = SynthesisRunner.createSynthesizer(env) - val sresult = synthesizer.synthesizeProc(spec, env, body) - - sresult._1 match { - case Nil => throw SynthesisException(s"Failed to synthesize:\n$sresult") - - case procs => - - val targetName = certTarget.name - - val certificate = certTarget.certify(procs.head, env) - - certificate.outputs.foreach({ - case CertificateOutput(filename, name, body) => - println(s"synthesized:\n${body}") - }) - } - - } - - } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala index 37d261cd4..82b9ed488 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala @@ -1,10 +1,12 @@ package org.tygus.suslik.certification.targets.vst.translation +import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.vst.clang.CTypes import org.tygus.suslik.certification.targets.vst.clang.CTypes._ import org.tygus.suslik.certification.targets.vst.clang.Expressions._ import org.tygus.suslik.certification.targets.vst.clang.Statements._ import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Procedure, Statement} import org.tygus.suslik.language._ @@ -13,6 +15,21 @@ import org.tygus.suslik.logic.Gamma /** encapsulates all functions for translating suslik terms to a C encoding */ object CTranslation { + def translate_body_from_proof(base_proof: ProofTree[SuslikProofStep], gamma: List[(CVar, VSTCType)]): CStatement = ??? + + + def translate_function_from_proof(base_proof: ProofTree[SuslikProofStep], gamma: Gamma): CProcedureDefinition = { + val init = base_proof.step.asInstanceOf[SuslikProofStep.Init] + val gamma = init.goal.programVars.map(v => (CVar(v.name), translate_type(init.goal.gamma(v)))) + CProcedureDefinition( + init.goal.fname, + CUnitType, + gamma, + translate_body_from_proof(base_proof.children(0), gamma) + ) + } + + def translate_unary_expr(e1: UnaryExpr): CExpr = { def translate_unary_op : UnOp => CUnOp = { case OpNot => COpNot diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 5b24e6038..7da98557a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -469,7 +469,6 @@ object ProofSpecTranslation { } case _ => None } - } // base context contains type information for every variable used in the diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 6be40755e..c3b7e683c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -1,9 +1,12 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.{CertTree, SuslikProofStep} import org.tygus.suslik.certification.targets.vst.VSTCertificate +import org.tygus.suslik.certification.targets.vst.clang.Statements import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, VSTProofStep} +import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -14,25 +17,32 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { - val procedure = CTranslation.translate_function(proc, root.goal.gamma) - val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) - val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) - val post_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.post) - println(procedure.pp) - println(spec.pp) - val predicates: List[VSTPredicate] = env.predicates.map({ case (_, predicate) => - ProofSpecTranslation.translate_predicate(env)(predicate) - }).toList + def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { + val base_proof = SuslikProofStep.of_certtree(root) - predicates.foreach(v => println(v.pp)) + // val procedure: Statements.CProcedureDefinition = CTranslation.translate_function(proc, root.goal.gamma) + // val new_procedure : Statements.CProcedureDefinition = CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) - val proof = ProofTranslation.translate_proof(proc.f.name, predicates, spec, root, pre_cond, post_cond) + // val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) + // val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) + // val post_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.post) - println(proof.pp) + // println(procedure.pp) + // println(new_procedure.pp) + val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList + + println(base_proof.pp) + val pred_map = predicates.map(v => (v.name,v)).toMap + implicit val initial_context: VSTClientContext = VSTClientContext.make_context(pred_map) + implicit val translator: VSTTranslator = new VSTTranslator + val evaluator = new StackEvaluator[SuslikProofStep, VSTProofStep, VSTClientContext] + val steps = evaluator.run(base_proof) + + val procedure = CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) + val (spec, _) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) + + val proof = Proof(proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep]) - // translate predicates - // translate proof VSTCertificate(proc.f.name, procedure, proof) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala new file mode 100644 index 000000000..04e0e2240 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala @@ -0,0 +1,89 @@ +package org.tygus.suslik.certification.targets.vst.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.clang.Expressions +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{IsValidPointerOrNull, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.{ProofTerms, ProofTypes, VSTProofStep} +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.language.Expressions.{Expr, Var} +import org.tygus.suslik.language.{Expressions, SSLType} + +import scala.collection.immutable.Queue + +case class VSTClientContext( + pred_map: Map[String, VSTPredicate], + coq_context: Map[String, ProofTypes.VSTProofType], + variable_map: Map[String, ProofTerms.Expressions.ProofCExpr] + ) extends ClientContext[VSTProofStep] { + def with_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + val new_vars: Map[String, ProofTypes.VSTProofType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_type(ty)) }).toMap + VSTClientContext(pred_map, coq_context ++ new_vars, variable_map) + } + + def with_mapping_between(from: String, to: Expr) : VSTClientContext = { + val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) + VSTClientContext(pred_map, coq_context, variable_map ++ Map(from -> to_expr)) + } + +} + +object VSTClientContext { + def make_context(pred_map: Map[String,VSTPredicate]) : VSTClientContext = + VSTClientContext(pred_map, Map(), Map()) +} + + +class VSTTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTClientContext] { + type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] + private val no_deferreds : Queue[Deferred] = Queue () + private def with_no_deferreds(ctx: VSTClientContext) = (no_deferreds, ctx) + + def with_no_op(context: VSTClientContext) : Result[VSTProofStep, VSTClientContext] = Result(List(), List((Queue(), context))) + + override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Translator.Result[VSTProofStep, VSTClientContext] = { + value match { + case SuslikProofStep.NilNotLval(vars) => + val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) + Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) + case SuslikProofStep.CheckPost(prePhi, postPhi) => + with_no_op(clientContext) + case SuslikProofStep.Pick(from, to_expr) => + val new_context = clientContext with_mapping_between(from.name, to_expr) + with_no_op(new_context) + case SuslikProofStep.Branch(cond, bLabel) => + val cont = with_no_deferreds(clientContext) + Result(List(ForwardIf), List(cont, cont)) + case SuslikProofStep.Write(stmt) => + Result(List(Forward), List(with_no_deferreds(clientContext))) + case SuslikProofStep.WeakenPre(unused) => + with_no_op(clientContext) + case SuslikProofStep.EmpRule(label) => ??? + case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? + case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? + case SuslikProofStep.SubstL(from, to) => ??? + case SuslikProofStep.SubstR(from, to) => ??? + case SuslikProofStep.Read(from, to, operation) => ??? + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? + case SuslikProofStep.HeapUnify(subst) => ??? + case SuslikProofStep.HeapUnifyPointer(from, to) => ??? + case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? + case SuslikProofStep.Call(subst, call) => ??? + case SuslikProofStep.Free(stmt, size) => ??? + case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? + case SuslikProofStep.PickCard(from, to) => ??? + case SuslikProofStep.PickArg(from, to) => ??? + case SuslikProofStep.Init(goal) => + val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) + val ctx = clientContext with_variables_of program_vars + val existentials = goal.existentials.toList.map(v => (v.name, goal.gamma(v))) + with_no_op(ctx) + case SuslikProofStep.Inconsistency(label) => ??? + } + } +} From 1b5152da6821f4941d71ff58cbbed69a4b31ff38 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 16 Feb 2021 17:41:32 +0800 Subject: [PATCH 095/211] Add basic evaluator and remove printer from evaluator implicits --- .../certification/SuslikProofStep.scala | 12 +-- .../targets/htt/logic/Proof.scala | 18 +--- .../targets/htt/program/Program.scala | 2 +- .../htt/translation/ProgramTranslator.scala | 16 ++-- .../htt/translation/ProofTranslator.scala | 82 +++++++++-------- .../htt/translation/TranslatableOps.scala | 4 +- .../targets/htt/translation/Translation.scala | 4 +- .../targets/vst/logic/Proof.scala | 3 +- .../targets/vst/logic/VSTProofStep.scala | 10 +-- .../vst/translation/ProofTranslation.scala | 2 +- .../traversal/BasicEvaluator.scala | 34 +++++++ .../certification/traversal/Evaluator.scala | 88 +++++++++++++++++-- .../certification/traversal/ProofTree.scala | 6 +- .../traversal/StackEvaluator.scala | 68 ++++---------- .../traversal/TranslatableOps.scala | 4 +- .../certification/traversal/Translator.scala | 6 +- 16 files changed, 207 insertions(+), 152 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index 72c61af60..d5d85d97d 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -29,7 +29,7 @@ object SuslikProofStep { tree.step match { case rule:Branch => rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) case rule:Open => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) - case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + case rule => rule.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") } } @@ -64,8 +64,8 @@ object SuslikProofStep { /** branches on a condition */ case class Branch(cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { - def branch_strings[T <: PrettyPrinting] (ifTrue: T, ifFalse: T) = - s"${ind}IfTrue:\n${with_scope(_ => ifTrue.pp)}\n${ind}IfFalse:\n${with_scope(_ => ifFalse.pp)}" + def branch_strings(ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep]) = + s"${ind}IfTrue:\n${with_scope(_ => ProofTreePrinter.pp(ifTrue))}\n${ind}IfFalse:\n${with_scope(_ => ProofTreePrinter.pp(ifFalse))}" override def pp: String = s"${ind}AbduceBranch(${cond.pp}, ${bLabel.pp});" } @@ -93,8 +93,8 @@ object SuslikProofStep { /** open constructor cases */ case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { - def branch_strings[T <: PrettyPrinting] (exprs: List[T]) = - s"${with_scope(_ => selectors.zip(exprs).map({case (sel,rest) => s"${ind}if ${sanitize(sel.pp)}:\n${with_scope(_ => rest.pp)}"}).mkString("\n"))}" + def branch_strings(exprs: List[ProofTree[SuslikProofStep]]) = + s"${with_scope(_ => selectors.zip(exprs).map({case (sel,rest) => s"${ind}if ${sanitize(sel.pp)}:\n${with_scope(_ => ProofTreePrinter.pp(rest))}"}).mkString("\n"))}" override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});" } @@ -594,7 +594,7 @@ case class AbduceCall( val initStep = Init(node.goal) val initItem = Item(initStep, None, Nil, Nil) val res = forward(node, List(initItem)) - Console.println(res.pp) + Console.println(ProofTreePrinter.pp(res)) res } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 7aa163c1c..245ef46cd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -9,7 +9,7 @@ import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.language.PrettyPrinting case class Proof(body: ProofTree[Proof.Step]) extends PrettyPrinting { - override def pp: String = s"${body.pp}Qed." + override def pp: String = s"${ProofPrinter.pp(body)}Qed." } object Proof { @@ -48,22 +48,6 @@ object Proof { case class Rename(from: String, to: String) extends Step { override def pp: String = s"try rename $from into $to" } - case class SubProof(branches: Seq[Step]) extends Step { - override val isNoop: Boolean = branches.forall(_.isNoop) - override def pp: String = branches.map(_.pp).mkString(".\n") - } - case class SeqComp(s1: Step, s2: Step) extends Step { - override val isNoop: Boolean = s1.isNoop && s2.isNoop - override def pp: String = s"${s1.pp}.\n${s2.pp}" - } - case class SeqCompAlt(s1: Step, s2: Step) extends Step { - override val isNoop: Boolean = s1.isNoop && s2.isNoop - override def pp: String = s"${s1.pp};\n${s2.pp}" - } - case class Solve(steps: Seq[Step]) extends Step { - override val isNoop: Boolean = steps.forall(_.isNoop) - override def pp: String = s"solve [\n${steps.map(_.pp).mkString(" |\n")} ]" - } case class MoveToCtx(items: Seq[CExpr]) extends Step { override val isNoop: Boolean = items.isEmpty override def pp: String = s"move=>${items.map(_.pp).mkString(" ")}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala index 8f763ffd7..29ef1bdf5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/Program.scala @@ -12,6 +12,6 @@ case class Program(name: String, tp: HTTType, formals: CFormals, body: ProofTree | Fix (fun ($name : ${name}_type) vprogs => | let: (${formals.map(_._1.pp).mkString(", ")}) := vprogs in | Do ( - | ${body.pp.replace("\n", "\n ")} + | ${ProgramPrinter.pp(body).replace("\n", "\n ")} | )).""".stripMargin } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index c0684f488..7a639cdf7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -3,25 +3,21 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.htt.program.Statements.{CFree, CGuarded, CIf, CSkip, CStatement, Noop} import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Evaluator.Deferreds +import org.tygus.suslik.certification.traversal.Evaluator.Deferred import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result -import scala.collection.immutable.Queue - object ProgramTranslator extends Translator[SuslikProofStep, CStatement, ProgramContext] { - private val noDeferreds: Deferreds[CStatement, ProgramContext] = Queue.empty - override def translate(value: SuslikProofStep, ctx: ProgramContext): Translator.Result[CStatement, ProgramContext] = { - val meta = (noDeferreds, ctx) + val withNoDeferred = (None, ctx) value match { case SuslikProofStep.Open(_, _, _, selectors) => val stmt = CIf(selectors.map(_.translate)) - val metas = selectors.map(_ => meta) - Result(List(stmt), metas) + val childParams = selectors.map(_ => withNoDeferred) + Result(List(stmt), childParams) case SuslikProofStep.Branch(cond, _) => val stmt = CGuarded(cond.translate) - Result(List(stmt), List(meta, meta)) + Result(List(stmt), List(withNoDeferred, withNoDeferred)) case _:SuslikProofStep.EmpRule | _:SuslikProofStep.Inconsistency => Result(List(CSkip), Nil) case _ => @@ -35,7 +31,7 @@ object ProgramTranslator extends Translator[SuslikProofStep, CStatement, Program case SuslikProofStep.Call(_, stmt) => List(stmt.translate) case _ => List(Noop) } - Result(stmts, List(meta)) + Result(stmts, List(withNoDeferred)) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 580b30e7e..bcffcb854 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -5,16 +5,14 @@ import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, C import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Evaluator.Deferreds +import org.tygus.suslik.certification.traversal.Evaluator.Deferred import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} -import scala.collection.immutable.Queue - object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofContext] { - private val noDeferreds: Deferreds[Proof.Step, ProofContext] = Queue.empty - private def withNoDeferreds(ctx: ProofContext): (Deferreds[Proof.Step, ProofContext], ProofContext) = (noDeferreds, ctx) + private def withNoDeferred(ctx: ProofContext): (Option[Deferred[Proof.Step, ProofContext]], ProofContext) = (None, ctx) private def initPre(asn: CAssertion, uniqueName: String): List[Proof.Step] = { val phi = asn.phi @@ -52,7 +50,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) val steps = Proof.Noop :: renameAppsStep(affectedApps) - Result(steps, List(withNoDeferreds(ctx1))) + Result(steps, List(withNoDeferred(ctx1))) } override def translate(value: SuslikProofStep, ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { @@ -71,24 +69,33 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // defer post-condition handling val sapps = cgoal.post.sigma.apps - val d1 = (ctx: ProofContext) => (Proof.Exists(ctx.postEx) andThen, ctx) - val q0 = sapps.foldLeft(Queue(d1)) { case (q, app) => - val d = (ctx: ProofContext) => { + val deferred = (ctx: ProofContext) => { + val ex = ctx.postEx.toSeq.map { + // if proof ended without instantiating this existential, provide default witness + case (start, (typ, end)) if start == end => typ.defaultExpr + // otherwise, provide final substituted expression + case (_, (_, end)) => end + } + val valueEx = Proof.Exists(ex) andThen + + val heapEx = sapps.map(app => { val app1 = ctx.currentAppAlias(app) val heap = ctx.unfoldedApp(app1) - (Proof.Exists(List(heap)) andThen, ctx) - } - q.enqueue(d) + Proof.Exists(List(heap)) andThen + }).toList + + val steps = valueEx :: heapEx ++ List(Proof.Auto) + + (steps, ctx) } - val d2 = (ctx: ProofContext) => (Proof.Auto, ctx) - val q = q0.enqueue(d2) // initialize context with post-condition info and predicate hints, if any val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) - val ctx1 = ctx.copy(postEx = cgoal.existentials, appAliases = appAliases) + val postEx = cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)).toMap + val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) - Result(steps, List((q, ctx1))) + Result(steps, List((Some(deferred), ctx1))) case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => val pre = f.pre.translate val post = f.post.translate @@ -96,12 +103,12 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val universalGhosts = pre.valueVars.diff(programVars) val goal = Proof.Goal(pre, post, gamma.translate, programVars, universalGhosts, f.name) val ctx1 = ctx.copy(callGoal = Some(goal)) - Result(List(Proof.Noop), List(withNoDeferreds(ctx1))) + Result(List(Proof.Noop), List(withNoDeferred(ctx1))) /** Control flow */ case SuslikProofStep.Branch(cond, _) => val childContexts = List(ctx.copy(numSubgoals = ctx.numSubgoals + 1), ctx) - Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferreds)) + Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferred)) case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => val pred = ctx.predicates(sapp.pred).subst(fresh_vars.translate).subst(sbst.translate) val csapp = sapp.translate @@ -121,7 +128,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val numSubgoals = numClauses - clause.idx + ctx.numSubgoals ctx.copy(appAliases = ctx.appAliases ++ newApps, numSubgoals = numSubgoals) }) - Result(steps, childCtxs.map(withNoDeferreds)) + Result(steps, childCtxs.map(withNoDeferred)) /** Program statements */ case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, _, from, offset)) => @@ -131,12 +138,12 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) val steps = readStep :: renameStep :: renameAppsStep(affectedApps) - Result(steps, List(withNoDeferreds(ctx1))) + Result(steps, List(withNoDeferred(ctx1))) case SuslikProofStep.Write(Store(to, offset, e)) => val writeStep = Proof.Write(to.translate, offset, e.translate) val writePostStep = Proof.WritePost(to.translate, offset) val steps = if (ctx.callGoal.isDefined) List(writeStep) else List(writeStep, writePostStep) - Result(steps, List(withNoDeferreds(ctx))) + Result(steps, List(withNoDeferred(ctx))) case SuslikProofStep.Malloc(ghostFrom, ghostTo, Malloc(to, tpe, sz)) => val allocStep = Proof.Alloc(to.translate, tpe.translate, sz) val renameStep = Proof.Rename(ghostFrom.name, ghostTo.name) @@ -144,10 +151,10 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) val steps = allocStep :: renameStep :: renameAppsStep(affectedApps) - Result(steps, List(withNoDeferreds(ctx1))) + Result(steps, List(withNoDeferred(ctx1))) case SuslikProofStep.Free(Free(v), size) => val steps = (0 until size).map(i => Proof.Dealloc(v.translate, i)).toList - Result(steps, List(withNoDeferreds(ctx))) + Result(steps, List(withNoDeferred(ctx))) case SuslikProofStep.Call(freshToActual, Call(_, args, _)) => val callId = ctx.nextCallId val callGoal = ctx.callGoal.get @@ -170,7 +177,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val newApps = goal.post.sigma.apps.map(a => a -> a).toMap val ctx1 = ctx.copy(callGoal = None, nextCallId = callId + 1, appAliases = ctx.appAliases ++ newApps) - Result(steps, List(withNoDeferreds(ctx1))) + Result(steps, List(withNoDeferred(ctx1))) /** Substitutions and unfoldings */ case SuslikProofStep.Pick(from, to) => @@ -196,23 +203,24 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val clause0 = ctx.predicates(csapp.pred).clauses.find(_.selector == cselector).get val clause = CInductiveClause(csapp.pred, clause0.idx, cselector, asn.translate, clause0.existentials.map(_.subst(m))) - // create deferreds to perform unfolding - val deferreds = Queue( - (ctx: ProofContext) => (Proof.UnfoldConstructor(clause.idx) andThen, ctx), - (ctx: ProofContext) => { - val csapp1 = ctx.currentAppAlias(csapp) - val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) - (Proof.Exists(valueEx ++ heapEx) andThen, ctx) - }, - (ctx: ProofContext) => (Proof.Auto, ctx) - ) + // create deferred to perform unfolding + val deferred = (ctx: ProofContext) => { + val csapp1 = ctx.currentAppAlias(csapp) + val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) + val steps = List( + Proof.UnfoldConstructor(clause.idx) andThen, + Proof.Exists(valueEx ++ heapEx) andThen, + Proof.Auto + ) + (steps, ctx) + } // update context with unfolding info val appAliases = ctx.appAliases ++ clause.asn.sigma.apps.map(a => a -> a) val unfoldings = ctx.unfoldings + (csapp -> clause) val ctx1 = ctx.copy(appAliases = appAliases, unfoldings = unfoldings) - Result(List(Proof.Noop), List((deferreds, ctx1))) + Result(List(Proof.Noop), List((Some(deferred), ctx1))) /** Terminals */ case SuslikProofStep.Inconsistency(label) => @@ -223,10 +231,10 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont /** Pure entailments */ case SuslikProofStep.CheckPost(prePhi, postPhi) => ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate)) - Result(List(Proof.Noop), List(withNoDeferreds(ctx))) + Result(List(Proof.Noop), List(withNoDeferred(ctx))) /** Ignored */ - case _ => Result(List(Proof.Noop), List(withNoDeferreds(ctx))) + case _ => Result(List(Proof.Noop), List(withNoDeferred(ctx))) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala index 75eab60e1..a1ac3c367 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala @@ -1,8 +1,8 @@ package org.tygus.suslik.certification.targets.htt.translation object TranslatableOps { - implicit class Translatable[A](value: A) { - def translate[B](implicit translator: HTTTranslator[A,B]): B = { + implicit class Translatable[S](value: S) { + def translate[D](implicit translator: HTTTranslator[S,D]): D = { translator.translate(value) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 5925b8b7a..6aa77c4de 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -34,10 +34,10 @@ object Translation { val suslikTree = SuslikProofStep.of_certtree(node) val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint]) - val proofBody = ProofEvaluator.run(suslikTree)(ProofTranslator, ProofPrinter, ctx) + val proofBody = ProofEvaluator.run(suslikTree)(ProofTranslator, ctx) val proof = Proof(proofBody) val hints = ctx.hints.filter(_.numHypotheses > 0) - val progBody = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramPrinter, ProgramContext()) + val progBody = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramContext()) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) HTTCertificate(cproc.name, cpreds, goal.toFunspec, proof, cproc, hints) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 4fb06dd0e..45487fdc3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.PrettyPrinting @@ -67,7 +68,7 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. lemma_prelude + "start_function.\n" + "ssl_open_context.\n" + - steps.pp + "\n" + + ProofTreePrinter.pp(steps) + "\n" + "Qed." } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 09a655a5f..96f68b9e7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -14,7 +14,7 @@ object VSTProofStep { override def pp(tree: ProofTree[VSTProofStep]): String = tree.step match { case rule@ForwardIf => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) case rule@ForwardIfConstructor(_,_,_) => tree.step.pp ++ "\n" ++ rule.branch_strings(tree.children) - case _ => tree.step.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + case _ => tree.step.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") } } @@ -36,7 +36,7 @@ object VSTProofStep { branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String])] ) extends VSTProofStep { - def branch_strings[T <: PrettyPrinting] (children : List[T]): String = { + def branch_strings(children : List[ProofTree[VSTProofStep]]): String = { val branches = this.branches.zip(children).map({ case ((clause, selector, args), value) => (clause, selector, args, value)}) def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" @@ -55,7 +55,7 @@ object VSTProofStep { case Nil => "" case args => s"Intros ${args.mkString(" ")}.\n" }) ++ - ls.pp ++ + ProofTreePrinter.pp(ls) ++ "\n}" } ).mkString("\n") @@ -68,11 +68,11 @@ object VSTProofStep { } case object ForwardIf extends VSTProofStep { - def branch_strings[T <: PrettyPrinting] (children: List[T]) = { + def branch_strings (children: List[ProofTree[VSTProofStep]]) = { val branches = children branches match { case Nil => "" - case _ => "\n" ++ branches.map(ls => " - {\n" ++ ls.pp ++ "\n}").mkString("\n") + case _ => "\n" ++ branches.map(ls => " - {\n" ++ ProofTreePrinter.pp(ls) ++ "\n}").mkString("\n") } } override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala index 2ee9547ee..253db4fd7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala @@ -696,7 +696,7 @@ object ProofTranslation { } val simplified = SuslikProofStep.of_certtree(root) - println(s"Suslik Proof:\n ${simplified.pp}") + println(s"Suslik Proof:\n ${SuslikProofStep.ProofTreePrinter.pp(simplified)}") val vst_proof: ProofTree[VSTProofStep] = translate_proof_rules(simplified)(initial_context) diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala new file mode 100644 index 000000000..a69ba9359 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -0,0 +1,34 @@ +package org.tygus.suslik.certification.traversal + +import TranslatableOps._ +import org.tygus.suslik.certification.traversal.Evaluator.{ClientContext, DeferredsStack, EvaluatorException} +import org.tygus.suslik.certification.traversal.Step.{DestStep, SourceStep} + +/** + * A basic tree traversal implementation + */ +class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { + override def run(tree: ProofTree[S])(implicit translator: Translator[S, D, C], initialClientContext: C): ProofTree[D] = { + def visit(tree: ProofTree[S], deferredsStack: DeferredsStack[D,C], clientContext: C): ProofTree[D] = { + val res = tree.step.translate[D, C](clientContext) + if (tree.children.length != res.childParams.length) { + throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") + } + + val action = tree.step.contextAction + val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) + val steps = res.steps ++ newSteps + val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => + action.updateStack(deferredsStack, newDeferred) + } + + val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList + val childResults = next.map { case (child, deferredsStack, ctx) => visit(child, deferredsStack, ctx) } + steps.reverse match { + case last :: rest => rest.foldLeft(ProofTree(last, childResults, tree.label)) { case (child, v) => ProofTree(v, List(child), tree.label) } + case Nil => throw EvaluatorException("expected at least one translated value for this task") + } + } + visit(tree, Nil, initialClientContext) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 86a2ae5d5..7102574a5 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -5,21 +5,91 @@ import org.tygus.suslik.certification.traversal.Step._ import scala.collection.immutable.Queue -trait Evaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] { - def run(node: ProofTree[A])(implicit translator: Translator[A,B,C], printer: ProofTreePrinter[B], initialClientContext: C): ProofTree[B] +trait Evaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { + def run(node: ProofTree[S])(implicit translator: Translator[S,D,C], initialClientContext: C): ProofTree[D] } object Evaluator { case class EvaluatorException(private val message: String) extends Exception(message) - trait ClientContext[S <: DestStep] - type Deferred[S <: DestStep, C <: ClientContext[S]] = C => (S, C) - type Deferreds[S <: DestStep, C <: ClientContext[S]] = Queue[Deferred[S,C]] + trait ClientContext[D <: DestStep] + type Deferred[D <: DestStep, C <: ClientContext[D]] = C => (List[D], C) + type Deferreds[D <: DestStep, C <: ClientContext[D]] = Queue[Deferred[D,C]] - abstract class EnvAction + // A stack of queued deferreds + type DeferredsStack[D <: DestStep, C <: ClientContext[D]] = List[Deferreds[D,C]] + + abstract class EnvAction { + /** + * Either release deferreds in topmost stack layer and update child contexts, or (by default) don't do anything + */ + def handleDeferreds[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D,C], currentContext: C, childContexts: List[C]): (List[D], List[C]) = (Nil, childContexts) + + /** + * Update the deferreds stack with a new deferred item + */ + def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D,C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D,C] + } object EnvAction { - case object PushLayer extends EnvAction - case object PopLayer extends EnvAction - case object CurrentLayer extends EnvAction + case object PushLayer extends EnvAction { + override def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D, C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D, C] = { + newDeferred match { + case None => Queue.empty :: deferredsStack + case Some(d) => Queue(d) :: deferredsStack + } + } + } + + case object PopLayer extends EnvAction { + override def handleDeferreds[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D,C], currentContext: C, childContexts: List[C]): (List[D], List[C]) = { + def release(deferreds: Deferreds[D,C], ctx: C): (List[D], C) = { + deferreds.foldLeft((List.empty[D], ctx)) { case ((steps, ctx), deferred) => + val (newSteps, ctx1) = deferred(ctx) + val steps1 = newSteps.foldLeft(steps){ case (steps, s) => s :: steps } + (steps1, ctx1) + } + } + deferredsStack match { + case ds :: _ => + if (childContexts.length > 1) { + throw EvaluatorException(s"step has ${childContexts.length} children, but pop action expects at most 1 child") + } + childContexts.headOption match { + case Some(ctx) => + val (res, ctx1) = release(ds, ctx) + (res.reverse, List(ctx1)) + case None => + val (res, _) = release(ds, currentContext) + (res.reverse, Nil) + } + case Nil => throw EvaluatorException("step expects a pop, but deferreds stack is empty") + } + } + + override def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D, C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D, C] = { + deferredsStack match { + case _ :: ds :: rest => + newDeferred match { + case None => ds :: rest + case Some(d) => ds.enqueue(d) :: deferredsStack + } + case _ :: Nil => Nil + case Nil => throw EvaluatorException("step expects a pop, but deferreds stack is empty") + } + } + } + + case object CurrentLayer extends EnvAction { + override def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D, C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D, C] = { + deferredsStack match { + case ds :: rest => + newDeferred match { + case None => deferredsStack + case Some(d) => ds.enqueue(d) :: rest + } + case Nil => throw EvaluatorException("cannot replace deferreds stack item for step") + } + } + } } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala index cc109e5b0..03c6d856b 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala @@ -1,6 +1,5 @@ package org.tygus.suslik.certification.traversal -import org.tygus.suslik.language.PrettyPrinting import org.tygus.suslik.logic.Specifications.GoalLabel /** @@ -10,7 +9,4 @@ import org.tygus.suslik.logic.Specifications.GoalLabel * @param children list of child nodes * @param label the label of the Suslik goal to which the rule was applied */ -sealed case class ProofTree[S <: Step](step: S, children: List[ProofTree[S]], label: Option[GoalLabel] = None)(implicit printer: ProofTreePrinter[S]) - extends PrettyPrinting { - override def pp : String = printer.pp(this) -} +case class ProofTree[S <: Step](step: S, children: List[ProofTree[S]], label: Option[GoalLabel] = None) \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index ff5354a38..f9420c3ba 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -7,20 +7,20 @@ import org.tygus.suslik.logic.Specifications.GoalLabel import scala.annotation.tailrec -class StackEvaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] extends Evaluator[A,B,C] { +/** + * A tail-recursive, stack-based tree traversal with an eval/apply loop + */ +class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { // A pending evaluation task; tracks which children have and have not been evaluated - case class Task(values: List[B], label: Option[GoalLabel], remainingBranches: List[(ProofTree[A], DeferredsStack, C)], resultsSoFar: List[ProofTree[B]]) + case class Task(values: List[D], label: Option[GoalLabel], remainingBranches: List[(ProofTree[S], DeferredsStack[D,C], C)], resultsSoFar: List[ProofTree[D]]) // A stack of pending evaluation tasks type TaskStack = List[Task] - // A stack of queued deferreds - type DeferredsStack = List[Deferreds[B,C]] - - def run(tree: ProofTree[A])(implicit translator: Translator[A,B,C], printer: ProofTreePrinter[B], initialClientContext: C): ProofTree[B] = { + def run(tree: ProofTree[S])(implicit translator: Translator[S,D,C], initialClientContext: C): ProofTree[D] = { // Use a child result to fulfill the evaluation task for a parent @tailrec - def backward(taskStack: TaskStack, childResult: ProofTree[B]): ProofTree[B] = + def backward(taskStack: TaskStack, childResult: ProofTree[D]): ProofTree[D] = taskStack match { case Nil => // no more tasks; return the result @@ -41,51 +41,17 @@ class StackEvaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] exte // Do step-wise translation of current tree node and explore next child @tailrec - def forward(tree: ProofTree[A], deferredsStack: DeferredsStack, clientContext: C, taskStack: TaskStack): ProofTree[B] = { - val res = tree.step.translate[B,C](clientContext) - if (tree.children.length != res.childrenMeta.length) { - throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childrenMeta.length} children") + def forward(tree: ProofTree[S], deferredsStack: DeferredsStack[D,C], clientContext: C, taskStack: TaskStack): ProofTree[D] = { + val res = tree.step.translate[D,C](clientContext) + if (tree.children.length != res.childParams.length) { + throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") } - val (childDeferreds, childClientContexts0) = res.childrenMeta.unzip - val (steps, childDeferredsStacks, childClientContexts) = tree.step.contextAction match { - case EnvAction.PopLayer => - if (tree.children.length > 1) { - throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children, but pop action expects at most 1 child") - } - deferredsStack match { - case deferreds :: remainingDeferreds => - // translation should have produced results for 0 or 1 children - val childDeferreds0 = childDeferreds.headOption.toList - val childClientContext0 = childClientContexts0.headOption.getOrElse(clientContext) - - // process all deferreds in current stack layer - val (steps, childClientContext) = deferreds.foldLeft((res.steps.reverse, childClientContext0)) { - case ((steps, clientCtx), deferred) => - val (step, clientCtx1) = deferred(clientCtx) - (step :: steps, clientCtx1) - } - // pop current stack layer and enqueue on next stack layer - val childDeferredsStacks = remainingDeferreds match { - case nextLayer :: remainingLayers => childDeferreds0.map(newDeferreds => (nextLayer ++ newDeferreds) :: remainingLayers) - case Nil => childDeferreds0.map(List(_)) - } - - (steps.reverse, childDeferredsStacks, List(childClientContext)) - case Nil => throw EvaluatorException(s"step ${tree.step.pp} expects a pop, but deferreds stack is empty") - } - case EnvAction.PushLayer => - // create fresh deferreds - val childDeferredsStacks = childDeferreds.map(_ :: deferredsStack) - (res.steps, childDeferredsStacks, childClientContexts0) - case EnvAction.CurrentLayer => - deferredsStack match { - case deferreds :: remainingDeferreds => - // enqueue on current deferreds - val childDeferredsStack = childDeferreds.map(deferreds ++ _ :: remainingDeferreds) - (res.steps, childDeferredsStack, childClientContexts0) - case Nil => throw EvaluatorException(s"cannot replace deferreds stack item for step ${tree.step.pp}") - } + val action = tree.step.contextAction + val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) + val steps = res.steps ++ newSteps + val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => + action.updateStack(deferredsStack, newDeferred) } (tree.children, childDeferredsStacks, childClientContexts).zipped.toList match { @@ -100,7 +66,7 @@ class StackEvaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] exte } } // Create a tree from a list of values - def foldStepsIntoTree(values: List[B], children: List[ProofTree[B]], label: Option[GoalLabel]): ProofTree[B] = + def foldStepsIntoTree(values: List[D], children: List[ProofTree[D]], label: Option[GoalLabel]): ProofTree[D] = values.reverse match { case last :: rest => rest.foldLeft(ProofTree(last, children, label)){ case (child, v) => ProofTree(v, List(child), label) } case Nil => throw EvaluatorException("expected at least one translated value for this task") diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala index 0fc95cdbc..363865a0f 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala @@ -4,8 +4,8 @@ import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ object TranslatableOps { - implicit class Translatable[A <: SourceStep](step: A) { - def translate[B <: DestStep, C <: ClientContext[B]](clientContext: C)(implicit translator: Translator[A, B, C]): Translator.Result[B,C] = { + implicit class Translatable[S <: SourceStep](step: S) { + def translate[D <: DestStep, C <: ClientContext[D]](clientContext: C)(implicit translator: Translator[S, D, C]): Translator.Result[D,C] = { translator.translate(step, clientContext) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala index df14bcd96..3f692d4cc 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala @@ -3,10 +3,10 @@ package org.tygus.suslik.certification.traversal import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ -trait Translator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] { - def translate(value: A, clientContext: C): Translator.Result[B,C] +trait Translator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { + def translate(value: S, clientContext: C): Translator.Result[D,C] } object Translator { - case class Result[S <: DestStep, C <: ClientContext[S]](steps: List[S], childrenMeta: List[(Deferreds[S,C], C)]) + case class Result[D <: DestStep, C <: ClientContext[D]](steps: List[D], childParams: List[(Option[Deferred[D,C]], C)]) } \ No newline at end of file From 40206ec0e3dc2f4e3094de1b4ae08e6551bb61e1 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 17 Feb 2021 12:22:15 +0800 Subject: [PATCH 096/211] More consistent naming for deferreds actions --- .../tygus/suslik/certification/SuslikProofStep.scala | 10 +++++----- .../targets/htt/translation/ProgramTranslator.scala | 1 - .../certification/traversal/BasicEvaluator.scala | 2 +- .../suslik/certification/traversal/Evaluator.scala | 10 +++++----- .../certification/traversal/StackEvaluator.scala | 2 +- .../tygus/suslik/certification/traversal/Step.scala | 5 ++--- 6 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index d5d85d97d..00b805452 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException -import org.tygus.suslik.certification.traversal.Evaluator.EnvAction +import org.tygus.suslik.certification.traversal.Evaluator.DeferredsAction import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.SourceStep import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} @@ -82,7 +82,7 @@ object SuslikProofStep { /** empty rule */ case class EmpRule(label: Option[GoalLabel]) extends SuslikProofStep { - override def contextAction: EnvAction = EnvAction.PopLayer + override def deferredsAction: DeferredsAction = DeferredsAction.PopLayer override def pp: String = s"${ind}EmpRule;" } @@ -126,7 +126,7 @@ case class AbduceCall( f: FunSpec, gamma: Gamma ) extends SuslikProofStep { - override def contextAction: EnvAction = EnvAction.PushLayer + override def deferredsAction: DeferredsAction = DeferredsAction.PushLayer override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" } @@ -148,7 +148,7 @@ case class AbduceCall( /** call operation */ case class Call(subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { - override def contextAction: EnvAction = EnvAction.PopLayer + override def deferredsAction: DeferredsAction = DeferredsAction.PopLayer override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } @@ -182,7 +182,7 @@ case class AbduceCall( } case class Init(goal: Goal) extends SuslikProofStep { - override def contextAction: EnvAction = EnvAction.PushLayer + override def deferredsAction: DeferredsAction = DeferredsAction.PushLayer override def pp: String = s"${ind}Init(${goal.pp});" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index 7a639cdf7..a63aaea3f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -3,7 +3,6 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.htt.program.Statements.{CFree, CGuarded, CIf, CSkip, CStatement, Noop} import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Evaluator.Deferred import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala index a69ba9359..ab631a258 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -15,7 +15,7 @@ class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] exte throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") } - val action = tree.step.contextAction + val action = tree.step.deferredsAction val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) val steps = res.steps ++ newSteps val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 7102574a5..b3459065b 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -19,7 +19,7 @@ object Evaluator { // A stack of queued deferreds type DeferredsStack[D <: DestStep, C <: ClientContext[D]] = List[Deferreds[D,C]] - abstract class EnvAction { + abstract class DeferredsAction { /** * Either release deferreds in topmost stack layer and update child contexts, or (by default) don't do anything */ @@ -30,8 +30,8 @@ object Evaluator { */ def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D,C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D,C] } - object EnvAction { - case object PushLayer extends EnvAction { + object DeferredsAction { + case object PushLayer extends DeferredsAction { override def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D, C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D, C] = { newDeferred match { case None => Queue.empty :: deferredsStack @@ -40,7 +40,7 @@ object Evaluator { } } - case object PopLayer extends EnvAction { + case object PopLayer extends DeferredsAction { override def handleDeferreds[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D,C], currentContext: C, childContexts: List[C]): (List[D], List[C]) = { def release(deferreds: Deferreds[D,C], ctx: C): (List[D], C) = { deferreds.foldLeft((List.empty[D], ctx)) { case ((steps, ctx), deferred) => @@ -79,7 +79,7 @@ object Evaluator { } } - case object CurrentLayer extends EnvAction { + case object CurrentLayer extends DeferredsAction { override def updateStack[D <: DestStep, C <: ClientContext[D]](deferredsStack: DeferredsStack[D, C], newDeferred: Option[Deferred[D, C]]): DeferredsStack[D, C] = { deferredsStack match { case ds :: rest => diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index f9420c3ba..797e15419 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -47,7 +47,7 @@ class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] exte throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") } - val action = tree.step.contextAction + val action = tree.step.deferredsAction val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) val steps = res.steps ++ newSteps val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala index 20c46f0ad..8d1b62ac4 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Step.scala @@ -1,14 +1,13 @@ package org.tygus.suslik.certification.traversal -import org.tygus.suslik.certification.traversal.Evaluator.EnvAction +import org.tygus.suslik.certification.traversal.Evaluator.DeferredsAction import org.tygus.suslik.language.PrettyPrinting -import org.tygus.suslik.logic.Specifications.GoalLabel trait Step extends PrettyPrinting object Step { trait SourceStep extends Step { - def contextAction: EnvAction = EnvAction.CurrentLayer + def deferredsAction: DeferredsAction = DeferredsAction.CurrentLayer } trait DestStep extends Step } From 58dcbcfecf14979c64946e4ff38c309fc91a7be4 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 17 Feb 2021 14:32:28 +0800 Subject: [PATCH 097/211] Major Refactoring of vst specification-generation code; simplifies VST types --- .../certification/SuslikProofStep.scala | 42 +- .../certification/targets/vst/Types.scala | 42 + .../targets/vst/clang/CTypes.scala | 58 -- .../targets/vst/clang/Statements.scala | 114 ++- .../targets/vst/clang/package.scala | 16 - .../targets/vst/logic/Expressions.scala | 235 ++++++ .../targets/vst/logic/Formulae.scala | 44 +- .../targets/vst/logic/Proof.scala | 34 +- .../targets/vst/logic/ProofSteps.scala | 177 ----- .../targets/vst/logic/ProofTerms.scala | 410 ++-------- .../targets/vst/logic/ProofTypes.scala | 61 -- .../targets/vst/logic/VSTProofStep.scala | 26 +- .../vst/translation/CTranslation.scala | 238 ------ .../translation/ProofSpecTranslation.scala | 415 ++++------ .../vst/translation/ProofTranslation.scala | 716 ------------------ .../targets/vst/translation/Translation.scala | 6 +- .../vst/translation/VSTTranslator.scala | 146 ++-- 17 files changed, 678 insertions(+), 2102 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index 72c61af60..4fe75a38b 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification -import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.certification.traversal.Evaluator.EnvAction import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.SourceStep @@ -18,10 +18,8 @@ import scala.annotation.tailrec import scala.collection.immutable.Map - /** compressed form of suslik rules */ -sealed abstract class SuslikProofStep extends SourceStep { -} +trait SuslikProofStep extends SourceStep {} object SuslikProofStep { implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { @@ -115,20 +113,20 @@ object SuslikProofStep { override def pp: String = s"${ind}Read(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(operation.pp)});" } -// /** abduce a call */ -case class AbduceCall( - new_vars: Map[Var, SSLType], - f_pre: Specifications.Assertion, - callePost: Specifications.Assertion, - call: Statements.Call, - freshSub: SubstVar, - freshToActual: Subst, - f: FunSpec, - gamma: Gamma - ) extends SuslikProofStep { - override def contextAction: EnvAction = EnvAction.PushLayer - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" -} + // /** abduce a call */ + case class AbduceCall( + new_vars: Map[Var, SSLType], + f_pre: Specifications.Assertion, + callePost: Specifications.Assertion, + call: Statements.Call, + freshSub: SubstVar, + freshToActual: Subst, + f: FunSpec, + gamma: Gamma + ) extends SuslikProofStep { + override def contextAction: EnvAction = EnvAction.PushLayer + override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" + } /** unification of heap (ignores block/pure distinction) */ @@ -197,9 +195,9 @@ case class AbduceCall( */ def translate(node: CertTree.Node): SuslikProofStep = { def fail_with_bad_proof_structure(): Nothing = - throw ProofRuleTranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") + throw TranslationException(s"continuation for ${node.rule} is not what was expected: ${node.kont.toString}") def fail_with_bad_children(ls: Seq[CertTree.Node], count: Int): Nothing = - throw ProofRuleTranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") + throw TranslationException(s"unexpected number of children for proof rule ${node.rule} - ${ls.length} != $count") val label = Some(node.goal.label) node.rule match { @@ -588,7 +586,7 @@ case class AbduceCall( // didn't find target goal label; check parent case _ => insertBranchPoint(item, label, rest, ret => k(next :: ret)) } - case Nil => throw ProofRuleTranslationException(s"branch point ${label.pp} not found for branch abduction step ${item.step.pp}") + case Nil => throw TranslationException(s"branch point ${label.pp} not found for branch abduction step ${item.step.pp}") } val initStep = Init(node.goal) @@ -597,4 +595,4 @@ case class AbduceCall( Console.println(res.pp) res } -} \ No newline at end of file +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala new file mode 100644 index 000000000..25d4fcfa7 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala @@ -0,0 +1,42 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting + +/** Encapsulates all types used in proofs - i.e if these types are pretty printed, then they will be valid Coq terms */ +object Types { + + /** proof types */ + sealed abstract class VSTType extends PrettyPrinting { + } + + sealed abstract class VSTCType extends VSTType { + def pp_as_ctype: String + } + + case object CoqPtrValType extends VSTCType { + override def pp: String = "val" + override def pp_as_ctype : String = "loc" + } + + /** type of integers (used to represent the values of variables in a C program) */ + case object CoqIntValType extends VSTCType { + override def pp: String = "val" + override def pp_as_ctype : String = "int" + } + + /** type of integers (used to represent the values of variables in a C program) */ + case object CoqZType extends VSTType { + override def pp: String = "Z" + } + + + case object CoqIntSetType extends VSTType { + override def pp: String = "(list Z)" + } + + /** type of natural numbers (used to type metavariables in Coq proofs) */ + case class CoqCardType(pred_type: String) extends VSTType { + override def pp: String = s"${pred_type}_card" + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala deleted file mode 100644 index 6d6622ad5..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/CTypes.scala +++ /dev/null @@ -1,58 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.clang - -object CTypes { - - - sealed trait AnalysisVSTTypes extends PrettyPrinting { - - /** the Analysis VST type encodes more precise information about pointers - * than is stored in a VST proof - after the analysis, we can drop this - * additional information */ - def downcast : VSTCType = this match { - case tType: VSTCType => tType - case _: PtrType => CVoidPtrType - case pureType: PureType =>pureType.asInstanceOf[VSTCType] - } - } - - /** types used in a C program */ - sealed abstract class VSTCType extends PrettyPrinting with AnalysisVSTTypes - sealed trait PtrType extends AnalysisVSTTypes - sealed trait PureType extends AnalysisVSTTypes - - case object CIntType extends VSTCType with PureType { - override def pp: String = "int" - } - - case object CUnitType extends VSTCType with PureType { - override def pp: String = "void" - } - - /** encoding of void pointer types */ - case object CVoidPtrType extends VSTCType with PureType { - override def pp: String = s"loc" - } - - // the following types are only used in the program analysis, so don't form part of the - // VSTType hierarchy, but can be used to track additional information - - /** encoding of void ** pointer types */ - case object CVoidPtrPtrType extends PtrType with PrettyPrinting { - override def pp: String = s"void **" - } - /** encoding of int* pointer types */ - case object CIntPtrType extends PtrType with PrettyPrinting { - override def pp: String = s"int *" - } - - def deref_type(ptrType: PtrType) = ptrType match { - case CVoidPtrPtrType => CVoidPtrType - case CIntPtrType => CIntType - } - - def ptr_type(elem_type: PureType) = elem_type match { - case CIntType => CIntPtrType - case CUnitType => CVoidPtrType - case CVoidPtrType => CVoidPtrPtrType - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index db1440063..fca6bbde4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -1,105 +1,83 @@ package org.tygus.suslik.certification.targets.vst.clang -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType +import org.tygus.suslik.certification.targets.vst.Types.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{ForwardIf, ForwardIfConstructor} import org.tygus.suslik.certification.targets.vst.translation.Translation.{TranslationException, fail_with} +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.logic.Specifications.GoalLabel /** Encoding of C statements */ object Statements { - /** pretty printing a VST C Statement returns a C embedding */ - sealed abstract class CStatement extends PrettyPrinting { } - - // skip - case object CSkip extends CStatement { - override def pp: String = { - "return;" + implicit object ProofTreePrinter extends ProofTreePrinter[StatementStep] { + override def pp(tree: ProofTree[StatementStep]): String = tree.step match { + case CIf(cond) => + s"if (${cond.pp} {\n" + + s"${tree.children(0).pp}" + + s"} else {\n" + + s"${tree.children(1).pp}" + + s"\n}" + case _ => tree.step.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") } } + /** pretty printing a VST C Statement returns a C embedding */ + sealed abstract class StatementStep extends DestStep { } - // ?? - case object CHole extends CStatement { - override def pp: String = { ??? } - } - - // assert false - case object CError extends CStatement { - override def pp: String = ??? + // skip + case object CSkip extends StatementStep { + override def pp: String = { "return;" } } // let to = malloc(n) - case class CMalloc(to: CVar, tpe: VSTCType, sz: Int = 1) extends CStatement { + case class CMalloc(to: String, sz: Int = 1) extends StatementStep { override def pp: String = - s"${tpe.pp} ${to.pp} = (${tpe.pp})malloc(${sz.toString} * sizeof(${tpe.pp}));" + s"loc ${to} = (loc) malloc(${sz.toString} * sizeof(loc));" } // free(v) - case class CFree(v: CVar) extends CStatement { - override def pp: String = s"free(${v.pp});" + case class CFree(v: String) extends StatementStep { + override def pp: String = s"free(${v});" } - /** encoding of a load operation - * let to = *from.offset - * */ - case class CLoad(to: CVar, elem_ty: VSTCType, from: CVar, - offset: Int = 0) extends CStatement { - override def pp: String = - elem_ty match { - case CTypes.CIntType => s"${elem_ty.pp} ${to.pp} = READ_INT(${from.pp}, ${offset});" - case CTypes.CVoidPtrType => s"${elem_ty.pp} ${to.pp} = READ_LOC(${from.pp}, ${offset});" - case CTypes.CUnitType => fail_with("inconsistent program - loading into a void variable") - } + case class CLoadInt(to: String, from: String, offset: Int = 0) extends StatementStep { + override def pp: String = s"int ${to} = READ_INT(${from}, ${offset});" } - /** Encoding of a store operation - * *to.offset = e */ - case class CStore(to: CVar, elem_ty: VSTCType, offset: Int, e: CExpr) extends CStatement { - override def pp: String = { - elem_ty match { - case CTypes.CIntType => s"WRITE_INT(${to.pp}, ${offset}, ${e.pp});" - case CTypes.CVoidPtrType => s"WRITE_LOC(${to.pp}, ${offset}, ${e.pp});" - case CTypes.CUnitType => fail_with("inconsistent program - writing into a void variable") - } - } + case class CLoadLoc(to: String, from: String, offset: Int = 0) extends StatementStep { + override def pp: String = s"loc ${to} = READ_LOC(${from}, ${offset});" } - /** encoding of a function call f(args) */ - case class CCall(fun: CVar, args: Seq[CExpr], companion: Option[GoalLabel]) extends CStatement { - override def pp: String = s"${fun.pp}(${args.map(_.pp).mkString(", ")});" + case class CWriteInt(to: CVar, value: CExpr, offset: Int = 0) extends StatementStep { + override def pp : String = + s"WRITE_INT(${to}, ${offset}, ${value.pp});" } - /** Encoding of sequential composition - * - * s1; s2 */ - case class CSeqComp(s1: CStatement, s2: CStatement) extends CStatement { - override def ppIndent(depth: Int): String = - s"${s1.ppIndent(depth)}\n${s2.ppIndent(depth)}" + case class CWriteLoc(to: CVar, value: CExpr, offset: Int = 0) extends StatementStep { + override def pp : String = + s"WRITE_LOC(${to}, ${offset}, ${value.pp});" + } + + /** encoding of a function call f(args) */ + case class CCall(fun: String, args: Seq[CExpr]) extends StatementStep { + override def pp: String = s"${fun}(${args.map(_.pp).mkString(", ")});" } /** Encoding of statement * if (cond) { tb } else { eb } * */ - case class CIf(cond: CExpr, tb: CStatement, eb: CStatement) extends CStatement { - override def ppIndent(depth: Int): String = - s"${getIndent(depth)}if(${cond.pp}) {\n${tb.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${eb.ppIndent(depth+1)}\n${getIndent(depth)}}" - } - - /** - * Encoding of a statement: - * assume cond { body } else { els } - * */ - case class CGuarded(cond: CExpr, body: CStatement, els: CStatement, branchPoint: GoalLabel) extends CStatement { - override def ppIndent(depth: Int): String = - s"${getIndent(depth)}if(${cond.pp}) {\n${body.ppIndent(depth+1)}\n${getIndent(depth)}} else {\n${els.ppIndent(depth+1)}\n${getIndent(depth)}}\n" + case class CIf(cond: CExpr) extends StatementStep { + override def pp : String = + s"if(${cond.pp})" } /** Definition of a CProcedure */ case class CProcedureDefinition( name: String, - rt: VSTCType, - params: Seq[(CVar, VSTCType)], - body: CStatement + params: Seq[(String, VSTCType)], + body: ProofTree[StatementStep] ) extends PrettyPrinting { val c_prelude = """ @@ -120,11 +98,11 @@ object Statements { |""".stripMargin override def pp: String = { - val body_string = body.ppIndent(1) + val body_string = body.pp val function_def = - s"${rt.pp} ${name}(${ + s"void ${name}(${ params.map({case (variable_name, variable_ty) => - s"${variable_ty.pp} ${variable_name.pp}" + s"${variable_ty.pp_as_ctype} ${variable_name}" }).mkString(", ") }) {\n${body_string}\n}\n" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala deleted file mode 100644 index a8a6cd628..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/package.scala +++ /dev/null @@ -1,16 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType - - -/** - * package contains encoding of suslik terms in C format */ -package object clang { - /** Typing context - maps variables to types */ - type CGamma = Map[CVar, VSTCType] - /** Formal parameter specification - types and names of parameters */ - type CFormals = Seq[(VSTCType, CVar)] - - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala new file mode 100644 index 000000000..bd3dd6561 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -0,0 +1,235 @@ +package org.tygus.suslik.certification.targets.vst.logic + +import org.tygus.suslik.certification.targets.vst.Types +import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqIntSetType, CoqIntValType, CoqPtrValType, CoqZType, VSTType} +import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException + + +/** Redefinition of expressions for use in VST proofs + * */ +object Expressions { + + /** encoding of expressions in VST proof script + * By default any boolean value will print to prop + * use pp_as_bool_value to print as a boolean + */ + sealed abstract class ProofCExpr extends PrettyPrinting { + + /** Applies a substitution to an expression */ + def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { + case expr@ProofCVar(name, _) => mapping.get(name) match { + case Some(value) => value.subst(mapping) + case None => expr + } + case expr@ProofCBoolConst(_) => expr + case expr@ProofCIntConst(_) => expr + case ProofCIntSetLiteral(elems) => + ProofCIntSetLiteral(elems.map(_.subst(mapping))) + case ProofCIfThenElse(cond, left, right) => + ProofCIfThenElse(cond.subst(mapping), left.subst(mapping), right.subst(mapping)) + case ProofCBinaryExpr(op, left, right) => + ProofCBinaryExpr(op, left.subst(mapping), right.subst(mapping)) + case ProofCUnaryExpr(op, e) => + ProofCUnaryExpr(op, e.subst(mapping)) + case ProofCCardinalityConstructor(pred_type, name, args) => + ProofCCardinalityConstructor(pred_type, name, args.map(_.subst(mapping))) + } + + def type_expr: VSTType = this match { + case ProofCVar(name, typ) => typ + case ProofCBoolConst(value) => throw new TranslationException("Attempted to type a boolean constant") + case ProofCIntConst(value) => CoqZType + case ProofCNullval => CoqPtrValType + case ProofCIntSetLiteral(elems) => CoqIntSetType + case ProofCIfThenElse(cond, left, right) => left.type_expr + case ProofCBinaryExpr(op, left, right) => op match { + case ProofCOpUnion | ProofCOpDiff | ProofCOpIntersect => CoqIntSetType + case ProofCOpPlus | ProofCOpMinus | ProofCOpMultiply => CoqZType + case ProofCOpImplication | ProofCOpSubset + | ProofCOpZEq | ProofCOpIntValEq | ProofCOpPtrValEq + | ProofCOpBoolEq | ProofCOpSetEq | ProofCOpLeq + | ProofCOpLt | ProofCOpAnd | ProofCOpOr | ProofCOpIn => + throw TranslationException(s"Attempted to type a comparison") + } + case ProofCUnaryExpr(op, e) => e.type_expr + case ProofCCardinalityConstructor(pred_type, _, _) => CoqCardType(pred_type) + } + + /** prints the expression as though it were an element + * of type val (vst's encoding of C-values) + */ + def pp_as_c_value: String = this match { + case ProofCVar(name, typ) => typ match { + case CoqPtrValType => s"(${name} : val)" + case CoqIntValType => s"(${name} : val)" + case CoqZType => s"((Vint (Int.repr ${name})) : val)" + case _ => throw TranslationException(s"Attempt to print incompatible type ${typ.pp} as a term of type val") + } + case ProofCIntConst(value) => s"((Vint (Int.repr ${value.toString})) : val)" + case v => v.type_expr match { + case Types.CoqPtrValType => v.pp + case Types.CoqIntValType => v.pp + case Types.CoqZType => s"((Vint (Int.repr (${v.pp}))) : val)" + case v => throw TranslationException(s"Attempt to print incompatible value ${v.pp} as term of type val") + } + } + + def pp_as_ssl_union_value: String = { + this.type_expr match { + case Types.CoqPtrValType => s"(inr ${this.pp_as_c_value})" + case Types.CoqIntValType => s"(inl ${this.pp_as_c_value})" + case Types.CoqZType => s"(inl ${this.pp_as_c_value})" + case _ => throw TranslationException(s"Attempt to print incompatible value ${this.pp} as a term of type val") + } + } + + def pp_as_bool_value: String = this match { + case value@ProofCBinaryExpr(op, expr_a, expr_b) => + op match { + case ProofCOpImplication => s"(implb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpZEq => s"(Nat.eqb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpBoolEq => s"(eqb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpLeq => s"(Z.leb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpLt => s"(Z.ltb ${expr_a.pp} ${expr_b.pp})" + case ProofCOpAnd => s"(andb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpOr => s"(orb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" + case ProofCOpSetEq => s"(perm_eq ${expr_a.pp} ${expr_b.pp})" + case _ => throw TranslationException(s"Attempted to print invalid expression ${this.pp} as bool value.") + } + case value@ProofCUnaryExpr(op, _) => op match { + case ProofCOpNot => value.pp + case _ => throw TranslationException(s"Attempted to print invalid expression ${this.pp} as bool value.") + } + case _ => throw TranslationException(s"Attempted to print invalid expression ${this.pp} as bool value.") + } + + def pp_as_Z_value: String = + this.type_expr match { + case cType: Types.VSTCType => + cType match { + case Types.CoqPtrValType => + throw TranslationException(s"Attempted to print invalid expression ${this.pp} as a Z value.") + case Types.CoqIntValType => s"(force_signed_int ${this.pp})" + } + case Types.CoqZType => this.pp + case _ => throw TranslationException(s"Attempted to print invalid expression ${this.pp} as a Z value.") + } + } + + case class ProofCCardinalityConstructor(pred_type: String, name: String, args: List[ProofCExpr]) extends ProofCExpr { + override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type}_card)" + } + + + /** a variable in a VST proof */ + case class ProofCVar(name: String, typ: VSTType) extends ProofCExpr { + override def pp: String = typ match { + case CoqPtrValType => s"(${name} : val)" + case CoqIntValType => s"(${name} : val)" + case CoqZType => s"(${name} : Z)" + case CoqCardType(ty) => s"(${name} : ${ty}_card)" + case CoqIntSetType => s"(${name} : list Z)" + } + } + + /** boolean constant in a VST proof */ + case class ProofCBoolConst(value: Boolean) extends ProofCExpr { + override def pp: String = value.toString + } + + /** integer constant in a VST proof */ + case object ProofCNullval extends ProofCExpr { + override def pp: String = "nullval" + } + + /** integer constant in a VST proof */ + case class ProofCIntConst(value: Int) extends ProofCExpr { + override def pp: String = + value.toString + } + + /** set literal (encoded as set) in a VST proof */ + case class ProofCIntSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { + override def pp: String = + s"([${elems.map(_.pp).mkString("; ")}] : list Z)" + } + + /** encodes a ternary expression in a VST proof */ + case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" + } + + case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpLt => s"(${left.pp_as_Z_value} < ${right.pp_as_Z_value})" + case ProofCOpLeq => s"(${left.pp_as_Z_value} <= ${right.pp_as_Z_value})" + case ProofCOpOr => s"(${left.pp} \\/ ${right.pp})" + case ProofCOpAnd => s"(${left.pp} /\\ ${right.pp})" + case ProofCOpPlus => s"(${left.pp} + ${right.pp})" + case ProofCOpMinus => s"(${left.pp} - ${right.pp})" + case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" + case ProofCOpZEq => s"(${left.pp} = ${right.pp})" + case ProofCOpBoolEq => s"(${left.pp} = ${right.pp})" + case ProofCOpPtrValEq => s"(${left.pp} = ${right.pp})" + case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" + case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" + // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" + } + } + + case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { + override def pp: String = + op match { + case ProofCOpNot => s"(~ ${e.pp})" + case ProofCOpUnaryMinus => s"(-(${e.pp}))" + } + } + + sealed abstract class ProofCUnOp + + object ProofCOpNot extends ProofCUnOp + + object ProofCOpUnaryMinus extends ProofCUnOp + + sealed abstract class ProofCBinOp + + object ProofCOpImplication extends ProofCBinOp + + object ProofCOpPlus extends ProofCBinOp + + object ProofCOpMinus extends ProofCBinOp + + object ProofCOpMultiply extends ProofCBinOp + + object ProofCOpZEq extends ProofCBinOp + + object ProofCOpIntValEq extends ProofCBinOp + + object ProofCOpPtrValEq extends ProofCBinOp + + object ProofCOpBoolEq extends ProofCBinOp + + object ProofCOpSetEq extends ProofCBinOp + + object ProofCOpLeq extends ProofCBinOp + + object ProofCOpLt extends ProofCBinOp + + object ProofCOpAnd extends ProofCBinOp + + object ProofCOpOr extends ProofCBinOp + + object ProofCOpIn extends ProofCBinOp + + object ProofCOpSubset extends ProofCBinOp + + object ProofCOpUnion extends ProofCBinOp + + object ProofCOpDiff extends ProofCBinOp + + object ProofCOpIntersect extends ProofCBinOp + +} + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index 3ab8e3f2a..eb8e21ffb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,38 +1,24 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.clang.{CFormals, CTypes} -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.clang.Expressions.CExpr -import ProofTerms.Expressions -import ProofTerms.Expressions.ProofCExpr -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqIntType, CoqListType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType, VSTType} +import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.PrettyPrinting object Formulae { /** abstract type categorizing all spatial formulae */ - sealed abstract class VSTHeaplet extends PrettyPrinting + trait VSTHeaplet extends PrettyPrinting /** Spatial formulae * =========================================== - * */ + **/ - /** spatial formuala indicating points to - loc + offset :-> value */ - case class CDataAt(loc: ProofCExpr, elem_typ: VSTProofType, count: Int, elem: ProofCExpr) extends VSTHeaplet { - // returns the type of a proof expression - override def pp: String = { -// s"(data_at Tsh ${elem_typ.pp_as_ctype} ${elem.pp_as_c_value} ${loc.pp})" - elem_typ match { - case ProofTypes.CoqParamType(CTypes.CVoidPtrType) => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [${elem.pp_as_ssl_union_value}] ${loc.pp})" - case ProofTypes.CoqParamType(_) => s"(data_at Tsh (tarray ${elem_typ.pp_as_ctype} 1) [${elem.pp_as_ssl_union_value}] ${loc.pp})" - case ProofTypes.CoqPtrType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inr ${elem.pp_as_c_value}] ${loc.pp})" - case ProofTypes.CoqIntType => s"(data_at Tsh (tarray (Tunion _sslval noattr) 1) [inl ${elem.pp_as_c_value}] ${loc.pp})" - case ProofTypes.CoqCardType(pred_type) => assert(false, "data at pointing to meta variable"); ??? - case CoqListType(_, Some (length)) => - s"(data_at Tsh (tarray (Tunion _sslval noattr) ${length}) ${elem.pp_as_ssl_union_value} ${loc.pp})" - } - } + /** spatial formuala indicating points to - i.e loc :-> [elem1; elem2;...] */ + case class CDataAt(loc: Expressions.ProofCExpr, elems: List[Expressions.ProofCExpr]) extends VSTHeaplet { + def count = elems.length + // returns the type of a proof expression + override def pp: String = s"(data_at Tsh (tarray (Tunion _sslval noattr) ${count.toString}) [${elems.map(_.pp_as_ssl_union_value).mkString("; ")}] ${loc.pp})" } /** predicate application @@ -40,19 +26,19 @@ object Formulae { * @param pred predicate name * @param args arguments * @param card cardinality of call - **/ - case class CSApp(pred: String, var args: Seq[ProofCExpr], card: ProofCExpr) extends VSTHeaplet { + * */ + case class CSApp(pred: String, var args: Seq[Expressions.ProofCExpr], card: Expressions.ProofCExpr) extends VSTHeaplet { override def pp: String = s"(${pred} ${(args ++ List(card)).map(_.pp).mkString(" ")})" } /** Spatial Formula + * * @param apps applications in the spatial formula - * */ + **/ case class VSTSFormula(apps: Seq[CSApp], data_at: Seq[CDataAt]) { override def productIterator: Iterator[VSTHeaplet] = (apps.iterator ++ data_at.iterator) - } - + } -} +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 4fb06dd0e..42120634f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms._ import org.tygus.suslik.language.PrettyPrinting @@ -19,17 +19,17 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. /** C standard library specs */ private def free_defs : String = """Definition free_spec := - | DECLARE _free - | WITH ty: type, x: val - | PRE [ (tptr tvoid) ] - | PROP() - | PARAMS(x) - | SEP (data_at_ Tsh ty x) - | POST [ Tvoid ] - | PROP() - | LOCAL() - | SEP (emp). - |""".stripMargin + | DECLARE _free + | WITH ty: type, x: val + | PRE [ (tptr tvoid) ] + | PROP() + | PARAMS(x) + | SEP (data_at_ Tsh ty x) + | POST [ Tvoid ] + | PROP() + | LOCAL() + | SEP (emp). + |""".stripMargin private def malloc_defs : String = """Definition malloc_spec := | DECLARE _malloc @@ -58,17 +58,17 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. override def pp: String = { coq_prelude + - (if (uses_free) { free_defs + "\n" } else { "" }) + - (if (uses_malloc) { malloc_defs + "\n" } else { "" }) + + (if (uses_free) { free_defs + "\n" } else { "" }) + + (if (uses_malloc) { malloc_defs + "\n" } else { "" }) + predicates.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"+ library_spec + "\n" + - lemma_prelude + - "start_function.\n" + + lemma_prelude + + "start_function.\n" + "ssl_open_context.\n" + steps.pp + "\n" + - "Qed." + "Qed." } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala deleted file mode 100644 index 9834b710f..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofSteps.scala +++ /dev/null @@ -1,177 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.logic - -import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting -import ProofTerms.CardConstructor -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType -import org.tygus.suslik.language.Ident - -sealed abstract class ProofSteps extends PrettyPrinting { - - def to_validity_assertion(var_name: Ident, var_type: VSTProofType): Option[ProofSteps] = { - var_type match { - case ProofTypes.CoqParamType(ty) => None - case ProofTypes.CoqPtrType => None - case ProofTypes.CoqIntType => None - case ProofTypes.CoqCardType(pred_type) => None - case ProofTypes.CoqListType(elem, length) => None - } - } - -} - -object ProofSteps { - - case class IntroEvar(variable: ProofCVar, next: ProofSteps) extends ProofSteps { - override def pp: String = s"try evar ${variable.pp}.\n${next.pp}" - } - - case class InstantiateEvar(name: Ident, value: ProofCExpr, next: ProofSteps) extends ProofSteps { - override def pp: String = s"instantiate (${name} := ${value.pp}).\n${next.pp}" - } - - case class Entailer(next: ProofSteps) extends ProofSteps { - override def pp: String = s"entailer!.\n${next.pp}" - } - - case class ForwardIfConstructor( - card_variable: String, - predicate_name: String, - branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String], ProofSteps)] - ) extends ProofSteps { - override def pp: String = { - def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { - case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" - case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" - } - - val branch_strings = - branches match { - case Nil => "" - case _ => - "\n" ++ branches.map( - { case ((cons_name, cons, cons_args), expr, args, ls) => - " - {\n" ++ - s"assert_PROP (${constructor_prop(cons_name, cons)}) as ssl_card_assert. { entailer!; ssl_dispatch_card. }\n" ++ - s"ssl_card ${predicate_name} ssl_card_assert ${cons_args.mkString(" ")}.\n" ++ - s"assert_PROP (${expr.pp}). { entailer!. }\n" ++ - (args match { - case Nil => "" - case args => s"Intros ${args.mkString(" ")}.\n" - }) ++ - ls.pp ++ - "\n}" - } - ).mkString("\n") - } - "forward_if." ++ branch_strings - } - } - - case class ForwardIf(branches: List[ProofSteps]) extends ProofSteps { - override def pp: String = { - val branch_strings = - branches match { - case Nil => "" - case _ => "\n" ++ branches.map(ls => " - {\n" ++ ls.pp ++ "\n}").mkString("\n") - } - "forward_if." ++ branch_strings - } - } - - case class Forward(next: ProofSteps) extends ProofSteps { - override def pp: String = s"forward.\n${next.pp}" - } - - case class Intros(variables: List[(Ident, VSTProofType)], next: ProofSteps) extends ProofSteps { - override def pp: String = { - val extra_assertions = - variables.flatMap({ case (var_name, var_type) => - to_validity_assertion(var_name, var_type) - }) match { - case Nil => "" - case ls => "\n" ++ ls.map(_.pp).mkString(".\n") - } - s"Intros ${variables.map(_._1).mkString(" ")}." ++ extra_assertions ++ s"\n${next.pp}" - } - } - - case class IntrosTuple(variables: List[(Ident, VSTProofType)], next: ProofSteps) extends ProofSteps { - override def pp: String = { - val extra_assertions: String = - variables.flatMap({ case (var_name, var_type) => - to_validity_assertion(var_name, var_type) - }) match { - case Nil => "" - case ls => "\n" ++ ls.map(_.pp).mkString(".\n") - } - - variables match { - case Nil => s"${next.pp}" - case ::((variable, _), Nil) => - s"Intros ${variable}." ++ extra_assertions ++ s"\n${next.pp}" - case _ => - def to_destruct_pattern(base: Option[String])(ls: List[(Ident, VSTProofType)]): String = { - (base, ls) match { - case (None, ::((vara, _), ::((varb, _), rest))) => to_destruct_pattern(Some(s"[${vara} ${varb}]"))(rest) - case (Some(base), ::((vara, _), rest)) => - to_destruct_pattern(Some(s"[${base} ${vara}]"))(rest) - case (Some(base), Nil) => base - } - } - - val destruct_pattern: String = to_destruct_pattern(None)(variables) - s"let ret := fresh vret in Intros ret; destruct ret as ${destruct_pattern}." ++ extra_assertions ++ s"\n${next.pp}" - } - } - } - - case class ValidPointerOrNull(variable: Ident, next: ProofSteps) extends ProofSteps { - override def pp: String = s"assert_PROP (is_pointer_or_null ${variable}). { entailer!. }\n${next.pp}" - } - - case class ValidPointer(variable: Ident, next: ProofSteps) extends ProofSteps { - override def pp: String = s"assert_PROP (isptr ${variable}). { entailer!. }\n${next.pp}" - } - - case class ForwardCall(args: List[Ident], next: ProofSteps) extends ProofSteps { - override def pp: String = s"forward_call (${args.mkString(", ")}).\n${next.pp}" - } - - case class Rename(old_name: Ident, new_name: Ident, next: ProofSteps) extends ProofSteps { - override def pp: String = s"try rename ${old_name} into ${new_name}.\n${next.pp}" - } - - case class Exists(variable: ProofCExpr, next: ProofSteps) extends ProofSteps { - override def pp: String = s"Exists ${variable.pp}.\n${next.pp}" - } - - case class Free(variable: Ident, sz: Int, next: ProofSteps) extends ProofSteps { - override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${sz}, ${variable}).\n${next.pp}" - } - - case class Malloc(size: Int, next: ProofSteps) extends ProofSteps { - override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${size}).\n${next.pp}" - } - - case class AssertPropSubst(variable: Ident, expr: ProofCExpr, next: ProofSteps) extends ProofSteps { - override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp_as_ptr_value}) as ssl_var; try rewrite ssl_var. { entailer!. }\n${next.pp}" - } - - case object Qed extends ProofSteps { - override def pp: String = "" - } - - case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCExpr, next: ProofSteps) extends ProofSteps { - override def pp: String = - s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1.\n${next.pp}" - - } - - case class ForwardEntailer(next: ProofSteps) extends ProofSteps { - override def pp: String = - s"forward; entailer!.\n${next.pp}" - - } - -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 933f34a24..f208cc8ca 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,332 +1,25 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar -import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} +import org.tygus.suslik.certification.targets.vst.Types._ +import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqBoolType, CoqCardType, CoqIntType, CoqListType, CoqParamType, VSTProofType} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Ident -import org.tygus.suslik.logic.SApp object ProofTerms { - sealed abstract class PureFormula extends PrettyPrinting + trait PureFormula extends PrettyPrinting /** predicate encoding that C-parameter (of type val) is a valid pointer */ - case class IsValidPointerOrNull(name: CVar) extends PureFormula { + case class IsValidPointerOrNull(name: String) extends PureFormula { override def pp: String = - s"is_pointer_or_null(${name.pp})" + s"is_pointer_or_null(${name})" } /** predicate encoding that C-parameter (of type val) is a valid int */ - case class IsValidInt(name: CVar) extends PureFormula { + case class IsValidInt(name: String) extends PureFormula { override def pp: String = - s"ssl_is_valid_int(${name.pp})" - } - - /** Redefinition of expressions for use in VST proofs - **/ - object Expressions { - - /** encoding of expressions in VST proof script - * By default any boolean value will print to prop - * use pp_as_bool_value to print as a boolean - */ - sealed abstract class ProofCExpr extends PrettyPrinting { - - /** Applies a substitution to an expression */ - def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { - case expr@ProofCVar(name, _) => mapping.get(name) match { - case Some(value) => value.subst(mapping) - case None => expr - } - case expr@ProofCBoolConst(_) => expr - case expr@ProofCIntConst(_, _) => expr - case ProofCSetLiteral(elems, ty) => - ProofCSetLiteral(elems.map(_.subst(mapping)), ty) - case ProofCIfThenElse(cond, left, right) => - ProofCIfThenElse(cond.subst(mapping), left.subst(mapping), right.subst(mapping)) - case ProofCBinaryExpr(op, left, right) => - ProofCBinaryExpr(op, left.subst(mapping), right.subst(mapping)) - case ProofCUnaryExpr(op, e) => - ProofCUnaryExpr(op, e.subst(mapping)) - case ProofCCardinalityConstructor(pred_type, name, args) => - ProofCCardinalityConstructor(pred_type, name, args.map(_.subst(mapping))) - } - - def type_expr: VSTProofType = this match { - case ProofCVar(name, typ) => typ - case ProofCBoolConst(value) => CoqBoolType - case ProofCIntConst(value, false) => CoqIntType - case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems, ty) => CoqListType(ty.getOrElse(elems.head.type_expr), Some(elems.length)) - case ProofCIfThenElse(cond, left, right) => left.type_expr - case ProofCBinaryExpr(op, left, right) => op match { - case ProofCOpPlus => CoqIntType - case ProofCOpMinus => CoqIntType - case ProofCOpMultiply => CoqIntType - case _ => CoqBoolType - } - case ProofCUnaryExpr(op, e) => e.type_expr - case ProofCCardinalityConstructor(pred_type, _, _) => CoqCardType(pred_type) - } - - /** prints the expression as though it were an element - * of type val (vst's encoding of C-values) - */ - def pp_as_c_value: String = this match { - case ProofCVar(name, typ) => typ match { - case ProofTypes.CoqParamType(ty) => name - case ProofTypes.CoqPtrType => name - case ProofTypes.CoqIntType => s"(Vint (Int.repr ${name}))" - case ProofTypes.CoqListType(elem, length) => name - case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") - } - case ProofCIntConst(value, false) => s"(Vint (Int.repr ${value.toString}))" - case ProofCSetLiteral(elems, ty) => - s"([${elems.map(_.pp_as_c_value).mkString("; ")}] : list val)" - case value@ProofCBinaryExpr(op, _, _) => - val is_int = op match { - case ProofCOpPlus => true - case ProofCOpMinus => true - case ProofCOpMultiply => true - case _ => false - } - if (is_int) { - s"(Vint (Int.repr ${value.pp}))" - } else { - value.pp - } - case value@ProofCUnaryExpr(op, _) => op match { - case ProofCOpNot => value.pp - case ProofCOpUnaryMinus => s"(Vint (Int.repr ${value.pp}))" - } - case v => v.pp - } - - def pp_as_ssl_union_value: String = this match { - case ProofCVar(name, typ) => typ match { - case ProofTypes.CoqParamType(ty) => ty match { - case CTypes.CIntType => s"inl ${name}" - case CTypes.CVoidPtrType => s"inl ${name}" - } - case ProofTypes.CoqPtrType => s"inr ${name}" - case ProofTypes.CoqIntType => s"inl (Vint (Int.repr ${name}))" - case ProofTypes.CoqListType(elem, length) => name - case ProofTypes.CoqCardType(_) => throw TranslationException("Error: inconsistent assumptions, attempting to print a cardinality as a c type") - } - case ProofCIntConst(value, false) => s"inl (Vint (Int.repr ${value.toString}))" - case ProofCIntConst(0, true) => s"inr nullval" - case ProofCSetLiteral(elems, ty) => - s"([${elems.map(_.pp_as_ssl_union_value).mkString("; ")}])" - case value@ProofCBinaryExpr(op, _, _) => - val is_int = op match { - case ProofCOpPlus => true - case ProofCOpMinus => true - case ProofCOpMultiply => true - case _ => false - } - if (is_int) { - s"inl (Vint (Int.repr ${value.pp}))" - } else { - throw TranslationException("Error: inconsistent assumptions, attempting to print an arithmetic operation on non-integral values as a c expression.") - } - case value@ProofCUnaryExpr(op, _) => op match { - case ProofCOpUnaryMinus => s"inl (Vint (Int.repr ${value.pp}))" - } - case v => v.pp - } - - def pp_as_bool_value: String = this match { - case value@ProofCBinaryExpr(op, expr_a, expr_b) => - op match { - case ProofCOpImplication => s"(implb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" - case ProofCOpIntEq => s"(Nat.eqb ${expr_a.pp} ${expr_b.pp})" - case ProofCOpBoolEq => s"(eqb ${expr_a.pp} ${expr_b.pp})" - case ProofCOpLeq => s"(Z.leb ${expr_a.pp} ${expr_b.pp})" - case ProofCOpLt => s"(Z.ltb ${expr_a.pp} ${expr_b.pp})" - case ProofCOpAnd => s"(andb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" - case ProofCOpOr => s"(orb ${expr_a.pp_as_bool_value} ${expr_b.pp_as_bool_value})" - case ProofCOpIn => ??? // TODO: how to handle in operations? don't seem to show up in proofs - case ProofCOpSetEq => ??? // TODO: see above - case ProofCOpPtrEq => ??? // TODO: ditto - } - case value@ProofCUnaryExpr(op, _) => op match { - case ProofCOpNot => value.pp - } - } - - def pp_as_int_value: String = this match { - case ProofCVar(name, typ) => typ match { - case ProofTypes.CoqParamType(CTypes.CIntType) => s"(force_signed_int ${name})" - case ProofTypes.CoqIntType => s"${name}" - } - case ProofCIntConst(value, is_ptr) => value.toString - case ProofCIfThenElse(cond, left, right) => s"(if ${cond.pp_as_bool_value} then ${left.pp_as_int_value} else ${right.pp_as_int_value})" - case ProofCBinaryExpr(op, left, right) => op match { - case ProofCOpPlus => s"(${left.pp_as_int_value} + ${right.pp_as_int_value})" - case ProofCOpMinus => s"(${left.pp_as_int_value} - ${right.pp_as_int_value})" - case ProofCOpMultiply => s"(${left.pp_as_int_value} * ${right.pp_as_int_value})" - case ProofCOpIntEq => s"(${left.pp_as_int_value} =? ${right.pp_as_int_value})" - case ProofCOpLeq => s"(${left.pp_as_int_value} <=? ${right.pp_as_int_value})" - case ProofCOpLt => s"(${left.pp_as_int_value} op match { - case ProofCOpUnaryMinus => s"(- ${e.pp_as_int_value})" - } - } - - - /** print as a pointer value - * - * @throws NotImplementedError if expression is not a variable or 0-int */ - def pp_as_ptr_value: String = this match { - case ProofCVar(name, typ) => name - case ProofCBoolConst(value) => this.pp - case ProofCIntConst(value, _) => if (value == 0) { - "nullval" - } else { - this.pp - } - case ProofCSetLiteral(elems, _) => this.pp - case ProofCIfThenElse(cond, left, right) => this.pp - case ProofCBinaryExpr(op, left, right) => this.pp - case ProofCUnaryExpr(op, e) => this.pp - } - - } - - case class ProofCCardinalityConstructor(pred_type: String, name: String, args: List[ProofCExpr]) extends ProofCExpr { - override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type}_card)" - } - - - /** a variable in a VST proof */ - case class ProofCVar(name: String, typ: VSTProofType) extends ProofCExpr { - override def pp: String = typ match { - case ProofTypes.CoqPtrType => name - case ProofTypes.CoqIntType => name - case ProofTypes.CoqCardType(ty) => s"(${name} : ${ty}_card)" - case ProofTypes.CoqParamType(ty) => - // if the variable has a param type then - // its actually of type val, and we need to - // extract it's contained value - ty match { - case CTypes.CIntType => s"(${name})" - case CTypes.CVoidPtrType => s"${name}" - case CTypes.CUnitType => throw new TranslationException("Error: inconsistent assumptions, attempting to create a variable of unit type") - } - case ProofTypes.CoqListType(elem, _) => name - } - } - - /** boolean constant in a VST proof */ - case class ProofCBoolConst(value: Boolean) extends ProofCExpr { - override def pp: String = value.toString - } - - /** integer constant in a VST proof */ - case class ProofCIntConst(value: Int, is_ptr: Boolean) extends ProofCExpr { - override def pp: String = if (is_ptr && value == 0) { - "nullval" - } else { - value.toString - } - } - - /** set literal (encoded as set) in a VST proof */ - case class ProofCSetLiteral(elems: List[ProofCExpr], elem_type : Option[VSTProofType]=None) extends ProofCExpr { - override def pp: String = - s"([${elems.map(_.pp_as_c_value).mkString("; ")}]${(elems, elem_type) match { - case (::(_,_), _) => "" - case (_, Some(ty)) => s": ${ty.pp}" - case (_, _) => "" - }})" - } - - /** encodes a ternary expression in a VST proof */ - case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" - - // TODO: if statements don't seem to be used inside suslik proofs, so implementing this would be pointless - // if this assumption changes, then the correct implementation will look something like: - // - // s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" - // - // where pp_as_bool_value should be a method that prints expressions in boolean form - } - - case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { - override def pp: String = - op match { - case ProofCOpLt => s"(${left.pp_as_int_value} < ${right.pp_as_int_value})" - case ProofCOpLeq => s"(${left.pp_as_int_value} <= ${right.pp_as_int_value})" - case ProofCOpOr => s"(${left.pp} \\/ ${right.pp})" - case ProofCOpAnd => s"(${left.pp} /\\ ${right.pp})" - case ProofCOpPlus => s"(${left.pp} + ${right.pp})" - case ProofCOpMinus => s"(${left.pp} - ${right.pp})" - case ProofCOpMultiply => s"(${left.pp} * ${right.pp})" - case ProofCOpIntEq => s"(${left.pp} = ${right.pp})" - case ProofCOpBoolEq => s"(${left.pp} = ${right.pp})" - case ProofCOpPtrEq => s"(${left.pp_as_ptr_value} = ${right.pp_as_ptr_value})" - case ProofCOpSetEq => s"(${left.pp} = ${right.pp})" - case ProofCOpUnion => s"(${left.pp} ++ ${right.pp})" - // case ProofCOpSetEq => s"(eqb_list _ ${left.pp} ${right.pp})" - } - } - - case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { - override def pp: String = - op match { - case ProofCOpNot => s"(~ ${e.pp})" - case ProofCOpUnaryMinus => s"(-(${e.pp}))" - } - } - - sealed abstract class ProofCUnOp - - object ProofCOpNot extends ProofCUnOp - - object ProofCOpUnaryMinus extends ProofCUnOp - - sealed abstract class ProofCBinOp - - object ProofCOpImplication extends ProofCBinOp - - object ProofCOpPlus extends ProofCBinOp - - object ProofCOpMinus extends ProofCBinOp - - object ProofCOpMultiply extends ProofCBinOp - - object ProofCOpIntEq extends ProofCBinOp - - object ProofCOpBoolEq extends ProofCBinOp - - object ProofCOpSetEq extends ProofCBinOp - - object ProofCOpPtrEq extends ProofCBinOp - - object ProofCOpLeq extends ProofCBinOp - - object ProofCOpLt extends ProofCBinOp - - object ProofCOpAnd extends ProofCBinOp - - object ProofCOpOr extends ProofCBinOp - - object ProofCOpIn extends ProofCBinOp - - object ProofCOpSubset extends ProofCBinOp - - object ProofCOpUnion extends ProofCBinOp - - object ProofCOpDiff extends ProofCBinOp - - object ProofCOpIntersect extends ProofCBinOp - + s"ssl_is_valid_int(${name})" } /** prop predicate encoding that a given propositional expression is true @@ -345,10 +38,10 @@ object ProofTerms { } - sealed case class FormalCondition( - pure_constraints: List[PureFormula], - spatial_constraints: List[VSTHeaplet] - ) + case class FormalCondition( + pure_constraints: List[PureFormula], + spatial_constraints: List[VSTHeaplet] + ) /** * Type encoding VST-compliant formal specifications of a C Function @@ -359,26 +52,23 @@ object ProofTerms { * @param existensial_params existential params of the function * @param precondition precondtion for the function * @param postcondition post condition of the function - * @param return_type return type of the function */ case class FormalSpecification( name: Ident, c_params: Seq[(Ident, VSTCType)], - formal_params: Seq[(Ident, VSTProofType)], - existensial_params: Seq[(Ident, VSTProofType)], + formal_params: Seq[(Ident, VSTType)], + existensial_params: Seq[(Ident, VSTType)], precondition: FormalCondition, postcondition: FormalCondition, - return_type: VSTCType ) extends PrettyPrinting { def as_vst_type(var_type: VSTCType) = var_type match { - case CTypes.CIntType => "tint" - case CTypes.CUnitType => "tvoid" - case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" + case CoqIntValType => "tint" + case CoqPtrValType => "(tptr (Tunion _sslval noattr))" + case _ => throw TranslationException(s"Attempt to convert invalid type ${var_type.pp} as a VST type term.") } - def params: List[(Ident, VSTProofType)] = - (c_params.map({ case (name, ty) => (name, CoqParamType(ty)) }) ++ formal_params).toList + def params: List[(Ident, VSTType)] = (c_params ++ formal_params).toList override def pp: String = { val formal_args = formal_params.map({ case (var_name, var_type) => s"${var_name}: ${var_type.pp}" }) @@ -392,7 +82,7 @@ object ProofTerms { | PROP( ${pre_pure_constraints.map(_.pp).mkString("; ")} ) | PARAMS(${c_params.map({ case (var_name, _) => var_name }).mkString("; ")}) | SEP (${pre_spatial_constraints.map(_.pp).mkString("; ")}) - | POST[ ${as_vst_type(return_type)} ]${ + | POST[ tvoid ]${ existensial_params match { case Nil => "" case _ => @@ -411,7 +101,7 @@ object ProofTerms { * Abstract constructors mapping cardinality constraints to * termination measures in Coq */ - sealed abstract class CardConstructor extends PrettyPrinting { + trait CardConstructor extends PrettyPrinting { def constructor_args: List[Ident] = this match { case CardNull => Nil @@ -433,7 +123,7 @@ object ProofTerms { /** * Represents helper lemmas and operations that are required to make VST handle the predicate automatically **/ - sealed trait VSTPredicateHelper extends PrettyPrinting + trait VSTPredicateHelper extends PrettyPrinting object VSTPredicateHelper { @@ -466,8 +156,8 @@ object ProofTerms { * * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause **/ - def predicate_to_determininant_term(clause: ProofTerms.VSTPredicateClause): String = - clause.pure.head.pp_as_ptr_value + def predicate_to_determininant_term(clause: VSTPredicateClause): String = + clause.pure.head.pp /** * * Converts a predicate clause into a corresponding fact. @@ -483,7 +173,7 @@ object ProofTerms { | ${predicate.name} ${predicate.formal_params.mkString(" ")}|-- !!(${ ( predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ - predicate.params.flatMap({ case (param, ProofTypes.CoqPtrType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) + predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) ).mkString("/\\") }). Proof. Admitted.""".stripMargin } @@ -498,28 +188,28 @@ object ProofTerms { * @param spatial are the spatial assertions * @param sub_constructor are the subconstructors **/ - case class VSTPredicateClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) { + case class VSTPredicateClause(pure: List[Expressions.ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) { val cardinality_param: String = "self_card" /** * @return the selector for the clause */ - def selector: ProofCExpr = pure.head + def selector: Expressions.ProofCExpr = pure.head /** finds existential variables in the expression using args */ - def find_existentials_args(args: Set[String]): List[(Ident, VSTProofType)] = { - def expr_existential: ProofCExpr => List[(Ident, VSTProofType)] = { + def find_existentials_args(args: Set[String]): List[(Ident, VSTType)] = { + def expr_existential: Expressions.ProofCExpr => List[(Ident, VSTType)] = { case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value, _) => Nil - case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(expr_existential) + case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCIntSetLiteral(elems) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) } - def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTProofType)] = { + def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTType)] = { case Formulae.CSApp(pred, args, card) => expr_existential(card) ::: args.flatMap(expr_existential).toList } @@ -528,22 +218,22 @@ object ProofTerms { } /** finds existential variables in the expression using args */ - def find_existentials(existentials: Map[Ident, VSTProofType]): List[(Ident, VSTProofType)] = { - def expr_existential: ProofCExpr => List[(Ident, VSTProofType)] = { + def find_existentials(existentials: Map[Ident, VSTType]): List[(Ident, VSTType)] = { + def expr_existential: Expressions.ProofCExpr => List[(Ident, VSTType)] = { case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value, _) => Nil - case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(expr_existential) + case Expressions.ProofCIntConst(value) => Nil + case Expressions.ProofCIntSetLiteral(elems) => elems.flatMap(expr_existential) case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) } - def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTProofType)] = { + def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTType)] = { case Formulae.CSApp(pred, args, card) => expr_existential(card) ::: args.flatMap(expr_existential).toList - case Formulae.CDataAt(loc, elem_typ, count, elem) => - (expr_existential(loc) ++ expr_existential(elem)) + case Formulae.CDataAt(loc, elems) => + (expr_existential(loc) ++ elems.flatMap(elem => expr_existential(elem))) } pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) @@ -564,8 +254,8 @@ object ProofTerms { * @param clauses is the mapping from cardinality constructors to clauses **/ case class VSTPredicate( - name: Ident, params: List[(String, VSTProofType)], - existentials: List[(String, VSTProofType)], + name: Ident, params: List[(String, VSTType)], + existentials: List[(String, VSTType)], clauses: Map[CardConstructor, VSTPredicateClause]) extends PrettyPrinting { @@ -582,7 +272,7 @@ object ProofTerms { * @param selector a expression corresponding to a selector of the predicate * @return cardinality and clause matched by predicate */ - def apply(selector: ProofCExpr): (CardConstructor, VSTPredicateClause) = + def apply(selector: Expressions.ProofCExpr): (CardConstructor, VSTPredicateClause) = clauses.find({ case (_, clause) => clause.selector.equals(selector) }).get @@ -590,7 +280,7 @@ object ProofTerms { def get_helpers: List[VSTPredicateHelper] = { val local_facts = VSTPredicateHelper.LocalFacts(this) params.flatMap({ - case (param, ProofTypes.CoqPtrType) => + case (param, CoqPtrValType) => val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) List( valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer") @@ -604,7 +294,7 @@ object ProofTerms { /** returns the existential variables introduced by a constructor invokation */ - def constructor_existentials(constructor: CardConstructor): List[(Ident, VSTProofType)] = { + def constructor_existentials(constructor: CardConstructor): List[(Ident, VSTType)] = { val param_map = params.toMap val existential_map = existentials.toMap.filterKeys(key => !param_map.contains(key)) val predicate = clauses(constructor) @@ -648,21 +338,21 @@ object ProofTerms { * @param pclause the corresponding clause of the predicate * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause **/ - def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTProofType)] = { + def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTType)] = { val param_map = params.toMap - val exist_map: Map[String, VSTProofType] = existentials.toMap + val exist_map: Map[String, VSTType] = existentials.toMap val card_map = cons.constructor_args pclause match { case VSTPredicateClause(pure, spatial, sub_clauses) => val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructor_args })).toSet - def to_variables(exp: ProofCExpr): List[String] = exp match { + def to_variables(exp: Expressions.ProofCExpr): List[String] = exp match { case Expressions.ProofCVar(name, typ) => param_map.get(name) match { case None if !clause_card_map.contains(name) => List(name) case _ => List() } - case Expressions.ProofCSetLiteral(elems, _) => elems.flatMap(to_variables) + case Expressions.ProofCIntSetLiteral(elems) => elems.flatMap(to_variables) case Expressions.ProofCIfThenElse(cond, left, right) => to_variables(cond) ++ to_variables(left) ++ to_variables(right) case Expressions.ProofCBinaryExpr(op, left, right) => @@ -673,8 +363,8 @@ object ProofTerms { } def to_variables_heap(heap: VSTHeaplet): List[String] = heap match { - case Formulae.CDataAt(loc, elem_typ, count, elem) => - to_variables(loc) ++ to_variables(elem) + case Formulae.CDataAt(loc, elems) => + to_variables(loc) ++ elems.flatMap(elem => to_variables(elem)) case Formulae.CSApp(pred, args, card) => args.flatMap(to_variables).toList } @@ -750,7 +440,7 @@ object ProofTerms { s"| | ${constructor_name(constructor)} ${ expand_args(sub_constructor)(constructor.constructor_args) } => ${ - val clause_existentials: List[(String, VSTProofType)] = find_existentials(constructor)(pclause) + val clause_existentials: List[(String, VSTType)] = find_existentials(constructor)(pclause) val str = clause_existentials.map({ case (name, ty) => s"| EX ${name} : ${ty.pp}," }).mkString("\n") clause_existentials match { case Nil => "" @@ -774,5 +464,3 @@ object ProofTerms { } - - diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala deleted file mode 100644 index 351380e1c..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTypes.scala +++ /dev/null @@ -1,61 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.logic - -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} - - -/** Encapsulates all types used in proofs - i.e if these types are pretty printed, then they will be valid Coq terms */ -object ProofTypes { - - def proof_type_of_c_type(vst: VSTCType) : VSTProofType = - vst match { - case CTypes.CIntType => CoqParamType(vst) - case CTypes.CVoidPtrType => CoqParamType(vst) - case CTypes.CUnitType => ??? - } - - /** proof types */ - sealed abstract class VSTProofType extends PrettyPrinting { - - /** prints the type as a term of type type (VST's definition) in Coq */ - def pp_as_ctype: String = - this match { - case CoqParamType(ty) => ty match { - case CTypes.CIntType => "tint" - case CTypes.CVoidPtrType => "(tptr (Tunion _sslval noattr))" - } - case CoqPtrType => "(tptr tvoid)" - case CoqIntType => "tint" - case CoqListType(elem, length) => s"(tarray ${elem.pp_as_ctype} ${length.get})" - } - } - - /** represents a val type in vst that maps to a parameter to the function of type ty */ - case class CoqParamType(ty: VSTCType) extends VSTProofType { - override def pp : String = "val" - } - - case object CoqPtrType extends VSTProofType { - override def pp: String = "val" - } - - case object CoqBoolType extends VSTProofType { - override def pp: String = ??? - } - - /** type of integers (used to represent the values of variables in a C program) */ - case object CoqIntType extends VSTProofType { - override def pp:String = "Z" - } - - /** type of natural numbers (used to type metavariables in Coq proofs) */ - case class CoqCardType(pred_type: String) extends VSTProofType { - override def pp:String = s"${pred_type}_card" - } - - sealed case class CoqListType(elem: VSTProofType, length: Option[Int]) extends VSTProofType { - override def pp: String = s"(list ${elem.pp})" - } - - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 09a655a5f..8260e062b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,12 +1,12 @@ package org.tygus.suslik.certification.targets.vst.logic +import org.tygus.suslik.certification.targets.vst.Types.VSTType import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.CardConstructor -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions.{ProofCExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.VSTProofType import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.{Ident, PrettyPrinting} + sealed abstract class VSTProofStep extends DestStep {} object VSTProofStep { @@ -18,13 +18,7 @@ object VSTProofStep { } } - case class IntroEvar(variable: ProofCVar) extends VSTProofStep { - override def pp: String = s"try evar ${variable.pp}." - } - case class InstantiateEvar(name: Ident, value: ProofCExpr) extends VSTProofStep { - override def pp: String = s"instantiate (${name} := ${value.pp})." - } case object Entailer extends VSTProofStep { override def pp: String = s"entailer!." @@ -33,7 +27,7 @@ object VSTProofStep { case class ForwardIfConstructor( card_variable: String, predicate_name: String, - branches: List[((Ident, CardConstructor, List[String]), ProofCExpr, List[String])] + branches: List[((Ident, CardConstructor, List[String]), Expressions.ProofCExpr, List[String])] ) extends VSTProofStep { def branch_strings[T <: PrettyPrinting] (children : List[T]): String = { @@ -84,19 +78,19 @@ object VSTProofStep { override def pp: String = s"forward." } - case class Intros(variables: List[(Ident, VSTProofType)]) extends VSTProofStep { + case class Intros(variables: List[(Ident, VSTType)]) extends VSTProofStep { override def pp: String = { s"Intros ${variables.map(_._1).mkString(" ")}." } } - case class IntrosTuple(variables: List[(Ident, VSTProofType)]) extends VSTProofStep { + case class IntrosTuple(variables: List[(Ident, VSTType)]) extends VSTProofStep { variables match { case Nil => "" case ::((variable, _), Nil) => s"Intros ${variable}." case _ => - def to_destruct_pattern(base: Option[String])(ls: List[(Ident, VSTProofType)]): String = { + def to_destruct_pattern(base: Option[String])(ls: List[(Ident, VSTType)]): String = { (base, ls) match { case (None, ::((vara, _), ::((varb, _), rest))) => to_destruct_pattern(Some(s"[${vara} ${varb}]"))(rest) case (Some(base), ::((vara, _), rest)) => @@ -125,7 +119,7 @@ object VSTProofStep { override def pp: String = s"try rename ${old_name} into ${new_name}." } - case class Exists(variable: ProofCExpr) extends VSTProofStep { + case class Exists(variable: Expressions.ProofCExpr) extends VSTProofStep { override def pp: String = s"Exists ${variable.pp}." } @@ -137,15 +131,15 @@ object VSTProofStep { override def pp: String = s"forward_call (tarray (Tunion _sslval noattr) ${size})." } - case class AssertPropSubst(variable: Ident, expr: ProofCExpr) extends VSTProofStep { - override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp_as_ptr_value}) as ssl_var; try rewrite ssl_var. { entailer!. }" + case class AssertPropSubst(variable: Ident, expr: Expressions.ProofCExpr) extends VSTProofStep { + override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp}) as ssl_var; try rewrite ssl_var. { entailer!. }" } case object Qed extends VSTProofStep { override def pp: String = "" } - case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: ProofCExpr) extends VSTProofStep { + case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: Expressions.ProofCExpr) extends VSTProofStep { override def pp: String = s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1." diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala deleted file mode 100644 index 82b9ed488..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/CTranslation.scala +++ /dev/null @@ -1,238 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.translation - -import org.tygus.suslik.certification.SuslikProofStep -import org.tygus.suslik.certification.targets.vst.clang.CTypes -import org.tygus.suslik.certification.targets.vst.clang.CTypes._ -import org.tygus.suslik.certification.targets.vst.clang.Expressions._ -import org.tygus.suslik.certification.targets.vst.clang.Statements._ -import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.language.Expressions._ -import org.tygus.suslik.language.Statements.{Procedure, Statement} -import org.tygus.suslik.language._ -import org.tygus.suslik.logic.Gamma - -/** encapsulates all functions for translating suslik terms to a C encoding */ -object CTranslation { - - def translate_body_from_proof(base_proof: ProofTree[SuslikProofStep], gamma: List[(CVar, VSTCType)]): CStatement = ??? - - - def translate_function_from_proof(base_proof: ProofTree[SuslikProofStep], gamma: Gamma): CProcedureDefinition = { - val init = base_proof.step.asInstanceOf[SuslikProofStep.Init] - val gamma = init.goal.programVars.map(v => (CVar(v.name), translate_type(init.goal.gamma(v)))) - CProcedureDefinition( - init.goal.fname, - CUnitType, - gamma, - translate_body_from_proof(base_proof.children(0), gamma) - ) - } - - - def translate_unary_expr(e1: UnaryExpr): CExpr = { - def translate_unary_op : UnOp => CUnOp = { - case OpNot => COpNot - case OpUnaryMinus => COpUnaryMinus - } - CUnaryExpr(translate_unary_op(e1.op), translate_expression(e1.arg)) - } - - /** translates a variable to C Expression type */ - def translate_variable : Var => CVar = { case Var(name) => CVar(name) } - - - /** translates a unary operation to a C expression */ - def translate_binary_op: BinOp => CBinOp = { - case OpPlus => COpPlus - case OpMinus => COpMinus - case OpMultiply => COpMultiply - case OpEq => COpEq - case OpLeq => COpLeq - case OpLt => COpLt - case OpAnd => COpAnd - case OpOr => COpOr - case v => throw TranslationException(s"translation to C program failed - use of unsupported binary operation ${v.pp}") - } - - def translate_binary_expr(op: BinOp, e1: Expr, e2: Expr): CExpr = { - CBinaryExpr(translate_binary_op(op), translate_expression(e1), translate_expression(e2)) - } - - - def translate_overloaded_binary_expr(expr: OverloadedBinaryExpr): CExpr = expr match { - case OverloadedBinaryExpr(overloaded_op, left, right) => - val tleft = translate_expression(left) - val tright = translate_expression(right) - overloaded_op match { - case op: BinOp => CBinaryExpr(translate_binary_op(op), tleft, tright) - case OpOverloadedEq => CBinaryExpr(COpEq, tleft, tright) - case OpNotEqual => - CUnaryExpr(COpNot, CBinaryExpr(COpEq, tleft, tright)) - case OpGt => - CUnaryExpr(COpNot, CBinaryExpr(COpLeq, tleft, tright)) - case OpGeq => - CUnaryExpr(COpNot, CBinaryExpr(COpLt, tleft, tright)) - case OpOverloadedPlus => - CBinaryExpr(COpPlus, tleft, tright) - case OpOverloadedMinus => - CBinaryExpr(COpMinus, tleft, tright) - case OpOverloadedLeq => - CBinaryExpr(COpLeq, tleft, tright) - case OpOverloadedStar => ??? - } - } - - /** translate a Suslik expression into VST specific encoding */ - def translate_expression(expr: Expr): CExpr = expr match { - case Var(name) => CVar(name) - case BoolConst(value) => CBoolConst(value) - case IntConst(value) => CNatConst(value) - case LocConst(value) => CNatConst(value) // TODO: handle ptr type - case e1@UnaryExpr(_, _) => translate_unary_expr(e1) - case BinaryExpr(op, e1, e2) => translate_binary_expr(op, e1, e2) - case IfThenElse(c, t, e) => CIfThenElse(translate_expression(c), translate_expression(t), translate_expression(e)) - case expr@OverloadedBinaryExpr(_, _, _) => - translate_overloaded_binary_expr(expr) - case v => throw TranslationException(s"Operation ${v.toString} not supported") - } - - - /** translate type to C Type */ - def translate_type(lType: SSLType): VSTCType = lType match { - case IntType => CIntType - case LocType => CVoidPtrType - case VoidType => CUnitType - case v => throw TranslationException(s"translating ${v} not implemeneted") - } - - def is_supported_c_type: SSLType => Boolean = { - case IntType => true - case LocType => true - case _ => false - } - - - /** translates a suslik encoding of a procedure to a VST C one */ - def translate_function(proc:Procedure, gamma: Gamma) = { - val ctx: Map[CVar, AnalysisVSTTypes] = - gamma - .filter({ case (_, lType) => is_supported_c_type(lType)}) - .map({ - case (variable, ty) => - (translate_variable(variable), translate_type(ty).asInstanceOf[AnalysisVSTTypes])}) - def type_expr (ctx: Map[CVar, AnalysisVSTTypes]) (expr: Expr): Option[AnalysisVSTTypes] = { - def translate_op : BinOp => Option[AnalysisVSTTypes] = { - case op: RelOp => op match { - case Expressions.OpEq => Some(CIntType) - case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - } - case Expressions.OpPlus => Some(CIntType) - case Expressions.OpMinus => Some(CIntType) - case Expressions.OpMultiply => Some(CIntType) - case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - } - expr match { - case Var(name) => ctx.get(CVar(name)) - case const: Const => const match { - case IntConst(value) => Some (CIntType) - case LocConst(value) => Some (CVoidPtrType) - case BoolConst(value) => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - } - case BinaryExpr(op, left, right) => translate_op(op) - case OverloadedBinaryExpr(overloaded_op, left, right) => - overloaded_op match { - case op: BinOp => translate_op(op) - case Expressions.OpOverloadedPlus => Some(CIntType) - case Expressions.OpOverloadedMinus => Some(CIntType) - case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - } - case UnaryExpr(op, arg) => op match { - case Expressions.OpUnaryMinus => Some(CIntType) - case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - } - case _ => throw TranslationException(s"Found incompatible expression in body ${expr.pp}") - case IfThenElse(cond, left, right) => type_expr(ctx)(left).orElse( type_expr(ctx)(right)) - } - } - - def translate_body(ctx: Map[CVar, AnalysisVSTTypes], body: Statement) : (Map[CVar, AnalysisVSTTypes], CStatement) = - body match { - case Statements.Skip => (ctx, CSkip) - case Statements.Malloc(to, tpe, sz) => - (ctx, CMalloc(translate_variable(to), translate_type(tpe), sz)) - case Statements.Free(v) => (ctx, CFree(translate_variable(v))) - case Statements.Load(to, tpe, from, offset) => - val (new_ctx, elem_typ) = ctx.get(translate_variable(to)) match { - case Some(value) => (ctx, value.downcast) - case None => ctx.get(translate_variable(from)) match { - case Some(CIntPtrType) => (ctx.updated(translate_variable(to), CIntType), CIntType) - case Some(CVoidPtrPtrType) => - (ctx.updated(translate_variable(to), CVoidPtrType), CVoidPtrType) - // TODO: Handle errors - case Some(CVoidPtrType) => - val value_type = translate_type(tpe) - val new_ctx = - ctx.updated(translate_variable(to), value_type) - .updated(translate_variable(from), CVoidPtrPtrType) - (new_ctx, value_type) - case None => ??? - } - } - (new_ctx, CLoad(translate_variable(to), elem_typ.downcast, translate_variable(from), offset)) - case Statements.Store(to, offset, e) => - val (new_ctx, elem_ty) = - type_expr(ctx)(e) match { - case Some(value) => (ctx.updated(translate_variable(to), value match { - case cType: VSTCType => cType match { - case CTypes.CIntType => CIntPtrType - case CTypes.CVoidPtrType => CVoidPtrPtrType - case CTypes.CUnitType => ??? - } - case ptrType: PtrType => ptrType match { - case CTypes.CVoidPtrPtrType => CVoidPtrPtrType - case CTypes.CIntPtrType => CVoidPtrPtrType - } - }), value) - case None => ctx.get(translate_variable(to)) match { - case Some(value) => value match { - case ptrType: PtrType => ptrType match { - case CTypes.CVoidPtrPtrType => (ctx, CVoidPtrType) - case CTypes.CIntPtrType => (ctx, CIntType) - } - case CTypes.CVoidPtrType => (ctx, CVoidPtrType) - case _ => ??? - } - case None => ??? - } - - } - (new_ctx, CStore(translate_variable(to), elem_ty.downcast, offset, translate_expression(e))) - case Statements.Call(fun, args, companion) => - (ctx, CCall(translate_variable(fun), args.map(translate_expression(_)), companion)) - case Statements.SeqComp(s1, s2) => - val (new_ctx, s1_up) = translate_body(ctx, s1) - val (new_ctx2, s2_up) = translate_body(new_ctx, s2) - (new_ctx2, CSeqComp(s1_up, s2_up)) - case Statements.If(cond, s1, s2) => - val (new_ctx, s1_up) = translate_body(ctx, s1) - val (new_ctx2, s2_up) = translate_body(new_ctx, s2) - (new_ctx2, CIf(translate_expression(cond), s1_up, s2_up)) - case Statements.Guarded(cond, s1, s2, branchPoint) => - val (new_ctx, s1_up) = translate_body(ctx, s1) - val (new_ctx2, s2_up) = translate_body(new_ctx, s2) - (new_ctx2, CIf(translate_expression(cond), s1_up, s2_up)) - case Statements.Hole => throw TranslationException(s"Unsupported value ${body.pp} found during translation") - case Statements.Error => throw TranslationException(s"Unsupported value ${body.pp} found during translation") - } - val (_, body) = translate_body(ctx, proc.body) - CProcedureDefinition( - proc.f.name, - translate_type(proc.f.rType), - proc.f.params.map({ case (variable, rtype) => (translate_variable(variable), translate_type(rtype)) }), - body - ) - } - - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 7da98557a..13f1e9a5a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -1,28 +1,36 @@ package org.tygus.suslik.certification.targets.vst.translation -import java.io -import org.tygus.suslik.certification.targets.htt.language.Expressions.CPointsTo -import org.tygus.suslik.certification.targets.vst.clang.{CTypes, PrettyPrinting} -import org.tygus.suslik.certification.targets.vst.clang.CTypes.{CIntType, CVoidPtrType, VSTCType} -import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar +import org.tygus.suslik.certification.targets.vst.Types +import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqIntSetType, CoqIntValType, CoqPtrValType, CoqZType, VSTCType, VSTType} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCIntSetLiteral, ProofCNullval, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntValEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrValEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCOpZEq, ProofCUnOp, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} -import org.tygus.suslik.certification.targets.vst.logic.{ProofTerms, ProofTypes} -import ProofTerms.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCSetLiteral, ProofCUnOp, ProofCUnaryExpr, ProofCVar} -import ProofTerms.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqIntType, CoqListType, CoqParamType, CoqPtrType, VSTProofType} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} -import scala.collection.immutable /** translates suslik proof terms to VST compatible proof terms */ object ProofSpecTranslation { + def to_ssl_context(gamma: Map[String, VSTType]) : Gamma = { + def to_ssl_type(ty: VSTType) : SSLType = ty match { + case cType: VSTCType => cType match { + case Types.CoqPtrValType => LocType + case Types.CoqIntValType => IntType + } + case Types.CoqZType => IntType + case Types.CoqIntSetType => IntSetType + case CoqCardType(pred_type) => CardType + } + gamma.map({case (name, ty) => (Var(name), to_ssl_type(ty))}) + } + /** Translates a cardinality to a vst expression that can be passed around */ def translate_cardinality(predicate: VSTPredicate, cardinality: CardConstructor): ProofCExpr = { ProofCCardinalityConstructor( @@ -37,55 +45,30 @@ object ProofSpecTranslation { /** translate a suslik type into a VST proof type */ - def translate_type(lType: SSLType): VSTProofType = + def translate_predicate_param_type(lType: SSLType): VSTType = lType match { - case IntType => CoqIntType - case LocType => CoqPtrType - case CardType => CoqCardType(???) // TODO: add a safe version of this (only used when initializing base context) - - // TODO: WARNING: Suslik has a loose model of memory that allows elements of different types - // to be allocated in the same block - i.e x :-> [loc; int] - this is technically possible - // but doesn't mesh well with C in which an allocated array must have all elements of the same type - // otherwise a separate struct definition would be needed - case IntSetType => CoqListType(CoqPtrType, None) + case IntType => CoqZType + case LocType => CoqPtrValType + case IntSetType => CoqIntSetType + case _ => throw TranslationException("Attempted to translate ssl type of invalid form into VST Type") } - /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) */ - def translate_expression(context: Map[Ident, VSTProofType])(expr: Expressions.Expr): ProofTerms.Expressions.ProofCExpr = { - def type_expr(left_1: ProofCExpr): VSTProofType = - left_1 match { - case ProofCVar(name, typ) => typ - - case ProofCIntConst(value, false) => CoqIntType - case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems, _) => CoqListType(CoqPtrType, Some(elems.length)) - case ProofCIfThenElse(cond, left, right) => type_expr(left) - case ProofCBinaryExpr(op, left, right) => - op match { - case ProofCOpPlus => CoqIntType - case ProofCOpMinus => CoqIntType - case ProofCOpMultiply => CoqIntType - case ProofCOpUnion => CoqListType(CoqPtrType, None) - } - case ProofCUnaryExpr(op, e) => op match { - case ProofCOpUnaryMinus => CoqIntType - } - } - - def translate_binop(op: Expressions.BinOp)(ty: VSTProofType): ProofCBinOp = { + /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) + * */ + def translate_expression(context: Map[Ident, VSTType])(expr: Expressions.Expr, target: Option[VSTType]=None): ProofCExpr = { + def type_expr(left_1: ProofCExpr): VSTType = left_1.type_expr + def translate_binop(op: Expressions.BinOp)(ty: VSTType): ProofCBinOp = { op match { case op: Expressions.RelOp => (op, ty) match { - case (Expressions.OpEq, CoqIntType) => ProofCOpIntEq - case (Expressions.OpEq, CoqParamType(CIntType)) => ProofCOpIntEq - case (Expressions.OpEq, CoqPtrType) => ProofCOpPtrEq - case (Expressions.OpEq, CoqParamType(CVoidPtrType)) => ProofCOpPtrEq - case (Expressions.OpEq, _) => ProofCOpIntEq + case (Expressions.OpEq, CoqIntValType) => ProofCOpIntValEq + case (Expressions.OpEq, CoqZType) => ProofCOpZEq + case (Expressions.OpEq, CoqPtrValType) => ProofCOpPtrValEq case (Expressions.OpBoolEq, _) => ProofCOpBoolEq case (Expressions.OpLeq, _) => ProofCOpLeq case (Expressions.OpLt, _) => ProofCOpLt case (Expressions.OpIn, _) => ProofCOpIn - case (Expressions.OpSetEq,_) => ProofCOpSetEq + case (Expressions.OpSetEq, _) => ProofCOpSetEq case (Expressions.OpSubset, _) => ProofCOpSubset } case op: Expressions.LogicOp => op match { @@ -102,16 +85,18 @@ object ProofSpecTranslation { } } - expr match { + + expr.resolveOverloading(to_ssl_context(context)) match { case const: Expressions.Const => const match { - case Expressions.IntConst(value) => ProofCIntConst(value, is_ptr=false) - case Expressions.LocConst(value) => ProofCIntConst(value, is_ptr=true) + case Expressions.IntConst(value) => target match { + case Some(CoqPtrValType) => ProofCNullval + case _ => ProofCIntConst(value) + } + case Expressions.LocConst(value) if value == 0 => ProofCNullval case Expressions.BoolConst(value) => ProofCBoolConst(value) } case Var(name) => ProofCVar(name, context(name)) - case Expressions.SetLiteral(elems) => { - ProofCSetLiteral(elems.map(translate_expression(context))) - } + case Expressions.SetLiteral(elems) => ProofCIntSetLiteral(elems.map(v => translate_expression(context)(v, Some(CoqIntSetType)))) case Expressions.UnaryExpr(op, arg) => val top: ProofCUnOp = op match { case Expressions.OpNot => ProofCOpNot @@ -122,73 +107,14 @@ object ProofSpecTranslation { val left_expr = translate_expression(context)(left) val type_of_expr = type_expr(left_expr) val top: ProofCBinOp = translate_binop(op)(type_of_expr) - ProofCBinaryExpr(top, left_expr, translate_expression(context)(right)) + ProofCBinaryExpr(top, left_expr, translate_expression(context)(right, target=Some(type_of_expr))) case Expressions.IfThenElse(cond, left, right) => + val left_expr = translate_expression(context)(left) + val l_type_1 = type_expr(left_expr) ProofCIfThenElse( - translate_expression(context)(cond), - translate_expression(context)(left), - translate_expression(context)(right) + translate_expression(context)(cond), left_expr, + translate_expression(context)(right, target=Some(l_type_1)) ) - case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => - val left_1 = translate_expression(context)(left) - val right_1 = translate_expression(context)(right) - overloaded_op match { - case op: Expressions.BinOp => - val type_of_expr = type_expr(left_1) - ProofCBinaryExpr(translate_binop(op)(type_of_expr), left_1, right_1) - case Expressions.OpOverloadedEq => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1) - case CoqListType(_, _) => - ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1) - case ProofTypes.CoqPtrType => - ProofCBinaryExpr(ProofCOpPtrEq, left_1, right_1) - } - case Expressions.OpNotEqual => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqIntType => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpIntEq, left_1, right_1)) - case CoqListType(elem, _) => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpSetEq, left_1, right_1)) - case ProofTypes.CoqPtrType => ??? // TODO: Handle pointer equality? or fail? - } - case Expressions.OpGt => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLeq, left_1, right_1)) - case Expressions.OpGeq => - ProofCUnaryExpr(ProofCOpNot, ProofCBinaryExpr(ProofCOpLt, left_1, right_1)) - case Expressions.OpOverloadedPlus => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpPlus, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpUnion, left_1, right_1) - } - case Expressions.OpOverloadedMinus => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpMinus, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpDiff, left_1, right_1) - } - case Expressions.OpOverloadedLeq => - val l1_ty: VSTProofType = type_expr(left_1) - l1_ty match { - case ProofTypes.CoqPtrType => ??? // TODO: handle pointer equality or fail? - case ProofTypes.CoqIntType => - ProofCBinaryExpr(ProofCOpLeq, left_1, right_1) - case CoqListType(elem, _) => - ProofCBinaryExpr(ProofCOpSubset, left_1, right_1) - } - case Expressions.OpOverloadedStar => ??? // TODO: Handle star operation - } - } } @@ -196,42 +122,28 @@ object ProofSpecTranslation { /** given a VST proof expression and a typing context, * this function will type the expression and return * a type */ - def type_expr(context: Map[Ident, VSTProofType]) (cvalue: ProofTerms.Expressions.ProofCExpr) : VSTProofType = - cvalue match { - case ProofCVar(name, typ) => typ - case ProofCIntConst(value, false) => CoqIntType - case ProofCIntConst(value, true) => CoqParamType(CTypes.CVoidPtrType) - case ProofCSetLiteral(elems, _) => CoqListType(type_expr(context)(elems.head), Some (elems.length)) - case ProofCIfThenElse(cond, left, right) => type_expr(context)(left) - case ProofCBinaryExpr(op, left, right) => op match { - case ProofCOpPlus => CoqIntType - case ProofCOpMinus => CoqIntType - case ProofCOpMultiply => CoqIntType - } - case ProofCUnaryExpr(op, e) => op match { - case ProofCOpUnaryMinus => CoqIntType - } - } + def type_expr(context: Map[Ident, VSTType])(cvalue: ProofCExpr): VSTType = cvalue.type_expr /** * Translate a list of suslik heaplets into a form accepted by VST - * @param context the typing context + * + * @param context the typing context * @param heaplets a list of suslik heaplets * @return a VST encoding of these heaplets * - * Note: Suslik encodes blocks of pointers slightly differently to - * VST - when dealing with a block of contiguous pointers in memory, - * Suslik first uses a block declaration to specify the size of the - * contiguous block, and then has a number of subsequent heaplets - * that assign values to each element of this block. + * Note: Suslik encodes blocks of pointers slightly differently to + * VST - when dealing with a block of contiguous pointers in memory, + * Suslik first uses a block declaration to specify the size of the + * contiguous block, and then has a number of subsequent heaplets + * that assign values to each element of this block. * - * VST combines these two declarations into one: `data_at` - a `data_at` declaration - * states what a given pointer points to - in the case of contiguous memory, - * we must list out the corresponding values in order - just as they would be encoded in memory + * VST combines these two declarations into one: `data_at` - a `data_at` declaration + * states what a given pointer points to - in the case of contiguous memory, + * we must list out the corresponding values in order - just as they would be encoded in memory * - * This function performs the translation between suslik's encoding and VST's encoding + * This function performs the translation between suslik's encoding and VST's encoding */ - def translate_heaplets(context: Map[Ident, VSTProofType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { + def translate_heaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { val initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty // we first build up a mapping from pointer variables @@ -239,50 +151,49 @@ object ProofSpecTranslation { // predicate applications are separated out unchanged // as these translate directly to vst val (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = - heaplets.foldLeft((initial_map, List(): List[CSApp]))({ - case ((map, acc), ty: Heaplet) => - ty match { - case ty@PointsTo(loc@Var(name), offset, value) => - val updated_map = map.get(name) match { - case None => map.updated(name, (List(ty), None)) - case Some((points_to_acc: List[_], block_acc)) => - map.updated(name, (List(ty) ++ points_to_acc, block_acc)) - } - (updated_map, acc: List[CSApp]) - case ty@Block(loc@Var(name), sz) => - val updated_map = map.get(name) match { - case None => map.updated(name, (List(), Some(ty))) - case Some((points_to_acc, None)) => map.updated(name, (points_to_acc, Some(ty))) - } - (updated_map, acc: List[CSApp]) - case SApp(pred, args, tag, card) => - (map, (List(CSApp(pred, args.map(translate_expression((context))), translate_expression(context)(card))) ++ acc) - ) - } - }) + heaplets.foldLeft((initial_map, List(): List[CSApp]))({ + case ((map, acc), ty: Heaplet) => + ty match { + case ty@PointsTo(loc@Var(name), offset, value) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(ty), None)) + case Some((points_to_acc: List[_], block_acc)) => + map.updated(name, (List(ty) ++ points_to_acc, block_acc)) + } + (updated_map, acc: List[CSApp]) + case ty@Block(loc@Var(name), sz) => + val updated_map = map.get(name) match { + case None => map.updated(name, (List(), Some(ty))) + case Some((points_to_acc, None)) => map.updated(name, (points_to_acc, Some(ty))) + } + (updated_map, acc: List[CSApp]) + case SApp(pred, args, tag, card) => + (map, (List(CSApp(pred, args.map(v => translate_expression((context))(v)), translate_expression(context)(card))) ++ acc) + ) + } + }) // having built the mapping, we then translate each (k,v) pair in this // mapping into a VST Data at declaration val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => o_block match { - case Some((_@Block(loc,sz))) => + case Some((_@Block(loc, sz))) => val loc_pos = translate_expression(context)(loc) - val o_array : Array[Option[ProofCExpr]] = Array.fill(sz)(None) - points_to.foreach({case PointsTo(_, offset, value) => - o_array.update(offset, Some(translate_expression(context)(value))) + val o_array: Array[Option[ProofCExpr]] = Array.fill(sz)(None) + points_to.foreach({ case PointsTo(_, offset, value) => + o_array.update(offset, Some(translate_expression(context)(value))) }) val elems = o_array.map(_.get).toList - val elem_type = type_expr(context)(elems.head) - CDataAt(loc_pos, CoqListType(elem_type, Some(sz)), sz, ProofCSetLiteral(elems)) + CDataAt(loc_pos, elems) case None => - assert( - points_to.length == 1, - "found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block" - ) - (points_to.head : PointsTo) match { + if(points_to.length != 1) { + throw TranslationException("found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block") + } + + (points_to.head: PointsTo) match { case PointsTo(loc, 0, value) => val c_value = translate_expression(context)(value) - CDataAt(translate_expression(context)(loc), type_expr(context)(c_value), 0, c_value) + CDataAt(translate_expression(context)(loc), List(c_value)) case PointsTo(_, _, _) => assert(false, "found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") ??? @@ -294,11 +205,10 @@ object ProofSpecTranslation { blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) } - def translate_assertion (context: Map[Ident, VSTProofType]) (assertion: Assertion): FormalCondition = assertion match { - case Assertion(phi, sigma) => - { + def translate_assertion(context: Map[Ident, VSTType])(assertion: Assertion): FormalCondition = assertion match { + case Assertion(phi, sigma) => { val pure_conditions = - phi.conjuncts.map(translate_expression(context)) + phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList val spatial_conditions: List[VSTHeaplet] = @@ -308,62 +218,67 @@ object ProofSpecTranslation { } } + def proof_type_of_c_type(cType: VSTType): VSTCType = cType match { + case cType: Types.VSTCType => cType + case _ => throw TranslationException(s"Attempt to create proof type of invalid term ${cType.pp}") + } + /** translates a Suslik function specification into a proof */ - def translate_conditions(proc: CProcedureDefinition)(goal: Goal): (FormalSpecification, Map[Ident, VSTProofType]) = { + def translate_conditions(proc: CProcedureDefinition)(goal: Goal): (FormalSpecification, Map[Ident, VSTType]) = { val name: Ident = proc.name - val c_params: Seq[(Ident, VSTCType)] = proc.params.map({ case (CVar(name), cType) => (name, cType) }) - + val c_params: Seq[(Ident, VSTCType)] = proc.params // collect all cardinality_params and their associated types val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ case PointsTo(loc, offset, value) => None - case Block(loc, sz) => None - case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) - case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") + case Block(loc, sz) => None + case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) + case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") }).toMap - val formal_params: List[(Ident, VSTProofType)] = { + val formal_params: List[(Ident, VSTType)] = { val c_param_set = c_params.map(_._1).toSet goal.universals .map({ case variable@Var(name) => - if (cardinality_params.contains(name)) { - (name, cardinality_params(name)) - } else { - (name, translate_type(goal.gamma(variable))) - }}) - .filterNot({case (name, _) => c_param_set.contains(name)}).toList + if (cardinality_params.contains(name)) { + (name, cardinality_params(name)) + } else { + (name, translate_predicate_param_type(goal.gamma(variable))) + } + }) + .filterNot({ case (name, _) => c_param_set.contains(name) }).toList } - val existential_params: List[(Ident, VSTProofType)] = + val existential_params: List[(Ident, VSTType)] = goal.existentials.map({ case variable@Var(name) => if (cardinality_params.contains(name)) { (name, cardinality_params(name)) } else { - (name, translate_type(goal.gamma(variable))) + (name, translate_predicate_param_type(goal.gamma(variable))) } }).toList - val return_type: VSTCType = proc.rt val context = ( formal_params ++ existential_params ++ - c_params.map({ case (ident, cType) => (ident, ProofTypes.proof_type_of_c_type(cType)) }) + c_params.map({ case (ident, cType) => (ident, proof_type_of_c_type(cType)) }) ).toMap val precondition: FormalCondition = { val pure_conditions = - goal.pre.phi.conjuncts.map(translate_expression(context)) + goal.pre.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => cType match { - case CTypes.CIntType => Some(IsValidInt(CVar(ident))) - case CTypes.CUnitType => None - case CTypes.CVoidPtrType => Some(IsValidPointerOrNull(CVar(ident))) + case CoqIntValType => Some(IsValidInt(ident)) + case CoqPtrValType => Some(IsValidPointerOrNull(ident)) + case _ => None } - }) ++ formal_params.flatMap({ case (ident, ty) => ty match { - case ProofTypes.CoqPtrType =>Some(IsValidPointerOrNull(CVar(ident))) - case ProofTypes.CoqIntType => Some(IsValidInt(CVar(ident))) + }) ++ formal_params.flatMap({ case (ident, ty) => ty match { + case CoqPtrValType => Some(IsValidPointerOrNull(ident)) + case CoqIntValType => Some(IsValidInt(ident)) case _ => None - }}) + } + }) val spatial_conditions: List[VSTHeaplet] = translate_heaplets(context)(goal.pre.sigma.chunks) @@ -371,16 +286,16 @@ object ProofSpecTranslation { } val postcondition: FormalCondition = { val pure_conditions = - goal.post.phi.conjuncts.map(translate_expression(context)) + goal.post.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList val spatial_conditions = translate_heaplets(context)(goal.post.sigma.chunks) - // goal.post.sigma.chunks.map(translate_heaplet(context)).toList + // goal.post.sigma.chunks.map(translate_heaplet(context)).toList FormalCondition(pure_conditions, spatial_conditions) } (FormalSpecification( - name, c_params, formal_params, existential_params, precondition, postcondition, return_type + name, c_params, formal_params, existential_params, precondition, postcondition ), context) } @@ -392,15 +307,16 @@ object ProofSpecTranslation { // where for every constraint of the form (a < b), we set map(b) = a :: map(b), // thus the mapping for a variable contains the list of other variables that are // constrainted to be immediately smaller than it - var child_map : Map[String, List[String]] = Map.empty - card_conds.foreach({case (child, parent) => - child_map.get(parent) match { - case None => child_map = child_map.updated(parent, List(child)) - case Some(children) => child_map = child_map.updated(parent, child :: children) - }}) + var child_map: Map[String, List[String]] = Map.empty + card_conds.foreach({ case (child, parent) => + child_map.get(parent) match { + case None => child_map = child_map.updated(parent, List(child)) + case Some(children) => child_map = child_map.updated(parent, child :: children) + } + }) // the keys of the map now become variables that are destructured // in the match case to produce the variables immediately below it - child_map.map({ case (str, strings) => (str, CardOf(strings))}) + child_map.map({ case (str, strings) => (str, CardOf(strings)) }) } @@ -415,9 +331,9 @@ object ProofSpecTranslation { * * lseg(x, s) { * - * x == 0 ==> ... (no cardinality constraints) + * x == 0 ==> ... (no cardinality constraints) * - * x <> 0 ==> a < self_card ... lseg(x',s') + * x <> 0 ==> a < self_card ... lseg(x',s') * * } * @@ -425,46 +341,46 @@ object ProofSpecTranslation { * * Inductive lseg_card : Set := * - * | lseg_card_0 : lseg_card + * | lseg_card_0 : lseg_card * - * | lseg_card_1 : lseg_card -> lseg_card. + * | lseg_card_1 : lseg_card -> lseg_card. * * And then implement lseg as taking in a third parameter being its cardinality, * and matching on this - taking the first clause if the input is `lseg_card0` and the * second clause if the input is `lseg_card1 a` (and recursing on `a` * - * */ + **/ def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { // Determines whether a given variable is a cardinality constraint // TODO: I've definitely seen some function elsewhere that already does this - def is_card (s: String) : Boolean = s.startsWith("_") || s.contentEquals("self_card") + def is_card(s: String): Boolean = s.startsWith("_") || s.contentEquals("self_card") // extracts a cardinality relation from an expression if it exists - def extract_card_constructor(expr: Expressions.Expr) : Option[(String, String)] = { + def extract_card_constructor(expr: Expressions.Expr): Option[(String, String)] = { expr match { case Expressions.BinaryExpr(op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => + if is_card(left) && is_card(parent) => op match { case op: Expressions.RelOp => op match { case Expressions.OpLt => - Some ((left, parent)) + Some((left, parent)) case _ => None } case _ => None } case Expressions.OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => + if is_card(left) && is_card(parent) => overloaded_op match { case op: Expressions.BinOp => op match { case op: Expressions.RelOp => op match { - case Expressions.OpLt => Some ((left, parent)) + case Expressions.OpLt => Some((left, parent)) case _ => None } case _ => None } - case Expressions.OpGt =>Some ((parent, left)) + case Expressions.OpGt => Some((parent, left)) case _ => None } case _ => None @@ -473,32 +389,31 @@ object ProofSpecTranslation { // base context contains type information for every variable used in the // predicate (even if it occurs at a top level or not) - val base_context : List[(Ident, VSTProofType)] = { - var gamma: Gamma = Map.empty - var pred_name : Option[Ident] = None - predicate match { + val base_context: List[(Ident, VSTType)] = { + var (pred_name, gamma) = predicate match { case InductivePredicate(name, params, clauses) => - pred_name = Some(name) - clauses.foreach({case InductiveClause(selector, assn) => - selector.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) - assn.phi.conjuncts.foreach(expr => - expr.resolve(gamma, Some(BoolType)).foreach(v => gamma = v) - ) - assn.sigma.resolve(gamma, env).foreach(v => gamma = v) + val pred_name = name + val gamma = clauses.foldLeft(params.toMap)({ case (base_gamma, InductiveClause(selector, asn)) => + var gamma = selector.resolve(base_gamma, Some(BoolType)).getOrElse(base_gamma) ++ base_gamma + gamma = asn.phi.conjuncts.foldLeft(gamma)({ case (gamma, expr) => expr.resolve(gamma, Some(BoolType)).getOrElse(gamma) }) ++ base_gamma + + asn.sigma.resolve(gamma, env).getOrElse(gamma) ++ base_gamma }) + (pred_name, gamma) } - gamma.map({case (Var(name), ty) => (name, ty match { - case CardType => CoqCardType(pred_name.get) - case _ => translate_type(ty) - })}).toList + gamma.map({ case (Var(name), ty) => (name, ty match { + case CardType => CoqCardType(pred_name) + case _ => translate_predicate_param_type(ty) + }) + }).toList } predicate match { case InductivePredicate(name, raw_params, raw_clauses) => { - val params: List[(String, VSTProofType)] = - raw_params.map({case (Var(name), sType) => (name, translate_type(sType))}) - val context: Map[Ident, VSTProofType] = (base_context ++ params).toMap + val params: List[(String, VSTType)] = + raw_params.map({ case (Var(name), sType) => (name, translate_predicate_param_type(sType)) }) + val context: Map[Ident, VSTType] = (base_context ++ params).toMap // separate clauses by cardinality constructors @@ -517,7 +432,7 @@ object ProofSpecTranslation { // translate the pure conditions into VST format val select = translate_expression(context)(selector) - val conds = r_conds.flatten.map(translate_expression(context)).toList + val conds = r_conds.flatten.map(v => translate_expression(context)(v)).toList // translate the spatial constraints val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) @@ -526,7 +441,7 @@ object ProofSpecTranslation { val card_conds = r_card_conds.flatten card_conds match { case card_conds@(::(_, _)) => - val card_cons : Map[String, CardConstructor] = build_card_cons(card_conds) + val card_cons: Map[String, CardConstructor] = build_card_cons(card_conds) (card_cons("self_card"), VSTPredicateClause(select :: conds, spat_conds, card_cons)) case Nil => (CardNull, VSTPredicateClause(select :: conds, spat_conds, Map.empty)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala deleted file mode 100644 index 2ee9547ee..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofTranslation.scala +++ /dev/null @@ -1,716 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.translation - -import org.tygus.suslik.certification.targets.vst.clang.CTypes -import org.tygus.suslik.certification.targets.vst.clang.Expressions.CVar -import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.Expressions._ -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -import org.tygus.suslik.certification.targets.vst.logic.ProofTypes.{CoqCardType, CoqPtrType, VSTProofType} -import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, ProofTypes, VSTProofStep} -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter -import org.tygus.suslik.certification.targets.vst.translation.Translation.fail_with -import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.certification.{CertTree, SuslikProofStep} -import org.tygus.suslik.language.Expressions.{Expr, Var} -import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc} -import org.tygus.suslik.language.{Expressions, Ident, Statements} -import org.tygus.suslik.logic.SApp - -import scala.collection.immutable.Map - - -object ProofTranslation { - - case class ProofRuleTranslationException(msg: String) extends Exception { - override def toString: String = s"ProofRuleTranslationException(${msg})" - } - - - def translate_proof( - name: String, - predicates: List[VSTPredicate], - spec: ProofTerms.FormalSpecification, - root: CertTree.Node, - pre_cond: ProofTerms.FormalCondition, - post_cond: ProofTerms.FormalCondition - ): Proof = { - val pred_map = predicates.map(v => (v.name, v)).toMap - - type FunSpec = (Ident, List[Ident], List[(Ident, VSTProofType)]) - - /** - * represents a unfold operation on a predicate - */ - case class Unfold( - VSTPredicate: VSTPredicate, - cardinality: String, - args: List[(String, VSTProofType)], - existentials: List[(String, VSTProofType)], - new_equalities: Map[String, ProofCExpr], - ) - - /** accumulating context used during proof translation - * - * @param gamma typing context - * @param functions stack of functions being abduced during execution - * @param queued_unfolds sequence of queued unfolds - **/ - case class Context(post: List[(Ident, VSTProofType)], - gamma: Map[Ident, VSTProofType], - variable_map: Map[Ident, ProofCExpr], - functions: List[(Ident, List[Ident], List[(Ident, VSTProofType)])], - queued_unfolds: List[Unfold], - coq_context: Map[Ident, VSTProofType], - card_map: Map[Ident, ProofCExpr] - ) - - - def unify_expr(context: Map[Ident, Ident])(pure: ProofCExpr)(call: ProofCExpr): Map[Ident, Ident] = - (pure, call) match { - case (ProofCVar(name, _), ProofCVar(call_name, _)) => context + (name -> call_name) - case (ProofCBoolConst(_), ProofCBoolConst(_)) => context - case (ProofCIntConst(_, _), ProofCIntConst(_, _)) => context - case (ProofCSetLiteral(elems, _), ProofCSetLiteral(call_elems, _)) => - elems.zip(call_elems).foldLeft(context)({ case (context, (expr, call_expr)) => unify_expr(context)(expr)(call_expr) }) - case (ProofCIfThenElse(cond, left, right), ProofCIfThenElse(call_cond, call_left, call_right)) => - var new_context = unify_expr(context)(cond)(call_cond) - new_context = unify_expr(new_context)(left)(call_left) - unify_expr(new_context)(right)(call_right) - case (ProofCBinaryExpr(_, left, right), ProofCBinaryExpr(_, call_left, call_right)) => - val new_context = unify_expr(context)(left)(call_left) - unify_expr(new_context)(right)(call_right) - case (ProofCUnaryExpr(_, e), ProofCUnaryExpr(_, call_e)) => - unify_expr(context)(e)(call_e) - } - - def unify_call_params(call_pre: ProofTerms.FormalCondition): List[(Ident, VSTProofType)] = { - (pre_cond, call_pre) match { - case (ProofTerms.FormalCondition(pure, spatial), ProofTerms.FormalCondition(call_pure, call_spatial)) => - def unify_pure(context: Map[Ident, Ident])(pure: ProofTerms.PureFormula)(call: ProofTerms.PureFormula): Map[Ident, Ident] = { - (pure, call) match { - case (ProofTerms.IsValidPointerOrNull(CVar(name)), ProofTerms.IsValidPointerOrNull(CVar(name1))) => - context + (name -> name1) - case (ProofTerms.IsValidInt(CVar(name)), ProofTerms.IsValidInt(CVar(name1))) => - context + (name -> name1) - case (ProofTerms.IsTrueProp(expr), ProofTerms.IsTrueProp(expr1)) => - unify_expr(context)(expr)(expr1) - case (ProofTerms.IsTrue(expr), ProofTerms.IsTrue(expr1)) => - unify_expr(context)(expr)(expr1) - } - } - - def unify_spatial(context: Map[Ident, Ident])(pure: VSTHeaplet)(call: VSTHeaplet): Map[Ident, Ident] = { - (pure, call) match { - case (CDataAt(loc, elem_ty, count, elem), CDataAt(call_loc, call_elem_ty, call_count, call_elem)) => - unify_expr(unify_expr(context)(loc)(call_loc))(elem)(call_elem) - case (CSApp(pred, args, card), CSApp(call_pred, call_args, call_card)) => - assert(pred == call_pred) - unify_expr(args.zip(call_args).foldRight(context)({ case ((exp, call_exp), context) => - unify_expr(context)(exp)(call_exp) - }))(card)(call_card) - } - } - - var context = pure.zip(call_pure).foldLeft(Map[Ident, Ident]())({ case (context, (pure, call_pure)) => unify_pure(context)(pure)(call_pure) }) - context = spatial.zip(call_spatial).foldLeft(context)({ case (context, (pure, call_pure)) => unify_spatial(context)(pure)(call_pure) }) - spec.params.map({ case (name, ty) => (context(name), ty) }) - } - } - - val initial_context: Context = - Context(spec.existensial_params.toList, ( - spec.c_params.map({ - case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) - case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) - case (name, CTypes.CUnitType) => ??? - // case (name, CTypes.) => (name, ProofTypes.CoqPtrType) - // case (name, ty) => (name, CoqParamType(ty)) - }) ++ - spec.formal_params ++ - spec.existensial_params - ).toMap, Map(), Nil, Nil, - ( - spec.c_params.map({ - case (name, CTypes.CIntType) => (name, ProofTypes.CoqIntType) - case (name, CTypes.CVoidPtrType) => (name, ProofTypes.CoqPtrType) - case (name, CTypes.CUnitType) => ??? - // case (name, CTypes.) => (name, ProofTypes.CoqPtrType) - // case (name, ty) => (name, CoqParamType(ty)) - }) ++ - spec.formal_params - ).toMap, - Map()) - - def retrieve_typing_context: Context => Map[Ident, VSTProofType] = _.gamma - - def add_new_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { - case Context(post, old_params, ex_map, funs, ufs, cc, cm) => Context(post, old_params ++ new_params, ex_map, funs, ufs, cc, cm) - } - - def add_new_coq_variables(new_params: Map[Ident, VSTProofType])(context: Context): Context = context match { - case Context(post, old_params, ex_map, funs, ufs, cc, cm) => Context(post, old_params, ex_map, funs, ufs, cc ++ new_params, cm) - } - - def pop_function(context: Context): (FunSpec, Context) = context match { - case Context(post, old_params, ex_map, fun :: funs, ufs, cc, cm) => (fun, Context(post, old_params, ex_map, funs, ufs, cc, cm)) - case _ => fail_with("Function called without prior abduce call") - } - - def push_function(fun_spec: FunSpec)(context: Context): Context = context match { - case Context(post, old_params, ex_map, old_funs, ufs, cc, cm) => Context(post, old_params, ex_map, fun_spec :: old_funs, ufs, cc, cm) - } - - def push_unfolding(context: Context)(unfolded_expr: Unfold, new_equalities: Map[String, ProofCExpr]): Context = - context match { - case Context(post, gamma, variable_map, functions, queued_unfolds, cc, cm) => - Context(post, gamma, variable_map, functions, unfolded_expr :: queued_unfolds, cc, cm) - } - - def record_variable_assignment(name: String, expr: Expr)(context: Context): Context = { - // when recording a mapping of a pointer, force any int constants to be pointers (suslik doesn't always place them in loc_consts) - val translated = ProofSpecTranslation.translate_expression(context.gamma)(expr) - val result = (context.gamma.get(name), translated) match { - case (Some(CoqPtrType), ProofCIntConst(value, _)) => ProofCIntConst(value, true) - case (Some(ty), ProofCSetLiteral(elems, None)) => ProofCSetLiteral(elems, Some(ty)) - case (_, translated) => translated - } - val mapping = Map(name -> result) - val variable_map = context.variable_map.map({ case (name, expr) => (name, expr.subst(mapping)) }) - Context(context.post, context.gamma, (variable_map ++ mapping), context.functions, context.queued_unfolds, context.coq_context, context.card_map) - } - - def record_variable_assignment_raw(name: String, expr: ProofCExpr)(context: Context): Context = { - // when recording a mapping of a pointer, force any int constants to be pointers (suslik doesn't always place them in loc_consts) - val mapping = Map(name -> expr) - val variable_map = context.variable_map.map({ case (name, expr) => (name, expr.subst(mapping)) }) - Context(context.post, context.gamma, (variable_map ++ mapping), context.functions, context.queued_unfolds, context.coq_context, context.card_map) - } - - def record_variable_assignment_card(name: String, expr: ProofCExpr)(context: Context) = - Context(context.post, context.gamma, (context.variable_map), context.functions, context.queued_unfolds, context.coq_context, context.card_map ++ Map(name -> expr)) - - - /** - * Updates context to account for renaming induced by a mapping - * - * @param mapping a mapping encoding a renaming of variables (ASSUMED TO BE RENAMING VARIABLES to VARIABLES) - * @param context the context - * @return an updated context with variables renamed - */ - def record_variable_mapping(mapping: Map[Var, Expr])(context: Context): Context = { - val variable_mapping = mapping.flatMap({ case (Var(old_name), Var(new_name)) => Some((old_name, new_name)) case _ => None }) - val expr_map = variable_mapping.flatMap({ case (name, to) => context.gamma.get(name).map(ty => (name, ProofCVar(to, ty))) }) - - def update_map(map: Map[Ident, ProofCExpr]): Map[Ident, ProofCExpr] = - map.map({ case (name, expr) => (variable_mapping.getOrElse(name, name), expr.subst(expr_map)) }) - - def update_map_static(map: Map[Ident, ProofCExpr]): Map[Ident, ProofCExpr] = - map.map({ case (name, expr) => (name, expr.subst(expr_map)) }) - - val post = context.post.map({ case (ident, proofType) => (variable_mapping.getOrElse(ident, ident), proofType) }) - // Convert mapping to a Map[String,String] using assumption that all mappings are between variables - // Then rename all terms in the context - val new_params = context.gamma.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) - val new_funs = context.functions.map({ case (fun_name, args, existentials) => - (fun_name, args.map(arg => variable_mapping.getOrElse(arg, arg)), existentials) - }) - val new_variable_map = update_map(context.variable_map) - val new_card_map = update_map(context.card_map) - val new_unfolds = context.queued_unfolds.map({ - case Unfold(predicate, cardinality, args, existentials, new_equalities) => - val new_cardinality = cardinality - // match { - // case ProofTerms.CardNull => ProofTerms.CardNull - // case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map({case (name) => variable_mapping.getOrElse(name,name)})) - // } - val new_args = args.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) - val new_existentials = existentials.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) - val new_new_equalities = new_equalities.map({ case (name, expr) => (variable_mapping.getOrElse(name, name), expr.subst(expr_map)) }) - Unfold(predicate, new_cardinality, new_args, new_existentials, new_new_equalities) - }) - val coq_context = context.coq_context.map({ case (name, ty) => (variable_mapping.getOrElse(name, name), ty) }) - Context(post, new_params, new_variable_map, new_funs, new_unfolds, coq_context, new_card_map) - } - - /** - * Translates a Suslik Read rule to a series of VST tactics implementing the same operation. - * - * A suslik read rule, such as Read (x -> y, y = *p) performs the following operations: - * - updates all instances of variable x with y - * - if y is used in the rest of the program, emits a read operation - * - * This then corresponds to the VST rules: - * - rename x (if defined) with y - * - if y used in rest of program, then: - * - first assert that the pointer being read from is non-null (VST idiosynracy) - * - emit a forward tactic to move over the operation - */ - def handle_read_rule(rule: SuslikProofStep.Read, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Read(old_var, new_var, option) => - def is_variable_used_in_exp(variable: Ident)(expr: Expr): Boolean = expr match { - case Var(name) => (name == variable) - case const: Expressions.Const => false - case Expressions.BinaryExpr(op, left, right) => - is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - case Expressions.OverloadedBinaryExpr(overloaded_op, left, right) => - is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - case Expressions.UnaryExpr(op, arg) => is_variable_used_in_exp(variable)(arg) - case Expressions.SetLiteral(elems) => elems.exists(is_variable_used_in_exp(variable)) - case Expressions.IfThenElse(cond, left, right) => - is_variable_used_in_exp(variable)(cond) || is_variable_used_in_exp(variable)(left) || is_variable_used_in_exp(variable)(right) - } - - def is_variable_used_in_proof(variable: Ident)(rule: ProofTree[SuslikProofStep]): Boolean = { - def map_varaible(map: Map[Var, Expr]): Ident = - map.get(Var(variable)).flatMap({ case Var(name) => Some(name) case _ => None }).getOrElse(variable) - - rule match { - case ProofTree(SuslikProofStep.NilNotLval(vars), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.CheckPost(_, _), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickCard(_, _), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.PickArg(from, to), List(next), _) => - to.name == variable || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Pick(from, to), List(next), _) => - val picked_variables = to match { case Var(v) => Some(v) case _ => None } - (picked_variables.contains(variable)) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Branch(cond, _), List(ifTrue, ifFalse), _) => - is_variable_used_in_exp(variable)(cond) || - is_variable_used_in_proof(variable)(ifTrue) || - is_variable_used_in_proof(variable)(ifFalse) - case ProofTree(SuslikProofStep.Write(Statements.Store(Var(tov), offset, e)), List(next), _) => - (tov == variable) || is_variable_used_in_exp(variable)(e) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.WeakenPre(unused), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.EmpRule(_), List(), _) => false - case ProofTree(SuslikProofStep.PureSynthesis(is_final, assignments), List(next), _) => - is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Open(pred, _, heaplet,cases), children, _) => - cases.zip(children).exists({ case (expr, rule) => - is_variable_used_in_exp(variable)(expr) || - is_variable_used_in_proof(variable)(rule) - }) - case ProofTree(SuslikProofStep.SubstL(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) - case ProofTree(SuslikProofStep.SubstR(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) - case ProofTree(SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, _, _, _), List(next), _) => - is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.HeapUnify(_), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.HeapUnifyPointer(from, to), List(next), _) => is_variable_used_in_proof(map_varaible(Map(from -> to)))(next) - case ProofTree(SuslikProofStep.FrameUnfold(h_pre, h_post), List(next), _) => is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Close(app, selector, asn, fresh_exist), List(next), _) => - is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.StarPartial(new_pre_phi, new_post_phi), List(next), _) => - is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Read(_, _, Load(Var(toe), _, Var(frome), offset)), List(next), _) => - (frome == variable) || ((toe != variable) && is_variable_used_in_proof(variable)(next)) - case ProofTree(SuslikProofStep.Call(_, Call(_, args, _)), List(next), _) => - args.exists(is_variable_used_in_exp(variable)) || - is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Free(Free(Var(v)), _), List(next), _) => - (v == variable) || is_variable_used_in_proof(variable)(next) - case ProofTree(SuslikProofStep.Malloc(_, _, Malloc(Var(toe), tpe, sz)), List(next), _) => - (toe != variable) && is_variable_used_in_proof(variable)(next) - } - } - - val new_context = record_variable_mapping(Map(old_var -> new_var))(context) - val rest = (retrieve_typing_context(context).get(old_var.name)) match { - case Some(CoqPtrType) => - ProofTree(VSTProofStep.ValidPointerOrNull(new_var.name), List(translate_proof_rules(next)(new_context))) - case _ => translate_proof_rules(next)(new_context) - } - if (is_variable_used_in_proof(new_var.name)(next)) { - ProofTree(VSTProofStep.Rename(old_var.name, new_var.name), List(ProofTree(VSTProofStep.Forward, List(rest)))) - } else { - ProofTree(VSTProofStep.Rename(old_var.name, new_var.name), List(rest)) - } - } - - /** - * Translates a suslik Open Rule into the corresponding VST rules - * - * Does this by mapping each constructor of the opened predicate to a branch of the rule, - * and then for each branch introducing the variables that it uses. - */ - def handle_open_rule(rule: SuslikProofStep.Open, children: List[ProofTree[SuslikProofStep]], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Open(SApp(predicate_name, args, _, Var(card_variable)), fresh_vars, _, cases) => - val pred = pred_map(predicate_name) - val context_branches = pred.clauses.zip(cases.zip(children)).map({ - case ((constructor, clause), (expr, rule)) => - // each clause of the type introduces existentials - val new_variables = pred.constructor_existentials(constructor).map({ - // rename existential variables if they have been assigned fresh variables - case (variable, ty) => fresh_vars.get(Var(variable)).map({ case Var(new_name) => (new_name, ty) }).getOrElse((variable, ty)) - }) - val constructor_args = constructor.constructor_args.map(v => fresh_vars(Var(v)).name) - val new_context_1 = add_new_variables( - new_variables.toMap ++ - constructor_args.map(v => (v, CoqCardType(pred.name))).toMap - )(context) - val new_context_2 = add_new_coq_variables(new_variables.toMap ++ - constructor_args.map(v => (v, CoqCardType(pred.name))).toMap)(new_context_1) - // val (args, constructor_args) = partition_cardinality_args(new_variables)() - - val card_value = ProofSpecTranslation.translate_cardinality(pred, constructor match { - case ProofTerms.CardNull => ProofTerms.CardNull - case ProofTerms.CardOf(args) => ProofTerms.CardOf(args.map(arg => fresh_vars.get(Var(arg)).map(_.name).getOrElse(arg))) - }) - val new_context = record_variable_assignment_raw(card_variable, card_value)(new_context_2) - val selector = ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr) - - ((pred.constructor_name(constructor), constructor, constructor_args), - selector, - new_variables.map(_._1), - translate_proof_rules(rule)(new_context)) - }).toList - val branches = context_branches.map({ case (expr, selector, vars, _) => (expr, selector, vars) }) - val next = context_branches.map({ case (_, _, _, next) => next }) - ProofTree(VSTProofStep.ForwardIfConstructor( - card_variable, - predicate_name, - branches - ), - next - ) - } - - def handle_pick_rule(rule: SuslikProofStep.Pick, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Pick(Var(name), expr) => - val new_context = record_variable_assignment(name, expr)(context) - translate_proof_rules(next)(new_context) - } - - def handle_pick_card_rule(rule: SuslikProofStep.PickCard, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickCard(Var(name), expr) => - - /** Given an expression representing a pick of a cardinality variable - * returns the corresponding cardinality constructor - * - * i.e - * _alpha_512 -> _alpha_514 + 1 - * - * produces - * (lseg_card_1 _alpha_514), [(_alpha_514, lseg_card)] - * (i.e, the picked cardinality is that alpha_512 maps to lseg_card_1 _alpha_514, where _alpha_514 is a new existential variable) - */ - def cardinality_expr_mapping_to_cardinality_map(base_expr: String)(expr: Expressions.Expr): (ProofCExpr, List[(Ident, VSTProofType)]) = - expr match { - case Var(name) => - context.gamma.get(name) match { - case Some(ty) => (ProofCVar(name, ty), Nil) - case None => (ProofCVar(name, context.gamma(base_expr)), List((name, context.gamma(base_expr)))) - } - case rule@Expressions.BinaryExpr(Expressions.OpPlus, expr, Expressions.IntConst(_)) => - val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(base_expr)(expr) - val pred_name = context.gamma(base_expr) match { - case ProofTypes.CoqCardType(pred_type) => pred_type - } - val predicate = pred_map(pred_name) - // NOTE: Assumes that all predicates have a 1-argument constructor - (ProofCCardinalityConstructor(predicate.name, predicate.constructor_name(predicate.constructor_by_arg(1)), List(translated_expr)), new_vars) - } - - val (translated_expr, new_vars) = cardinality_expr_mapping_to_cardinality_map(name)(expr) - val new_context = record_variable_assignment_card(name, translated_expr)( - add_new_variables(new_vars.toMap)(context) - ) - translate_proof_rules(next)(new_context) - } - - def handle_pick_arg_rule(rule: SuslikProofStep.PickArg, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PickArg(Var(name), expr) => - val new_context = record_variable_assignment(name, expr)(context) - translate_proof_rules(next)(new_context) - } - - def handle_emp_rule(context: Context) = { - def instantiate_existentials(existentials: List[(Ident, VSTProofType)])(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = - existentials.foldRight(then)( - (variable, next) => - ProofTree(VSTProofStep.Exists(context.variable_map.getOrElse(variable._1, ProofCVar(variable._1, variable._2))), List(next)) - ) - - def add_evars(unfolds: List[Unfold])(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = - unfolds.flatMap(unfold => unfold.new_equalities.map(v => ProofCVar(v._1, v._2.type_expr))).foldRight(then)({ case (proof_var, rest) => - if (context.coq_context.contains(proof_var.name)) { - rest - } else { - ProofTree(VSTProofStep.IntroEvar(proof_var), List(rest)) - } - }) - - def unfold_predicates(then: ProofTree[VSTProofStep]): ProofTree[VSTProofStep] = - context.queued_unfolds.foldLeft(then)( - { case (next, unfold) => - val predicate: VSTPredicate = unfold.VSTPredicate - unfold.new_equalities.foldRight( - ProofTree( - VSTProofStep.Unfold( - predicate, - unfold.VSTPredicate.params.length, - context.variable_map.getOrElse(unfold.cardinality, ProofCVar(unfold.cardinality, CoqCardType(predicate.name)))), - List(unfold.existentials.foldRight(next)({ case ((variable, ty), next) => ProofTree(VSTProofStep.Exists(context.variable_map.getOrElse(variable, ProofCVar(variable, ty))), List(next)) }))) - : ProofTree[VSTProofStep]) ({ - case ((evar_name, evar_value), (next: ProofTree[VSTProofStep])) => - if (context.coq_context.contains(evar_name)) { - next - } else { - ProofTree(VSTProofStep.InstantiateEvar(evar_name, context.card_map.getOrElse(evar_name, evar_value)), List(next)) - } - }) - } - ) - - ProofTree(VSTProofStep.ForwardEntailer, List( - add_evars(context.queued_unfolds)( - instantiate_existentials(context.post)( - context.post match { // If no existentials, only entailer will be at the end of the unfoldings - case Nil => - context.queued_unfolds match { - case Nil => ProofTree(VSTProofStep.Qed, List()) - case ::(_, _) => unfold_predicates(ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List())))) - } - case ::(_, _) => - context.queued_unfolds match { - case Nil => ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List()))) - case ::(_, _) => ProofTree(VSTProofStep.Entailer, List(unfold_predicates(ProofTree(VSTProofStep.Entailer, List(ProofTree(VSTProofStep.Qed, List())))))) - } - } - ) - ) - )) - } - - def handle_pure_synthesis_rule(rule: SuslikProofStep.PureSynthesis, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.PureSynthesis(is_final, subst) => - val new_context = subst.map({ case (Var(name), expr) => (name, expr) }).foldRight(context)({ - case ((name, expr), context) => record_variable_assignment(name, expr)(context) - }) - translate_proof_rules(next)(new_context) - } - - def handle_heap_unify(rule: SuslikProofStep.HeapUnify, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.HeapUnify(_) => - // val new_context = subst.map({case (Var(name), expr) => (name,expr) }).foldRight(context)({ - // case ((name,expr), context) => record_variable_assignment(name,expr)(context) - // }) - translate_proof_rules(next)(context) - } - - def handle_heap_unify_pointer(rule: SuslikProofStep.HeapUnifyPointer, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.HeapUnifyPointer(Var(name), expr) => - val new_context = record_variable_assignment(name, expr)(context) - translate_proof_rules(next)(new_context) - } - - def handle_substl_rule(rule: SuslikProofStep.SubstL, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstL(Var(name), expr) => - ProofTree( - VSTProofStep.AssertPropSubst(name, ProofSpecTranslation.translate_expression(retrieve_typing_context(context))(expr)), - List(translate_proof_rules(next)(context)) - ) - } - - def handle_substr_rule(rule: SuslikProofStep.SubstR, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.SubstR(Var(old_name), expr) => - expr match { - case Var(new_name) => - val new_context = record_variable_mapping(Map(Var(old_name) -> Var(new_name)))(context) - ProofTree(VSTProofStep.Rename(old_name, new_name), List(translate_proof_rules(next)(new_context))) - case _ => - val new_context = record_variable_assignment(old_name, expr)(context) - translate_proof_rules(next)(new_context) - } - } - - def handle_abduce_call(rule: SuslikProofStep.AbduceCall, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, Call(Var(fun), _, _), freshSub, _, _, _) => - var typing_context = retrieve_typing_context(context) - f_pre.vars.foreach({ case Var(name) => - if (!typing_context.contains(name)) { - typing_context = typing_context + (name -> CoqPtrType) - } - }) - val call_precond = ProofSpecTranslation.translate_assertion(typing_context)(f_pre) - val call_args = unify_call_params(call_precond).map({ case (name, _) => name }) - val existentials = spec.existensial_params.toList.map({ case (name, ty) => (freshSub(Var(name)).name, ty) }) - var new_context = push_function((fun, call_args, existentials))(context) - translate_proof_rules(next)(new_context) - } - - def handle_nilnotnval_rule(rule: SuslikProofStep.NilNotLval, next: ProofTree[SuslikProofStep], context: Context) = rule match { - case SuslikProofStep.NilNotLval(vars) => - vars.foldRight(translate_proof_rules(next)(context))({ - case (_@Var(name), rest) => - ProofTree(VSTProofStep.ValidPointer( - name - ), List(rest)) - }) - } - - def handle_abduce_branch_rule(rule: SuslikProofStep.Branch, ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Branch(cond, _) => - ProofTree(VSTProofStep.ForwardIf, List( - translate_proof_rules(ifTrue)(context), - translate_proof_rules(ifFalse)(context) - )) - } - - def handle_call_rule(rule: SuslikProofStep.Call, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Call(_, call) => - val ((fun, args, existentials), new_context_1) = pop_function(context) - ProofTree(VSTProofStep.ForwardCall(args), - existentials match { - case Nil => List(translate_proof_rules(next)(new_context_1)) - case _ => - val new_context = add_new_coq_variables(existentials.toMap)(new_context_1) - List(ProofTree(VSTProofStep.IntrosTuple(existentials), List(translate_proof_rules(next)(new_context)))) - }) - } - - def handle_write_rule(rule: SuslikProofStep.Write, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Write(stmt) => ProofTree(VSTProofStep.Forward, List(translate_proof_rules(next)(context))) - } - - def handle_free_rule(rule: SuslikProofStep.Free, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Free(Free(Var(name)), size) => ProofTree(VSTProofStep.Free(name, size), List(translate_proof_rules(next)(context))) - } - - def handle_close_rule(rule: SuslikProofStep.Close, next: ProofTree[SuslikProofStep], context: Context): ProofTree[VSTProofStep] = rule match { - case SuslikProofStep.Close(app, o_selector, asn, fresh_exist) => - - // Use application of of constructor to infer mapping of variables - val predicate = pred_map(app.pred) - val selector: ProofCExpr = ProofSpecTranslation.translate_expression(predicate.params.toMap)(o_selector) - val (cardinality, clause) = predicate(selector) - val substitution: Map[String, ProofCExpr] = - predicate.params.zip(app.args).map({ case ((name, ty), arg) => (name, ProofSpecTranslation.translate_expression(context.gamma)(arg)) }).toMap - - val clause_existentials = predicate.find_existentials(cardinality)(clause).map({ - case (name, ty) => (fresh_exist(Var(name)).name, ty) - }) - - val card_equality: List[(String, ProofCExpr)] = List((app.card match { - case Var(name) => name - }, ProofSpecTranslation.translate_cardinality(predicate, cardinality))) - - /** - * Given two equal expressions, attempts to extract a mapping on variables - */ - def equal_variables_to_variable_mapping(expr_a: ProofCExpr)(expr_b: ProofCExpr) = (expr_a, expr_b) match { - case (ProofCVar(var_a, a_ty), ProofCVar(var_b, b_ty)) => - if (context.gamma.contains(var_a)) { - List((var_a, ProofCVar(var_b, b_ty))) - } - else { - List((var_b, ProofCVar(var_a, a_ty))) - } - case (ProofCVar(var_a, _), b_expr) => - List((var_a, b_expr)) - case (a_expr, ProofCVar(var_b, _)) => - List((var_b, a_expr)) - case _ => List() - } - - def extract_equalities(expr: ProofCExpr): List[(String, ProofCExpr)] = expr match { - case ProofCBinaryExpr(ProofTerms.Expressions.ProofCOpAnd, left, right) => - extract_equalities(left) ++ extract_equalities(right) - case ProofCBinaryExpr(op, left, right) => op match { - case ProofTerms.Expressions.ProofCOpIntEq => equal_variables_to_variable_mapping(left)(right) - case ProofTerms.Expressions.ProofCOpBoolEq => equal_variables_to_variable_mapping(left)(right) - case ProofTerms.Expressions.ProofCOpSetEq => equal_variables_to_variable_mapping(left)(right) - case ProofTerms.Expressions.ProofCOpPtrEq => equal_variables_to_variable_mapping(left)(right) - case _ => List() - } - case _ => List() - } - - // val expr_equalities = clause.pure.map(_.subst(substitution)).flatMap(extract_equalities) - - val new_equalities = card_equality.toMap - - val args = cardinality.constructor_args.map( - name => (fresh_exist(Var(name)).name, CoqCardType(predicate.name)) - ) - val unfolding = Unfold(predicate, app.card.asInstanceOf[Var].name, args, clause_existentials, new_equalities) - val new_context = push_unfolding(context)(unfolding, new_equalities) - val new_context_1 = record_variable_mapping(fresh_exist)(new_context) // record_variable_mapping(fresh_exist)(new_context) - val new_context_2 = add_new_variables(clause_existentials.toMap)(new_context_1) // record_variable_mapping(fresh_exist)(new_context) - - translate_proof_rules(next)(new_context_2) - } - - def handle_malloc_rule(rule: SuslikProofStep.Malloc, next: ProofTree[SuslikProofStep], context: Context) = rule match { - case SuslikProofStep.Malloc(Var(original), Var(name), Malloc(Var(to_var), _, sz)) => - val new_context_0 = add_new_variables(Map(name -> CoqPtrType))(context) - val new_context_1 = record_variable_assignment(original, Var(name))(new_context_0) - val new_context = add_new_coq_variables(Map(to_var -> CoqPtrType))(new_context_1) - ProofTree(VSTProofStep.Malloc(sz), - List(ProofTree(VSTProofStep.Intros( - List((to_var, CoqPtrType))), - List(translate_proof_rules(next)(new_context)) - ))) - } - - def translate_proof_rules(rule: ProofTree[SuslikProofStep])(context: Context): ProofTree[VSTProofStep] = { - rule match { - // Branching rules - case ProofTree(rule:SuslikProofStep.Open, children, _) => handle_open_rule(rule, children, context) - case ProofTree(rule:SuslikProofStep.Branch, List(ifTrue, ifFalse), _) => handle_abduce_branch_rule(rule, ifTrue, ifFalse, context) - - // Read and write Operations - case ProofTree(rule:SuslikProofStep.Write, List(next), _) => handle_write_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.Read, List(next), _) => handle_read_rule(rule, next, context) - - // Memory management rules - case ProofTree(rule:SuslikProofStep.Free, List(next), _) => handle_free_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.Malloc, List(next), _) => handle_malloc_rule(rule, next, context) - - // Abduce call & Existentials - case ProofTree(rule:SuslikProofStep.AbduceCall, List(next), _) => handle_abduce_call(rule, next, context) - case ProofTree(rule:SuslikProofStep.Pick, List(next), _) => handle_pick_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.PureSynthesis, List(next), _) => handle_pure_synthesis_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.PickCard, List(next), _) => handle_pick_card_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.PickArg, List(next), _) => handle_pick_arg_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.Call, List(next), _) => handle_call_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.Close, List(next), _) => handle_close_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.HeapUnify, List(next), _) => handle_heap_unify(rule, next, context) - case ProofTree(rule:SuslikProofStep.HeapUnifyPointer, List(next), _) => handle_heap_unify_pointer(rule, next, context) - - - // Completion rule - case ProofTree(_:SuslikProofStep.EmpRule, List(), _) => handle_emp_rule(context) - - // Context changing rules - case ProofTree(rule:SuslikProofStep.NilNotLval, List(next), _) => handle_nilnotnval_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.SubstL, List(next), _) => handle_substl_rule(rule, next, context) - case ProofTree(rule:SuslikProofStep.SubstR, List(next), _) => handle_substr_rule(rule, next, context) - - // Ignored rules - case ProofTree(_:SuslikProofStep.WeakenPre, List(next), _) => translate_proof_rules(next)(context) - case ProofTree(_:SuslikProofStep.CheckPost, List(next), _) => translate_proof_rules(next)(context) - - case ProofTree(_:SuslikProofStep.FrameUnfold, List(next), _) => translate_proof_rules(next)(context) - - - case ProofTree(_:SuslikProofStep.StarPartial, List(next), _) => translate_proof_rules(next)(context) - } - } - - val simplified = SuslikProofStep.of_certtree(root) - println(s"Suslik Proof:\n ${simplified.pp}") - - val vst_proof: ProofTree[VSTProofStep] = translate_proof_rules(simplified)(initial_context) - - Proof(name, predicates, spec, vst_proof, contains_free(simplified), contains_malloc(simplified)) - } - - def contains_free(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { - case _:SuslikProofStep.Free => true - case _ => proof.children.exists(contains_free) - } - - def contains_malloc(proof: ProofTree[SuslikProofStep]): Boolean = proof.step match { - case _:SuslikProofStep.Malloc => true - case _ => proof.children.exists(contains_malloc) - } - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index c3b7e683c..f9b6e83f5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -6,6 +6,7 @@ import org.tygus.suslik.certification.targets.vst.VSTCertificate import org.tygus.suslik.certification.targets.vst.clang.Statements import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, VSTProofStep} +import org.tygus.suslik.certification.targets.vst.translation.VSTTranslator.{VSTClientContext, VSTTranslator} import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -30,15 +31,14 @@ object Translation { // println(procedure.pp) // println(new_procedure.pp) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList - - println(base_proof.pp) + predicates.foreach(v => println(v.pp)) val pred_map = predicates.map(v => (v.name,v)).toMap implicit val initial_context: VSTClientContext = VSTClientContext.make_context(pred_map) implicit val translator: VSTTranslator = new VSTTranslator val evaluator = new StackEvaluator[SuslikProofStep, VSTProofStep, VSTClientContext] val steps = evaluator.run(base_proof) - val procedure = CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) + val procedure = ??? // CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) val (spec, _) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) val proof = Proof(proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep]) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala index 04e0e2240..f12de96cb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala @@ -1,89 +1,95 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.SuslikProofStep -import org.tygus.suslik.certification.targets.vst.clang.CTypes.VSTCType -import org.tygus.suslik.certification.targets.vst.clang.Expressions -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{IsValidPointerOrNull, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.Types.VSTType +import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} -import org.tygus.suslik.certification.targets.vst.logic.{ProofTerms, ProofTypes, VSTProofStep} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} -import org.tygus.suslik.language.{Expressions, SSLType} +import org.tygus.suslik.language.SSLType +import org.tygus.suslik.certification.SuslikProofStep import scala.collection.immutable.Queue -case class VSTClientContext( - pred_map: Map[String, VSTPredicate], - coq_context: Map[String, ProofTypes.VSTProofType], - variable_map: Map[String, ProofTerms.Expressions.ProofCExpr] - ) extends ClientContext[VSTProofStep] { - def with_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { - val new_vars: Map[String, ProofTypes.VSTProofType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_type(ty)) }).toMap - VSTClientContext(pred_map, coq_context ++ new_vars, variable_map) - } +object VSTTranslator { + + case class VSTClientContext( + pred_map: Map[String, VSTPredicate], + coq_context: Map[String, VSTType], + variable_map: Map[String, ProofCExpr] + ) extends ClientContext[VSTProofStep] { + + def with_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + VSTClientContext(pred_map, coq_context ++ new_vars, variable_map) + } + + def with_mapping_between(from: String, to: Expr): VSTClientContext = { + val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) + VSTClientContext(pred_map, coq_context, variable_map ++ Map(from -> to_expr)) + } - def with_mapping_between(from: String, to: Expr) : VSTClientContext = { - val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) - VSTClientContext(pred_map, coq_context, variable_map ++ Map(from -> to_expr)) } -} + object VSTClientContext { + def make_context(pred_map: Map[String, VSTPredicate]): VSTClientContext = + VSTClientContext(pred_map, Map(), Map()) + } -object VSTClientContext { - def make_context(pred_map: Map[String,VSTPredicate]) : VSTClientContext = - VSTClientContext(pred_map, Map(), Map()) -} + class VSTTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTClientContext] { + type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] + private val no_deferreds: Queue[Deferred] = Queue() -class VSTTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTClientContext] { - type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] - private val no_deferreds : Queue[Deferred] = Queue () - private def with_no_deferreds(ctx: VSTClientContext) = (no_deferreds, ctx) + private def with_no_deferreds(ctx: VSTClientContext) = (no_deferreds, ctx) - def with_no_op(context: VSTClientContext) : Result[VSTProofStep, VSTClientContext] = Result(List(), List((Queue(), context))) + def with_no_op(context: VSTClientContext): Result[VSTProofStep, VSTClientContext] = Result(List(), List((Queue(), context))) - override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Translator.Result[VSTProofStep, VSTClientContext] = { - value match { - case SuslikProofStep.NilNotLval(vars) => - val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) - Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) - case SuslikProofStep.CheckPost(prePhi, postPhi) => - with_no_op(clientContext) - case SuslikProofStep.Pick(from, to_expr) => - val new_context = clientContext with_mapping_between(from.name, to_expr) - with_no_op(new_context) - case SuslikProofStep.Branch(cond, bLabel) => - val cont = with_no_deferreds(clientContext) - Result(List(ForwardIf), List(cont, cont)) - case SuslikProofStep.Write(stmt) => - Result(List(Forward), List(with_no_deferreds(clientContext))) - case SuslikProofStep.WeakenPre(unused) => - with_no_op(clientContext) - case SuslikProofStep.EmpRule(label) => ??? - case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? - case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? - case SuslikProofStep.SubstL(from, to) => ??? - case SuslikProofStep.SubstR(from, to) => ??? - case SuslikProofStep.Read(from, to, operation) => ??? - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? - case SuslikProofStep.HeapUnify(subst) => ??? - case SuslikProofStep.HeapUnifyPointer(from, to) => ??? - case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? - case SuslikProofStep.Call(subst, call) => ??? - case SuslikProofStep.Free(stmt, size) => ??? - case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? - case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? - case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? - case SuslikProofStep.PickCard(from, to) => ??? - case SuslikProofStep.PickArg(from, to) => ??? - case SuslikProofStep.Init(goal) => - val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) - val ctx = clientContext with_variables_of program_vars - val existentials = goal.existentials.toList.map(v => (v.name, goal.gamma(v))) - with_no_op(ctx) - case SuslikProofStep.Inconsistency(label) => ??? + override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Translator.Result[VSTProofStep, VSTClientContext] = { + value match { + case SuslikProofStep.NilNotLval(vars) => + val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) + Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) + case SuslikProofStep.CheckPost(prePhi, postPhi) => + with_no_op(clientContext) + case SuslikProofStep.Pick(from, to_expr) => + val new_context = clientContext with_mapping_between(from.name, to_expr) + with_no_op(new_context) + case SuslikProofStep.Branch(cond, bLabel) => + val cont = with_no_deferreds(clientContext) + Result(List(ForwardIf), List(cont, cont)) + case SuslikProofStep.Write(stmt) => + Result(List(Forward), List(with_no_deferreds(clientContext))) + case SuslikProofStep.WeakenPre(unused) => + with_no_op(clientContext) + case SuslikProofStep.EmpRule(label) => ??? + case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? + case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? + case SuslikProofStep.SubstL(from, to) => ??? + case SuslikProofStep.SubstR(from, to) => ??? + case SuslikProofStep.Read(from, to, operation) => ??? + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? + case SuslikProofStep.HeapUnify(subst) => ??? + case SuslikProofStep.HeapUnifyPointer(from, to) => ??? + case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? + case SuslikProofStep.Call(subst, call) => ??? + case SuslikProofStep.Free(stmt, size) => ??? + case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? + case SuslikProofStep.PickCard(from, to) => ??? + case SuslikProofStep.PickArg(from, to) => ??? + case SuslikProofStep.Init(goal) => + val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) + val ctx = clientContext with_variables_of program_vars + val existentials = goal.existentials.toList.map(v => (v.name, goal.gamma(v))) + with_no_op(ctx) + case SuslikProofStep.Inconsistency(label) => ??? + } } } -} + +} \ No newline at end of file From 410f5c0231463443bf297994c8fadc7cfeeddc98 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 17 Feb 2021 16:27:14 +0800 Subject: [PATCH 098/211] HTT: provide default existential value when never used --- .../certification/targets/htt/language/Types.scala | 12 +++++++++++- .../targets/htt/translation/ProofContext.scala | 7 ++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala index 27bf9f7a6..497acb654 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala @@ -1,7 +1,17 @@ package org.tygus.suslik.certification.targets.htt.language +import org.tygus.suslik.certification.targets.htt.language.Expressions._ + object Types { - sealed abstract class HTTType extends PrettyPrinting + sealed abstract class HTTType extends PrettyPrinting { + lazy val defaultExpr: CExpr = this match { + case CBoolType => CBoolConst(true) + case CNatType => CNatConst(0) + case CPtrType => CPtrConst(0) + case CNatSeqType => CSetLiteral(Nil) + case _ => CBoolConst(true) + } + } case object CBoolType extends HTTType { override def pp: String = "bool" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index 580c3a4da..645dbb60d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} +import org.tygus.suslik.certification.targets.htt.language.Types.HTTType import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CInductiveClause, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv @@ -9,12 +10,12 @@ import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import scala.collection.mutable.ListBuffer case class ProofContext(predicates: PredicateEnv = Map.empty, - postEx: Seq[CExpr] = Seq.empty, + postEx: Map[CVar, (HTTType, CExpr)] = Map.empty, appAliases: Map[CSApp, CSApp] = Map.empty, unfoldings: Map[CSApp, CInductiveClause] = Map.empty, callGoal: Option[Proof.Goal] = None, nextCallId: Int = 0, - hints: ListBuffer[Hint], + hints: ListBuffer[Hint] = new ListBuffer[Hint], numSubgoals: Int = 0) extends ClientContext[Proof.Step] { @@ -54,7 +55,7 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, * Update the current context with new substitutions */ def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): ProofContext = { - val postEx1 = postEx.map(_.subst(m)) + val postEx1 = postEx.mapValues(v => (v._1, v._2.subst(m))) val appAliases1 = affectedApps.foldLeft(appAliases) { case (appAliases, (app, alias)) => appAliases + (app -> alias) + (alias -> alias) } val unfoldings1 = unfoldings.map { case (app, clause) => app.subst(m) -> clause.subst(m) } this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) From ed667cb7321150dbef45d9d901d6a4f41b5cc876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Wed, 17 Feb 2021 16:34:21 +0800 Subject: [PATCH 099/211] Iris: WIP print HeapLang programs --- .../certification/targets/iris/Iris.scala | 24 +++ .../targets/iris/IrisCertificate.scala | 29 ++++ .../targets/iris/heaplang/Expressions.scala | 145 ++++++++++++++++++ .../targets/iris/heaplang/Types.scala | 15 ++ .../targets/iris/logic/Assertions.scala | 71 +++++++++ .../iris/translation/IrisTranslator.scala | 76 +++++++++ .../iris/translation/ProgramContext.scala | 6 + .../iris/translation/ProgramEvaluator.scala | 7 + .../iris/translation/ProgramPrinter.scala | 26 ++++ .../iris/translation/ProgramTranslator.scala | 45 ++++++ .../iris/translation/TranslatableOps.scala | 9 ++ .../iris/translation/Translation.scala | 22 +++ .../suslik/synthesis/SynthesisRunner.scala | 1 + 13 files changed, 476 insertions(+) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala new file mode 100644 index 000000000..879d8462f --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -0,0 +1,24 @@ +package org.tygus.suslik.certification.targets.iris + +import org.tygus.suslik.certification.targets.iris.translation.Translation +import org.tygus.suslik.certification.{CertTree, CertificationTarget, SuslikProofStep} +import org.tygus.suslik.language.Statements.Procedure +import org.tygus.suslik.logic.Environment +import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException + +object Iris extends CertificationTarget { + val name: String = "HTT" + val suffix: String = ".v" + + def certify(proc: Procedure, env: Environment) : IrisCertificate = { + val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) + val cert = Translation.translate(root, proc)(env) + + val simplified = SuslikProofStep.of_certtree(root) + println(s"Suslik Proof:\n ${SuslikProofStep.ProofTreePrinter.pp(simplified)}") + + CertTree.clear() // [Certify]: Clear tree after certification complete + cert + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala new file mode 100644 index 000000000..c4b13e46d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -0,0 +1,29 @@ +package org.tygus.suslik.certification.targets.iris + +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} + +case class IrisCertificate(name: String, funDef: HFunDef) extends Certificate { + val target: CertificationTarget = Iris + + private val prelude = + s"""From iris.program_logic Require Export weakestpre. + |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. + |From iris.heap_lang Require Import lang notation proofmode. + |From Hammer Require Import Hammer. + |Context `{!heapG Σ}. + |Context {PROP : bi}. + |Implicit Types Δ : envs PROP. + |Set Default Proof Using "Type". + | + |""".stripMargin + + def pp : String = { + val b = new StringBuilder + b.append(prelude) + b.append(funDef.pp) + b.toString() + } + + override def outputs: List[CertificateOutput] = List(CertificateOutput(None, name, pp)) +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala new file mode 100644 index 000000000..68533b8cb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -0,0 +1,145 @@ +package org.tygus.suslik.certification.targets.iris.heaplang + +import org.tygus.suslik.certification.targets.iris.translation.ProgramPrinter +import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.language.PrettyPrinting + +/** Encoding of HeapLang expressions. + * + * HeapLang does distinguish between statements and expressions: everything is an expression. + * We model only a very restricted subset of HeapLang. + * */ +object Expressions { + + sealed abstract class HExpr extends DestStep + + /** Literals */ + class HLit(repr: String) extends HExpr { + override def pp: String = s"#${repr}" + } + + case class HLitUnit() extends HLit("()") {} + + case class HLitInt(value: Int) extends HLit(value.toString) + + case class HLitBool(value: Boolean) extends HLit(value.toString) + + case class HLitLoc(value: Int) extends HLit(value.toString) + + /** Variables */ + case class HProgVar(name: String) extends HExpr { + override def pp: String = s""""${name}"""" + } + + /** Operators */ + sealed abstract class HUnOp extends PrettyPrinting + + object HOpNot extends HUnOp { + override def pp: String = "~" + } + + object HOpUnaryMinus extends HUnOp { + override def pp: String = "-" + } + + sealed abstract class HBinOp extends PrettyPrinting + + object HOpPlus extends HBinOp { + override def pp: String = "+" + } + + object HOpMinus extends HBinOp { + override def pp: String = "-" + } + + object HOpMultiply extends HBinOp { + override def pp: String = "*" + } + + object HOpEq extends HBinOp { + override def pp: String = "=" + } + + object HOpLt extends HBinOp { + override def pp: String = "<" + } + + object HOpLe extends HBinOp { + override def pp: String = "≤" + } + + object HOpOffset extends HBinOp { + override def pp: String = "+ₗ" + } + + /** Operations */ + case class HUnaryExpr(op: HUnOp, e: HExpr) extends HExpr { + override def pp: String = s"${op.pp} ${e.pp}" + } + + case class HBinaryExpr(op: HBinOp, left: HExpr, right: HExpr) extends HExpr { + override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" + } + + /** Condition */ + case class HIfThenElse(cond: HExpr, trueBranch: HExpr, falseBranch: HExpr) extends HExpr { + override def pp: String = + s"""|if: (${cond.pp}) then ( + |${trueBranch.pp} + |) else ( + |${falseBranch.pp} + |)""".stripMargin + } + + /** Let-bindings. We use HeapLang's syntactic sugar over lambdas. */ + case class HLet(x: HExpr, lhs: HExpr, in: HExpr) extends HExpr { + override def pp: String = + s"""|let: ${x.pp} := ${lhs.pp} in + |${in.pp}""".stripMargin + } + + case class HSeqComp(s1: HExpr, s2: HExpr) extends HExpr { + override def pp: String = + s"""|${s1.pp} ;; + |${s2.pp}""".stripMargin + } + + /** Heap manipulation */ + case class HLoad(e: HExpr) extends HExpr { + override def pp: String = s"! (${e.pp})" + } + case class HStore(lhs: HExpr, rhs: HExpr) extends HExpr { + override def pp: String = s"${lhs.pp} <- ${rhs.pp}" + } + case class HFree(e: HExpr) extends HExpr { + override def pp: String = s"Free ${e.pp}" + } + case class HAlloc(e: HExpr) extends HExpr { + override def pp: String = s"Alloc ${e.pp}" + } + case class HAllocN(n: HExpr, e: HExpr) extends HExpr { + override def pp: String = s"AllocN ${n.pp} ${e.pp}" + } + case class HCall(name: HExpr, params: Seq[HExpr]) extends HExpr { + override def pp: String = s"${name.pp} ${params.map(p => p.pp).mkString(" ")}" + } + + case class HFunDef(name: String, params: Seq[HProgVar], body: ProofTree[HExpr]) extends PrettyPrinting { + override def pp: String = { + s""" + |Definition ${name} : val := + |rec: "${name}" ${params.map(p => p.pp).mkString(" ")} := + |${ProgramPrinter.pp(body)}. + |""".stripMargin + } + } + + /** Artificial statements to match SSL proof rules. */ + case class HIf(selectors: Seq[HExpr]) extends HExpr + + case class HGuarded(cond: HExpr) extends HExpr + + case object HNoOp extends HExpr + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala new file mode 100644 index 000000000..829537b58 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala @@ -0,0 +1,15 @@ +package org.tygus.suslik.certification.targets.iris.heaplang + +import org.tygus.suslik.language.PrettyPrinting + +object Types { + + sealed abstract class HType extends PrettyPrinting + + case class HIntType() extends HType { + override def pp: String = "int" + } + case class HLocType() extends HType { + override def pp: String = "loc" + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala new file mode 100644 index 000000000..6f19d1173 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -0,0 +1,71 @@ +package org.tygus.suslik.certification.targets.iris.logic + +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HExpr, HOpEq, HOpLe, HOpLt} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HLocType, HType} +import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.language.Statements.Procedure + +object Assertions { + + /** Unlike HTT, which encodes programs in a shallow embedding, Iris has a deep embedding of programs. + * As such, program expressions are NOT Iris assertions (phi != HExpr). We define a lifting of + * program-level expressions to spec-level. */ + abstract class HSpecExpr extends PrettyPrinting + + case class HSpecVar(name: String) extends HSpecExpr { + override def pp: String = s"${name}" + } + + case class HSpecBinaryExpr(op: HBinOp, left: HSpecExpr, right: HSpecExpr) extends HSpecExpr { + override def pp: String = op match { + case HOpLe | HOpLt | HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" + case _ => ??? + } + } + + abstract class IPureAssertion extends PrettyPrinting + abstract class ISpatialAssertion extends PrettyPrinting + + abstract class ISpecification extends PrettyPrinting + + case class IPointsTo(loc: HExpr, value: HExpr) extends ISpatialAssertion { + override def pp: String = s"${loc.pp} ↦ ${value.pp}" + } + + case class IHeap(heaplets: Seq[ISpatialAssertion]) extends ISpatialAssertion { + override def pp: String = s"${heaplets.map(_.pp).mkString(" ∗ ")}" + } + + case class IFunSpec(proc: Procedure, + funArgs: Seq[(HSpecVar, HType)], + specUniversal: Seq[HSpecVar], + specExistential: Seq[HSpecVar], + pre: ISpatialAssertion, + post: ISpatialAssertion + ) extends ISpecification { + + override def pp: String = { + def getArgLitVal(v : HSpecVar, t: HType) : HSpecVar = + (v, t) match { + case (HSpecVar(name), HLocType()) => HSpecVar(s"l${name}") + case (HSpecVar(name), _) => HSpecVar(s"v${name}") + } + + val var_at = (v:HSpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" + val postExist = + if (specExistential.nonEmpty) + s"∃ ${specExistential.map(v => v.pp).mkString(" ")}" + else "" + + s""" + |Lemma ${proc.name}_spec : + |∀ ${specUniversal.map(v => v.pp).mkString(" ")}, + |${funArgs.map(v => var_at(v._1, v._2)).mkString(" →\n")} → + |{{{ ${pre.pp} }}} + | ${proc.name} ${funArgs.map(v => v._1.pp).mkString(" ")} + |{{{ RET #(); ${postExist}, ${post.pp} }}}. + |""".stripMargin + } + } + +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala new file mode 100644 index 000000000..ad4c56a66 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -0,0 +1,76 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HAllocN, HBinOp, HBinaryExpr, HCall, HExpr, HIfThenElse, HLet, HLitBool, HLitInt, HLitLoc, HLitUnit, HLoad, HOpEq, HOpLe, HOpLt, HOpMinus, HOpMultiply, HOpNot, HOpOffset, HOpPlus, HOpUnaryMinus, HProgVar, HStore, HUnOp, HUnaryExpr} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HIntType, HLocType, HType} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.HSpecExpr +import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.language.Expressions.{BinOp, BinaryExpr, BoolConst, Expr, IfThenElse, IntConst, LocConst, OpBoolEq, OpEq, OpLeq, OpLt, OpMinus, OpMultiply, OpNot, OpPlus, OpUnaryMinus, UnOp, UnaryExpr, Var} +import org.tygus.suslik.language.{IntType, LocType, SSLType, Statements} +import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} + +trait IrisTranslator[A, B] { + def translate(value: A): B +} + +object IrisTranslator { + case class TranslatorException(msg: String) extends Exception(msg) + + implicit val binOpTranslator: IrisTranslator[BinOp, HBinOp] = { + case OpPlus => HOpPlus + case OpMinus => HOpMinus + case OpMultiply => HOpMultiply + case OpEq | OpBoolEq => HOpEq + case OpLeq => HOpLe + case OpLt => HOpLt + case _ => ??? + } + + implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = el => el._1.translate + + implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = { + case OpNot => HOpNot + case OpUnaryMinus => HOpUnaryMinus + } + + implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr: Expr) => { + def visit(expr: Expr): HExpr = expr match { + case Var(name) => HProgVar(name) + case IntConst(v) => HLitInt(v) + case LocConst(v) => HLitLoc(v) + case BoolConst(v) => HLitBool(v) + case UnaryExpr(op, arg) => HUnaryExpr(op.translate, visit(arg)) + case BinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) + case IfThenElse(cond, t, f) => HIfThenElse(visit(cond), visit(t), visit(f)) + case _ => ??? + } + visit(expr) + } + + implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = pv => HProgVar(pv.name) + + implicit val typeTranslator: IrisTranslator[SSLType, HType] = { + case IntType => HIntType() + case LocType => HLocType() + case _ => ??? + } + + implicit val mallocTranslator: IrisTranslator[Malloc, HStore] = + stmt => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) + implicit val loadTranslator: IrisTranslator[Load, HLet] = + stmt => { + val baseVar = stmt.from.translate + val fromAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) + HLet(stmt.to.translate, HLoad(fromAddr), HLitUnit()) + } + implicit val storeTranslator: IrisTranslator[Store, HStore] = + stmt => { + val baseVar = stmt.to.translate + val toAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) + HStore(toAddr, stmt.e.translate) + } + + implicit val callTranslator: IrisTranslator[Call, HCall] = stmt => HCall(stmt.fun.translate, stmt.args.map(_.translate)) + + + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala new file mode 100644 index 000000000..031ec26fb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala @@ -0,0 +1,6 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext + +case class ProgramContext() extends ClientContext[HExpr] \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala new file mode 100644 index 000000000..35d93542e --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr +import org.tygus.suslik.certification.traversal.StackEvaluator + +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramContext] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala new file mode 100644 index 000000000..c536e7420 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala @@ -0,0 +1,26 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.targets.htt.Printer +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HGuarded, HIf, HNoOp} +import org.tygus.suslik.certification.traversal.ProofTree + +object ProgramPrinter extends Printer[HExpr] { + + case class Item(tree: ProofTree[HExpr]) + + override def initialize(tree: ProofTree[HExpr]): Item = Item(tree) + + override def process(item: Item): (List[String] => String, List[Item]) = { + val tree = item.tree + val k = tree.step match { + case HIf(selectors) => (children: List[String]) => { + val branches = selectors.zip(children).reverse + branches.tail.foldLeft(branches.head._2) { case (eb, (c, tb)) => s"if: ${c.pp}\nthen (\n$tb\n)\nelse (\n$eb\n)" } + } + case HGuarded(c) => (children: List[String]) => s"if: ${c.pp}\nthen (\n${children.head}\n)\nelse (\n${children(1)}\n)" + case HNoOp => (children: List[String]) => children.head + case _ => (children: List[String]) => s"${tree.step.pp}${children.headOption.map(";; \n" + _).getOrElse("")}" + } + (k, tree.children.map(Item)) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala new file mode 100644 index 000000000..4dd44b293 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -0,0 +1,45 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset} +import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.traversal.Evaluator.Deferreds +import org.tygus.suslik.certification.traversal.Translator +import org.tygus.suslik.certification.traversal.Translator.Result + +import scala.collection.immutable.Queue + +/** + * Extract a HeapLang program directly from the SSL proof. + */ +object ProgramTranslator extends Translator[SuslikProofStep, HExpr, ProgramContext] { + + override def translate(step: SuslikProofStep, ctx: ProgramContext): Translator.Result[HExpr, ProgramContext] = { + val withNoDeferred = (None, ctx) + step match { + case SuslikProofStep.Open(_, _, _, selectors) => + val stmt = HIf(selectors.map(_.translate)) + val conts = selectors.map(_ => withNoDeferred) + Result(List(stmt), conts) + case SuslikProofStep.Branch(cond, _) => + val stmt = HGuarded(cond.translate) + Result(List(stmt), List(withNoDeferred, withNoDeferred)) + case _:SuslikProofStep.EmpRule | _: SuslikProofStep.Inconsistency => + Result(List(HLitUnit()), Nil) + case _ => + val stmts = step match { + case SuslikProofStep.Write(stmt) => List(stmt.translate) + case SuslikProofStep.Read(_, _, stmt) => List(stmt.translate) + case SuslikProofStep.Malloc(_, _, stmt) => List(stmt.translate) + case SuslikProofStep.Free(stmt, sz) => + val v = stmt.v.translate + val addr = (sz: Int) => if (sz == 0) v else HBinaryExpr(HOpOffset, v, HLitLoc(sz)) + (0 until sz).map(i => HFree(addr(i))).toList + case SuslikProofStep.Call(_, stmt) => List(stmt.translate) + case _ => List(HNoOp) + } + Result(stmts, List(withNoDeferred)) + } + } + +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala new file mode 100644 index 000000000..c4b7f335d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala @@ -0,0 +1,9 @@ +package org.tygus.suslik.certification.targets.iris.translation + +object TranslatableOps { + implicit class Translatable[A](value: A) { + def translate[B](implicit translator: IrisTranslator[A,B]): B = { + translator.translate(value) + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala new file mode 100644 index 000000000..a73827466 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -0,0 +1,22 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.{CertTree, SuslikProofStep} +import org.tygus.suslik.certification.targets.iris.IrisCertificate +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef +import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.language.Statements.Procedure +import org.tygus.suslik.logic.Environment + +object Translation { + + case class TranslationException(msg: String) extends Exception(msg) + + def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): IrisCertificate = { + + val suslikTree = SuslikProofStep.of_certtree(node) + val progTree = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramContext()) + val funDef = HFunDef(proc.name, proc.formals.map(_.translate), progTree) + + IrisCertificate(proc.name, funDef) + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index e2667d179..0461b70dd 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -107,6 +107,7 @@ object SynthesisRunner extends SynthesisRunnerUtil { scopt.Read.reads { case "htt" => htt.HTT case "vst" => vst.VST + case "iris" => iris.Iris case _ => ??? } From a1fd5d2805bef0dbbde03c654da50adad4c14222 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 17 Feb 2021 16:53:58 +0800 Subject: [PATCH 100/211] Implementing program translation as well. --- .../certification/SuslikProofStep.scala | 4 +- .../translation/ProofSpecTranslation.scala | 5 +- .../targets/vst/translation/Translation.scala | 24 +++++--- .../translation/VSTProgramTranslator.scala | 57 +++++++++++++++++++ ...nslator.scala => VSTProofTranslator.scala} | 10 ++-- .../certification/traversal/ProofTree.scala | 5 +- .../traversal/StackEvaluator.scala | 2 +- 7 files changed, 82 insertions(+), 25 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala rename src/main/scala/org/tygus/suslik/certification/targets/vst/translation/{VSTTranslator.scala => VSTProofTranslator.scala} (95%) diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index 4fe75a38b..812f1d4b7 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -19,7 +19,7 @@ import scala.collection.immutable.Map /** compressed form of suslik rules */ -trait SuslikProofStep extends SourceStep {} +sealed trait SuslikProofStep extends SourceStep {} object SuslikProofStep { implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { @@ -27,7 +27,7 @@ object SuslikProofStep { tree.step match { case rule:Branch => rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) case rule:Open => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) - case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + case rule => rule.pp ++ "\n" ++ tree.children.map(_.pp(this)).mkString("\n") } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 13f1e9a5a..abd053e3f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -224,10 +224,7 @@ object ProofSpecTranslation { } /** translates a Suslik function specification into a proof */ - def translate_conditions(proc: CProcedureDefinition)(goal: Goal): (FormalSpecification, Map[Ident, VSTType]) = { - - val name: Ident = proc.name - val c_params: Seq[(Ident, VSTCType)] = proc.params + def translate_conditions(name: String, c_params: List[(Ident, VSTCType)])(goal: Goal): (FormalSpecification, Map[Ident, VSTType]) = { // collect all cardinality_params and their associated types val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index f9b6e83f5..1ce0a6abe 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -6,8 +6,9 @@ import org.tygus.suslik.certification.targets.vst.VSTCertificate import org.tygus.suslik.certification.targets.vst.clang.Statements import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, VSTProofStep} -import org.tygus.suslik.certification.targets.vst.translation.VSTTranslator.{VSTClientContext, VSTTranslator} -import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator} +import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{VSTClientContext, VSTProofTranslator} +import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator, Translator} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -17,6 +18,10 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) + def translate_proof[S,C](proof: ProofTree[SuslikProofStep])(translator: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { + val evaluator = new StackEvaluator[SuslikProofStep, S, C] + evaluator.run(proof)(translator, initial_context) + } def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val base_proof = SuslikProofStep.of_certtree(root) @@ -32,14 +37,15 @@ object Translation { // println(new_procedure.pp) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList predicates.foreach(v => println(v.pp)) - val pred_map = predicates.map(v => (v.name,v)).toMap - implicit val initial_context: VSTClientContext = VSTClientContext.make_context(pred_map) - implicit val translator: VSTTranslator = new VSTTranslator - val evaluator = new StackEvaluator[SuslikProofStep, VSTProofStep, VSTClientContext] - val steps = evaluator.run(base_proof) + val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, List())(root.goal) + + val program_steps = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramContext(Map())) + val procedure = ??? - val procedure = ??? // CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) - val (spec, _) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) + + + val pred_map = predicates.map(v => (v.name,v)).toMap + val steps = translate_proof(base_proof)(new VSTProofTranslator, VSTClientContext.make_context(pred_map)) val proof = Proof(proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep]) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala new file mode 100644 index 000000000..2ec7fa65d --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -0,0 +1,57 @@ +package org.tygus.suslik.certification.targets.vst.translation + +import org.tygus.suslik.certification.targets.vst.Types.{VSTCType, VSTType} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.language.Expressions.{Expr, Var} +import org.tygus.suslik.language.SSLType +import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.vst.clang.Statements.StatementStep +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext + +import scala.collection.immutable.Queue + +object VSTProgramTranslator { case class VSTProgramContext(typing_context: Map[String, VSTCType]) extends ClientContext[VSTProofStep] { } } + +class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VSTProgramTranslator.VSTProgramContext] { + type Deferred = Evaluator.Deferred[StatementStep, VSTProgramTranslator.VSTProgramContext] + private val no_deferreds: Queue[Deferred] = Queue() + + def with_no_op (implicit context: VSTClientContext) = (List(), List((Queue(), context))) + + override def translate(value: SuslikProofStep, clientContext: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = { + implicit ctx : VSTProgramTranslator.VSTProgramContext => clientContext + value match { + case SuslikProofStep.NilNotLval(vars) => ??? + case SuslikProofStep.CheckPost(prePhi, postPhi) => ??? + case SuslikProofStep.Pick(from, to) => ??? + case SuslikProofStep.Branch(cond, bLabel) => ??? + case SuslikProofStep.Write(stmt) => ??? + case SuslikProofStep.WeakenPre(unused) => ??? + case SuslikProofStep.EmpRule(label) => ??? + case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? + case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? + case SuslikProofStep.SubstL(from, to) => ??? + case SuslikProofStep.SubstR(from, to) => ??? + case SuslikProofStep.Read(ghostFrom, ghostTo, operation) => ??? + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? + case SuslikProofStep.HeapUnify(subst) => ??? + case SuslikProofStep.HeapUnifyPointer(from, to) => ??? + case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? + case SuslikProofStep.Call(subst, call) => ??? + case SuslikProofStep.Free(stmt, size) => ??? + case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? + case SuslikProofStep.PickCard(from, to) => ??? + case SuslikProofStep.PickArg(from, to) => ??? + case SuslikProofStep.Init(goal) => ??? + case SuslikProofStep.Inconsistency(label) => ??? + } + } +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala similarity index 95% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index f12de96cb..370cede6d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -11,10 +11,11 @@ import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.SSLType import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import scala.collection.immutable.Queue -object VSTTranslator { +object VSTProofTranslator { case class VSTClientContext( pred_map: Map[String, VSTPredicate], @@ -38,9 +39,8 @@ object VSTTranslator { def make_context(pred_map: Map[String, VSTPredicate]): VSTClientContext = VSTClientContext(pred_map, Map(), Map()) } - - - class VSTTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTClientContext] { +} +class VSTProofTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] private val no_deferreds: Queue[Deferred] = Queue() @@ -91,5 +91,3 @@ object VSTTranslator { } } } - -} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala index cc109e5b0..8acb74ffb 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/ProofTree.scala @@ -10,7 +10,6 @@ import org.tygus.suslik.logic.Specifications.GoalLabel * @param children list of child nodes * @param label the label of the Suslik goal to which the rule was applied */ -sealed case class ProofTree[S <: Step](step: S, children: List[ProofTree[S]], label: Option[GoalLabel] = None)(implicit printer: ProofTreePrinter[S]) - extends PrettyPrinting { - override def pp : String = printer.pp(this) +sealed case class ProofTree[S <: Step](step: S, children: List[ProofTree[S]], label: Option[GoalLabel] = None) { + def pp (implicit printer: ProofTreePrinter[S]) : String = printer.pp(this) } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index ff5354a38..b8c4afe25 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -17,7 +17,7 @@ class StackEvaluator[A <: SourceStep, B <: DestStep, C <: ClientContext[B]] exte // A stack of queued deferreds type DeferredsStack = List[Deferreds[B,C]] - def run(tree: ProofTree[A])(implicit translator: Translator[A,B,C], printer: ProofTreePrinter[B], initialClientContext: C): ProofTree[B] = { + def run(tree: ProofTree[A])(implicit translator: Translator[A,B,C], initialClientContext: C): ProofTree[B] = { // Use a child result to fulfill the evaluation task for a parent @tailrec def backward(taskStack: TaskStack, childResult: ProofTree[B]): ProofTree[B] = From 0672fd1234d87b0e9fc6e1e6ab6acdd11a2d9172 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 17 Feb 2021 17:11:16 +0800 Subject: [PATCH 101/211] updates VST backend with more principalled encoding of coq terms --- .../suslik/certification/SuslikProofStep.scala | 12 +++++------- .../targets/vst/clang/Statements.scala | 8 ++++---- .../targets/vst/translation/Translation.scala | 15 +++++++-------- .../vst/translation/VSTProgramTranslator.scala | 4 ++-- .../vst/translation/VSTProofTranslator.scala | 4 ++-- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala index 68b2f194d..3fcef72e6 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala @@ -1,24 +1,22 @@ package org.tygus.suslik.certification -import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.certification.traversal.Evaluator.EnvAction -import org.tygus.suslik.certification.targets.vst.translation.ProofTranslation.ProofRuleTranslationException import org.tygus.suslik.certification.traversal.Evaluator.DeferredsAction -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.SourceStep -import org.tygus.suslik.language.Expressions.{Expr, NilPtr, Subst, SubstVar, Var} +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} -import org.tygus.suslik.language.{PrettyPrinting, SSLType, Statements} +import org.tygus.suslik.language.{SSLType, Statements} import org.tygus.suslik.logic.Preprocessor.findMatchingHeaplets import org.tygus.suslik.logic.Specifications.{Assertion, Goal, GoalLabel, SuspendedCallGoal} import org.tygus.suslik.logic._ +import org.tygus.suslik.synthesis.StmtProducer._ import org.tygus.suslik.synthesis.rules.LogicalRules.StarPartial.extendPure import org.tygus.suslik.synthesis.rules._ -import org.tygus.suslik.synthesis.StmtProducer._ import scala.annotation.tailrec import scala.collection.immutable.Map +case class ProofRuleTranslationException(msg: String) extends Exception /** compressed form of suslik rules */ sealed trait SuslikProofStep extends SourceStep {} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index fca6bbde4..dd7c1e9dc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -16,11 +16,11 @@ object Statements { override def pp(tree: ProofTree[StatementStep]): String = tree.step match { case CIf(cond) => s"if (${cond.pp} {\n" + - s"${tree.children(0).pp}" + + s"${pp(tree.children(0))}" + s"} else {\n" + - s"${tree.children(1).pp}" + + s"${pp(tree.children(1))}" + s"\n}" - case _ => tree.step.pp ++ "\n" ++ tree.children.map(_.pp).mkString("\n") + case _ => tree.step.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") } } /** pretty printing a VST C Statement returns a C embedding */ @@ -98,7 +98,7 @@ object Statements { |""".stripMargin override def pp: String = { - val body_string = body.pp + val body_string = ProofTreePrinter.pp(body) val function_def = s"void ${name}(${ params.map({case (variable_name, variable_ty) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 1ce0a6abe..9ac89ff31 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -1,14 +1,13 @@ package org.tygus.suslik.certification.targets.vst.translation -import org.tygus.suslik.certification.{CertTree, SuslikProofStep} import org.tygus.suslik.certification.targets.vst.VSTCertificate -import org.tygus.suslik.certification.targets.vst.clang.Statements -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -import org.tygus.suslik.certification.targets.vst.logic.{Proof, ProofTerms, VSTProofStep} +import org.tygus.suslik.certification.targets.vst.logic.{Proof, VSTProofStep} import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{VSTClientContext, VSTProofTranslator} -import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator, Translator} +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Translator} +import org.tygus.suslik.certification.{CertTree, SuslikProofStep} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -18,7 +17,7 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - def translate_proof[S,C](proof: ProofTree[SuslikProofStep])(translator: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { + def translate_proof[S <: DestStep,C <: Evaluator.ClientContext[S]](proof: ProofTree[SuslikProofStep])(translator: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { val evaluator = new StackEvaluator[SuslikProofStep, S, C] evaluator.run(proof)(translator, initial_context) } @@ -38,7 +37,7 @@ object Translation { val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList predicates.foreach(v => println(v.pp)) val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, List())(root.goal) - + println(spec.pp) val program_steps = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramContext(Map())) val procedure = ??? diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index 2ec7fa65d..0e838e814 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -16,7 +16,7 @@ import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator import scala.collection.immutable.Queue -object VSTProgramTranslator { case class VSTProgramContext(typing_context: Map[String, VSTCType]) extends ClientContext[VSTProofStep] { } } +object VSTProgramTranslator { case class VSTProgramContext(typing_context: Map[String, VSTCType]) extends ClientContext[StatementStep] { } } class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VSTProgramTranslator.VSTProgramContext] { type Deferred = Evaluator.Deferred[StatementStep, VSTProgramTranslator.VSTProgramContext] @@ -25,7 +25,7 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS def with_no_op (implicit context: VSTClientContext) = (List(), List((Queue(), context))) override def translate(value: SuslikProofStep, clientContext: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = { - implicit ctx : VSTProgramTranslator.VSTProgramContext => clientContext + implicit val ctx : VSTProgramTranslator.VSTProgramContext = clientContext value match { case SuslikProofStep.NilNotLval(vars) => ??? case SuslikProofStep.CheckPost(prePhi, postPhi) => ??? diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 370cede6d..a6412fa53 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -42,11 +42,11 @@ object VSTProofTranslator { } class VSTProofTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] - private val no_deferreds: Queue[Deferred] = Queue() + private val no_deferreds: Option[Deferred] = None private def with_no_deferreds(ctx: VSTClientContext) = (no_deferreds, ctx) - def with_no_op(context: VSTClientContext): Result[VSTProofStep, VSTClientContext] = Result(List(), List((Queue(), context))) + def with_no_op(context: VSTClientContext): Result[VSTProofStep, VSTClientContext] = Result(List(), List((None, context))) override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Translator.Result[VSTProofStep, VSTClientContext] = { value match { From f2e400693e4f07f9af4dc590e6ae62f79ec4ae75 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 17 Feb 2021 17:11:55 +0800 Subject: [PATCH 102/211] Allow emitting no immediate steps if there is exactly one child --- .../certification/targets/htt/logic/Proof.scala | 4 ---- .../targets/htt/translation/ProofTranslator.scala | 10 +++++----- .../certification/traversal/BasicEvaluator.scala | 6 ++---- .../suslik/certification/traversal/Evaluator.scala | 14 +++++++++++++- .../certification/traversal/StackEvaluator.scala | 6 ------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 245ef46cd..1f650b018 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -142,10 +142,6 @@ object Proof { case object Inconsistency extends Step { override def pp: String = "ssl_inconsistency" } - case object Noop extends Step { - override val isNoop: Boolean = true - override def pp: String = "" - } case class StartProof(params: Seq[CVar]) extends Step { override def pp: String = s"Obligation Tactic := intro; ${MoveToCtxDestructFoldLeft(params).pp}; ssl_program_simpl.\nNext Obligation" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index bcffcb854..6223ed836 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -49,7 +49,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont def handleSubstitution(m: Map[CVar, CExpr], ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) - val steps = Proof.Noop :: renameAppsStep(affectedApps) + val steps = renameAppsStep(affectedApps) Result(steps, List(withNoDeferred(ctx1))) } @@ -103,7 +103,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val universalGhosts = pre.valueVars.diff(programVars) val goal = Proof.Goal(pre, post, gamma.translate, programVars, universalGhosts, f.name) val ctx1 = ctx.copy(callGoal = Some(goal)) - Result(List(Proof.Noop), List(withNoDeferred(ctx1))) + Result(List(), List(withNoDeferred(ctx1))) /** Control flow */ case SuslikProofStep.Branch(cond, _) => @@ -220,7 +220,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val unfoldings = ctx.unfoldings + (csapp -> clause) val ctx1 = ctx.copy(appAliases = appAliases, unfoldings = unfoldings) - Result(List(Proof.Noop), List((Some(deferred), ctx1))) + Result(List(), List((Some(deferred), ctx1))) /** Terminals */ case SuslikProofStep.Inconsistency(label) => @@ -231,10 +231,10 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont /** Pure entailments */ case SuslikProofStep.CheckPost(prePhi, postPhi) => ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate)) - Result(List(Proof.Noop), List(withNoDeferred(ctx))) + Result(List(), List(withNoDeferred(ctx))) /** Ignored */ - case _ => Result(List(Proof.Noop), List(withNoDeferred(ctx))) + case _ => Result(List(), List(withNoDeferred(ctx))) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala index ab631a258..d7bd6fb84 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -24,11 +24,9 @@ class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] exte val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList val childResults = next.map { case (child, deferredsStack, ctx) => visit(child, deferredsStack, ctx) } - steps.reverse match { - case last :: rest => rest.foldLeft(ProofTree(last, childResults, tree.label)) { case (child, v) => ProofTree(v, List(child), tree.label) } - case Nil => throw EvaluatorException("expected at least one translated value for this task") - } + foldStepsIntoTree(steps, childResults, tree.label) } + visit(tree, Nil, initialClientContext) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index b3459065b..904f4c974 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -1,12 +1,24 @@ package org.tygus.suslik.certification.traversal -import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.certification.traversal.Evaluator.{ClientContext, EvaluatorException} import org.tygus.suslik.certification.traversal.Step._ +import org.tygus.suslik.logic.Specifications.GoalLabel import scala.collection.immutable.Queue trait Evaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { def run(node: ProofTree[S])(implicit translator: Translator[S,D,C], initialClientContext: C): ProofTree[D] + + // Create a straight-line tree from a list of values + def foldStepsIntoTree(values: List[D], children: List[ProofTree[D]], label: Option[GoalLabel]): ProofTree[D] = + values.reverse match { + case last :: rest => rest.foldLeft(ProofTree(last, children, label)){ case (child, v) => ProofTree(v, List(child), label) } + case Nil => + children match { + case child :: Nil => child // special case: allow translator to emit no immediate steps if there is exactly one child + case _ => throw EvaluatorException(s"translator produced no immediate steps: expected 1 child but got ${values.length} instead") + } + } } object Evaluator { diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index 797e15419..98dd6de4d 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -65,12 +65,6 @@ class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] exte forward(nextChild, nextDeferredsStack, nextClientCtx, task :: taskStack) } } - // Create a tree from a list of values - def foldStepsIntoTree(values: List[D], children: List[ProofTree[D]], label: Option[GoalLabel]): ProofTree[D] = - values.reverse match { - case last :: rest => rest.foldLeft(ProofTree(last, children, label)){ case (child, v) => ProofTree(v, List(child), label) } - case Nil => throw EvaluatorException("expected at least one translated value for this task") - } forward(tree, Nil, initialClientContext, Nil) } From 722258b02f7fdad9612b4bf63ae2cc29ba3bc4c3 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 17 Feb 2021 17:19:45 +0800 Subject: [PATCH 103/211] fixed specification translation --- .../certification/targets/vst/logic/ProofTerms.scala | 4 ++-- .../targets/vst/translation/Translation.scala | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index f208cc8ca..e4029a810 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -26,14 +26,14 @@ object ProofTerms { **/ case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { - s"${expr.pp_as_c_value}" + s"${expr.pp}" } } /** prop predicate encoding that a given boolean expression is true */ case class IsTrue(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { - s"(is_true ${expr.pp})" + s"(is_true ${expr.pp_as_c_value})" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 9ac89ff31..022d779da 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.vst.translation +import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType} import org.tygus.suslik.certification.targets.vst.VSTCertificate import org.tygus.suslik.certification.targets.vst.logic.{Proof, VSTProofStep} import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext @@ -8,6 +9,8 @@ import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Translator} import org.tygus.suslik.certification.{CertTree, SuslikProofStep} +import org.tygus.suslik.language.Expressions.Var +import org.tygus.suslik.language.{IntType, LocType} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -36,7 +39,11 @@ object Translation { // println(new_procedure.pp) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList predicates.foreach(v => println(v.pp)) - val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, List())(root.goal) + val params = proc.formals.map({case (Var(name), ty) => ty match { + case LocType => (name, CoqPtrValType) + case IntType => (name, CoqIntValType) + }}) + val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) println(spec.pp) val program_steps = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramContext(Map())) val procedure = ??? From 43f77837df5e60acd8a44c7434a010d3a850449a Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 17 Feb 2021 20:21:21 +0800 Subject: [PATCH 104/211] Define generic, reusable tree traversal logic and clarify evaluator signature --- .../tygus/suslik/certification/CertTree.scala | 1 + .../certification/source/SuslikPrinter.scala | 25 ++++++ .../{ => source}/SuslikProofStep.scala | 82 +++++++------------ .../certification/targets/htt/HTT.scala | 6 +- .../certification/targets/htt/Printer.scala | 44 ---------- .../targets/htt/logic/ProofPrinter.scala | 12 +-- .../targets/htt/program/ProgramPrinter.scala | 20 ++--- .../htt/translation/ProgramEvaluator.scala | 6 +- .../htt/translation/ProgramTranslator.scala | 2 +- .../htt/translation/ProofEvaluator.scala | 6 +- .../htt/translation/ProofTranslator.scala | 2 +- .../targets/htt/translation/Translation.scala | 12 +-- .../certification/targets/iris/Iris.scala | 6 +- .../iris/translation/ProgramEvaluator.scala | 6 +- .../iris/translation/ProgramPrinter.scala | 19 ++--- .../iris/translation/ProgramTranslator.scala | 2 +- .../iris/translation/Translation.scala | 5 +- .../targets/vst/translation/Translation.scala | 11 ++- .../translation/VSTProgramTranslator.scala | 2 +- .../vst/translation/VSTProofTranslator.scala | 2 +- .../traversal/BasicEvaluator.scala | 4 +- .../certification/traversal/Evaluator.scala | 4 +- .../traversal/StackEvaluator.scala | 73 ++++------------- .../certification/traversal/TreeVisitor.scala | 43 ++++++++++ 24 files changed, 181 insertions(+), 214 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/source/SuslikPrinter.scala rename src/main/scala/org/tygus/suslik/certification/{ => source}/SuslikProofStep.scala (87%) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/traversal/TreeVisitor.scala diff --git a/src/main/scala/org/tygus/suslik/certification/CertTree.scala b/src/main/scala/org/tygus/suslik/certification/CertTree.scala index 46b0a92f2..96fb2e784 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertTree.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertTree.scala @@ -84,6 +84,7 @@ object CertTree { n.head } } + clear() trace.pruneFailed() traverse(trace.root) } diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikPrinter.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikPrinter.scala new file mode 100644 index 000000000..41026266f --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikPrinter.scala @@ -0,0 +1,25 @@ +package org.tygus.suslik.certification.source + +import SuslikProofStep.{Branch, Open} +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter, TreeVisitor} + +object SuslikPrinter extends ProofTreePrinter[SuslikProofStep] with TreeVisitor { + case class Item(tree: ProofTree[SuslikProofStep], pre: String) + type Result = String + + override def pp(tree: ProofTree[SuslikProofStep]): String = visit(Item(tree, "")) + + override def process(item: Item): (List[String] => String, List[Item]) = { + val Item(tree, pre) = item + val k = (children: List[String]) => tree.step match { + case r:Branch => s"$pre${r.pp}\n${pre}IfTrue:\n${children.head}\n${pre}IfFalse:\n${children(1)}" + case r:Open => s"$pre${r.pp}\n${r.selectors.zip(children).map { case (sel, child) => s"${pre}if ${sel.pp}:\n$child" } mkString "\n"}" + case r => s"$pre${r.pp}${children.headOption.map("\n" + _).getOrElse("")}" + } + val pre1 = tree.step match { + case _:Branch | _:Open => pre + " " + case _ => pre + } + (k, tree.children.map(Item(_, pre1))) + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala similarity index 87% rename from src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala rename to src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index 3fcef72e6..4f19fcda9 100644 --- a/src/main/scala/org/tygus/suslik/certification/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -1,8 +1,9 @@ -package org.tygus.suslik.certification +package org.tygus.suslik.certification.source +import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.traversal.Evaluator.DeferredsAction +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.traversal.Step.SourceStep -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Error, Load, Skip, Store} import org.tygus.suslik.language.{SSLType, Statements} @@ -22,95 +23,68 @@ case class ProofRuleTranslationException(msg: String) extends Exception sealed trait SuslikProofStep extends SourceStep {} object SuslikProofStep { - implicit object ProofTreePrinter extends ProofTreePrinter[SuslikProofStep] { - override def pp(tree: ProofTree[SuslikProofStep]): String = - tree.step match { - case rule:Branch => rule.pp ++ "\n" ++ rule.branch_strings(tree.children.head, tree.children(1)) - case rule:Open => rule.pp ++ "\n" ++ rule.branch_strings(tree.children) - case rule => rule.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") - } - } - - var indent : Int = 0 - - def ind : String = " " * indent - def sanitize (str: String) = str.replace(";\n","") - def with_scope[A] (f: Unit => A) : A = { - val old_indent_value = indent - indent = indent + 4 - val result = f(()) - indent = old_indent_value - result - } - /** corresponds to asserting all the variables in vars are not null */ case class NilNotLval(vars: List[Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}NilNotLval(${vars.map(_.pp).mkString(", ")});" + override def pp: String = s"NilNotLval(${vars.map(_.pp).mkString(", ")});" } /** solves a pure entailment with SMT */ case class CheckPost(prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { - override def pp: String = s"${ind}CheckPost(${prePhi.pp}; ${postPhi.pp});" + override def pp: String = s"CheckPost(${prePhi.pp}; ${postPhi.pp});" } /** picks an arbitrary instantiation of the proof rules */ case class Pick(from: Var, to: Expr) extends SuslikProofStep { - override def pp: String = s"${ind}Pick(${from.pp} -> ${to.pp});" + override def pp: String = s"Pick(${from.pp} -> ${to.pp});" } /** branches on a condition */ case class Branch(cond: Expr, bLabel: GoalLabel) extends SuslikProofStep { - def branch_strings(ifTrue: ProofTree[SuslikProofStep], ifFalse: ProofTree[SuslikProofStep]) = - s"${ind}IfTrue:\n${with_scope(_ => ProofTreePrinter.pp(ifTrue))}\n${ind}IfFalse:\n${with_scope(_ => ProofTreePrinter.pp(ifFalse))}" - - override def pp: String = s"${ind}AbduceBranch(${cond.pp}, ${bLabel.pp});" + override def pp: String = s"AbduceBranch(${cond.pp}, ${bLabel.pp});" } /** write a value */ case class Write(stmt: Store) extends SuslikProofStep { - override def pp: String = s"${ind}Write(${sanitize(stmt.pp)});" + override def pp: String = s"Write(${sanitize(stmt.pp)});" } /** weaken the precondition by removing unused formulae */ case class WeakenPre(unused: PFormula) extends SuslikProofStep { - override def pp: String = s"${ind}WeakenPre(${unused.pp});" + override def pp: String = s"WeakenPre(${unused.pp});" } /** empty rule */ case class EmpRule(label: Option[GoalLabel]) extends SuslikProofStep { override def deferredsAction: DeferredsAction = DeferredsAction.PopLayer - override def pp: String = s"${ind}EmpRule;" + override def pp: String = s"EmpRule;" } /** pure synthesis rules */ case class PureSynthesis(is_final: Boolean, assignments:Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}PureSynthesis(${is_final}, ${assignments.mkString(",")});" + override def pp: String = s"PureSynthesis(${is_final}, ${assignments.mkString(",")});" } /** open constructor cases */ case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { - def branch_strings(exprs: List[ProofTree[SuslikProofStep]]) = - s"${with_scope(_ => selectors.zip(exprs).map({case (sel,rest) => s"${ind}if ${sanitize(sel.pp)}:\n${with_scope(_ => ProofTreePrinter.pp(rest))}"}).mkString("\n"))}" - - override def pp: String = s"${ind}Open(${pred.pp}, ${fresh_vars.mkString(", ")});" + override def pp: String = s"Open(${pred.pp}, ${fresh_vars.mkString(", ")});" } /** subst L */ case class SubstL(from: Var, to: Expr) extends SuslikProofStep { - override def pp: String = s"${ind}SubstL(${from.pp} -> ${to.pp});" + override def pp: String = s"SubstL(${from.pp} -> ${to.pp});" } /** subst R */ case class SubstR(from: Var, to: Expr) extends SuslikProofStep { - override def pp: String = s"${ind}SubstR(${from.pp} -> ${to.pp});" + override def pp: String = s"SubstR(${from.pp} -> ${to.pp});" } /** read rule */ case class Read(ghostFrom: Var, ghostTo: Var, operation: Load) extends SuslikProofStep { - override def pp: String = s"${ind}Read(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(operation.pp)});" + override def pp: String = s"Read(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(operation.pp)});" } // /** abduce a call */ @@ -125,63 +99,63 @@ case class AbduceCall( gamma: Gamma ) extends SuslikProofStep { override def deferredsAction: DeferredsAction = DeferredsAction.PushLayer - override def pp: String = s"${ind}AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" + override def pp: String = s"AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" } /** unification of heap (ignores block/pure distinction) */ case class HeapUnify(subst: Map[Var, Expr]) extends SuslikProofStep { - override def pp: String = s"${ind}HeapUnify(${subst.mkString(",")});" + override def pp: String = s"HeapUnify(${subst.mkString(",")});" } /** unification of pointers */ case class HeapUnifyPointer(from: Var, to: Var) extends SuslikProofStep { - override def pp: String = s"${ind}HeapUnifyPointer(${from.pp} -> ${to.pp});" + override def pp: String = s"HeapUnifyPointer(${from.pp} -> ${to.pp});" } /** unfolds frame */ case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { - override def pp: String = s"${ind}FrameUnfold(${h_pre.pp}, ${h_post.pp});" + override def pp: String = s"FrameUnfold(${h_pre.pp}, ${h_post.pp});" } /** call operation */ case class Call(subst: Map[Var, Expr], call: Statements.Call) extends SuslikProofStep { override def deferredsAction: DeferredsAction = DeferredsAction.PopLayer - override def pp: String = s"${ind}Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" + override def pp: String = s"Call({${subst.mkString(",")}}, ${sanitize(call.pp)});" } /** free operation */ case class Free(stmt: Statements.Free, size: Int) extends SuslikProofStep { - override def pp: String = s"${ind}Free(${sanitize(stmt.pp)});" + override def pp: String = s"Free(${sanitize(stmt.pp)});" } /** malloc rule */ case class Malloc(ghostFrom: Var, ghostTo: Var, stmt: Statements.Malloc) extends SuslikProofStep { - override def pp: String = s"${ind}Malloc(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(stmt.pp)});" + override def pp: String = s"Malloc(${ghostFrom.pp} -> ${ghostTo.pp}, ${sanitize(stmt.pp)});" } /** close rule */ case class Close(app: SApp, selector: Expr, asn: Assertion, fresh_exist: SubstVar) extends SuslikProofStep { - override def pp: String = s"${ind}Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" + override def pp: String = s"Close(${app.pp}, ${sanitize(selector.pp)}, ${asn.pp}, {${fresh_exist.mkString(",")}});" } /** star partial */ case class StarPartial(new_pre_phi: PFormula, new_post_phi: PFormula) extends SuslikProofStep { - override def pp: String = s"${ind}StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" + override def pp: String = s"StarPartial(${new_pre_phi.pp}, ${new_post_phi.pp});" } case class PickCard(from: Var, to: Expr) extends SuslikProofStep { - override def pp: String = s"${ind}PickCard(${from.pp} -> ${to.pp});" + override def pp: String = s"PickCard(${from.pp} -> ${to.pp});" } case class PickArg(from: Var, to: Var) extends SuslikProofStep { - override def pp: String = s"${ind}PickArg(${from.pp} -> ${to.pp});" + override def pp: String = s"PickArg(${from.pp} -> ${to.pp});" } case class Init(goal: Goal) extends SuslikProofStep { override def deferredsAction: DeferredsAction = DeferredsAction.PushLayer - override def pp: String = s"${ind}Init(${goal.pp});" + override def pp: String = s"Init(${goal.pp});" } case class Inconsistency(label: Option[GoalLabel]) extends SuslikProofStep { @@ -592,7 +566,7 @@ case class AbduceCall( val initStep = Init(node.goal) val initItem = Item(initStep, None, Nil, Nil) val res = forward(node, List(initItem)) - Console.println(ProofTreePrinter.pp(res)) + Console.println(SuslikPrinter.pp(res)) res } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 0df7f85c0..4e12c036d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -12,10 +12,6 @@ object HTT extends CertificationTarget { def certify(proc: Procedure, env: Environment): HTTCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val cert = Translation.translate(root, proc)(env) - - CertTree.clear() // [Certify]: Clear tree after certification complete - - cert + Translation.translate(root, proc)(env) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala deleted file mode 100644 index 7fdaa31bb..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/Printer.scala +++ /dev/null @@ -1,44 +0,0 @@ -package org.tygus.suslik.certification.targets.htt - -import org.tygus.suslik.certification.traversal.Step.DestStep -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} - -import scala.annotation.tailrec - -trait Printer[S <: DestStep] extends ProofTreePrinter[S] { - type Item - - case class Task(k: List[String] => String, todo: List[Item], done: List[String]) - - def initialize(tree: ProofTree[S]): Item - - def process(item: Item): (List[String] => String, List[Item]) - - @tailrec - private def backward(stack: List[Task], res: String): String = stack match { - case Nil => res - case curr :: rest => - curr.todo match { - case Nil => backward(rest, curr.k((res :: curr.done).reverse)) - case next :: todo => - val task = curr.copy(todo = todo, done = res :: curr.done) - forward(next, task :: rest) - } - } - - @tailrec - private def forward(item: Item, stack: List[Task]): String = { - val (k, items) = process(item) - items match { - case Nil => - backward(stack, k(Nil)) - case next :: rest => - val task = Task(k, rest, Nil) - forward(next, task :: stack) - } - } - - override def pp(tree: ProofTree[S]): String = { - forward(initialize(tree), Nil) - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala index ddfa155c3..4685761c5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/ProofPrinter.scala @@ -1,13 +1,13 @@ package org.tygus.suslik.certification.targets.htt.logic -import org.tygus.suslik.certification.targets.htt.Printer -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter, TreeVisitor} -import scala.annotation.tailrec - -object ProofPrinter extends Printer[Proof.Step] { +object ProofPrinter extends ProofTreePrinter[Proof.Step] with TreeVisitor { type Item = ProofTree[Proof.Step] - def initialize(tree: ProofTree[Proof.Step]): Item = tree + type Result = String + + override def pp(tree: ProofTree[Proof.Step]): String = visit(tree) + def process(item: Item): (List[String] => String, List[Item]) = { val k = (children: List[String]) => { val res = item.step match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala index a0953df60..d767e9b27 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/program/ProgramPrinter.scala @@ -1,26 +1,24 @@ package org.tygus.suslik.certification.targets.htt.program -import org.tygus.suslik.certification.targets.htt.Printer import org.tygus.suslik.certification.targets.htt.program.Statements.{CGuarded, CIf, CStatement, Noop} -import org.tygus.suslik.certification.traversal.ProofTree - -object ProgramPrinter extends Printer[CStatement] { +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter, TreeVisitor} +object ProgramPrinter extends ProofTreePrinter[CStatement] with TreeVisitor { case class Item(tree: ProofTree[CStatement], pre: String) + type Result = String - override def initialize(tree: ProofTree[CStatement]): Item = Item(tree, "") + override def pp(tree: ProofTree[CStatement]): String = visit(Item(tree, "")) override def process(item: Item): (List[String] => String, List[Item]) = { val pre = item.pre val tree = item.tree - val k = tree.step match { - case CIf(selectors) => (children: List[String]) => { + val k = (children: List[String]) => tree.step match { + case CIf(selectors) => val branches = selectors.zip(children).reverse branches.tail.foldLeft(branches.head._2) { case (eb, (c, tb)) => s"${pre}if ${c.pp}\n${pre}then\n$tb\n${pre}else\n$eb" } - } - case CGuarded(c) => (children: List[String]) => s"${pre}if ${c.pp}\n${pre}then\n${children.head}\n${pre}else\n${children(1)}" - case Noop => (children: List[String]) => children.head - case _ => (children: List[String]) => s"$pre${tree.step.pp}${children.headOption.map("\n" + _).getOrElse("")}" + case CGuarded(c) => s"${pre}if ${c.pp}\n${pre}then\n${children.head}\n${pre}else\n${children(1)}" + case Noop => children.head + case _ => s"$pre${tree.step.pp}${children.headOption.map("\n" + _).getOrElse("")}" } val pre1 = tree.step match { case _: CIf | _: CGuarded => pre + " " diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala index f32bb4536..d3595573a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement import org.tygus.suslik.certification.traversal.StackEvaluator -object ProgramEvaluator extends StackEvaluator[SuslikProofStep, CStatement, ProgramContext] +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, CStatement, ProgramContext] { + val translator = ProgramTranslator +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index a63aaea3f..c56395b81 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.program.Statements.{CFree, CGuarded, CIf, CSkip, CStatement, Noop} import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Translator diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala index ffcd61bae..9062ccb7b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.logic.Proof import org.tygus.suslik.certification.traversal.StackEvaluator -object ProofEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, ProofContext] +object ProofEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, ProofContext] { + val translator = ProofTranslator +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 6223ed836..676ee8619 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CVar} import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 6aa77c4de..5f0769a6b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -1,13 +1,13 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.HTTCertificate -import org.tygus.suslik.certification.{CertTree, SuslikProofStep} +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.language.Types._ -import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof, ProofPrinter} +import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences._ -import org.tygus.suslik.certification.targets.htt.program.{Program, ProgramPrinter} +import org.tygus.suslik.certification.targets.htt.program.Program import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements._ import org.tygus.suslik.logic.{Environment, Gamma, InductivePredicate} @@ -34,10 +34,10 @@ object Translation { val suslikTree = SuslikProofStep.of_certtree(node) val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint]) - val proofBody = ProofEvaluator.run(suslikTree)(ProofTranslator, ctx) + val proofBody = ProofEvaluator.run(suslikTree, ctx) val proof = Proof(proofBody) val hints = ctx.hints.filter(_.numHypotheses > 0) - val progBody = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramContext()) + val progBody = ProgramEvaluator.run(suslikTree, ProgramContext()) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) HTTCertificate(cproc.name, cpreds, goal.toFunspec, proof, cproc, hints) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 879d8462f..88c407dd2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -1,7 +1,8 @@ package org.tygus.suslik.certification.targets.iris +import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.translation.Translation -import org.tygus.suslik.certification.{CertTree, CertificationTarget, SuslikProofStep} +import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException @@ -15,9 +16,8 @@ object Iris extends CertificationTarget { val cert = Translation.translate(root, proc)(env) val simplified = SuslikProofStep.of_certtree(root) - println(s"Suslik Proof:\n ${SuslikProofStep.ProofTreePrinter.pp(simplified)}") + println(s"Suslik Proof:\n ${SuslikPrinter.pp(simplified)}") - CertTree.clear() // [Certify]: Clear tree after certification complete cert } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala index 35d93542e..12d5b37cb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.iris.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr import org.tygus.suslik.certification.traversal.StackEvaluator -object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramContext] +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramContext] { + val translator = ProgramTranslator +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala index c536e7420..de2c5eaa2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramPrinter.scala @@ -1,25 +1,24 @@ package org.tygus.suslik.certification.targets.iris.translation -import org.tygus.suslik.certification.targets.htt.Printer import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HGuarded, HIf, HNoOp} -import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter, TreeVisitor} -object ProgramPrinter extends Printer[HExpr] { +object ProgramPrinter extends ProofTreePrinter[HExpr] with TreeVisitor { case class Item(tree: ProofTree[HExpr]) + type Result = String - override def initialize(tree: ProofTree[HExpr]): Item = Item(tree) + override def pp(tree: ProofTree[HExpr]): String = visit(Item(tree)) override def process(item: Item): (List[String] => String, List[Item]) = { val tree = item.tree - val k = tree.step match { - case HIf(selectors) => (children: List[String]) => { + val k = (children: List[String]) => tree.step match { + case HIf(selectors) => val branches = selectors.zip(children).reverse branches.tail.foldLeft(branches.head._2) { case (eb, (c, tb)) => s"if: ${c.pp}\nthen (\n$tb\n)\nelse (\n$eb\n)" } - } - case HGuarded(c) => (children: List[String]) => s"if: ${c.pp}\nthen (\n${children.head}\n)\nelse (\n${children(1)}\n)" - case HNoOp => (children: List[String]) => children.head - case _ => (children: List[String]) => s"${tree.step.pp}${children.headOption.map(";; \n" + _).getOrElse("")}" + case HGuarded(c) => s"if: ${c.pp}\nthen (\n${children.head}\n)\nelse (\n${children(1)}\n)" + case HNoOp => children.head + case _ => s"${tree.step.pp}${children.headOption.map(";; \n" + _).getOrElse("")}" } (k, tree.children.map(Item)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala index 4dd44b293..b3e6490ec 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.iris.translation -import org.tygus.suslik.certification.SuslikProofStep +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset} import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.Deferreds diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index a73827466..e89382a06 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.iris.translation -import org.tygus.suslik.certification.{CertTree, SuslikProofStep} +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.IrisCertificate import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable @@ -14,7 +15,7 @@ object Translation { def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): IrisCertificate = { val suslikTree = SuslikProofStep.of_certtree(node) - val progTree = ProgramEvaluator.run(suslikTree)(ProgramTranslator, ProgramContext()) + val progTree = ProgramEvaluator.run(suslikTree, ProgramContext()) val funDef = HFunDef(proc.name, proc.formals.map(_.translate), progTree) IrisCertificate(proc.name, funDef) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 022d779da..956baa102 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -8,7 +8,8 @@ import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslat import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Translator} -import org.tygus.suslik.certification.{CertTree, SuslikProofStep} +import org.tygus.suslik.certification.CertTree +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{IntType, LocType} import org.tygus.suslik.language.Statements.Procedure @@ -20,9 +21,11 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - def translate_proof[S <: DestStep,C <: Evaluator.ClientContext[S]](proof: ProofTree[SuslikProofStep])(translator: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { - val evaluator = new StackEvaluator[SuslikProofStep, S, C] - evaluator.run(proof)(translator, initial_context) + def translate_proof[S <: DestStep,C <: Evaluator.ClientContext[S]](proof: ProofTree[SuslikProofStep])(implicit t: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { + val evaluator = new StackEvaluator[SuslikProofStep, S, C] { + val translator = t + } + evaluator.run(proof, initial_context) } def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index 0e838e814..1ffa173f0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.vst.translation +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types.{VSTCType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate @@ -10,7 +11,6 @@ import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.SSLType -import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.vst.clang.Statements.StatementStep import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index a6412fa53..03499b441 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.vst.translation +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types.VSTType import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate @@ -10,7 +11,6 @@ import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.SSLType -import org.tygus.suslik.certification.SuslikProofStep import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import scala.collection.immutable.Queue diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala index d7bd6fb84..6b4621362 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -7,8 +7,8 @@ import org.tygus.suslik.certification.traversal.Step.{DestStep, SourceStep} /** * A basic tree traversal implementation */ -class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { - override def run(tree: ProofTree[S])(implicit translator: Translator[S, D, C], initialClientContext: C): ProofTree[D] = { +abstract class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { + override def run(tree: ProofTree[S], initialClientContext: C): ProofTree[D] = { def visit(tree: ProofTree[S], deferredsStack: DeferredsStack[D,C], clientContext: C): ProofTree[D] = { val res = tree.step.translate[D, C](clientContext) if (tree.children.length != res.childParams.length) { diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 904f4c974..1bce36716 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -7,7 +7,9 @@ import org.tygus.suslik.logic.Specifications.GoalLabel import scala.collection.immutable.Queue trait Evaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { - def run(node: ProofTree[S])(implicit translator: Translator[S,D,C], initialClientContext: C): ProofTree[D] + implicit val translator: Translator[S,D,C] + + def run(node: ProofTree[S], initialClientContext: C): ProofTree[D] // Create a straight-line tree from a list of values def foldStepsIntoTree(values: List[D], children: List[ProofTree[D]], label: Option[GoalLabel]): ProofTree[D] = diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index 98dd6de4d..366d60ab5 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -3,69 +3,32 @@ package org.tygus.suslik.certification.traversal import TranslatableOps._ import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ -import org.tygus.suslik.logic.Specifications.GoalLabel - -import scala.annotation.tailrec /** * A tail-recursive, stack-based tree traversal with an eval/apply loop */ -class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { - // A pending evaluation task; tracks which children have and have not been evaluated - case class Task(values: List[D], label: Option[GoalLabel], remainingBranches: List[(ProofTree[S], DeferredsStack[D,C], C)], resultsSoFar: List[ProofTree[D]]) - - // A stack of pending evaluation tasks - type TaskStack = List[Task] +abstract class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] with TreeVisitor { + type Result = ProofTree[D] + type Item = (ProofTree[S], DeferredsStack[D,C], C) - def run(tree: ProofTree[S])(implicit translator: Translator[S,D,C], initialClientContext: C): ProofTree[D] = { - // Use a child result to fulfill the evaluation task for a parent - @tailrec - def backward(taskStack: TaskStack, childResult: ProofTree[D]): ProofTree[D] = - taskStack match { - case Nil => - // no more tasks; return the result - childResult - case currTask :: remainingStack => - currTask.remainingBranches match { - case Nil => - // received results for all children so topmost task is done; remove from stack and evaluate parent task - val results = childResult :: currTask.resultsSoFar - val translatedTree = foldStepsIntoTree(currTask.values, results.reverse, currTask.label) - backward(remainingStack, translatedTree) - case (nextChild, nextDeferreds, nextTricks) :: remainingBranches => - // some siblings remain unvisited; update topmost task with child result and explore next sibling - val updatedTask = currTask.copy(remainingBranches = remainingBranches, resultsSoFar = childResult :: currTask.resultsSoFar) - forward(nextChild, nextDeferreds, nextTricks, updatedTask :: remainingStack) - } - } + def run(tree: ProofTree[S], initialClientContext: C): ProofTree[D] = visit((tree, Nil, initialClientContext)) - // Do step-wise translation of current tree node and explore next child - @tailrec - def forward(tree: ProofTree[S], deferredsStack: DeferredsStack[D,C], clientContext: C, taskStack: TaskStack): ProofTree[D] = { - val res = tree.step.translate[D,C](clientContext) - if (tree.children.length != res.childParams.length) { - throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") - } - - val action = tree.step.deferredsAction - val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) - val steps = res.steps ++ newSteps - val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => - action.updateStack(deferredsStack, newDeferred) - } + def process(item: Item): (List[Result] => Result, List[Item]) = { + val (tree, deferredsStack, clientContext) = item + val res = tree.step.translate[D,C](clientContext) + if (tree.children.length != res.childParams.length) { + throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") + } - (tree.children, childDeferredsStacks, childClientContexts).zipped.toList match { - case Nil => - // terminal; evaluate the converted node in the context of the current stack - val result = foldStepsIntoTree(steps, Nil, tree.label) - backward(taskStack, result) - case (nextChild, nextDeferredsStack, nextClientCtx) :: remainingBranches => - // non-terminal; create evaluation task for current tree and visit the first child - val task = Task(steps, tree.label, remainingBranches, List.empty) - forward(nextChild, nextDeferredsStack, nextClientCtx, task :: taskStack) - } + val action = tree.step.deferredsAction + val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) + val steps = res.steps ++ newSteps + val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => + action.updateStack(deferredsStack, newDeferred) } - forward(tree, Nil, initialClientContext, Nil) + val k = (results: List[Result]) => foldStepsIntoTree(steps, results, tree.label) + val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList + (k, next) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TreeVisitor.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TreeVisitor.scala new file mode 100644 index 000000000..63bac6c6b --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TreeVisitor.scala @@ -0,0 +1,43 @@ +package org.tygus.suslik.certification.traversal + +import scala.annotation.tailrec + +trait TreeVisitor { + // The result type of the traversal + type Result + + // A traversal item (include the tree node and any accumulators) + type Item + + // Generate a continuation to process the current traversal item, and a list of child items + def process(item: Item): (List[Result] => Result, List[Item]) + + // Recursively visit tree branches and produce result + def visit(item: Item): Result = forward(item, Nil) + + private case class Task(k: List[Result] => Result, todo: List[Item], done: List[Result]) + + @tailrec + private def backward(stack: List[Task], res: Result): Result = stack match { + case Nil => res + case curr :: rest => + curr.todo match { + case Nil => backward(rest, curr.k((res :: curr.done).reverse)) + case next :: todo => + val task = curr.copy(todo = todo, done = res :: curr.done) + forward(next, task :: rest) + } + } + + @tailrec + private def forward(item: Item, stack: List[Task]): Result = { + val (k, items) = process(item) + items match { + case Nil => + backward(stack, k(Nil)) + case next :: rest => + val task = Task(k, rest, Nil) + forward(next, task :: stack) + } + } +} From fadc69c2b582d91b128ab9fb1456fa633569743d Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 17 Feb 2021 21:57:48 +0800 Subject: [PATCH 105/211] HTT: Handle auxiliary specs, fix SMT ptr type inference, and add benchmarks --- .../targets/htt/HTTCertificate.scala | 9 ++++- .../targets/htt/translation/Translation.scala | 13 +++++-- .../traversal/BasicEvaluator.scala | 2 +- .../rules/DelegatePureSynthesis.scala | 16 +++++---- .../srtl/insertion-sort-free.syn | 26 ++++++++++++++ .../certification/srtl/insertion-sort.syn | 27 ++++++++++++++ .../certification/tree/tree-flatten.syn | 35 +++++++++++++++++++ 7 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 src/test/resources/synthesis/certification/srtl/insertion-sort-free.syn create mode 100644 src/test/resources/synthesis/certification/srtl/insertion-sort.syn create mode 100644 src/test/resources/synthesis/certification/tree/tree-flatten.syn diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 2742d5661..0d86e99e8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.certification.targets.htt.program.Program import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { +case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT // Replace hyphens with underscores @@ -33,8 +33,15 @@ case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, pro builder.append(hints.map(_.pp).mkString("\n")) builder.append("\n\n") } + + for (spec <- auxSpecs) { + builder.append(spec.pp) + builder.append(s"\n\nVariable ${spec.name} : ${spec.name}_type.\n\n") + } + builder.append(spec.pp) builder.append("\n\n") + builder.append(proc.pp) builder.append("\n") builder.append(proof.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 5f0769a6b..095c0551e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -10,7 +10,7 @@ import org.tygus.suslik.certification.targets.htt.logic.Sentences._ import org.tygus.suslik.certification.targets.htt.program.Program import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.{Environment, Gamma, InductivePredicate} +import org.tygus.suslik.logic.{Environment, FunSpec, Gamma, InductivePredicate, Specifications} import scala.collection.mutable.ListBuffer @@ -30,7 +30,14 @@ object Translation { val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) translateInductivePredicate(p1, gamma) }) - val goal = node.goal.translate + + val auxSpecs = env.functions.values.toSeq.map { spec0 => + val FunSpec(name, _, params, pre, post, var_decl) = spec0.resolveOverloading(env) + val goal = Specifications.topLevelGoal(pre, post, params, name, env, Hole, var_decl) + goal.translate.toFunspec + } + + val spec = node.goal.translate.toFunspec val suslikTree = SuslikProofStep.of_certtree(node) val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint]) @@ -40,7 +47,7 @@ object Translation { val progBody = ProgramEvaluator.run(suslikTree, ProgramContext()) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) - HTTCertificate(cproc.name, cpreds, goal.toFunspec, proof, cproc, hints) + HTTCertificate(cproc.name, cpreds, spec, auxSpecs, proof, cproc, hints) } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala index 6b4621362..ed3847e1b 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -23,7 +23,7 @@ abstract class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext } val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList - val childResults = next.map { case (child, deferredsStack, ctx) => visit(child, deferredsStack, ctx) } + val childResults = next.map(Function.tupled(visit)) foldStepsIntoTree(steps, childResults, tree.label) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala index 3e9913d38..70ee16d3e 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/DelegatePureSynthesis.scala @@ -4,12 +4,12 @@ package org.tygus.suslik.synthesis.rules import org.bitbucket.franck44.scalasmt.parser.SMTLIB2Parser import org.bitbucket.franck44.scalasmt.parser.SMTLIB2Syntax._ import org.bitbucket.inkytonik.kiama.util.StringSource -import org.tygus.suslik.language.Expressions.{IntConst, SetLiteral, Subst} +import org.tygus.suslik.language.Expressions.{IntConst, LocConst, SetLiteral, Subst} import org.tygus.suslik.language._ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} -import org.tygus.suslik.logic.{PFormula, Specifications} +import org.tygus.suslik.logic.{Gamma, PFormula, Specifications} import org.tygus.suslik.synthesis.rules.Rules.{InvertibleRule, RuleResult, SynthesisRule} -import org.tygus.suslik.synthesis.StmtProducer.{SubstMapProducer, ExtractHelper, HandleGuard, IdProducer} +import org.tygus.suslik.synthesis.StmtProducer.{ExtractHelper, HandleGuard, IdProducer, SubstMapProducer} import scala.sys.process._ import scala.util.{Failure, Success} @@ -172,7 +172,7 @@ object DelegatePureSynthesis { val parser = SMTLIB2Parser[GetModelResponses] - def parseAssignments(cvc4Res: String): Subst = { + def parseAssignments(cvc4Res: String, gamma: Gamma = Map.empty): Subst = { //〈FunDefCmd〉::= (define-fun〈Symbol〉((〈Symbol〉 〈SortExpr〉)∗)〈SortExpr〉 〈Term〉 parser.apply(StringSource("(model " + cvc4Res + ")")) match { case Failure(exception) => Map.empty @@ -182,7 +182,11 @@ object DelegatePureSynthesis { val expr = response.funDef.term match { case QIdTerm(SimpleQId(SymbolId(SSymbol(simpleSymbol)))) => if (simpleSymbol == empsetName) Expressions.SetLiteral(List()) else Expressions.Var(simpleSymbol) - case ConstantTerm(NumLit(numeralLiteral)) => IntConst(numeralLiteral.toInt) + case ConstantTerm(NumLit(numeralLiteral)) => + gamma.get(Expressions.Var(existential)) match { + case Some(LocType) => LocConst(numeralLiteral.toInt) + case _ => IntConst(numeralLiteral.toInt) + } } Expressions.Var(existential) -> expr }.toMap @@ -214,7 +218,7 @@ object DelegatePureSynthesis { } else { //parse me - val assignments: Subst = parseAssignments(cvc4Res.get) + val assignments: Subst = parseAssignments(cvc4Res.get, goal.gamma) val newPost = goal.post.subst(assignments) val newCallGoal = goal.callGoal.map(_.updateSubstitution(assignments)) val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) diff --git a/src/test/resources/synthesis/certification/srtl/insertion-sort-free.syn b/src/test/resources/synthesis/certification/srtl/insertion-sort-free.syn new file mode 100644 index 000000000..e889d6a8d --- /dev/null +++ b/src/test/resources/synthesis/certification/srtl/insertion-sort-free.syn @@ -0,0 +1,26 @@ +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == 1 + n /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +{ 0 <= n ; r :-> null ** sll(x, n, lo, hi) } +void insertion_sort_free (loc x, loc r) +{ true ; r :-> y ** srtl(y, n, lo, hi) } + +##### + +void insertion_sort_free (loc x, loc r) { + if (x == null) { + } else { + let n = *(x + 1); + insertion_sort_free(n, r); + let yn = *r; + srtl_insert(yn, x); + let y = *x; + free(x); + *r = y; + } +} diff --git a/src/test/resources/synthesis/certification/srtl/insertion-sort.syn b/src/test/resources/synthesis/certification/srtl/insertion-sort.syn new file mode 100644 index 000000000..7dfa81d31 --- /dev/null +++ b/src/test/resources/synthesis/certification/srtl/insertion-sort.syn @@ -0,0 +1,27 @@ +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == 1 + n /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +{ 0 <= n ; r :-> null ** sll(x, n, lo, hi) } +void insertion_sort (loc x, loc r) +{ true ; r :-> y ** sll(x, n, lo, hi) ** srtl(y, n, lo, hi) } + +##### + +void insertion_sort (loc x, loc r) { + if (x == null) { + } else { + let v = *x; + let n = *(x + 1); + insertion_sort(n, r); + let yn = *r; + srtl_insert(yn, x); + let y = *x; + *r = y; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/tree/tree-flatten.syn b/src/test/resources/synthesis/certification/tree/tree-flatten.syn new file mode 100644 index 000000000..d8039fe0e --- /dev/null +++ b/src/test/resources/synthesis/certification/tree/tree-flatten.syn @@ -0,0 +1,35 @@ +should be able to flatten the tree into a list given an auxiliary function for list appending + +#### + +{true ; sll(x1, s1) ** sll(x2, s2) ** ret :-> x2} +void sll_append (loc x1, loc ret) +{s =i s1 ++ s2 ; sll(y, s) ** ret :-> y } + +{ true ; z :-> x ** tree(x, s)} +void tree_flatten(loc z) +{ true ; z :-> y ** sll(y, s)} + +#### + +void tree_flatten (loc z) { + let x = *z; + if (x == null) { + } else { + let v = *x; + let l = *(x + 1); + let r = *(x + 2); + *x = l; + tree_flatten(x); + *z = r; + tree_flatten(z); + let yz = *z; + sll_append(yz, x); + let yy = *x; + let y = malloc(2); + free(x); + *z = y; + *(y + 1) = yy; + *y = v; + } +} From 3abbe82e58b06f29025b7e78b28f0cbdeb6cf02e Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 18 Feb 2021 13:15:15 +0800 Subject: [PATCH 106/211] HTT: separate constructor definition from application --- .../source/SuslikProofStep.scala | 4 +- .../targets/htt/logic/Proof.scala | 4 +- .../targets/htt/logic/Sentences.scala | 13 +--- .../htt/translation/ProofContext.scala | 24 ++++--- .../htt/translation/ProofTranslator.scala | 62 +++++++++++-------- 5 files changed, 56 insertions(+), 51 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index 4f19fcda9..b5247cad1 100644 --- a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -67,8 +67,8 @@ object SuslikProofStep { } /** open constructor cases */ - case class Open(pred: SApp, fresh_vars: SubstVar, sbst: Subst, selectors: List[Expr]) extends SuslikProofStep { - override def pp: String = s"Open(${pred.pp}, ${fresh_vars.mkString(", ")});" + case class Open(pred: SApp, fresh_exists: SubstVar, fresh_params: Subst, selectors: List[Expr]) extends SuslikProofStep { + override def pp: String = s"Open(${pred.pp}, existentials: ${fresh_exists.mkString(", ")}, params: ${fresh_params.mkString(", ")});" } /** subst L */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 1f650b018..8d26ef54c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -68,7 +68,7 @@ object Proof { override val isNoop: Boolean = items.isEmpty override def pp: String = s"move: ${items.map(_.pp).mkString(" ")}" } - case class ElimExistential(items: Seq[CExpr]) extends Step { + case class ElimExistential(items: Seq[CVar]) extends Step { override val isNoop: Boolean = items.isEmpty override def pp: String = items.map(_.pp).grouped(5).map(s => s"ex_elim ${s.mkString(" ")}").mkString(".\n") } @@ -121,7 +121,7 @@ object Proof { case class CallPre(heap: CSFormula) extends Step { override def pp: String = s"ssl_call_pre (${heap.ppHeap})" } - case class Call(args: Seq[CExpr], ghosts: Seq[CVar]) extends Step { + case class Call(ghosts: Seq[CExpr]) extends Step { override def pp: String = s"ssl_call (${ghosts.map(_.pp).mkString(", ")})" } case object StoreValid extends Step { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index c73743818..c10ce31ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -48,22 +48,11 @@ object Sentences { } } - case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CExpr]) extends PrettyPrinting { - def subst(sub: CSubst): CInductiveClause = - CInductiveClause(pred, idx, selector.subst(sub), asn.subst(sub), existentials.map(_.subst(sub))) - } + case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CVar]) extends PrettyPrinting case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends PrettyPrinting { val paramVars: Seq[CVar] = params.map(_._1) - def subst(sub: CSubst): CInductivePredicate = - CInductivePredicate( - name, - params.map(p => (p._1.substVar(sub), p._2)), - clauses.map(_.subst(sub)), - gamma.map(g => (g._1.substVar(sub), g._2)) - ) - override def pp: String = { val builder = new StringBuilder() builder.append(s"Inductive $name ${params.map{ case (v, t) => s"(${v.pp} : ${t.pp})" }.mkString(" ")} : Prop :=\n") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index 645dbb60d..cb28fe0c2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -1,10 +1,10 @@ package org.tygus.suslik.certification.targets.htt.translation -import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CVar} +import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CSubst, CVar} import org.tygus.suslik.certification.targets.htt.language.Types.HTTType import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CInductiveClause, CInductivePredicate} -import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause, CInductivePredicate} +import org.tygus.suslik.certification.targets.htt.translation.ProofContext.{PredicateEnv, AppliedConstructor} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import scala.collection.mutable.ListBuffer @@ -12,7 +12,7 @@ import scala.collection.mutable.ListBuffer case class ProofContext(predicates: PredicateEnv = Map.empty, postEx: Map[CVar, (HTTType, CExpr)] = Map.empty, appAliases: Map[CSApp, CSApp] = Map.empty, - unfoldings: Map[CSApp, CInductiveClause] = Map.empty, + unfoldings: Map[CSApp, AppliedConstructor] = Map.empty, callGoal: Option[Proof.Goal] = None, nextCallId: Int = 0, hints: ListBuffer[Hint] = new ListBuffer[Hint], @@ -25,9 +25,9 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, */ def unfoldedAppExistentials(app: CSApp): (Seq[CExpr], Seq[CSFormula]) = { unfoldings.get(app) match { - case Some(clause) => - val heapEx = clause.asn.sigma.apps.map(unfoldedApp) - (clause.existentials, heapEx) + case Some(constructor) => + val heapEx = constructor.asn.sigma.apps.map(unfoldedApp) + (constructor.existentials, heapEx) case _ => (Seq.empty, Seq.empty) } } @@ -45,8 +45,8 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, */ def unfoldedApp(app: CSApp): CSFormula = unfoldings.get(app) match { case None => CSFormula(app.heapName, Seq(app), Seq.empty) - case Some(clause) => - val sigma = clause.asn.sigma + case Some(constructor) => + val sigma = constructor.asn.sigma val (apps, ptss) = sigma.apps.map(unfoldedApp).map(h => (h.apps, h.ptss)).unzip CSFormula(app.heapName, apps.flatten, sigma.ptss ++ ptss.flatten) } @@ -57,7 +57,7 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): ProofContext = { val postEx1 = postEx.mapValues(v => (v._1, v._2.subst(m))) val appAliases1 = affectedApps.foldLeft(appAliases) { case (appAliases, (app, alias)) => appAliases + (app -> alias) + (alias -> alias) } - val unfoldings1 = unfoldings.map { case (app, clause) => app.subst(m) -> clause.subst(m) } + val unfoldings1 = unfoldings.map { case (app, constructor) => app.subst(m) -> constructor.subst(m) } this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) } @@ -74,4 +74,8 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, object ProofContext { type PredicateEnv = Map[String, CInductivePredicate] + case class AppliedConstructor(idx: Int, existentials: Seq[CExpr], asn: CAssertion) { + def subst(sub: CSubst): AppliedConstructor = + this.copy(existentials = existentials.map(_.subst(sub)), asn = asn.subst(sub)) + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 676ee8619..31215cb65 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -4,6 +4,7 @@ import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CVar} import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} +import org.tygus.suslik.certification.targets.htt.translation.ProofContext.AppliedConstructor import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.Deferred import org.tygus.suslik.certification.traversal.Translator @@ -109,25 +110,32 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont case SuslikProofStep.Branch(cond, _) => val childContexts = List(ctx.copy(numSubgoals = ctx.numSubgoals + 1), ctx) Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferred)) - case SuslikProofStep.Open(sapp, fresh_vars, sbst, selectors) => - val pred = ctx.predicates(sapp.pred).subst(fresh_vars.translate).subst(sbst.translate) + case SuslikProofStep.Open(sapp, freshExistentials, freshParamArgs, selectors) => + val exSub = freshExistentials.translate + val argSub = freshParamArgs.translate + val pred = ctx.predicates(sapp.pred) + val clauses = pred.clauses.toList.map { clause => + val existentials = clause.existentials.map(_.substVar(exSub)) + val asn = clause.asn.subst(exSub).subst(argSub) + (clause.idx, existentials, asn) + } val csapp = sapp.translate val cselectors = selectors.map(_.translate) - val branchSteps = pred.clauses.flatMap { clause => + val branchSteps = clauses.flatMap { case (_, existentials, asn) => List( - Proof.ElimExistential(clause.existentials), - Proof.ElimExistential(clause.asn.heapVars) - ) ++ initPre(clause.asn, csapp.uniqueName) ++ List(Proof.Shelve) + Proof.ElimExistential(existentials), + Proof.ElimExistential(asn.heapVars) + ) ++ initPre(asn, csapp.uniqueName) ++ List(Proof.Shelve) } val parentGoalShelves = (1 to ctx.numSubgoals).map(_ => Proof.Shelve).toList val steps = List(Proof.Open(cselectors, csapp)) ++ branchSteps ++ parentGoalShelves ++ List(Proof.Unshelve) val numClauses = pred.clauses.length - val childCtxs = pred.clauses.toList.map(clause => { - val newApps = clause.asn.sigma.apps.map(a => a -> a).toMap - val numSubgoals = numClauses - clause.idx + ctx.numSubgoals + val childCtxs = clauses.map { case (idx, _, asn) => + val newApps = asn.sigma.apps.map(a => a -> a).toMap + val numSubgoals = numClauses - idx + ctx.numSubgoals ctx.copy(appAliases = ctx.appAliases ++ newApps, numSubgoals = numSubgoals) - }) + } Result(steps, childCtxs.map(withNoDeferred)) /** Program statements */ @@ -155,26 +163,30 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont case SuslikProofStep.Free(Free(v), size) => val steps = (0 until size).map(i => Proof.Dealloc(v.translate, i)).toList Result(steps, List(withNoDeferred(ctx))) - case SuslikProofStep.Call(freshToActual, Call(_, args, _)) => + case SuslikProofStep.Call(freshToActual, _) => val callId = ctx.nextCallId val callGoal = ctx.callGoal.get - val goal = callGoal.subst(freshToActual.translate) + val sub = freshToActual.translate + val pre = callGoal.pre.subst(sub) + val post = callGoal.post.subst(sub) + val universalGhosts = callGoal.universalGhosts.filterNot(_.isCard).map(_.subst(sub)) + val existentials = callGoal.existentials.filterNot(_.isCard).map(_.substVar(sub)) val steps = List( // Move the part of the heap relevant to the call abduction to the beginning - Proof.CallPre(goal.pre.sigma), + Proof.CallPre(pre.sigma), // Provide the necessary existentials so that the precondition of the call goal is met, // and then execute the call - Proof.Call(args.map(_.translate), goal.universalGhosts.filterNot(_.isCard)), - Proof.Exists(goal.pre.sigma.apps.map(ctx.currentAppAlias).map(ctx.unfoldedApp)) andThen, + Proof.Call(universalGhosts), + Proof.Exists(pre.sigma.apps.map(ctx.currentAppAlias).map(ctx.unfoldedApp)) andThen, Proof.Auto, Proof.MoveToCtx(List(CVar(s"h_call$callId"))), // The postcondition of the call abduction becomes the precondition of the companion - Proof.ElimExistential(goal.existentials), - Proof.ElimExistential(goal.post.heapVars), - ) ++ initPre(goal.post, s"call$callId") ++ List(Proof.StoreValid) + Proof.ElimExistential(existentials), + Proof.ElimExistential(post.heapVars), + ) ++ initPre(post, s"call$callId") ++ List(Proof.StoreValid) - val newApps = goal.post.sigma.apps.map(a => a -> a).toMap + val newApps = post.sigma.apps.map(a => a -> a).toMap val ctx1 = ctx.copy(callGoal = None, nextCallId = callId + 1, appAliases = ctx.appAliases ++ newApps) Result(steps, List(withNoDeferred(ctx1))) @@ -196,19 +208,19 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val m = Map(from.translate -> to.translate) handleSubstitution(m, ctx) case SuslikProofStep.Close(app, selector, asn, fresh_exist) => - // construct an up-to-date inductive clause with synthesis info + // build an instance of an invoked inductive clause with synthesis info val csapp = app.translate val cselector = selector.translate val m = fresh_exist.translate - val clause0 = ctx.predicates(csapp.pred).clauses.find(_.selector == cselector).get - val clause = CInductiveClause(csapp.pred, clause0.idx, cselector, asn.translate, clause0.existentials.map(_.subst(m))) + val clause = ctx.predicates(csapp.pred).clauses.find(_.selector == cselector).get + val constructor = AppliedConstructor(clause.idx, clause.existentials.map(_.subst(m)), asn.translate) // create deferred to perform unfolding val deferred = (ctx: ProofContext) => { val csapp1 = ctx.currentAppAlias(csapp) val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) val steps = List( - Proof.UnfoldConstructor(clause.idx) andThen, + Proof.UnfoldConstructor(constructor.idx) andThen, Proof.Exists(valueEx ++ heapEx) andThen, Proof.Auto ) @@ -216,8 +228,8 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont } // update context with unfolding info - val appAliases = ctx.appAliases ++ clause.asn.sigma.apps.map(a => a -> a) - val unfoldings = ctx.unfoldings + (csapp -> clause) + val appAliases = ctx.appAliases ++ constructor.asn.sigma.apps.map(a => a -> a) + val unfoldings = ctx.unfoldings + (csapp -> constructor) val ctx1 = ctx.copy(appAliases = appAliases, unfoldings = unfoldings) Result(List(), List((Some(deferred), ctx1))) From c975562a0da66f9b519d0c582fb63e508c913d25 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 18 Feb 2021 14:46:45 +0800 Subject: [PATCH 107/211] fixes VST program generation --- .../htt/translation/ProgramTranslator.scala | 2 +- .../targets/vst/clang/Statements.scala | 53 ++++-- .../targets/vst/logic/Expressions.scala | 93 +++++++--- .../targets/vst/translation/Translation.scala | 13 +- .../translation/VSTProgramTranslator.scala | 161 ++++++++++++++---- 5 files changed, 251 insertions(+), 71 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index c56395b81..36e092716 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -28,7 +28,7 @@ object ProgramTranslator extends Translator[SuslikProofStep, CStatement, Program val v = stmt.v.translate (0 until sz).map(i => CFree(v, i)).toList case SuslikProofStep.Call(_, stmt) => List(stmt.translate) - case _ => List(Noop) + case _ => List() } Result(stmts, List(withNoDeferred)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index dd7c1e9dc..080416055 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification.targets.vst.clang import org.tygus.suslik.certification.targets.vst.Types.VSTCType import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.CLangExpr import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{ForwardIf, ForwardIfConstructor} import org.tygus.suslik.certification.targets.vst.translation.Translation.{TranslationException, fail_with} @@ -15,11 +16,22 @@ object Statements { implicit object ProofTreePrinter extends ProofTreePrinter[StatementStep] { override def pp(tree: ProofTree[StatementStep]): String = tree.step match { case CIf(cond) => - s"if (${cond.pp} {\n" + + s"if (${cond.pp_as_clang_expr} {\n" + s"${pp(tree.children(0))}" + s"} else {\n" + s"${pp(tree.children(1))}" + - s"\n}" + s"}" + case CElif(conds) => + val branches = conds.zip(tree.children) + val (first_cond, first_body) = branches.head + val rest = branches.tail + val rest_cases = rest.reverse match { + case ::((_, last_child), remaining) => + remaining.foldLeft(s"\nelse {\n${pp(last_child)}}")({case (str, (cond, body)) => + s"\nelse if (${cond.pp_as_clang_expr}) {\n${pp(body)}}${str}" + }) + } + s"if (${first_cond.pp_as_clang_expr}) {\n${pp(first_body)}}${rest_cases}" case _ => tree.step.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") } } @@ -50,29 +62,50 @@ object Statements { override def pp: String = s"loc ${to} = READ_LOC(${from}, ${offset});" } - case class CWriteInt(to: CVar, value: CExpr, offset: Int = 0) extends StatementStep { + case class CWriteInt(to: String, value: CLangExpr, offset: Int = 0) extends StatementStep { override def pp : String = - s"WRITE_INT(${to}, ${offset}, ${value.pp});" + s"WRITE_INT(${to}, ${offset}, ${value.pp_as_clang_expr});" } - case class CWriteLoc(to: CVar, value: CExpr, offset: Int = 0) extends StatementStep { + case class CWriteLoc(to: String, value: CLangExpr, offset: Int = 0) extends StatementStep { override def pp : String = - s"WRITE_LOC(${to}, ${offset}, ${value.pp});" + s"WRITE_LOC(${to}, ${offset}, ${value.pp_as_clang_expr});" } /** encoding of a function call f(args) */ - case class CCall(fun: String, args: Seq[CExpr]) extends StatementStep { - override def pp: String = s"${fun}(${args.map(_.pp).mkString(", ")});" + case class CCall(fun: String, args: Seq[CLangExpr]) extends StatementStep { + override def pp: String = s"${fun}(${args.map(_.pp_as_clang_expr).mkString(", ")});" } /** Encoding of statement * if (cond) { tb } else { eb } * */ - case class CIf(cond: CExpr) extends StatementStep { + case class CIf(cond: CLangExpr) extends StatementStep { override def pp : String = - s"if(${cond.pp})" + s"if(${cond.pp_as_clang_expr})" } + /** Encoding of statement + * if (cond1) { tb } else if(cond2) { eb } else (cond3) + * */ + case class CElif(cond: List[CLangExpr]) extends StatementStep { + override def pp : String = + cond match { + case Nil => ??? + case ::(head, tl) => + s"if (${head.pp_as_clang_expr}) { ??? } ${ + tl.reverse match { + case Nil => ??? + case ::(last, remaining) => + remaining.foldLeft("else { ??? }")((str, elt) => + s"else if (${elt.pp_as_clang_expr}) { ??? } ${str}" + ) + } + }" + } + } + + /** Definition of a CProcedure */ case class CProcedureDefinition( name: String, diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index bd3dd6561..62275890a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types -import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqIntSetType, CoqIntValType, CoqPtrValType, CoqZType, VSTType} +import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqIntSetType, CoqIntValType, CoqPtrValType, CoqZType, VSTCType, VSTType} import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException @@ -10,6 +10,57 @@ import org.tygus.suslik.certification.targets.vst.translation.Translation.Transl * */ object Expressions { + sealed trait CLangOp {} + sealed trait CLangExpr { + def type_cexpr : VSTCType = this match { + case ProofCVar(name, typ) => typ.asInstanceOf[VSTCType] + case ProofCBoolConst(value) => throw TranslationException("Attempted to type a boolean expression") + case ProofCNullval => CoqPtrValType + case ProofCIntConst(value) => CoqIntValType + case ProofCIfThenElse(cond, left : CLangExpr, right : CLangExpr) => left.type_cexpr + case ProofCBinaryExpr(op : CLangOp, left : CLangExpr, right : CLangExpr) => + op match { + case ProofCOpPlus => CoqIntValType + case ProofCOpMinus => CoqIntValType + case ProofCOpMultiply => CoqIntValType + case ProofCOpZEq | ProofCOpIntValEq | ProofCOpPtrValEq | ProofCOpBoolEq + | ProofCOpLeq | ProofCOpLt | ProofCOpAnd | ProofCOpOr => + throw TranslationException("Attempted to type a boolean expression") + } + case ProofCUnaryExpr(op : CLangOp, e : CLangExpr) => + op match { + case ProofCOpNot => throw TranslationException("Attempted to type a boolean expression") + case ProofCOpUnaryMinus => CoqIntValType + } + } + def pp_as_clang_expr: String = this match { + case ProofCVar(name, typ) => name + case ProofCBoolConst(value) => value.toString + case ProofCNullval => "NULL" + case ProofCIntConst(value) =>value.toString + case ProofCIfThenElse(cond : CLangExpr, left : CLangExpr, right : CLangExpr) => + s"(${cond.pp_as_clang_expr} ? ${left.pp_as_clang_expr} : ${right.pp_as_clang_expr})" + case ProofCBinaryExpr(op : CLangOp, left : CLangExpr, right : CLangExpr) => + op match { + case ProofCOpPlus => s"(${left.pp_as_clang_expr} + ${right.pp_as_clang_expr})" + case ProofCOpMinus => s"(${left.pp_as_clang_expr} - ${right.pp_as_clang_expr})" + case ProofCOpMultiply => s"(${left.pp_as_clang_expr} * ${right.pp_as_clang_expr})" + case ProofCOpZEq => s"(${left.pp_as_clang_expr} == ${right.pp_as_clang_expr})" + case ProofCOpIntValEq => s"(${left.pp_as_clang_expr} == ${right.pp_as_clang_expr})" + case ProofCOpPtrValEq => s"(${left.pp_as_clang_expr} == ${right.pp_as_clang_expr})" + case ProofCOpBoolEq => s"(${left.pp_as_clang_expr} == ${right.pp_as_clang_expr})" + case ProofCOpLeq => s"(${left.pp_as_clang_expr} <= ${right.pp_as_clang_expr})" + case ProofCOpLt => s"(${left.pp_as_clang_expr} < ${right.pp_as_clang_expr})" + case ProofCOpAnd => s"(${left.pp_as_clang_expr} && ${right.pp_as_clang_expr})" + case ProofCOpOr => s"(${left.pp_as_clang_expr} || ${right.pp_as_clang_expr})" + } + case ProofCUnaryExpr(op : CLangOp, e : CLangExpr) => + op match { + case ProofCOpNot => s"!(${e.pp_as_clang_expr})" + case ProofCOpUnaryMinus => s"-(${e.pp_as_clang_expr})" + } + } + } /** encoding of expressions in VST proof script * By default any boolean value will print to prop * use pp_as_bool_value to print as a boolean @@ -123,7 +174,7 @@ object Expressions { /** a variable in a VST proof */ - case class ProofCVar(name: String, typ: VSTType) extends ProofCExpr { + case class ProofCVar(name: String, typ: VSTType) extends ProofCExpr with CLangExpr { override def pp: String = typ match { case CoqPtrValType => s"(${name} : val)" case CoqIntValType => s"(${name} : val)" @@ -134,17 +185,17 @@ object Expressions { } /** boolean constant in a VST proof */ - case class ProofCBoolConst(value: Boolean) extends ProofCExpr { + case class ProofCBoolConst(value: Boolean) extends ProofCExpr with CLangExpr { override def pp: String = value.toString } /** integer constant in a VST proof */ - case object ProofCNullval extends ProofCExpr { + case object ProofCNullval extends ProofCExpr with CLangExpr { override def pp: String = "nullval" } /** integer constant in a VST proof */ - case class ProofCIntConst(value: Int) extends ProofCExpr { + case class ProofCIntConst(value: Int) extends ProofCExpr with CLangExpr { override def pp: String = value.toString } @@ -156,11 +207,11 @@ object Expressions { } /** encodes a ternary expression in a VST proof */ - case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + case class ProofCIfThenElse(cond: ProofCExpr, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr with CLangExpr { override def pp: String = s"(if ${cond.pp_as_bool_value} then ${left.pp} else ${right.pp})" } - case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr { + case class ProofCBinaryExpr(op: ProofCBinOp, left: ProofCExpr, right: ProofCExpr) extends ProofCExpr with CLangExpr { override def pp: String = op match { case ProofCOpLt => s"(${left.pp_as_Z_value} < ${right.pp_as_Z_value})" @@ -179,7 +230,7 @@ object Expressions { } } - case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr { + case class ProofCUnaryExpr(op: ProofCUnOp, e: ProofCExpr) extends ProofCExpr with CLangExpr { override def pp: String = op match { case ProofCOpNot => s"(~ ${e.pp})" @@ -189,37 +240,37 @@ object Expressions { sealed abstract class ProofCUnOp - object ProofCOpNot extends ProofCUnOp + object ProofCOpNot extends ProofCUnOp with CLangOp - object ProofCOpUnaryMinus extends ProofCUnOp + object ProofCOpUnaryMinus extends ProofCUnOp with CLangOp sealed abstract class ProofCBinOp object ProofCOpImplication extends ProofCBinOp - object ProofCOpPlus extends ProofCBinOp + object ProofCOpPlus extends ProofCBinOp with CLangOp - object ProofCOpMinus extends ProofCBinOp + object ProofCOpMinus extends ProofCBinOp with CLangOp - object ProofCOpMultiply extends ProofCBinOp + object ProofCOpMultiply extends ProofCBinOp with CLangOp - object ProofCOpZEq extends ProofCBinOp + object ProofCOpZEq extends ProofCBinOp with CLangOp - object ProofCOpIntValEq extends ProofCBinOp + object ProofCOpIntValEq extends ProofCBinOp with CLangOp - object ProofCOpPtrValEq extends ProofCBinOp + object ProofCOpPtrValEq extends ProofCBinOp with CLangOp - object ProofCOpBoolEq extends ProofCBinOp + object ProofCOpBoolEq extends ProofCBinOp with CLangOp object ProofCOpSetEq extends ProofCBinOp - object ProofCOpLeq extends ProofCBinOp + object ProofCOpLeq extends ProofCBinOp with CLangOp - object ProofCOpLt extends ProofCBinOp + object ProofCOpLt extends ProofCBinOp with CLangOp - object ProofCOpAnd extends ProofCBinOp + object ProofCOpAnd extends ProofCBinOp with CLangOp - object ProofCOpOr extends ProofCBinOp + object ProofCOpOr extends ProofCBinOp with CLangOp object ProofCOpIn extends ProofCBinOp diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 956baa102..01b1f1b19 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -10,6 +10,7 @@ import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Translator} import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.source.SuslikProofStep +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{IntType, LocType} import org.tygus.suslik.language.Statements.Procedure @@ -47,11 +48,15 @@ object Translation { case IntType => (name, CoqIntValType) }}) val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) - println(spec.pp) - val program_steps = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramContext(Map())) - val procedure = ??? - + val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) + val procedure = CProcedureDefinition( + proc.name, + params, + program_body + ) + println(procedure.pp) + println(spec.pp) val pred_map = predicates.map(v => (v.name,v)).toMap val steps = translate_proof(base_proof)(new VSTProofTranslator, VSTClientContext.make_context(pred_map)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index 1ffa173f0..ad9fd8fff 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -1,57 +1,148 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.vst.Types.{VSTCType, VSTType} -import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.Types +import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType, VSTCType, VSTType} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{CLangExpr, ProofCExpr} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} -import org.tygus.suslik.language.Expressions.{Expr, Var} -import org.tygus.suslik.language.SSLType -import org.tygus.suslik.certification.targets.vst.clang.Statements.StatementStep +import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} +import org.tygus.suslik.language.{IntType, LocType, SSLType} +import org.tygus.suslik.certification.targets.vst.clang.Statements.{CCall, CElif, CFree, CLoadInt, CLoadLoc, CMalloc, CSkip, CWriteInt, CWriteLoc, StatementStep} +import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} +import org.tygus.suslik.logic.{Block, Gamma, Heaplet, PointsTo, SApp} import scala.collection.immutable.Queue -object VSTProgramTranslator { case class VSTProgramContext(typing_context: Map[String, VSTCType]) extends ClientContext[StatementStep] { } } +object VSTProgramTranslator { + + case class VSTProgramContext + ( + typing_context: Map[String, VSTCType] + ) extends ClientContext[StatementStep] { + def with_new_variable(to: String, variable_type: VSTCType) = + VSTProgramContext(typing_context ++ Map(to -> variable_type)) + } + + def empty_context = VSTProgramContext(Map()) + +} + +class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VSTProgramContext] { + type Deferred = Evaluator.Deferred[StatementStep, VSTProgramContext] + private val no_deferreds: Option[Deferred] = None + private val no_ops : List[StatementStep] = List() + + def single_child_result_of(a: List[StatementStep], b : (Option[Deferred], VSTProgramContext)) = Result(a, List(b)) + + def no_child_result_of(a: List[StatementStep]) : Result[StatementStep, VSTProgramContext] = Result(a, List()) -class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VSTProgramTranslator.VSTProgramContext] { - type Deferred = Evaluator.Deferred[StatementStep, VSTProgramTranslator.VSTProgramContext] - private val no_deferreds: Queue[Deferred] = Queue() - def with_no_op (implicit context: VSTClientContext) = (List(), List((Queue(), context))) + + def with_no_op(implicit context: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = + Result(List(), List((None, context))) + + + def to_heap(typing_context: Map[String, VSTCType], chunks: List[Heaplet]) = { + val gamma: Gamma = typing_context.map({ + case (name, CoqPtrValType) => (Var(name), LocType) + case (name, CoqIntValType) => (Var(name), IntType) + }) + val block_map = chunks.flatMap({ + case Block(Var(ptr), sz) => Some((ptr, sz)) + case _ => None + }).toMap + val free_ptrs = chunks.flatMap({ + case PointsTo(Var(loc), 0, value) if !(block_map contains loc) => Some((loc, 1)) + case _ => None + }).toMap + (block_map ++ free_ptrs).map { + case (ptr, sz) => + val types : Array[Option[VSTCType]] = Array.fill(sz)(None) + chunks.foreach({ + case PointsTo(Var(ptr_prime), offset, value) if ptr_prime == ptr => + types.update(offset, value.getType(gamma).flatMap({ + case IntType => Some(CoqIntValType) + case LocType => Some(CoqPtrValType) + case _ => None + })) + case _ => () + }) + (ptr, types.map(_.get).toList) + } + } + + def translate_type(tpe: SSLType): VSTCType = tpe match { + case IntType => CoqIntValType + case LocType => CoqPtrValType + } override def translate(value: SuslikProofStep, clientContext: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = { - implicit val ctx : VSTProgramTranslator.VSTProgramContext = clientContext + implicit val ctx: VSTProgramTranslator.VSTProgramContext = clientContext value match { - case SuslikProofStep.NilNotLval(vars) => ??? - case SuslikProofStep.CheckPost(prePhi, postPhi) => ??? - case SuslikProofStep.Pick(from, to) => ??? + case SuslikProofStep.NilNotLval(_) + | SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.Pick(_, _) + | SuslikProofStep.Close(_, _, _, _) + | SuslikProofStep.StarPartial(_, _) + | SuslikProofStep.PickCard(_, _) + | SuslikProofStep.PickArg(_, _) + | SuslikProofStep.AbduceCall(_, _, _, _, _, _, _, _) + | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyPointer(_, _) + | SuslikProofStep.FrameUnfold(_, _) + | SuslikProofStep.WeakenPre(_) + | SuslikProofStep.PureSynthesis(_, _) + | SuslikProofStep.SubstL(_, _) + | SuslikProofStep.SubstR(_, _) => with_no_op + case SuslikProofStep.Init(goal) => + val new_typing_context = + ctx.typing_context ++ goal.gamma.flatMap({ case (Var(name), lType) => lType match { + case IntType => Some((name, CoqIntValType)) + case LocType => Some((name, CoqPtrValType)) + case _ => None + }}) + val new_context = VSTProgramContext(new_typing_context) + single_child_result_of(no_ops, (no_deferreds, new_context)) + case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => + val ops = CElif(selectors.map(v => ProofSpecTranslation.translate_expression(ctx.typing_context)(v).asInstanceOf[CLangExpr])) + val children = selectors.map(_ => { + (no_deferreds, ctx) + }) + Result(List(ops), children) case SuslikProofStep.Branch(cond, bLabel) => ??? - case SuslikProofStep.Write(stmt) => ??? - case SuslikProofStep.WeakenPre(unused) => ??? - case SuslikProofStep.EmpRule(label) => ??? - case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? - case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? - case SuslikProofStep.SubstL(from, to) => ??? - case SuslikProofStep.SubstR(from, to) => ??? - case SuslikProofStep.Read(ghostFrom, ghostTo, operation) => ??? - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? - case SuslikProofStep.HeapUnify(subst) => ??? - case SuslikProofStep.HeapUnifyPointer(from, to) => ??? - case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? - case SuslikProofStep.Call(subst, call) => ??? - case SuslikProofStep.Free(stmt, size) => ??? - case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? - case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? - case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? - case SuslikProofStep.PickCard(from, to) => ??? - case SuslikProofStep.PickArg(from, to) => ??? - case SuslikProofStep.Init(goal) => ??? - case SuslikProofStep.Inconsistency(label) => ??? + case SuslikProofStep.Write(Store(Var(to), offset, base_expr)) => + val expr = ProofSpecTranslation.translate_expression(ctx.typing_context)(base_expr).asInstanceOf[CLangExpr] + val op = expr.type_cexpr match { + case Types.CoqPtrValType => CWriteLoc(to, expr, offset) + case Types.CoqIntValType => CWriteInt(to, expr, offset) + } + single_child_result_of(List(op), (no_deferreds, ctx)) + case SuslikProofStep.Read(from_var, to_var, Load(Var(to),tpe, Var(from), offset)) => + val (op, variable_type) = tpe match { + case IntType => (CLoadInt(to, from, offset), CoqIntValType) + case LocType => (CLoadLoc(to, from, offset), CoqPtrValType) + } + val new_context = ctx with_new_variable(to, variable_type) + single_child_result_of(List(op), (no_deferreds, new_context)) + case SuslikProofStep.Call(subst, Call(Var(fname), args, _)) => + val exprs = args.map(ProofSpecTranslation.translate_expression(ctx.typing_context)(_).asInstanceOf[CLangExpr]) + val op = CCall(fname, exprs) + single_child_result_of(List(op), (no_deferreds, ctx)) + case SuslikProofStep.Free(Free(Var(name)), size) => + val op = CFree(name) + single_child_result_of(List(op), (no_deferreds, ctx)) + case SuslikProofStep.Malloc(Var(_), Var(_), Malloc(Var(to), tpe, sz)) => + val new_context = ctx with_new_variable (to, translate_type(tpe)) + single_child_result_of(List(CMalloc(to, sz)), (no_deferreds, new_context)) + case SuslikProofStep.Inconsistency(label) => no_child_result_of(List(CSkip)) + case SuslikProofStep.EmpRule(label) => no_child_result_of(List(CSkip)) } } } \ No newline at end of file From 77eff34b5cb7bd58f73f6b66e1943091c4e02daf Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 18 Feb 2021 16:04:01 +0800 Subject: [PATCH 108/211] fixes synthesis for bst-right-rotate --- .../targets/vst/translation/ProofSpecTranslation.scala | 3 +-- .../resources/synthesis/certification/bst/bst-right-rotate.syn | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index abd053e3f..263254ec8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -195,8 +195,7 @@ object ProofSpecTranslation { val c_value = translate_expression(context)(value) CDataAt(translate_expression(context)(loc), List(c_value)) case PointsTo(_, _, _) => - assert(false, "found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") - ??? + throw TranslationException("found points to information without a block that references a non-zero element (i.e (x + 1) :-> 2)") } } }).toList diff --git a/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn index 5b9920d06..657219333 100644 --- a/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn +++ b/src/test/resources/synthesis/certification/bst/bst-right-rotate.syn @@ -2,7 +2,7 @@ binary search tree: rotate right ##### -{ not (l == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; +{ not (l == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } void bst_right_rotate (loc x, loc retv) { sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; From 0e18488b96aa1625968de11ea9a6b2e718997c43 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 18 Feb 2021 16:08:49 +0800 Subject: [PATCH 109/211] cleans up VST dead code --- .../certification/targets/vst/Types.scala | 2 +- .../targets/vst/clang/Expressions.scala | 71 ------------------- .../targets/vst/clang/PrettyPrinting.scala | 7 -- .../targets/vst/clang/Statements.scala | 8 +-- .../targets/vst/logic/Expressions.scala | 2 +- .../targets/vst/logic/ProofTerms.scala | 3 +- .../targets/vst/translation/Translation.scala | 19 +---- 7 files changed, 6 insertions(+), 106 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala index 25d4fcfa7..7e2e9b1ac 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/Types.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting +import org.tygus.suslik.language.PrettyPrinting /** Encapsulates all types used in proofs - i.e if these types are pretty printed, then they will be valid Coq terms */ object Types { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala deleted file mode 100644 index 07103cc8a..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Expressions.scala +++ /dev/null @@ -1,71 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.clang - -/** Encodes VST specific C expressions */ -object Expressions { - - sealed abstract class CExpr extends PrettyPrinting { } - - /** VST specific Encoding of variables */ - case class CVar(name: String) extends CExpr { - override def pp: String = s"${name}" - } - - case class CBoolConst(value: Boolean) extends CExpr { - override def pp: String = value.toString - } - - case class CNatConst(value: Int) extends CExpr { - override def pp: String = value.toString - } - - case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { - override def pp: String = - s"(${cond.pp} ? ${left.pp} : ${right.pp})" - } - - case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { - override def pp: String = - op match { - case COpEq => s"(${left.pp} == ${right.pp})" - case COpLt => s"(${left.pp} < ${right.pp})" - case COpLeq => s"(${left.pp} <= ${right.pp})" - case COpOr => s"${left.pp} || ${right.pp}" - case COpAnd => s"${left.pp} && ${right.pp}" - case COpPlus => s"(${left.pp} + ${right.pp})" - case COpMinus => s"(${left.pp} - ${right.pp})" - case COpMultiply => s"(${left.pp} * ${right.pp})" - } - } - - case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { - override def pp: String = - op match { - case COpNot => s"!(${e.pp})" - case COpUnaryMinus => s"-(${e.pp})" - } - } - - sealed abstract class CUnOp - - object COpNot extends CUnOp - object COpUnaryMinus extends CUnOp - - sealed abstract class CBinOp - - object COpPlus extends CBinOp - - object COpMinus extends CBinOp - - object COpMultiply extends CBinOp - - object COpEq extends CBinOp - - object COpLeq extends CBinOp - - object COpLt extends CBinOp - - object COpAnd extends CBinOp - - object COpOr extends CBinOp - -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala deleted file mode 100644 index 25f4f7576..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/PrettyPrinting.scala +++ /dev/null @@ -1,7 +0,0 @@ -package org.tygus.suslik.certification.targets.vst.clang - -trait PrettyPrinting { - def pp: String = toString - def ppIndent(depth: Int) = s"${getIndent(depth)}${pp.replaceAll("\n", s"\n${getIndent(depth)}")}" - def getIndent(depth: Int): String = " " * depth -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index 080416055..fe19fd278 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -1,14 +1,10 @@ package org.tygus.suslik.certification.targets.vst.clang import org.tygus.suslik.certification.targets.vst.Types.VSTCType -import org.tygus.suslik.certification.targets.vst.clang.Expressions.{CExpr, CVar} import org.tygus.suslik.certification.targets.vst.logic.Expressions.CLangExpr -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{ForwardIf, ForwardIfConstructor} -import org.tygus.suslik.certification.targets.vst.translation.Translation.{TranslationException, fail_with} -import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.DestStep -import org.tygus.suslik.logic.Specifications.GoalLabel +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.language.PrettyPrinting /** Encoding of C statements */ object Statements { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 62275890a..73588d73a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -2,8 +2,8 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqIntSetType, CoqIntValType, CoqPtrValType, CoqZType, VSTCType, VSTType} -import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.language.PrettyPrinting /** Redefinition of expressions for use in VST proofs diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index e4029a810..97af7d54f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,10 +1,9 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types._ -import org.tygus.suslik.certification.targets.vst.clang.PrettyPrinting import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.language.Ident +import org.tygus.suslik.language.{Ident, PrettyPrinting} object ProofTerms { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 01b1f1b19..ab3aae207 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -31,16 +31,6 @@ object Translation { def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val base_proof = SuslikProofStep.of_certtree(root) - - // val procedure: Statements.CProcedureDefinition = CTranslation.translate_function(proc, root.goal.gamma) - // val new_procedure : Statements.CProcedureDefinition = CTranslation.translate_function_from_proof(base_proof, root.goal.gamma) - - // val (spec, context) = ProofSpecTranslation.translate_conditions(procedure)(root.goal) - // val pre_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.pre) - // val post_cond = ProofSpecTranslation.translate_assertion(context)(root.goal.post) - - // println(procedure.pp) - // println(new_procedure.pp) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList predicates.foreach(v => println(v.pp)) val params = proc.formals.map({case (Var(name), ty) => ty match { @@ -49,14 +39,7 @@ object Translation { }}) val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) - - val procedure = CProcedureDefinition( - proc.name, - params, - program_body - ) - println(procedure.pp) - println(spec.pp) + val procedure = CProcedureDefinition(proc.name, params, program_body) val pred_map = predicates.map(v => (v.name,v)).toMap val steps = translate_proof(base_proof)(new VSTProofTranslator, VSTClientContext.make_context(pred_map)) From d09d4a1e59dca874bc2e414a2197c7683e04abc4 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 18 Feb 2021 17:30:26 +0800 Subject: [PATCH 110/211] prepares VST translator for implementation of proof translation --- .../targets/vst/clang/Statements.scala | 2 +- .../targets/vst/translation/Translation.scala | 4 +- .../translation/VSTProgramTranslator.scala | 7 +- .../vst/translation/VSTProofTranslator.scala | 111 ++++++++++++------ 4 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index fe19fd278..6dcd7d980 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -12,7 +12,7 @@ object Statements { implicit object ProofTreePrinter extends ProofTreePrinter[StatementStep] { override def pp(tree: ProofTree[StatementStep]): String = tree.step match { case CIf(cond) => - s"if (${cond.pp_as_clang_expr} {\n" + + s"if (${cond.pp_as_clang_expr}) {\n" + s"${pp(tree.children(0))}" + s"} else {\n" + s"${pp(tree.children(1))}" + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index ab3aae207..8f25dbfd6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -32,7 +32,6 @@ object Translation { def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val base_proof = SuslikProofStep.of_certtree(root) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList - predicates.foreach(v => println(v.pp)) val params = proc.formals.map({case (Var(name), ty) => ty match { case LocType => (name, CoqPtrValType) case IntType => (name, CoqIntValType) @@ -40,9 +39,10 @@ object Translation { val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body) + println(procedure.pp) val pred_map = predicates.map(v => (v.name,v)).toMap - val steps = translate_proof(base_proof)(new VSTProofTranslator, VSTClientContext.make_context(pred_map)) + val steps = translate_proof(base_proof)(VSTProofTranslator(spec), VSTClientContext.make_context(pred_map)) val proof = Proof(proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep]) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index ad9fd8fff..245b71602 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -12,7 +12,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} import org.tygus.suslik.language.{IntType, LocType, SSLType} -import org.tygus.suslik.certification.targets.vst.clang.Statements.{CCall, CElif, CFree, CLoadInt, CLoadLoc, CMalloc, CSkip, CWriteInt, CWriteLoc, StatementStep} +import org.tygus.suslik.certification.targets.vst.clang.Statements.{CCall, CElif, CFree, CIf, CLoadInt, CLoadLoc, CMalloc, CSkip, CWriteInt, CWriteLoc, StatementStep} import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} @@ -116,7 +116,10 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS (no_deferreds, ctx) }) Result(List(ops), children) - case SuslikProofStep.Branch(cond, bLabel) => ??? + case SuslikProofStep.Branch(cond, bLabel) => + val ops = CIf(ProofSpecTranslation.translate_expression(ctx.typing_context)(cond).asInstanceOf[CLangExpr]) + val children = List((no_deferreds, ctx), (no_deferreds, ctx)) + Result(List(ops), children) case SuslikProofStep.Write(Store(Var(to), offset, base_expr)) => val expr = ProofSpecTranslation.translate_expression(ctx.typing_context)(base_expr).asInstanceOf[CLangExpr] val op = expr.type_cexpr match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 03499b441..b0ab569d1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -3,14 +3,14 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types.VSTType import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Exists, Forward, ForwardIf, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} -import org.tygus.suslik.language.SSLType +import org.tygus.suslik.language.{Ident, SSLType} import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import scala.collection.immutable.Queue @@ -20,74 +20,107 @@ object VSTProofTranslator { case class VSTClientContext( pred_map: Map[String, VSTPredicate], coq_context: Map[String, VSTType], + existential_context: Map[String, VSTType], variable_map: Map[String, ProofCExpr] ) extends ClientContext[VSTProofStep] { + def with_existential_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + VSTClientContext(pred_map, coq_context, existential_context ++ new_vars, variable_map) + } + + def resolve_existential(ident: Ident) : ProofCExpr = ??? + def with_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap - VSTClientContext(pred_map, coq_context ++ new_vars, variable_map) + VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) } def with_mapping_between(from: String, to: Expr): VSTClientContext = { val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) - VSTClientContext(pred_map, coq_context, variable_map ++ Map(from -> to_expr)) + VSTClientContext(pred_map, coq_context, existential_context, variable_map ++ Map(from -> to_expr)) } } object VSTClientContext { def make_context(pred_map: Map[String, VSTPredicate]): VSTClientContext = - VSTClientContext(pred_map, Map(), Map()) + VSTClientContext(pred_map, Map(), Map(), Map()) } } -class VSTProofTranslator extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { +case class VSTProofTranslator(spec: FormalSpecification) extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] + type Result = Translator.Result[VSTProofStep, VSTClientContext] private val no_deferreds: Option[Deferred] = None - private def with_no_deferreds(ctx: VSTClientContext) = (no_deferreds, ctx) + private def with_no_deferreds(ctx: VSTClientContext) : (Option[Deferred], VSTClientContext) = (no_deferreds, ctx) - def with_no_op(context: VSTClientContext): Result[VSTProofStep, VSTClientContext] = Result(List(), List((None, context))) + def with_no_op(context: VSTClientContext): Result = Result(List(), List((None, context))) - override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Translator.Result[VSTProofStep, VSTClientContext] = { + override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { value match { - case SuslikProofStep.NilNotLval(vars) => - val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) - Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) - case SuslikProofStep.CheckPost(prePhi, postPhi) => - with_no_op(clientContext) - case SuslikProofStep.Pick(from, to_expr) => - val new_context = clientContext with_mapping_between(from.name, to_expr) - with_no_op(new_context) + /** Initialization */ + case SuslikProofStep.Init(goal) => + val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) + var ctx = clientContext with_variables_of program_vars + val existential_params = goal.existentials.map(v => (v.name, goal.gamma(v))).toList + ctx = ctx with_existential_variables_of existential_params + val existentials = spec.existensial_params + val deferreds : Deferred = (ctx: VSTClientContext) => { + val steps : List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList + (steps, ctx) + } + Result(List(), List((Some(deferreds), ctx))) + + /** Branching rules */ case SuslikProofStep.Branch(cond, bLabel) => val cont = with_no_deferreds(clientContext) Result(List(ForwardIf), List(cont, cont)) + + /** Call handling */ + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? + case SuslikProofStep.Call(subst, call) => ??? + + /** Proof Termination rules */ + case SuslikProofStep.EmpRule(label) => ??? + case SuslikProofStep.Inconsistency(label) => ??? + case SuslikProofStep.Write(stmt) => Result(List(Forward), List(with_no_deferreds(clientContext))) - case SuslikProofStep.WeakenPre(unused) => - with_no_op(clientContext) - case SuslikProofStep.EmpRule(label) => ??? - case SuslikProofStep.PureSynthesis(is_final, assignments) => ??? - case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? - case SuslikProofStep.SubstL(from, to) => ??? - case SuslikProofStep.SubstR(from, to) => ??? case SuslikProofStep.Read(from, to, operation) => ??? - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? - case SuslikProofStep.HeapUnify(subst) => ??? - case SuslikProofStep.HeapUnifyPointer(from, to) => ??? - case SuslikProofStep.FrameUnfold(h_pre, h_post) => ??? - case SuslikProofStep.Call(subst, call) => ??? case SuslikProofStep.Free(stmt, size) => ??? case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? - case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? - case SuslikProofStep.StarPartial(new_pre_phi, new_post_phi) => ??? - case SuslikProofStep.PickCard(from, to) => ??? + + /** Substitutions and renaming */ + case SuslikProofStep.Pick(from, to_expr) => + val new_context = clientContext with_mapping_between(from.name, to_expr) + with_no_op(new_context) + case SuslikProofStep.PureSynthesis(is_final, assignments) => + val new_context = + assignments.foldLeft(clientContext)({case (ctx, (from, to_expr)) => ctx with_mapping_between(from.name, to_expr)}) + with_no_op(new_context) case SuslikProofStep.PickArg(from, to) => ??? - case SuslikProofStep.Init(goal) => - val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) - val ctx = clientContext with_variables_of program_vars - val existentials = goal.existentials.toList.map(v => (v.name, goal.gamma(v))) - with_no_op(ctx) - case SuslikProofStep.Inconsistency(label) => ??? + case SuslikProofStep.PickCard(from, to) => ??? + case SuslikProofStep.SubstL(from, to) => ??? + case SuslikProofStep.SubstR(from, to) => ??? + + /** Predicate folding/unfolding */ + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? + + /** Misc. facts */ + case SuslikProofStep.NilNotLval(vars) => + val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) + Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) + + /** Ignored rules */ + case SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.WeakenPre(_) + | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyPointer(_, _) + | SuslikProofStep.StarPartial(_, _) + | SuslikProofStep.FrameUnfold(_, _) => + with_no_op(clientContext) } } } From 9d6969e0ef1d75034fbcd7fd42d6bbabef425b0a Mon Sep 17 00:00:00 2001 From: Ilya Sergey Date: Thu, 18 Feb 2021 18:12:50 +0800 Subject: [PATCH 111/211] account example added --- examples/mkacc.syn | 12 ++++++++++++ examples/predicates.def | 4 ++++ 2 files changed, 16 insertions(+) create mode 100644 examples/mkacc.syn diff --git a/examples/mkacc.syn b/examples/mkacc.syn new file mode 100644 index 000000000..09adbff74 --- /dev/null +++ b/examples/mkacc.syn @@ -0,0 +1,12 @@ +#. -b true +An account constructor + +### + +{ r :-> bal } + +void mk_acc (loc r, int id) + +{ r :-> x ** account(x, id, bal) } + +### diff --git a/examples/predicates.def b/examples/predicates.def index 31f14bc2e..6d8b13469 100644 --- a/examples/predicates.def +++ b/examples/predicates.def @@ -24,3 +24,7 @@ predicate sll(loc x, int len, int lo, int hi) { | not (x == 0) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } } + +predicate account(loc x, int id, int bal) { +| true => { [x, 2] ** x :-> id ** (x + 1) :-> bal } +} From 91f768718d5606d6fb9fc9c57ae8bda034a42cf0 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Fri, 19 Feb 2021 11:27:26 +0800 Subject: [PATCH 112/211] fixes min/max/swap proofs in VST --- .../targets/vst/logic/Expressions.scala | 21 ++++++++ .../translation/ProofSpecTranslation.scala | 1 - .../vst/translation/VSTProofTranslator.scala | 52 +++++++++++++++---- 3 files changed, 64 insertions(+), 10 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 73588d73a..6e3881f4b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -67,6 +67,27 @@ object Expressions { */ sealed abstract class ProofCExpr extends PrettyPrinting { + /** Applies a renaming to an expression */ + def rename(mapping: Map[String, String]): ProofCExpr = this match { + case expr@ProofCVar(name, ty) => mapping.get(name) match { + case Some(name) => ProofCVar(name, ty) + case None => expr + } + case expr@ProofCBoolConst(_) => expr + case expr@ProofCIntConst(_) => expr + case ProofCIntSetLiteral(elems) => + ProofCIntSetLiteral(elems.map(_.rename(mapping))) + case ProofCIfThenElse(cond, left, right) => + ProofCIfThenElse(cond.rename(mapping), left.rename(mapping), right.rename(mapping)) + case ProofCBinaryExpr(op, left, right) => + ProofCBinaryExpr(op, left.rename(mapping), right.rename(mapping)) + case ProofCUnaryExpr(op, e) => + ProofCUnaryExpr(op, e.rename(mapping)) + case ProofCCardinalityConstructor(pred_type, name, args) => + ProofCCardinalityConstructor(pred_type, name, args.map(_.rename(mapping))) + } + + /** Applies a substitution to an expression */ def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { case expr@ProofCVar(name, _) => mapping.get(name) match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 263254ec8..37a6b05c9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -43,7 +43,6 @@ object ProofSpecTranslation { ) } - /** translate a suslik type into a VST proof type */ def translate_predicate_param_type(lType: SSLType): VSTType = lType match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index b0ab569d1..b30862b44 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -2,16 +2,17 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types.VSTType -import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Exists, Forward, ForwardIf, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Entailer, Exists, Forward, ForwardEntailer, ForwardIf, Rename, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.{Ident, SSLType} import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.language.Statements.Load import scala.collection.immutable.Queue @@ -24,21 +25,46 @@ object VSTProofTranslator { variable_map: Map[String, ProofCExpr] ) extends ClientContext[VSTProofStep] { + def with_renaming(vl: (String, String)) : VSTClientContext = { + val (from, to) = vl + val renaming = Map(from -> to) + def true_name(vl : String) = renaming.getOrElse(vl,vl) + val new_coq_context = coq_context.map({case (name, ty) => (true_name(name), ty)}) + val new_existential_context = existential_context.map({case (name, ty) => (true_name(name), ty)}) + val new_variable_map = variable_map.map { + case (name, expr) => (true_name(name), expr.rename(renaming)) + } + VSTClientContext(pred_map, new_coq_context, new_existential_context, variable_map) + } + def with_existential_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap VSTClientContext(pred_map, coq_context, existential_context ++ new_vars, variable_map) } - def resolve_existential(ident: Ident) : ProofCExpr = ??? + def resolve_existential(ident: Ident) : ProofCExpr = variable_map(ident) - def with_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + def with_program_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + // Note: translate_predicate_param_type maps val (containing ints) -> Z, and val (containing ptrs) -> val. + // This is valid because the ssl_open_context tactic called at the start of a proof in VST will unwrap any + // variable x in the context where ssl_is_valid_int(x) is known into a term of type Z (preserving the name - i.e (x : Z)). + // As such, both ghost and program variables will correctly have type Z in the coq context (as the precondition for + // specifications asserts that their values are valid ints). val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) } + def with_ghost_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) + } + + def with_mapping_between(from: String, to: Expr): VSTClientContext = { val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) - VSTClientContext(pred_map, coq_context, existential_context, variable_map ++ Map(from -> to_expr)) + val subst = Map(from -> to_expr) + val new_variable_map = variable_map.map({case (name, vl) => (name, vl.subst(subst))}) ++ subst + VSTClientContext(pred_map, coq_context, existential_context, new_variable_map) } } @@ -62,13 +88,15 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl /** Initialization */ case SuslikProofStep.Init(goal) => val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) - var ctx = clientContext with_variables_of program_vars + val ghost_vars = goal.universalGhosts.map(v => (v.name, goal.gamma(v))).toList + var ctx = clientContext with_program_variables_of program_vars + ctx = ctx with_ghost_variables_of ghost_vars val existential_params = goal.existentials.map(v => (v.name, goal.gamma(v))).toList ctx = ctx with_existential_variables_of existential_params val existentials = spec.existensial_params val deferreds : Deferred = (ctx: VSTClientContext) => { val steps : List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList - (steps, ctx) + (steps ++ List(Entailer), ctx) } Result(List(), List((Some(deferreds), ctx))) @@ -82,12 +110,18 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.Call(subst, call) => ??? /** Proof Termination rules */ - case SuslikProofStep.EmpRule(label) => ??? + case SuslikProofStep.EmpRule(label) => + + Result(List(ForwardEntailer), List()) case SuslikProofStep.Inconsistency(label) => ??? case SuslikProofStep.Write(stmt) => Result(List(Forward), List(with_no_deferreds(clientContext))) - case SuslikProofStep.Read(from, to, operation) => ??? + case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => + var ctx = clientContext with_renaming(ghost_from -> ghost_to) + val rename_step = Rename(ghost_from, ghost_to) + val read_step = Forward + Result(List(rename_step, read_step), List(with_no_deferreds(ctx))) case SuslikProofStep.Free(stmt, size) => ??? case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? From 56478cd3cef9814bbe8daba6d7cd5a80e41a839f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Fri, 19 Feb 2021 14:18:29 +0800 Subject: [PATCH 113/211] WIP: generate Iris specs --- .../targets/iris/IrisCertificate.scala | 71 +++++++++++++++- .../targets/iris/logic/Assertions.scala | 68 ++++++++++----- .../iris/translation/IrisTranslator.scala | 83 +++++++++++++++---- .../iris/translation/ProgramContext.scala | 6 -- .../iris/translation/ProgramEvaluator.scala | 2 +- .../iris/translation/ProgramTranslator.scala | 7 +- .../iris/translation/TranslatableOps.scala | 4 +- .../iris/translation/Translation.scala | 16 +++- .../iris/translation/TranslationContext.scala | 8 ++ .../translation/ProofSpecTranslation.scala | 2 +- 10 files changed, 213 insertions(+), 54 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index c4b13e46d..cbbf86f47 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -1,9 +1,10 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IFunSpec import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class IrisCertificate(name: String, funDef: HFunDef) extends Certificate { +case class IrisCertificate(name: String, funDef: HFunDef, funSpec: IFunSpec) extends Certificate { val target: CertificationTarget = Iris private val prelude = @@ -16,12 +17,80 @@ case class IrisCertificate(name: String, funDef: HFunDef) extends Certificate { |Implicit Types Δ : envs PROP. |Set Default Proof Using "Type". | + |Definition loc_at x lx := x = LitV (LitLoc lx). + |Definition int_at x vx := x = LitV (LitInt vx). + | + |(* This is a less clever version of tac_and_destruct, which + | does NOT break ↦ assertions into fractional assertions. *) + |Lemma tac_sep_destruct Δ i j1 j2 P P1 P2 Q : + | envs_lookup i Δ = Some (false, P) → + | (P -∗ P1 ∗ P2) → + | match envs_simple_replace i false (Esnoc (Esnoc Enil j1 P1) j2 P2) Δ with + | | None => False + | | Some Δ' => envs_entails Δ' Q + | end → + | envs_entails Δ Q. + |Proof. + | destruct (envs_simple_replace _ _ _ _) as [Δ'|] eqn:Hrep; last done. + | rewrite envs_entails_eq. intros H0 H1 H2. rewrite envs_simple_replace_sound //=. + | by rewrite /= right_id -(comm _ P1) H2 -H1 bi.wand_elim_r. + |Qed. + | + |Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := + | eapply tac_sep_destruct with H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) + | [pm_reflexivity + | |pm_reduce; iSolveTC + | |pm_reduce; + | lazymatch goal with + | | |- False => fail + | | _ => idtac + | end]. + | + |Local Ltac iSplitAllHyps := + | iStartProof; + | let rec go Hs := + | match Hs with [] => idtac | ?H :: ?Hs => + | let Hn := iFresh in + | try iAndDestruct H as H Hn; go Hs end in + | match goal with + | | |- envs_entails ?Δ _ => + | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs + | end. + | + |Local Ltac iFindApply := + | iStartProof; + | let rec go Hs := + | match Hs with [] => idtac | ?H :: ?Hs => + | try iApply H; go Hs end in + | match goal with + | | |- envs_entails ?Δ _ => + | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs + | end. + | + |Local Ltac iRewriteHyp := + | repeat match goal with + | | [H: loc_at _ _ |- _ ] => rewrite H + | | [H: int_at _ _ |- _ ] => rewrite H + | | [H: bool_decide _ = _ |- _ ] => rewrite H + | end. + | + |Local Ltac iSimplContext := + | wp_pures; iSplitAllHyps; iRewriteHyp; iSimpl in "# ∗"; iSimpl. + | + |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. + |Ltac ssl_load := wp_load; wp_let. + |Ltac ssl_store := wp_store. + |Ltac ssl_finish := by iFindApply; iFrame "% # ∗". + | |""".stripMargin def pp : String = { val b = new StringBuilder b.append(prelude) b.append(funDef.pp) + b.append("\n") + b.append(funSpec.pp) + b.append("Proof.\nAdmitted.\n") b.toString() } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 6f19d1173..d66536c84 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.iris.logic -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HExpr, HOpEq, HOpLe, HOpLt} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HExpr, HLit, HOpEq, HOpLe, HOpLt, HProgVar} import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HLocType, HType} +import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator +import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.PrettyPrinting import org.tygus.suslik.language.Statements.Procedure @@ -10,14 +12,25 @@ object Assertions { /** Unlike HTT, which encodes programs in a shallow embedding, Iris has a deep embedding of programs. * As such, program expressions are NOT Iris assertions (phi != HExpr). We define a lifting of * program-level expressions to spec-level. */ - abstract class HSpecExpr extends PrettyPrinting + abstract class ISpecExpr extends PrettyPrinting + abstract class IQuantifiedVar extends ISpecExpr - case class HSpecVar(name: String) extends HSpecExpr { + case class ISpecLit(lit: HLit) extends ISpecExpr { + override def pp: String = lit.pp + } + + case class ISpecVar(name: String) extends IQuantifiedVar { override def pp: String = s"${name}" } - case class HSpecBinaryExpr(op: HBinOp, left: HSpecExpr, right: HSpecExpr) extends HSpecExpr { - override def pp: String = op match { + case class ISpecQuantifiedValue(name: String) extends IQuantifiedVar { + override def pp: String = s"${name}" + } + + case class ISpecBinaryExpr(op: HBinOp, left: ISpecExpr, right: ISpecExpr) extends ISpecExpr { + override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" + + def ppAsPhi: String = op match { case HOpLe | HOpLt | HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" case _ => ??? } @@ -26,9 +39,11 @@ object Assertions { abstract class IPureAssertion extends PrettyPrinting abstract class ISpatialAssertion extends PrettyPrinting - abstract class ISpecification extends PrettyPrinting + case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { + override def pp: String = s"${conjuncts.map(_.pp).mkString(" ∧ ")}" + } - case class IPointsTo(loc: HExpr, value: HExpr) extends ISpatialAssertion { + case class IPointsTo(loc: ISpecExpr, value: ISpecExpr) extends ISpatialAssertion { override def pp: String = s"${loc.pp} ↦ ${value.pp}" } @@ -36,34 +51,45 @@ object Assertions { override def pp: String = s"${heaplets.map(_.pp).mkString(" ∗ ")}" } - case class IFunSpec(proc: Procedure, - funArgs: Seq[(HSpecVar, HType)], - specUniversal: Seq[HSpecVar], - specExistential: Seq[HSpecVar], - pre: ISpatialAssertion, - post: ISpatialAssertion + case class IAssertion(phi: IPureAssertion, sigma: ISpatialAssertion) extends PrettyPrinting { + override def pp: String = { + val pure = if (phi.pp.isEmpty) "" else s"⌜${phi.pp}⌝ ∗ " + val whole = s"${pure}${sigma.pp}" + if (whole.isEmpty) "True" else whole + } + } + + abstract class ISpecification extends PrettyPrinting + + case class IFunSpec(fname: String, + funArgs: Seq[(ISpecVar, HType)], + specUniversal: Seq[IQuantifiedVar], + specExistential: Seq[IQuantifiedVar], + pre: IAssertion, + post: IAssertion ) extends ISpecification { override def pp: String = { - def getArgLitVal(v : HSpecVar, t: HType) : HSpecVar = + // TODO: make this use the general translation mechanism + def getArgLitVal(v : ISpecVar, t: HType) : ISpecQuantifiedValue = (v, t) match { - case (HSpecVar(name), HLocType()) => HSpecVar(s"l${name}") - case (HSpecVar(name), _) => HSpecVar(s"v${name}") + case (ISpecVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}") + case (ISpecVar(name), _) => ISpecQuantifiedValue(s"v${name}") } - val var_at = (v:HSpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" + val var_at = (v:ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" val postExist = if (specExistential.nonEmpty) - s"∃ ${specExistential.map(v => v.pp).mkString(" ")}" + s"∃ ${specExistential.map(v => v.pp).mkString(" ")}, " else "" s""" - |Lemma ${proc.name}_spec : + |Lemma ${fname}_spec : |∀ ${specUniversal.map(v => v.pp).mkString(" ")}, |${funArgs.map(v => var_at(v._1, v._2)).mkString(" →\n")} → |{{{ ${pre.pp} }}} - | ${proc.name} ${funArgs.map(v => v._1.pp).mkString(" ")} - |{{{ RET #(); ${postExist}, ${post.pp} }}}. + | ${fname} ${funArgs.map(v => v._1.pp).mkString(" ")} + |{{{ RET #(); ${postExist}${post.pp} }}}. |""".stripMargin } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index ad4c56a66..fad77f416 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -2,20 +2,22 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HAllocN, HBinOp, HBinaryExpr, HCall, HExpr, HIfThenElse, HLet, HLitBool, HLitInt, HLitLoc, HLitUnit, HLoad, HOpEq, HOpLe, HOpLt, HOpMinus, HOpMultiply, HOpNot, HOpOffset, HOpPlus, HOpUnaryMinus, HProgVar, HStore, HUnOp, HUnaryExpr} import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HIntType, HLocType, HType} -import org.tygus.suslik.certification.targets.iris.logic.Assertions.HSpecExpr +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IAnd, IAssertion, IFunSpec, IHeap, IPointsTo, IPureAssertion, ISpatialAssertion, ISpecBinaryExpr, ISpecExpr, ISpecLit, ISpecQuantifiedValue, ISpecVar} import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Expressions.{BinOp, BinaryExpr, BoolConst, Expr, IfThenElse, IntConst, LocConst, OpBoolEq, OpEq, OpLeq, OpLt, OpMinus, OpMultiply, OpNot, OpPlus, OpUnaryMinus, UnOp, UnaryExpr, Var} -import org.tygus.suslik.language.{IntType, LocType, SSLType, Statements} +import org.tygus.suslik.language.{IntType, LocType, SSLType} import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} +import org.tygus.suslik.logic.{Heaplet, PFormula, PointsTo, SFormula} +import org.tygus.suslik.logic.Specifications.{Assertion, Goal} -trait IrisTranslator[A, B] { - def translate(value: A): B +trait IrisTranslator[From, To] { + def translate(value: From, ctx: Option[TranslationContext] = None): To } object IrisTranslator { case class TranslatorException(msg: String) extends Exception(msg) - implicit val binOpTranslator: IrisTranslator[BinOp, HBinOp] = { + implicit val binOpTranslator: IrisTranslator[BinOp, HBinOp] = (value, _) => value match { case OpPlus => HOpPlus case OpMinus => HOpMinus case OpMultiply => HOpMultiply @@ -25,14 +27,14 @@ object IrisTranslator { case _ => ??? } - implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = el => el._1.translate + implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _) => el._1.translate(progVarTranslator) - implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = { + implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = (value, _) => value match { case OpNot => HOpNot case OpUnaryMinus => HOpUnaryMinus } - implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr: Expr) => { + implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr, _) => { def visit(expr: Expr): HExpr = expr match { case Var(name) => HProgVar(name) case IntConst(v) => HLitInt(v) @@ -46,31 +48,84 @@ object IrisTranslator { visit(expr) } - implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = pv => HProgVar(pv.name) + implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _) => HProgVar(pv.name) + implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, _) => ISpecVar(hv.name) + implicit val progVarToSpecQuantifiedValue: IrisTranslator[HProgVar, ISpecQuantifiedValue] = (pv, ctx) => { + assert(ctx.isDefined) + (pv, ctx.get.gamma(Var(pv.name)).translate) match { + case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}") + case (HProgVar(name), _) => ISpecQuantifiedValue(s"v${name}") + } + } - implicit val typeTranslator: IrisTranslator[SSLType, HType] = { + implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _) => value match { case IntType => HIntType() case LocType => HLocType() case _ => ??? } implicit val mallocTranslator: IrisTranslator[Malloc, HStore] = - stmt => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) + (stmt, _) => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) implicit val loadTranslator: IrisTranslator[Load, HLet] = - stmt => { + (stmt, _) => { val baseVar = stmt.from.translate val fromAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) HLet(stmt.to.translate, HLoad(fromAddr), HLitUnit()) } implicit val storeTranslator: IrisTranslator[Store, HStore] = - stmt => { + (stmt, _) => { val baseVar = stmt.to.translate val toAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) HStore(toAddr, stmt.e.translate) } - implicit val callTranslator: IrisTranslator[Call, HCall] = stmt => HCall(stmt.fun.translate, stmt.args.map(_.translate)) + implicit val callTranslator: IrisTranslator[Call, HCall] = (stmt, _) => HCall(stmt.fun.translate, stmt.args.map(_.translate)) + + implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, _) => IAnd(Seq()) + + implicit val toSpecExpr: IrisTranslator[HExpr, ISpecExpr] = (expr, ctx) => { + assert(ctx.isDefined) + expr match { + // Get the quantified value if it is quantified, or just translate if ghost + case v: HProgVar => ctx.get.pts.getOrElse(v, v.translate(progVarToSpecVar)) + case _ => ??? + } + } + + implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx) => { + val base = f.loc.translate.translate(toSpecExpr, ctx) + val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitLoc(f.offset))) else base + val value = f.value.translate.translate(toSpecExpr, ctx) + IPointsTo(loc, value) + } + + implicit val heapleatTranslator: IrisTranslator[Heaplet, ISpatialAssertion] = (h, ctx) => { + assert(ctx.isDefined) + h match { + case pt:PointsTo => pt.translate(pointsToTranslator, ctx) + case _ => ??? + } + } + implicit val sigmaTranslator: IrisTranslator[SFormula, ISpatialAssertion] = (f, ctx) => { + IHeap(f.chunks.map(_.translate(heapleatTranslator, ctx))) + } + implicit val assertionTranslator: IrisTranslator[Assertion, IAssertion] = (f, ctx) => { + assert(ctx.isDefined) + IAssertion(f.phi.translate(phiTranslator, ctx), f.sigma.translate(sigmaTranslator, ctx)) + } + implicit val goalToFunSpecTranslator: IrisTranslator[Goal, IFunSpec] = (g, ctx) => { + assert(ctx.isDefined) + val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) + val quantifiedValues = g.programVars.map( + v => v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)) + ) + val specUniversal = g.universals.map(_.translate.translate(progVarToSpecVar)).toSeq ++ quantifiedValues + val specExistential = g.existentials.map(_.translate.translate(progVarToSpecVar)).toSeq + val pre = g.pre.translate(assertionTranslator, ctx) + val post = g.post.translate(assertionTranslator, ctx) + IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar), x._2)), specUniversal, specExistential, pre, post) + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala deleted file mode 100644 index 031ec26fb..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramContext.scala +++ /dev/null @@ -1,6 +0,0 @@ -package org.tygus.suslik.certification.targets.iris.translation - -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr -import org.tygus.suslik.certification.traversal.Evaluator.ClientContext - -case class ProgramContext() extends ClientContext[HExpr] \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala index 12d5b37cb..74263d59a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala @@ -4,6 +4,6 @@ import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr import org.tygus.suslik.certification.traversal.StackEvaluator -object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramContext] { +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, TranslationContext] { val translator = ProgramTranslator } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala index b3e6490ec..adcd0d88c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -3,18 +3,15 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset} import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Evaluator.Deferreds import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result -import scala.collection.immutable.Queue - /** * Extract a HeapLang program directly from the SSL proof. */ -object ProgramTranslator extends Translator[SuslikProofStep, HExpr, ProgramContext] { +object ProgramTranslator extends Translator[SuslikProofStep, HExpr, TranslationContext] { - override def translate(step: SuslikProofStep, ctx: ProgramContext): Translator.Result[HExpr, ProgramContext] = { + override def translate(step: SuslikProofStep, ctx: TranslationContext): Translator.Result[HExpr, TranslationContext] = { val withNoDeferred = (None, ctx) step match { case SuslikProofStep.Open(_, _, _, selectors) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala index c4b7f335d..edaa832ba 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala @@ -2,8 +2,8 @@ package org.tygus.suslik.certification.targets.iris.translation object TranslatableOps { implicit class Translatable[A](value: A) { - def translate[B](implicit translator: IrisTranslator[A,B]): B = { - translator.translate(value) + def translate[B](implicit translator: IrisTranslator[A,B], ctx: Option[TranslationContext] = None): B = { + translator.translate(value, ctx) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index e89382a06..ef157166d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -7,6 +7,7 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator._ object Translation { @@ -15,9 +16,18 @@ object Translation { def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): IrisCertificate = { val suslikTree = SuslikProofStep.of_certtree(node) - val progTree = ProgramEvaluator.run(suslikTree, ProgramContext()) - val funDef = HFunDef(proc.name, proc.formals.map(_.translate), progTree) + val params = proc.formals.map(_.translate) - IrisCertificate(proc.name, funDef) + // We have this "dummy" value to generate progToSpec for the actual context, ctx + val pre_ctx = Some(TranslationContext(node.goal.gamma, List().toMap)) + val progToSpec = params.map(p => (p, p.translate(progVarToSpecQuantifiedValue, pre_ctx))) + + val ctx = TranslationContext(node.goal.gamma, progToSpec.toMap) + val progTree = ProgramEvaluator.run(suslikTree, ctx) + + val funDef = HFunDef(proc.name, params, progTree) + val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) + + IrisCertificate(proc.name, funDef, funSpec) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala new file mode 100644 index 000000000..d9f1f539b --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala @@ -0,0 +1,8 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HProgVar} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.logic.Gamma + +case class TranslationContext(gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar]) extends ClientContext[HExpr] \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index abd053e3f..dc381b62e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -16,7 +16,7 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} /** translates suslik proof terms to VST compatible proof terms */ -object ProofSpecTranslation { +object ProofSpecTranslation { def to_ssl_context(gamma: Map[String, VSTType]) : Gamma = { def to_ssl_type(ty: VSTType) : SSLType = ty match { From 585b390ab5d88069a5b214ccb0b0c1a1750c5004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Fri, 19 Feb 2021 16:29:01 +0800 Subject: [PATCH 114/211] WIP: generic predicate translation --- .../certification/targets/iris/Iris.scala | 4 + .../targets/iris/logic/Assertions.scala | 24 ++- .../iris/translation/IrisTranslator.scala | 39 +++-- .../targets/vst/logic/ProofTerms.scala | 24 +-- .../targets/vst/logic/VSTProofStep.scala | 6 +- .../translation/ProofSpecTranslation.scala | 153 ++++-------------- .../targets/vst/translation/Translation.scala | 1 + .../translation/GenericPredicate.scala | 38 +++++ .../translation/PredicateTranslation.scala | 122 ++++++++++++++ 9 files changed, 237 insertions(+), 174 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 88c407dd2..08f6ca81f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -6,6 +6,7 @@ import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException +import org.tygus.suslik.certification.targets.vst.translation.ProofSpecTranslation object Iris extends CertificationTarget { val name: String = "HTT" @@ -15,6 +16,9 @@ object Iris extends CertificationTarget { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) val cert = Translation.translate(root, proc)(env) + val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList + + val simplified = SuslikProofStep.of_certtree(root) println(s"Suslik Proof:\n ${SuslikPrinter.pp(simplified)}") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index d66536c84..b3003fa6a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -1,21 +1,20 @@ package org.tygus.suslik.certification.targets.iris.logic -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HExpr, HLit, HOpEq, HOpLe, HOpLt, HProgVar} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HLit, HOpEq, HOpLe, HOpLt} import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HLocType, HType} -import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator -import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.PrettyPrinting -import org.tygus.suslik.language.Statements.Procedure object Assertions { /** Unlike HTT, which encodes programs in a shallow embedding, Iris has a deep embedding of programs. * As such, program expressions are NOT Iris assertions (phi != HExpr). We define a lifting of * program-level expressions to spec-level. */ - abstract class ISpecExpr extends PrettyPrinting - abstract class IQuantifiedVar extends ISpecExpr + abstract class IPureAssertion extends PrettyPrinting { + def ppAsPhi: String = pp + } + abstract class IQuantifiedVar extends IPureAssertion - case class ISpecLit(lit: HLit) extends ISpecExpr { + case class ISpecLit(lit: HLit) extends IPureAssertion { override def pp: String = lit.pp } @@ -27,23 +26,22 @@ object Assertions { override def pp: String = s"${name}" } - case class ISpecBinaryExpr(op: HBinOp, left: ISpecExpr, right: ISpecExpr) extends ISpecExpr { + case class ISpecBinaryExpr(op: HBinOp, left: IPureAssertion, right: IPureAssertion) extends IPureAssertion { override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" - def ppAsPhi: String = op match { + override def ppAsPhi: String = op match { case HOpLe | HOpLt | HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" case _ => ??? } } - abstract class IPureAssertion extends PrettyPrinting abstract class ISpatialAssertion extends PrettyPrinting case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { - override def pp: String = s"${conjuncts.map(_.pp).mkString(" ∧ ")}" + override def pp: String = s"${conjuncts.map(_.ppAsPhi).mkString(" ∧ ")}" } - case class IPointsTo(loc: ISpecExpr, value: ISpecExpr) extends ISpatialAssertion { + case class IPointsTo(loc: IPureAssertion, value: IPureAssertion) extends ISpatialAssertion { override def pp: String = s"${loc.pp} ↦ ${value.pp}" } @@ -53,7 +51,7 @@ object Assertions { case class IAssertion(phi: IPureAssertion, sigma: ISpatialAssertion) extends PrettyPrinting { override def pp: String = { - val pure = if (phi.pp.isEmpty) "" else s"⌜${phi.pp}⌝ ∗ " + val pure = if (phi.ppAsPhi.isEmpty) "" else s"⌜${phi.ppAsPhi}⌝ ∗ " val whole = s"${pure}${sigma.pp}" if (whole.isEmpty) "True" else whole } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index fad77f416..04c402e60 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -1,14 +1,14 @@ package org.tygus.suslik.certification.targets.iris.translation -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HAllocN, HBinOp, HBinaryExpr, HCall, HExpr, HIfThenElse, HLet, HLitBool, HLitInt, HLitLoc, HLitUnit, HLoad, HOpEq, HOpLe, HOpLt, HOpMinus, HOpMultiply, HOpNot, HOpOffset, HOpPlus, HOpUnaryMinus, HProgVar, HStore, HUnOp, HUnaryExpr} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HIntType, HLocType, HType} -import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IAnd, IAssertion, IFunSpec, IHeap, IPointsTo, IPureAssertion, ISpatialAssertion, ISpecBinaryExpr, ISpecExpr, ISpecLit, ISpecQuantifiedValue, ISpecVar} +import org.tygus.suslik.certification.targets.iris.logic.Assertions._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable -import org.tygus.suslik.language.Expressions.{BinOp, BinaryExpr, BoolConst, Expr, IfThenElse, IntConst, LocConst, OpBoolEq, OpEq, OpLeq, OpLt, OpMinus, OpMultiply, OpNot, OpPlus, OpUnaryMinus, UnOp, UnaryExpr, Var} -import org.tygus.suslik.language.{IntType, LocType, SSLType} +import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} -import org.tygus.suslik.logic.{Heaplet, PFormula, PointsTo, SFormula} +import org.tygus.suslik.language.{IntType, LocType, SSLType} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} +import org.tygus.suslik.logic._ trait IrisTranslator[From, To] { def translate(value: From, ctx: Option[TranslationContext] = None): To @@ -81,21 +81,40 @@ object IrisTranslator { implicit val callTranslator: IrisTranslator[Call, HCall] = (stmt, _) => HCall(stmt.fun.translate, stmt.args.map(_.translate)) - implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, _) => IAnd(Seq()) + implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, ctx) => { + assert(ctx.isDefined) + IAnd(f.conjuncts.map(_.translate.translate(toSpecExpr, ctx)).toSeq) + } - implicit val toSpecExpr: IrisTranslator[HExpr, ISpecExpr] = (expr, ctx) => { + // TODO: remove duplication between toSpecExpr and toSpecVal + implicit val toSpecExpr: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { assert(ctx.isDefined) expr match { - // Get the quantified value if it is quantified, or just translate if ghost + // Get the quantified value if it is quantified case v: HProgVar => ctx.get.pts.getOrElse(v, v.translate(progVarToSpecVar)) + case l: HLit => ISpecLit(l) + case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx), right.translate(toSpecExpr, ctx)) + case _ => ??? + } + } + + implicit val toSpecVal: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { + assert(ctx.isDefined) + expr match { + // Get the quantified value if it is quantified + // If it's a ghost, it has a non-value type (e.g. Z), so we have to make it into a value + case v: HProgVar => ctx.get.pts.getOrElse(v, ISpecLit(new HLit(v.name))) + case l: HLit => ISpecLit(l) + case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecVal, ctx), right.translate(toSpecVal, ctx)) case _ => ??? } } + implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx) => { val base = f.loc.translate.translate(toSpecExpr, ctx) val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitLoc(f.offset))) else base - val value = f.value.translate.translate(toSpecExpr, ctx) + val value = f.value.translate.translate(toSpecVal, ctx) IPointsTo(loc, value) } @@ -103,6 +122,7 @@ object IrisTranslator { assert(ctx.isDefined) h match { case pt:PointsTo => pt.translate(pointsToTranslator, ctx) + case pa:SApp => IHeap(Seq()) case _ => ??? } } @@ -128,4 +148,5 @@ object IrisTranslator { val post = g.post.translate(assertionTranslator, ctx) IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar), x._2)), specUniversal, specExistential, pre, post) } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 97af7d54f..865edb6be 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf} import org.tygus.suslik.language.{Ident, PrettyPrinting} object ProofTerms { @@ -96,29 +97,6 @@ object ProofTerms { } } - /** - * Abstract constructors mapping cardinality constraints to - * termination measures in Coq - */ - trait CardConstructor extends PrettyPrinting { - def constructor_args: List[Ident] = - this match { - case CardNull => Nil - case CardOf(args) => args - } - } - - /** - * Null constructor of 0 cardinality - */ - case object CardNull extends CardConstructor {} - - /** Cardinality constructor of multiple components - * - * @param args the variables produced by unwrwapping this element - */ - case class CardOf(args: List[Ident]) extends CardConstructor {} - /** * Represents helper lemmas and operations that are required to make VST handle the predicate automatically **/ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 3c86a8cd4..20c499112 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.VSTType -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.CardConstructor +import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf} import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -33,8 +33,8 @@ object VSTProofStep { def branch_strings(children : List[ProofTree[VSTProofStep]]): String = { val branches = this.branches.zip(children).map({ case ((clause, selector, args), value) => (clause, selector, args, value)}) def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { - case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" - case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + case CardNull => s"${card_variable} = ${cons_name}" + case CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" } branches match { case Nil => "" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index e7ba06b32..1e32b2d64 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -7,8 +7,9 @@ import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDef import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCBinOp, ProofCBinaryExpr, ProofCBoolConst, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCIntConst, ProofCIntSetLiteral, ProofCNullval, ProofCOpAnd, ProofCOpBoolEq, ProofCOpDiff, ProofCOpImplication, ProofCOpIn, ProofCOpIntValEq, ProofCOpIntersect, ProofCOpLeq, ProofCOpLt, ProofCOpMinus, ProofCOpMultiply, ProofCOpNot, ProofCOpOr, ProofCOpPlus, ProofCOpPtrValEq, ProofCOpSetEq, ProofCOpSubset, ProofCOpUnaryMinus, ProofCOpUnion, ProofCOpZEq, ProofCUnOp, ProofCUnaryExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp, VSTHeaplet} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, CardNull, CardOf, FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException +import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, PredicateTranslation} import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} @@ -18,8 +19,8 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} /** translates suslik proof terms to VST compatible proof terms */ object ProofSpecTranslation { - def to_ssl_context(gamma: Map[String, VSTType]) : Gamma = { - def to_ssl_type(ty: VSTType) : SSLType = ty match { + def to_ssl_context(gamma: Map[String, VSTType]): Gamma = { + def to_ssl_type(ty: VSTType): SSLType = ty match { case cType: VSTCType => cType match { case Types.CoqPtrValType => LocType case Types.CoqIntValType => IntType @@ -28,16 +29,17 @@ object ProofSpecTranslation { case Types.CoqIntSetType => IntSetType case CoqCardType(pred_type) => CardType } - gamma.map({case (name, ty) => (Var(name), to_ssl_type(ty))}) + + gamma.map({ case (name, ty) => (Var(name), to_ssl_type(ty)) }) } - /** Translates a cardinality to a vst expression that can be passed around */ + /** Translates a cardinality to a vst expression that can be passed around */ def translate_cardinality(predicate: VSTPredicate, cardinality: CardConstructor): ProofCExpr = { ProofCCardinalityConstructor( predicate.name, predicate.constructor_name(cardinality), cardinality match { - case ProofTerms.CardNull => List() + case CardNull => List() case CardOf(args) => args.map(arg => ProofCVar(arg, CoqCardType(predicate.name))) } ) @@ -55,8 +57,9 @@ object ProofSpecTranslation { /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) * */ - def translate_expression(context: Map[Ident, VSTType])(expr: Expressions.Expr, target: Option[VSTType]=None): ProofCExpr = { + def translate_expression(context: Map[Ident, VSTType])(expr: Expressions.Expr, target: Option[VSTType] = None): ProofCExpr = { def type_expr(left_1: ProofCExpr): VSTType = left_1.type_expr + def translate_binop(op: Expressions.BinOp)(ty: VSTType): ProofCBinOp = { op match { case op: Expressions.RelOp => (op, ty) match { @@ -106,13 +109,13 @@ object ProofSpecTranslation { val left_expr = translate_expression(context)(left) val type_of_expr = type_expr(left_expr) val top: ProofCBinOp = translate_binop(op)(type_of_expr) - ProofCBinaryExpr(top, left_expr, translate_expression(context)(right, target=Some(type_of_expr))) + ProofCBinaryExpr(top, left_expr, translate_expression(context)(right, target = Some(type_of_expr))) case Expressions.IfThenElse(cond, left, right) => val left_expr = translate_expression(context)(left) val l_type_1 = type_expr(left_expr) ProofCIfThenElse( translate_expression(context)(cond), left_expr, - translate_expression(context)(right, target=Some(l_type_1)) + translate_expression(context)(right, target = Some(l_type_1)) ) } } @@ -185,7 +188,7 @@ object ProofSpecTranslation { val elems = o_array.map(_.get).toList CDataAt(loc_pos, elems) case None => - if(points_to.length != 1) { + if (points_to.length != 1) { throw TranslationException("found multiple points to information (i.e x :-> 1, (x + 1) :-> 2) for a variable without an associated block") } @@ -294,27 +297,6 @@ object ProofSpecTranslation { ), context) } - - /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map - * from cardinality name to constructors */ - def build_card_cons(card_conds: List[(String, String)]): Map[String, CardOf] = { - // to perform this translation, we first construct a mapping of relations - // where for every constraint of the form (a < b), we set map(b) = a :: map(b), - // thus the mapping for a variable contains the list of other variables that are - // constrainted to be immediately smaller than it - var child_map: Map[String, List[String]] = Map.empty - card_conds.foreach({ case (child, parent) => - child_map.get(parent) match { - case None => child_map = child_map.updated(parent, List(child)) - case Some(children) => child_map = child_map.updated(parent, child :: children) - } - }) - // the keys of the map now become variables that are destructured - // in the match case to produce the variables immediately below it - child_map.map({ case (str, strings) => (str, CardOf(strings)) }) - } - - /** translates suslik's inductive predicate into a format that is * accepted by VST * @@ -344,105 +326,24 @@ object ProofSpecTranslation { * and matching on this - taking the first clause if the input is `lseg_card0` and the * second clause if the input is `lseg_card1 a` (and recursing on `a` * - **/ + * */ def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { - - - // Determines whether a given variable is a cardinality constraint - // TODO: I've definitely seen some function elsewhere that already does this - def is_card(s: String): Boolean = s.startsWith("_") || s.contentEquals("self_card") - - // extracts a cardinality relation from an expression if it exists - def extract_card_constructor(expr: Expressions.Expr): Option[(String, String)] = { - expr match { - case Expressions.BinaryExpr(op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => - op match { - case op: Expressions.RelOp => op match { - case Expressions.OpLt => - Some((left, parent)) - case _ => None - } - case _ => None - } - case Expressions.OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) - if is_card(left) && is_card(parent) => - overloaded_op match { - case op: Expressions.BinOp => op match { - case op: Expressions.RelOp => op match { - case Expressions.OpLt => Some((left, parent)) - case _ => None - } - case _ => None - } - case Expressions.OpGt => Some((parent, left)) - case _ => None - } - case _ => None + class VSTPredicateTranslation extends PredicateTranslation[ProofCExpr, VSTHeaplet, VSTType] { + override def translatePredicateParamType(predName: String, ty: SSLType): VSTType = ty match { + case CardType => CoqCardType(predName) + case _ => translate_predicate_param_type(ty) } - } - // base context contains type information for every variable used in the - // predicate (even if it occurs at a top level or not) - val base_context: List[(Ident, VSTType)] = { - var (pred_name, gamma) = predicate match { - case InductivePredicate(name, params, clauses) => - val pred_name = name - val gamma = clauses.foldLeft(params.toMap)({ case (base_gamma, InductiveClause(selector, asn)) => - var gamma = selector.resolve(base_gamma, Some(BoolType)).getOrElse(base_gamma) ++ base_gamma - gamma = asn.phi.conjuncts.foldLeft(gamma)({ case (gamma, expr) => expr.resolve(gamma, Some(BoolType)).getOrElse(gamma) }) ++ base_gamma - - asn.sigma.resolve(gamma, env).getOrElse(gamma) ++ base_gamma - }) - (pred_name, gamma) - } - gamma.map({ case (Var(name), ty) => (name, ty match { - case CardType => CoqCardType(pred_name) - case _ => translate_predicate_param_type(ty) - }) - }).toList - } + override def translateExpression(context: Map[Ident, VSTType])(expr: Expressions.Expr): ProofCExpr = + translate_expression(context)(expr) - predicate match { - case InductivePredicate(name, raw_params, raw_clauses) => { - - val params: List[(String, VSTType)] = - raw_params.map({ case (Var(name), sType) => (name, translate_predicate_param_type(sType)) }) - val context: Map[Ident, VSTType] = (base_context ++ params).toMap - - - // separate clauses by cardinality constructors - // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a - // unique cardinality constraint - val clauses: Map[CardConstructor, VSTPredicateClause] = raw_clauses.map({ - case InductiveClause(selector, asn) => - // first, split the pure conditions in the predicate between those that - // encode cardinality constraints and those that don't - val (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => - extract_card_constructor(expr) match { - case value@Some(_) => (None, value) - case None => (Some(expr), None) - } - ).toList.unzip - - // translate the pure conditions into VST format - val select = translate_expression(context)(selector) - val conds = r_conds.flatten.map(v => translate_expression(context)(v)).toList - - // translate the spatial constraints - val spat_conds = translate_heaplets(context)(asn.sigma.chunks.toList) - - // Convert the cardinality constraints into an associated constructor - val card_conds = r_card_conds.flatten - card_conds match { - case card_conds@(::(_, _)) => - val card_cons: Map[String, CardConstructor] = build_card_cons(card_conds) - (card_cons("self_card"), VSTPredicateClause(select :: conds, spat_conds, card_cons)) - case Nil => (CardNull, VSTPredicateClause(select :: conds, spat_conds, Map.empty)) - } - }).toMap - VSTPredicate(name, params, base_context, clauses) - } + override def translateHeaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = + translate_heaplets(context)(heaplets) } + + val pred = new VSTPredicateTranslation().translatePredicate(env)(predicate) + val clauses = pred.clauses.map(c => (c._1, VSTPredicateClause(c._2.phi, c._2.sigma, c._2.subConstructor))) + VSTPredicate(pred.name, pred.params, pred.existentials, clauses) } } + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 8f25dbfd6..621a1077e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -37,6 +37,7 @@ object Translation { case IntType => (name, CoqIntValType) }}) val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) + println(spec.pp) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body) println(procedure.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala new file mode 100644 index 000000000..44f9b855a --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala @@ -0,0 +1,38 @@ +package org.tygus.suslik.certification.translation + +import org.tygus.suslik.language.{Ident, PrettyPrinting} + + +/** + * Abstract constructors mapping cardinality constraints to + * termination measures in Coq + */ +trait CardConstructor extends PrettyPrinting { + def constructor_args: List[Ident] = + this match { + case CardNull => Nil + case CardOf(args) => args + } +} + +/** + * Null constructor of 0 cardinality + */ +case object CardNull extends CardConstructor {} + +/** Cardinality constructor of multiple components + * + * @param args the variables produced by unwrwapping this element + */ +case class CardOf(args: List[Ident]) extends CardConstructor {} + + +case class GenericPredicateClause[Pure, Spatial](phi: List[Pure], + sigma: List[Spatial], + subConstructor: Map[String, CardConstructor]) + +case class GenericPredicate[Pure, Spatial, Type](name: Ident, + params: List[(String, Type)], + existentials: List[(String, Type)], + clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]]) + extends PrettyPrinting {} diff --git a/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala b/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala new file mode 100644 index 000000000..68586ce7e --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala @@ -0,0 +1,122 @@ +package org.tygus.suslik.certification.translation + +import org.tygus.suslik.language.Expressions._ +import org.tygus.suslik.language.{BoolType, Ident, SSLType} +import org.tygus.suslik.logic.{Environment, Heaplet, InductiveClause, InductivePredicate} + +abstract class PredicateTranslation[Pure, Spatial, Type] { + + def translatePredicateParamType(predName: String, t: SSLType): Type + + def translateExpression(context: Map[Ident, Type])(expr: Expr): Pure + + def translateHeaplets(context: Map[Ident, Type])(heaplets: List[Heaplet]): List[Spatial] + + def translatePredicate(env: Environment)(predicate: InductivePredicate): GenericPredicate[Pure, Spatial, Type] = { + // Determines whether a given variable is a cardinality constraint + def isCard(s: String): Boolean = s.startsWith("_") || s.contentEquals("self_card") + + // extracts a cardinality relation from an expression if it exists + def extractCardConstructor(expr: Expr): Option[(String, String)] = { + expr match { + case BinaryExpr(op, Var(left), Var(parent)) + if isCard(left) && isCard(parent) => + op match { + case op: RelOp => op match { + case OpLt => + Some((left, parent)) + case _ => None + } + case _ => None + } + case OverloadedBinaryExpr(overloaded_op, Var(left), Var(parent)) + if isCard(left) && isCard(parent) => + overloaded_op match { + case op: BinOp => op match { + case op: RelOp => op match { + case OpLt => Some((left, parent)) + case _ => None + } + case _ => None + } + case OpGt => Some((parent, left)) + case _ => None + } + case _ => None + } + } + + // base context contains type information for every variable used in the + // predicate (even if it occurs at a top level or not) + val (predName: Ident, baseContext: List[(Ident, Type)]) = { + val (predName, gamma) = predicate match { + case InductivePredicate(name, params, clauses) => + val gamma = clauses.foldLeft(params.toMap)({ case (baseGamma, InductiveClause(selector, asn)) => + var gamma = selector.resolve(baseGamma, Some(BoolType)).getOrElse(baseGamma) ++ baseGamma + gamma = asn.phi.conjuncts.foldLeft(gamma)({ case (gamma, expr) => expr.resolve(gamma, Some(BoolType)).getOrElse(gamma) }) ++ baseGamma + asn.sigma.resolve(gamma, env).getOrElse(gamma) ++ baseGamma + }) + (name, gamma) + } + (predName, gamma.map({ case (Var(name), ty) => (name, translatePredicateParamType(predName, ty)) }).toList) + } + + predicate match { + case InductivePredicate(name, raw_params, raw_clauses) => + val params: List[(String, Type)] = + raw_params.map({ case (Var(name), sType) => (name, translatePredicateParamType(predName, sType)) }) + val context: Map[Ident, Type] = (baseContext ++ params).toMap + + // separate clauses by cardinality constructors + // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a + // unique cardinality constraint + val clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]] = raw_clauses.map({ + case InductiveClause(selector, asn) => + // first, split the pure conditions in the predicate between those that + // encode cardinality constraints and those that don't + val (r_conds, r_card_conds) = asn.phi.conjuncts.map(expr => + extractCardConstructor(expr) match { + case value@Some(_) => (None, value) + case None => (Some(expr), None) + } + ).toList.unzip + + // translate the pure conditions + val select = translateExpression(context)(selector) + val conds = r_conds.flatten.map(v => translateExpression(context)(v)).toList + + // translate the spatial constraints + val spat_conds = translateHeaplets(context)(asn.sigma.chunks.toList) + + // Convert the cardinality constraints into an associated constructor + val card_conds = r_card_conds.flatten + card_conds match { + case card_conds@(::(_, _)) => + val card_cons: Map[String, CardConstructor] = buildCardCons(card_conds) + (card_cons("self_card"), GenericPredicateClause[Pure, Spatial](select :: conds, spat_conds, card_cons)) + case Nil => (CardNull, GenericPredicateClause[Pure, Spatial](select :: conds, spat_conds, Map.empty)) + } + }).toMap + GenericPredicate[Pure, Spatial, Type](name, params, baseContext, clauses) + } + } + + /** convert a list of cardinality relations (child, parent) (i.e child < parent) into a map + * from cardinality name to constructors */ + def buildCardCons(cardConds: List[(String, String)]): Map[String, CardOf] = { + // to perform this translation, we first construct a mapping of relations + // where for every constraint of the form (a < b), we set map(b) = a :: map(b), + // thus the mapping for a variable contains the list of other variables that are + // constrainted to be immediately smaller than it + var childMap: Map[String, List[String]] = Map.empty + cardConds.foreach({ case (child, parent) => + childMap.get(parent) match { + case None => childMap = childMap.updated(parent, List(child)) + case Some(children) => childMap = childMap.updated(parent, child :: children) + } + }) + // the keys of the map now become variables that are destructured + // in the match case to produce the variables immediately below it + childMap.map({ case (str, strings) => (str, CardOf(strings)) }) + } +} From 56579167322de6a9d8f45fc65d0e00e4ab9d4973 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 20 Feb 2021 13:28:05 +0800 Subject: [PATCH 115/211] handles openrules correctly --- .../targets/vst/logic/Expressions.scala | 1 + .../targets/vst/logic/Formulae.scala | 16 ++- .../targets/vst/logic/ProofTerms.scala | 13 ++- .../targets/vst/logic/VSTProofStep.scala | 34 ++++-- .../translation/ProofSpecTranslation.scala | 2 +- .../targets/vst/translation/Translation.scala | 1 + .../vst/translation/VSTProofTranslator.scala | 103 +++++++++++++++--- 7 files changed, 138 insertions(+), 32 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 6e3881f4b..d664ffe4f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -75,6 +75,7 @@ object Expressions { } case expr@ProofCBoolConst(_) => expr case expr@ProofCIntConst(_) => expr + case expr@ProofCNullval => expr case ProofCIntSetLiteral(elems) => ProofCIntSetLiteral(elems.map(_.rename(mapping))) case ProofCIfThenElse(cond, left, right) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index eb8e21ffb..f454c6fd0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,13 +1,27 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType, VSTType} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.PrettyPrinting object Formulae { /** abstract type categorizing all spatial formulae */ - trait VSTHeaplet extends PrettyPrinting + sealed trait VSTHeaplet extends PrettyPrinting { + def rename(renaming: Map[String, String]): VSTHeaplet = + this match { + case CDataAt(loc, elems) => CDataAt(loc.rename(renaming), elems.map(_.rename(renaming))) + case CSApp(pred, args, card) => CSApp(pred, args.map(_.rename(renaming)), card.rename(renaming)) + } + + def subst(mapping: Map[String, ProofCExpr]): VSTHeaplet = + this match { + case CDataAt(loc, elems) => CDataAt(loc.subst(mapping), elems.map(_.subst(mapping))) + case CSApp(pred, args, card) => CSApp(pred, args.map(_.subst(mapping)), card.subst(mapping)) + } + + } /** Spatial formulae diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 97af7d54f..314f88724 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -185,9 +185,20 @@ object ProofTerms { * * @param pure are the pure assertions * @param spatial are the spatial assertions - * @param sub_constructor are the subconstructors + * @param sub_constructor is a mapping that represents the pattern that matches this clause + * (it is encoded as a mapping to capture recursive matches - i.e (lseg_card_1 (lseg_card_1 vl as vl1)) + * Printing of this pattern is done by starting from the constructor mapped from `self_card`, and then + * recursively generating subpatterns if they are within the mapping as well. **/ case class VSTPredicateClause(pure: List[Expressions.ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) { + def rename(renaming: Map[String, String]) = + VSTPredicateClause( + pure.map(_.rename(renaming)), + spatial.map(_.rename(renaming)), + sub_constructor.map({ case (name, constr) => (renaming.getOrElse(name,name), constr) }) + ) + + val cardinality_param: String = "self_card" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 3c86a8cd4..0edc7732b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.VSTType -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.CardConstructor +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, VSTPredicate} import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -24,32 +24,44 @@ object VSTProofStep { override def pp: String = s"entailer!." } + /** + * Step capturing open rules on a predicate. + * @param card_variable primary variable tracking the cardinality of the predicate (i.e _alpha in lseg x s _alpha) + * @param predicate predicate being case analysed + * @param branches + */ case class ForwardIfConstructor( card_variable: String, - predicate_name: String, - branches: List[((Ident, CardConstructor, List[String]), Expressions.ProofCExpr, List[String])] + predicate: VSTPredicate, + branches: List[((CardConstructor, List[String]), Expressions.ProofCExpr, List[String])] ) extends VSTProofStep { def branch_strings(children : List[ProofTree[VSTProofStep]]): String = { - val branches = this.branches.zip(children).map({ case ((clause, selector, args), value) => (clause, selector, args, value)}) - def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { - case ProofTerms.CardNull => s"${card_variable} = ${cons_name}" - case ProofTerms.CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + val branches = this.branches.zip(children).map({ case ((clause, selector, args), child) => (clause, selector, args, child)}) + def constructor_prop(cons: CardConstructor): String = { + val cons_name = predicate.constructor_name(cons) + cons match { + case ProofTerms.CardNull => + s"${card_variable} = ${cons_name}" + case ProofTerms.CardOf(args) => + s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + } } + val predicate_name = predicate.name branches match { case Nil => "" case _ => "\n" ++ branches.map( - { case ((cons_name, cons, cons_args), expr, args, ls) => + { case ((cons, cons_args), expr, args, branch) => " - {\n" ++ - s"assert_PROP (${constructor_prop(cons_name, cons)}) as ssl_card_assert. { entailer!; ssl_dispatch_card. }\n" ++ + s"assert_PROP (${constructor_prop(cons)}) as ssl_card_assert. { entailer!; ssl_dispatch_card. }\n" ++ s"ssl_card ${predicate_name} ssl_card_assert ${cons_args.mkString(" ")}.\n" ++ s"assert_PROP (${expr.pp}). { entailer!. }\n" ++ (args match { case Nil => "" case args => s"Intros ${args.mkString(" ")}.\n" }) ++ - ProofTreePrinter.pp(ls) ++ + ProofTreePrinter.pp(branch) ++ "\n}" } ).mkString("\n") @@ -132,7 +144,7 @@ object VSTProofStep { } case class AssertPropSubst(variable: Ident, expr: Expressions.ProofCExpr) extends VSTProofStep { - override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp}) as ssl_var; try rewrite ssl_var. { entailer!. }" + override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp}) as ssl_var; try rewrite ssl_var in *. { entailer!. }" } case object Qed extends VSTProofStep { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 37a6b05c9..695547acf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -49,7 +49,7 @@ object ProofSpecTranslation { case IntType => CoqZType case LocType => CoqPtrValType case IntSetType => CoqIntSetType - case _ => throw TranslationException("Attempted to translate ssl type of invalid form into VST Type") + case _ => throw TranslationException(s"Attempted to translate ssl type ${lType.pp} of invalid form into VST Type") } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 8f25dbfd6..ec82ba9ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -40,6 +40,7 @@ object Translation { val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body) println(procedure.pp) + println(spec.pp) val pred_map = predicates.map(v => (v.name,v)).toMap val steps = translate_proof(base_proof)(VSTProofTranslator(spec), VSTClientContext.make_context(pred_map)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index b30862b44..506ea5529 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -1,15 +1,16 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.vst.Types.VSTType +import org.tygus.suslik.certification.targets.vst.Types +import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Entailer, Exists, Forward, ForwardEntailer, ForwardIf, Rename, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertPropSubst, Entailer, Exists, Forward, ForwardEntailer, ForwardIf, ForwardIfConstructor, Rename, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} -import org.tygus.suslik.language.Expressions.{Expr, Var} +import org.tygus.suslik.language.Expressions.{Expr, SubstVar, Var} import org.tygus.suslik.language.{Ident, SSLType} import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext import org.tygus.suslik.language.Statements.Load @@ -25,6 +26,10 @@ object VSTProofTranslator { variable_map: Map[String, ProofCExpr] ) extends ClientContext[VSTProofStep] { + def typing_context: Map[String, VSTType] = coq_context + + def find_predicate_with_name(pred: Ident): VSTPredicate = pred_map(pred) + def with_renaming(vl: (String, String)) : VSTClientContext = { val (from, to) = vl val renaming = Map(from -> to) @@ -37,25 +42,25 @@ object VSTProofTranslator { VSTClientContext(pred_map, new_coq_context, new_existential_context, variable_map) } - def with_existential_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { - val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + def with_existential_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { + val new_vars: Map[String, VSTType] = variables.toMap VSTClientContext(pred_map, coq_context, existential_context ++ new_vars, variable_map) } def resolve_existential(ident: Ident) : ProofCExpr = variable_map(ident) - def with_program_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { + def with_program_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { // Note: translate_predicate_param_type maps val (containing ints) -> Z, and val (containing ptrs) -> val. // This is valid because the ssl_open_context tactic called at the start of a proof in VST will unwrap any // variable x in the context where ssl_is_valid_int(x) is known into a term of type Z (preserving the name - i.e (x : Z)). // As such, both ghost and program variables will correctly have type Z in the coq context (as the precondition for // specifications asserts that their values are valid ints). - val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + val new_vars: Map[String, VSTType] = variables.toMap VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) } - def with_ghost_variables_of(variables: List[(String, SSLType)]): VSTClientContext = { - val new_vars: Map[String, VSTType] = variables.map({ case (name, ty) => (name, ProofSpecTranslation.translate_predicate_param_type(ty)) }).toMap + def with_ghost_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { + val new_vars: Map[String, VSTType] = variables.toMap VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) } @@ -83,16 +88,24 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl def with_no_op(context: VSTClientContext): Result = Result(List(), List((None, context))) - override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { + def unwrap_val_type(ty: VSTType) : VSTType = ty match { + case Types.CoqPtrValType => Types.CoqPtrValType + case Types.CoqIntValType => Types.CoqZType + case v => v + } + + override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { value match { /** Initialization */ case SuslikProofStep.Init(goal) => - val program_vars = goal.programVars.map(v => (v.name, goal.gamma(v))) - val ghost_vars = goal.universalGhosts.map(v => (v.name, goal.gamma(v))).toList - var ctx = clientContext with_program_variables_of program_vars + var ctx = clientContext + val program_vars = spec.c_params.toList.map {case (name, ty) => (name, unwrap_val_type(ty : VSTType))} + val ghost_vars = spec.formal_params.toList.map {case (name, ty) => (name, unwrap_val_type(ty))} + val existential_params = spec.existensial_params.toList + ctx = ctx with_program_variables_of program_vars ctx = ctx with_ghost_variables_of ghost_vars - val existential_params = goal.existentials.map(v => (v.name, goal.gamma(v))).toList ctx = ctx with_existential_variables_of existential_params + println(goal.pp) val existentials = spec.existensial_params val deferreds : Deferred = (ctx: VSTClientContext) => { val steps : List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList @@ -106,7 +119,9 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl Result(List(ForwardIf), List(cont, cont)) /** Call handling */ - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => ??? + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => + + ??? case SuslikProofStep.Call(subst, call) => ??? /** Proof Termination rules */ @@ -118,7 +133,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.Write(stmt) => Result(List(Forward), List(with_no_deferreds(clientContext))) case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - var ctx = clientContext with_renaming(ghost_from -> ghost_to) + val ctx = clientContext with_renaming (ghost_from -> ghost_to) val rename_step = Rename(ghost_from, ghost_to) val read_step = Forward Result(List(rename_step, read_step), List(with_no_deferreds(ctx))) @@ -129,21 +144,73 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.Pick(from, to_expr) => val new_context = clientContext with_mapping_between(from.name, to_expr) with_no_op(new_context) + case SuslikProofStep.PureSynthesis(is_final, assignments) => val new_context = assignments.foldLeft(clientContext)({case (ctx, (from, to_expr)) => ctx with_mapping_between(from.name, to_expr)}) with_no_op(new_context) + case SuslikProofStep.PickArg(from, to) => ??? + case SuslikProofStep.PickCard(from, to) => ??? - case SuslikProofStep.SubstL(from, to) => ??? + + case SuslikProofStep.SubstL(Var(from), to) => + // SubstL translated into `assert (from = to) as H; rewrite H in *` + // No changes to context + val from_type = clientContext.typing_context(from) + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) + val step = AssertPropSubst(from, to_expr) + Result(List(step), List((with_no_deferreds(clientContext)))) + case SuslikProofStep.SubstR(from, to) => ??? /** Predicate folding/unfolding */ case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? - case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => ??? + case SuslikProofStep.Open(pred, fresh_exists, fresh_params, selectors) => + // Open rules translated into forward_if. - { ... } - { ... } ... - { ... } + // each case branch intros two types of variables (and thus changes the coq context) + // - existential variables in the predicate - i.e lseg x s a = lseg_card_1 a' => exists ..., _ + // - cardinality variables within the + val existentials_sub = fresh_exists.map { case (var1, var2) => (var1.name, var2.name)} + val params_sub = fresh_params.map { case (var1 : Var, var2 : Var) => (var1.name, var2.name)} + val card_variable = pred.card.asInstanceOf[Var].name + val predicate_name = pred.pred + val predicate = clientContext.find_predicate_with_name(predicate_name) + val c_selectors = selectors.map(ProofSpecTranslation.translate_expression(clientContext.typing_context)(_)) + val clauses = predicate.clauses.toList.zip(c_selectors).map { + case ((constructor, body), expr) => + val existentials = predicate.constructor_existentials(constructor).map({ + case (name, ty) => ((existentials_sub(name), ty)) + }) + val constructor_arg_existentials = + constructor.constructor_args.map(existentials_sub(_)).map(v => (v, CoqCardType(predicate_name))) + val renamed_body = body.rename(existentials_sub).rename(params_sub) + (constructor, constructor_arg_existentials, existentials, renamed_body, expr) + } + + val branches = clauses.map { + case (constructor, cons_intro_variables, intro_variables, body, selector) => + val intro_names = intro_variables.map(_._1) + val cons_intro_names = cons_intro_variables.map(_._1) + ((constructor, cons_intro_names), selector, intro_names) + } + val step = ForwardIfConstructor( + card_variable, + predicate, + branches + ) + val child_rules = clauses.map { + case (_, constructor_intro_gamma, intro_gamma, _, _) => + var ctx = clientContext + ctx = ctx with_ghost_variables_of constructor_intro_gamma ++ intro_gamma + (no_deferreds, ctx) + } + Result(List(step), child_rules) /** Misc. facts */ case SuslikProofStep.NilNotLval(vars) => + // NilNotLval translated into `assert_prop (is_valid_pointer (var)) for var in vars` + // No changes to context val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) From ca300fdd196b3db10596b1545dce0a38ac464d68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 20 Feb 2021 16:59:36 +0800 Subject: [PATCH 116/211] Make predicate to fixpoint translation generic --- .../targets/iris/logic/Assertions.scala | 49 ++- .../targets/vst/logic/ProofTerms.scala | 359 +++++------------- .../translation/ProofSpecTranslation.scala | 19 +- .../targets/vst/translation/Translation.scala | 1 + .../translation/GenericPredicate.scala | 145 ++++++- .../translation/PredicateTranslation.scala | 24 +- 6 files changed, 302 insertions(+), 295 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index b3003fa6a..28db8fd93 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -1,19 +1,45 @@ package org.tygus.suslik.certification.targets.iris.logic -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinOp, HLit, HOpEq, HOpLe, HOpLt} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HLocType, HType} -import org.tygus.suslik.language.PrettyPrinting +import org.tygus.suslik.certification.translation.{CardConstructor, GenericPredicate, GenericPredicateClause} +import org.tygus.suslik.language.{Ident, PrettyPrinting} object Assertions { /** Unlike HTT, which encodes programs in a shallow embedding, Iris has a deep embedding of programs. - * As such, program expressions are NOT Iris assertions (phi != HExpr). We define a lifting of - * program-level expressions to spec-level. */ + * As such, program expressions are NOT Iris assertions (phi != HExpr). We define a lifting of + * program-level expressions to spec-level. */ abstract class IPureAssertion extends PrettyPrinting { def ppAsPhi: String = pp } + abstract class IQuantifiedVar extends IPureAssertion + abstract class ISpatialAssertion extends PrettyPrinting + + abstract class ISpecification extends PrettyPrinting + + case class IPredicate(override val name: Ident, + override val params: List[(Ident, HType)], + override val existentials: List[(Ident, HType)], + override val clauses: Map[CardConstructor, IPredicateClause] + ) + extends GenericPredicate[IPureAssertion, ISpatialAssertion, HType](name, params, existentials, clauses) { + + def ppPredicate = "(* PREDICATE NOT IMPLEMENTED *)" + + /** + * For a given clause of the predicate and its associated constructor, + * return the list of existential variables used in the body of the clause + * + * @param cons a constructor matching some clause of the predicate + * @param pclause the corresponding clause of the predicate + * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause + * */ + override def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[IPureAssertion, ISpatialAssertion]): List[(Ident, HType)] = List() + } + case class ISpecLit(lit: HLit) extends IPureAssertion { override def pp: String = lit.pp } @@ -35,8 +61,6 @@ object Assertions { } } - abstract class ISpatialAssertion extends PrettyPrinting - case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { override def pp: String = s"${conjuncts.map(_.ppAsPhi).mkString(" ∧ ")}" } @@ -57,8 +81,6 @@ object Assertions { } } - abstract class ISpecification extends PrettyPrinting - case class IFunSpec(fname: String, funArgs: Seq[(ISpecVar, HType)], specUniversal: Seq[IQuantifiedVar], @@ -69,13 +91,13 @@ object Assertions { override def pp: String = { // TODO: make this use the general translation mechanism - def getArgLitVal(v : ISpecVar, t: HType) : ISpecQuantifiedValue = - (v, t) match { + def getArgLitVal(v: ISpecVar, t: HType): ISpecQuantifiedValue = + (v, t) match { case (ISpecVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}") case (ISpecVar(name), _) => ISpecQuantifiedValue(s"v${name}") } - val var_at = (v:ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" + val var_at = (v: ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" val postExist = if (specExistential.nonEmpty) s"∃ ${specExistential.map(v => v.pp).mkString(" ")}, " @@ -92,4 +114,9 @@ object Assertions { } } + class IPredicateClause(pure: List[IPureAssertion], + spatial: List[ISpatialAssertion], + subConstructor: Map[String, CardConstructor]) + extends GenericPredicateClause[IPureAssertion, ISpatialAssertion](pure, spatial, subConstructor) + } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 865edb6be..37990d8cb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -3,13 +3,18 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf} +import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, GenericPredicate, GenericPredicateClause} import org.tygus.suslik.language.{Ident, PrettyPrinting} object ProofTerms { trait PureFormula extends PrettyPrinting + /** + * Represents helper lemmas and operations that are required to make VST handle the predicate automatically + * */ + trait VSTPredicateHelper extends PrettyPrinting + /** predicate encoding that C-parameter (of type val) is a valid pointer */ case class IsValidPointerOrNull(name: String) extends PureFormula { override def pp: String = @@ -23,7 +28,7 @@ object ProofTerms { } /** prop predicate encoding that a given propositional expression is true - **/ + * */ case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { s"${expr.pp}" @@ -37,7 +42,6 @@ object ProofTerms { } } - case class FormalCondition( pure_constraints: List[PureFormula], spatial_constraints: List[VSTHeaplet] @@ -62,12 +66,6 @@ object ProofTerms { postcondition: FormalCondition, ) extends PrettyPrinting { - def as_vst_type(var_type: VSTCType) = var_type match { - case CoqIntValType => "tint" - case CoqPtrValType => "(tptr (Tunion _sslval noattr))" - case _ => throw TranslationException(s"Attempt to convert invalid type ${var_type.pp} as a VST type term.") - } - def params: List[(Ident, VSTType)] = (c_params ++ formal_params).toList override def pp: String = { @@ -95,68 +93,12 @@ object ProofTerms { | SEP (${post_spatial_constraints.map(_.pp).mkString("; ")}). |""".stripMargin } - } - - /** - * Represents helper lemmas and operations that are required to make VST handle the predicate automatically - **/ - trait VSTPredicateHelper extends PrettyPrinting - - object VSTPredicateHelper { - - case class ValidPointer(predicate: String, args: List[String], ptr: String) extends VSTPredicateHelper { - def lemma_name: String = s"${predicate}_${ptr}_valid_pointerP" - - override def pp: String = - s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. Admitted." - } - - case class HintResolve(lemma_name: String, hint_db: String) extends VSTPredicateHelper { - override def pp: String = - s"Hint Resolve ${lemma_name} : ${hint_db}." - } - - case class LocalFacts(predicate: VSTPredicate) extends VSTPredicateHelper { - - def lemma_name: String = s"${predicate.name}_local_factsP" - - override def pp: String = { - - def constructor_to_equality_term(vl: String, cons: CardConstructor) = - if (cons.constructor_args.isEmpty) { - s"${vl} = ${predicate.constructor_name(cons)}" - } else { - s"exists ${cons.constructor_args.mkString(" ")}, ${vl} = ${predicate.constructor_name(cons)} ${cons.constructor_args.mkString(" ")}" - } - - /** Converts a predicate clause into a clause that mutually exclusively matches the clause - * - * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause - **/ - def predicate_to_determininant_term(clause: VSTPredicateClause): String = - clause.pure.head.pp - - /** * - * Converts a predicate clause into a corresponding fact. - * - * NOTE: !!!ASSUMPTION!!! We assume that the first clause of the vst predicate is mutually exclusive - i.e - * if the first clause holds, then no other clause can hold. - */ - def clause_fact(cardConstructor: CardConstructor, predicate: VSTPredicateClause): String = - s"((${predicate_to_determininant_term(predicate)}) -> (${constructor_to_equality_term(predicate.cardinality_param, cardConstructor)}))" - - - s"""Lemma ${lemma_name} ${predicate.formal_params.mkString(" ")} : - | ${predicate.name} ${predicate.formal_params.mkString(" ")}|-- !!(${ - ( - predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ - predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) - ).mkString("/\\") - }). Proof. Admitted.""".stripMargin - } + def as_vst_type(var_type: VSTCType) = var_type match { + case CoqIntValType => "tint" + case CoqPtrValType => "(tptr (Tunion _sslval noattr))" + case _ => throw TranslationException(s"Attempt to convert invalid type ${var_type.pp} as a VST type term.") } - } /** represents a clause of the VST predicate, @@ -164,60 +106,11 @@ object ProofTerms { * @param pure are the pure assertions * @param spatial are the spatial assertions * @param sub_constructor are the subconstructors - **/ - case class VSTPredicateClause(pure: List[Expressions.ProofCExpr], spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) { - - val cardinality_param: String = "self_card" - - /** - * @return the selector for the clause - */ - def selector: Expressions.ProofCExpr = pure.head - - /** finds existential variables in the expression using args */ - def find_existentials_args(args: Set[String]): List[(Ident, VSTType)] = { - def expr_existential: Expressions.ProofCExpr => List[(Ident, VSTType)] = { - case Expressions.ProofCVar(name, typ) => if (!args.contains(name)) List((name, typ)) else Nil - case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value) => Nil - case Expressions.ProofCIntSetLiteral(elems) => elems.flatMap(expr_existential) - case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) - case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) - case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) - } - - def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTType)] = { - case Formulae.CSApp(pred, args, card) => - expr_existential(card) ::: args.flatMap(expr_existential).toList - } - - pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) - } - - /** finds existential variables in the expression using args */ - def find_existentials(existentials: Map[Ident, VSTType]): List[(Ident, VSTType)] = { - def expr_existential: Expressions.ProofCExpr => List[(Ident, VSTType)] = { - case Expressions.ProofCVar(name, _) => existentials.get(name).map(typ => (name, typ)).toList - case Expressions.ProofCBoolConst(value) => Nil - case Expressions.ProofCIntConst(value) => Nil - case Expressions.ProofCIntSetLiteral(elems) => elems.flatMap(expr_existential) - case Expressions.ProofCIfThenElse(cond, left, right) => List(cond, left, right).flatMap(expr_existential) - case Expressions.ProofCBinaryExpr(op, left, right) => List(left, right).flatMap(expr_existential) - case Expressions.ProofCUnaryExpr(op, e) => expr_existential(e) - } - - def spatial_expr_existential: VSTHeaplet => List[(Ident, VSTType)] = { - case Formulae.CSApp(pred, args, card) => - expr_existential(card) ::: args.flatMap(expr_existential).toList - case Formulae.CDataAt(loc, elems) => - (expr_existential(loc) ++ elems.flatMap(elem => expr_existential(elem))) - } - - pure.flatMap(expr_existential) ++ spatial.flatMap(spatial_expr_existential) - } - - } - + * */ + case class VSTPredicateClause(override val pure: List[Expressions.ProofCExpr], + override val spatial: List[VSTHeaplet], + sub_constructor: Map[String, CardConstructor]) + extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) /** * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker * @@ -229,36 +122,19 @@ object ProofTerms { * @param name is the name of the predicate * @param params is the list of arguments to the predicate * @param clauses is the mapping from cardinality constructors to clauses - **/ - case class VSTPredicate( - name: Ident, params: List[(String, VSTType)], - existentials: List[(String, VSTType)], - clauses: Map[CardConstructor, VSTPredicateClause]) - extends PrettyPrinting { - - /** - * Returns the constructor of the predicate with n arguments - * - * @param n number of arguments - */ - def constructor_by_arg(n: Int): CardConstructor = - clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n }).get._1 - - - /** - * @param selector a expression corresponding to a selector of the predicate - * @return cardinality and clause matched by predicate - */ - def apply(selector: Expressions.ProofCExpr): (CardConstructor, VSTPredicateClause) = - clauses.find({ case (_, clause) => clause.selector.equals(selector) }).get - + * */ + case class VSTPredicate(override val name: Ident, + override val params: List[(String, VSTType)], + override val existentials: List[(String, VSTType)], + override val clauses: Map[CardConstructor, VSTPredicateClause]) + extends GenericPredicate[Expressions.ProofCExpr, VSTHeaplet, VSTType](name, params, existentials, clauses) { /** returns any helper lemmas that need to be constructed for the helper */ def get_helpers: List[VSTPredicateHelper] = { val local_facts = VSTPredicateHelper.LocalFacts(this) params.flatMap({ case (param, CoqPtrValType) => - val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formal_params, param) + val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formalParams, param) List( valid_lemma, VSTPredicateHelper.HintResolve(valid_lemma.lemma_name, "valid_pointer") ) @@ -269,44 +145,36 @@ object ProofTerms { ) } - - /** returns the existential variables introduced by a constructor invokation */ - def constructor_existentials(constructor: CardConstructor): List[(Ident, VSTType)] = { - val param_map = params.toMap - val existential_map = existentials.toMap.filterKeys(key => !param_map.contains(key)) - val predicate = clauses(constructor) - // clauses(constructor).find_existentials(existential_map) - find_existentials(constructor)(predicate) + /** pretty print the constructor */ + def ppPredicate: String = { + val predicate_definition = + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductiveName}) : mpred := match self_card with + ${ + clauses.map({ case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => + s"| | ${constructorName(constructor)} ${ + expandArgs(sub_constructor)(constructor.constructor_args) + } => ${ + val clause_existentials: List[(String, VSTType)] = findExistentials(constructor)(pclause) + val str = clause_existentials.map({ case (name, ty) => s"| EX ${name} : ${ty.pp}," }).mkString("\n") + clause_existentials match { + case Nil => "" + case ::(_, _) => "\n" + str + "\n" + } + } ${ + (pure.map(v => s"!!${v.pp}") + ++ + List((spatial match { + case Nil => List("emp") + case v => v.map(_.pp) + }).mkString(" * "))).mkString(" && ") + }" + }).mkString("\n") + } + |end. + |""".stripMargin + s"${predicate_definition}" } - /** returns all instances of constructors and the bindings they expose */ - def constructors: List[CardConstructor] = - clauses.flatMap({ case (constructor, VSTPredicateClause(_, _, sub_constructor)) => - constructor :: sub_constructor.toList.map({ case (_, constructor) => constructor }) - }).toList - - /** returns all the constructors used in the base match, assumed to be a superset - * of the constructors used elsewhere - * - * Note: to see how there might be constructors elsewhere, suppose we had a cardinality - * constraint of the form: - * - * - `a < self_card` - * - * - `b < a` - * - * Then the corresponding match case would look like: - * - * `pred_1 ((pred_1 b) as a) => ... ` - * - * I don't this this actually happens in practice, so this is probably just a bit of - * over-engineering - **/ - def base_constructors: List[CardConstructor] = - clauses.map({ case (constructor, _) => - constructor - }).toList - /** * For a given clause of the predicate and its associated constructor, * return the list of existential variables used in the body of the clause @@ -314,8 +182,8 @@ object ProofTerms { * @param cons a constructor matching some clause of the predicate * @param pclause the corresponding clause of the predicate * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause - **/ - def find_existentials(cons: CardConstructor)(pclause: VSTPredicateClause): List[(String, VSTType)] = { + * */ + def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet]): List[(String, VSTType)] = { val param_map = params.toMap val exist_map: Map[String, VSTType] = existentials.toMap val card_map = cons.constructor_args @@ -351,92 +219,63 @@ object ProofTerms { .map((v: String) => (v, exist_map(v))).toList } } + } - /** returns the name of the associated cardinality datatype - * for this predicate */ - def inductive_name: String = s"${name}_card" + object VSTPredicateHelper { - /** Given a cardinality constructor, return the Coq name of the - * associated cardinality constructor */ - def constructor_name(constructor: CardConstructor): String = { - val count = constructor match { - case CardNull => 0 - case CardOf(args) => args.length - } - s"${inductive_name}_${count}" + case class ValidPointer(predicate: String, args: List[String], ptr: String) extends VSTPredicateHelper { + override def pp: String = + s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. Admitted." + + def lemma_name: String = s"${predicate}_${ptr}_valid_pointerP" } - /** formal parameters of the predicate. - * This is the sequence of identifiers that will need to be passed to the predicate to instantiate it. */ - def formal_params: List[String] = params.map({ case (a, _) => a }) ++ List("self_card") + case class HintResolve(lemma_name: String, hint_db: String) extends VSTPredicateHelper { + override def pp: String = + s"Hint Resolve ${lemma_name} : ${hint_db}." + } - /** pretty print the constructor */ - override def pp: String = { - val constructor_map = base_constructors.map({ - case CardNull => (0, CardNull) - case v@CardOf(args) => (args.length, v) - }).toMap - - def pp_constructor(constructor: CardConstructor) = { - constructor match { - case CardNull => s"${constructor_name(constructor)} : ${inductive_name}" - case CardOf(args) => - s"${constructor_name(constructor)} : ${(args ++ List(inductive_name)).map(_ => inductive_name).mkString(" -> ")}" - } - } + case class LocalFacts(predicate: VSTPredicate) extends VSTPredicateHelper { - val inductive_definition = { - s"""Inductive ${inductive_name} : Set := - ${ - constructor_map.map({ case (_, constructor) => - s"| | ${pp_constructor(constructor)}" - }).mkString("\n") - }. - | - |""".stripMargin - } + override def pp: String = { - // This function expands the arguments of a constructor and - // creates recursive pattern matches if necassary - i.e - // - // S ((S b) as a) => ..... - def expand_args(sub_constructor: Map[String, CardConstructor])(idents: List[Ident]): String = { - idents.map(arg => - sub_constructor.get(arg) match { - case Some(constructor) => - s"(${constructor_name(constructor)} ${expand_args(sub_constructor)(constructor.constructor_args)} as ${arg})" - case None => arg + def constructor_to_equality_term(vl: String, cons: CardConstructor) = + if (cons.constructor_args.isEmpty) { + s"${vl} = ${predicate.constructorName(cons)}" + } else { + s"exists ${cons.constructor_args.mkString(" ")}, ${vl} = ${predicate.constructorName(cons)} ${cons.constructor_args.mkString(" ")}" } - ).mkString(" ") + + /** Converts a predicate clause into a clause that mutually exclusively matches the clause + * + * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause + * */ + def predicate_to_determininant_term(clause: VSTPredicateClause): String = + clause.pure.head.pp + + /** * + * Converts a predicate clause into a corresponding fact. + * + * NOTE: !!!ASSUMPTION!!! We assume that the first clause of the vst predicate is mutually exclusive - i.e + * if the first clause holds, then no other clause can hold. + */ + def clause_fact(cardConstructor: CardConstructor, predicate: VSTPredicateClause): String = + s"((${predicate_to_determininant_term(predicate)}) -> (${constructor_to_equality_term(predicate.cardinalityParam, cardConstructor)}))" + + + s"""Lemma ${lemma_name} ${predicate.formalParams.mkString(" ")} : + | ${predicate.name} ${predicate.formalParams.mkString(" ")}|-- !!(${ + ( + predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ + predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) + ).mkString("/\\") + }). Proof. Admitted.""".stripMargin } - val predicate_definition = - s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductive_name}) : mpred := match self_card with - ${ - clauses.map({ case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => - s"| | ${constructor_name(constructor)} ${ - expand_args(sub_constructor)(constructor.constructor_args) - } => ${ - val clause_existentials: List[(String, VSTType)] = find_existentials(constructor)(pclause) - val str = clause_existentials.map({ case (name, ty) => s"| EX ${name} : ${ty.pp}," }).mkString("\n") - clause_existentials match { - case Nil => "" - case ::(_, _) => "\n" + str + "\n" - } - } ${ - (pure.map(v => s"!!${v.pp}") - ++ - List((spatial match { - case Nil => List("emp") - case v => v.map(_.pp) - }).mkString(" * "))).mkString(" && ") - }" - }).mkString("\n") - } - |end. - |""".stripMargin - s"${inductive_definition}${predicate_definition}" + def lemma_name: String = s"${predicate.name}_local_factsP" + } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 1e32b2d64..2e0990a42 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -9,7 +9,7 @@ import org.tygus.suslik.certification.targets.vst.logic.Formulae.{CDataAt, CSApp import org.tygus.suslik.certification.targets.vst.logic.ProofTerms import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalCondition, FormalSpecification, IsTrueProp, IsValidInt, IsValidPointerOrNull, VSTPredicate, VSTPredicateClause} import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException -import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, PredicateTranslation} +import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, GenericPredicateClause, PredicateTranslation} import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.{BoolType, CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.{Block, Environment, FunSpec, Gamma, Heaplet, InductiveClause, InductivePredicate, PointsTo, PredicateEnv, SApp} @@ -37,7 +37,7 @@ object ProofSpecTranslation { def translate_cardinality(predicate: VSTPredicate, cardinality: CardConstructor): ProofCExpr = { ProofCCardinalityConstructor( predicate.name, - predicate.constructor_name(cardinality), + predicate.constructorName(cardinality), cardinality match { case CardNull => List() case CardOf(args) => args.map(arg => ProofCVar(arg, CoqCardType(predicate.name))) @@ -328,7 +328,7 @@ object ProofSpecTranslation { * * */ def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { - class VSTPredicateTranslation extends PredicateTranslation[ProofCExpr, VSTHeaplet, VSTType] { + class VSTPredicateTranslation extends PredicateTranslation[ProofCExpr, VSTHeaplet, VSTType, VSTPredicateClause, VSTPredicate] { override def translatePredicateParamType(predName: String, ty: SSLType): VSTType = ty match { case CardType => CoqCardType(predName) case _ => translate_predicate_param_type(ty) @@ -339,11 +339,16 @@ object ProofSpecTranslation { override def translateHeaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = translate_heaplets(context)(heaplets) - } - val pred = new VSTPredicateTranslation().translatePredicate(env)(predicate) - val clauses = pred.clauses.map(c => (c._1, VSTPredicateClause(c._2.phi, c._2.sigma, c._2.subConstructor))) - VSTPredicate(pred.name, pred.params, pred.existentials, clauses) + override def constructClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], subConstructor: Map[String, CardConstructor]): VSTPredicateClause = { + VSTPredicateClause(pure, spatial, subConstructor) + } + + override def constructPred(name: Ident, params: List[(Ident, VSTType)], existentials: List[(Ident, VSTType)], clauses: Map[CardConstructor, VSTPredicateClause]): VSTPredicate = { + VSTPredicate(name, params, existentials, clauses) + } + } + new VSTPredicateTranslation().translatePredicate(env)(predicate) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 621a1077e..bacb7052c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -32,6 +32,7 @@ object Translation { def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val base_proof = SuslikProofStep.of_certtree(root) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList + predicates.foreach(p => println(p.pp)) val params = proc.formals.map({case (Var(name), ty) => ty match { case LocType => (name, CoqPtrValType) case IntType => (name, CoqIntValType) diff --git a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala index 44f9b855a..7e4538a18 100644 --- a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala +++ b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala @@ -27,12 +27,139 @@ case object CardNull extends CardConstructor {} case class CardOf(args: List[Ident]) extends CardConstructor {} -case class GenericPredicateClause[Pure, Spatial](phi: List[Pure], - sigma: List[Spatial], - subConstructor: Map[String, CardConstructor]) - -case class GenericPredicate[Pure, Spatial, Type](name: Ident, - params: List[(String, Type)], - existentials: List[(String, Type)], - clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]]) - extends PrettyPrinting {} +abstract class GenericPredicateClause[Pure, Spatial](val pure: List[Pure], + val spatial: List[Spatial], + val subConstructor: Map[String, CardConstructor]) { + val cardinalityParam: String = "self_card" + + def selector: Pure = pure.head +} + +abstract class GenericPredicate[Pure, Spatial, Type](val name: Ident, + val params: List[(Ident, Type)], + val existentials: List[(Ident, Type)], + val clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]]) + extends PrettyPrinting { + + // When extending GenericPredicate, you should implement these methods + def ppPredicate: String + + /** + * For a given clause of the predicate and its associated constructor, + * return the list of existential variables used in the body of the clause + * + * @param cons a constructor matching some clause of the predicate + * @param pclause the corresponding clause of the predicate + * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause + * */ + def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[Pure, Spatial]): List[(Ident, Type)] + + /** formal parameters of the predicate. + * This is the sequence of identifiers that will need to be passed to the predicate to instantiate it. */ + def formalParams: List[Ident] = params.map({ case (a, _) => a }) ++ List("self_card") + + /** + * Returns the constructor of the predicate with n arguments + * + * @param n number of arguments + */ + def constructorByArg(n: Int): CardConstructor = + clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n }).get._1 + + /** returns all instances of constructors and the bindings they expose */ + def constructors: List[CardConstructor] = + clauses.flatMap({ case (constructor, clause) => + constructor :: (clause.subConstructor).toList.map({ case (_, constructor) => constructor }) + }).toList + + /** + * @param selector a expression corresponding to a selector of the predicate + * @return cardinality and clause matched by predicate + */ + def apply(selector: Pure): (CardConstructor, GenericPredicateClause[Pure, Spatial]) = + clauses.find({ case (_, clause) => clause.selector.equals(selector) }).get + + // This function expands the arguments of a constructor and + // creates recursive pattern matches if necassary - i.e + // + // S ((S b) as a) => ..... + def expandArgs(sub_constructor: Map[String, CardConstructor])(idents: List[Ident]): String = { + idents.map(arg => + sub_constructor.get(arg) match { + case Some(constructor) => + s"(${constructorName(constructor)} ${expandArgs(sub_constructor)(constructor.constructor_args)} as ${arg})" + case None => arg + } + ).mkString(" ") + } + + /** returns the existential variables introduced by a constructor invokation */ + def constructorExistentials(constructor: CardConstructor): List[(Ident, Type)] = { + val predicate = clauses(constructor) + findExistentials(constructor)(predicate) + } + + override def pp: String = s"${ppInductive}${ppPredicate}" + + /** pretty print the constructor */ + def ppInductive: String = { + val constructor_map = baseConstructors.map({ + case CardNull => (0, CardNull) + case v@CardOf(args) => (args.length, v) + }).toMap + + def pp_constructor(constructor: CardConstructor) = { + constructor match { + case CardNull => s"${constructorName(constructor)} : ${inductiveName}" + case CardOf(args) => + s"${constructorName(constructor)} : ${(args ++ List(inductiveName)).map(_ => inductiveName).mkString(" -> ")}" + } + } + + val inductive_definition = { + s"""Inductive ${inductiveName} : Set := + ${ + constructor_map.map({ case (_, constructor) => + s"| | ${pp_constructor(constructor)}" + }).mkString("\n") + }. + | + |""".stripMargin + } + + s"${inductive_definition}" + } + + def constructorName(constructor: CardConstructor): String = { + val count = constructor match { + case CardNull => 0 + case CardOf(args) => args.length + } + s"${inductiveName}_${count}" + } + + def inductiveName: String = s"${name}_card" + + /** returns all the constructors used in the base match, assumed to be a superset + * of the constructors used elsewhere + * + * Note: to see how there might be constructors elsewhere, suppose we had a cardinality + * constraint of the form: + * + * - `a < self_card` + * + * - `b < a` + * + * Then the corresponding match case would look like: + * + * `pred_1 ((pred_1 b) as a) => ... ` + * + * I don't this this actually happens in practice, so this is probably just a bit of + * over-engineering + * */ + def baseConstructors: List[CardConstructor] = + clauses.map({ case (constructor, _) => + constructor + }).toList + +} diff --git a/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala b/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala index 68586ce7e..09961a25f 100644 --- a/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/translation/PredicateTranslation.scala @@ -4,7 +4,15 @@ import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.{BoolType, Ident, SSLType} import org.tygus.suslik.logic.{Environment, Heaplet, InductiveClause, InductivePredicate} -abstract class PredicateTranslation[Pure, Spatial, Type] { +abstract class PredicateTranslation[Pure, Spatial, Type, + Clause <: GenericPredicateClause[Pure, Spatial], + Pred <: GenericPredicate[Pure, Spatial, Type]] { + + def constructClause(pure: List[Pure], spatial: List[Spatial], subConstructor: Map[String, CardConstructor]) : Clause + + def constructPred(name: Ident, params: List[(Ident, Type)], + existentials: List[(Ident, Type)], + clauses: Map[CardConstructor, Clause]) : Pred def translatePredicateParamType(predName: String, t: SSLType): Type @@ -12,7 +20,7 @@ abstract class PredicateTranslation[Pure, Spatial, Type] { def translateHeaplets(context: Map[Ident, Type])(heaplets: List[Heaplet]): List[Spatial] - def translatePredicate(env: Environment)(predicate: InductivePredicate): GenericPredicate[Pure, Spatial, Type] = { + def translatePredicate(env: Environment)(predicate: InductivePredicate): Pred = { // Determines whether a given variable is a cardinality constraint def isCard(s: String): Boolean = s.startsWith("_") || s.contentEquals("self_card") @@ -70,7 +78,7 @@ abstract class PredicateTranslation[Pure, Spatial, Type] { // separate clauses by cardinality constructors // NOTE: here we assume that cardinality constructors are unique - i.e each clause maps to a // unique cardinality constraint - val clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]] = raw_clauses.map({ + val clauses: Map[CardConstructor, Clause] = raw_clauses.map({ case InductiveClause(selector, asn) => // first, split the pure conditions in the predicate between those that // encode cardinality constraints and those that don't @@ -83,21 +91,21 @@ abstract class PredicateTranslation[Pure, Spatial, Type] { // translate the pure conditions val select = translateExpression(context)(selector) - val conds = r_conds.flatten.map(v => translateExpression(context)(v)).toList + val conds = r_conds.flatten.map(v => translateExpression(context)(v)) // translate the spatial constraints - val spat_conds = translateHeaplets(context)(asn.sigma.chunks.toList) + val spat_conds = translateHeaplets(context)(asn.sigma.chunks) // Convert the cardinality constraints into an associated constructor val card_conds = r_card_conds.flatten card_conds match { case card_conds@(::(_, _)) => val card_cons: Map[String, CardConstructor] = buildCardCons(card_conds) - (card_cons("self_card"), GenericPredicateClause[Pure, Spatial](select :: conds, spat_conds, card_cons)) - case Nil => (CardNull, GenericPredicateClause[Pure, Spatial](select :: conds, spat_conds, Map.empty)) + (card_cons("self_card"), constructClause(select :: conds, spat_conds, card_cons)) + case Nil => (CardNull, constructClause(select :: conds, spat_conds, Map.empty)) } }).toMap - GenericPredicate[Pure, Spatial, Type](name, params, baseContext, clauses) + constructPred(name, params, baseContext, clauses) } } From e63c603938baa5b6e89bea3a90d0ab7128bd8108 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 20 Feb 2021 23:18:09 +0800 Subject: [PATCH 117/211] Allow translator to return immediate steps for children --- .../htt/translation/ProgramTranslator.scala | 2 +- .../htt/translation/ProofContext.scala | 3 +-- .../htt/translation/ProofTranslator.scala | 20 +++++++++---------- .../iris/translation/ProgramTranslator.scala | 2 +- .../translation/VSTProgramTranslator.scala | 20 +++++++++---------- .../vst/translation/VSTProofTranslator.scala | 6 +++--- .../traversal/BasicEvaluator.scala | 16 ++++++++------- .../traversal/StackEvaluator.scala | 18 ++++++++++------- .../certification/traversal/Translator.scala | 2 +- 9 files changed, 46 insertions(+), 43 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index 36e092716..b42505582 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -8,7 +8,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result object ProgramTranslator extends Translator[SuslikProofStep, CStatement, ProgramContext] { override def translate(value: SuslikProofStep, ctx: ProgramContext): Translator.Result[CStatement, ProgramContext] = { - val withNoDeferred = (None, ctx) + val withNoDeferred = (Nil, None, ctx) value match { case SuslikProofStep.Open(_, _, _, selectors) => val stmt = CIf(selectors.map(_.translate)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index cb28fe0c2..f9dfb535d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -15,8 +15,7 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, unfoldings: Map[CSApp, AppliedConstructor] = Map.empty, callGoal: Option[Proof.Goal] = None, nextCallId: Int = 0, - hints: ListBuffer[Hint] = new ListBuffer[Hint], - numSubgoals: Int = 0) + hints: ListBuffer[Hint] = new ListBuffer[Hint]) extends ClientContext[Proof.Step] { /** diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 31215cb65..c26f90e9b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -13,7 +13,7 @@ import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofContext] { - private def withNoDeferred(ctx: ProofContext): (Option[Deferred[Proof.Step, ProofContext]], ProofContext) = (None, ctx) + private def withNoDeferred(ctx: ProofContext): (List[Proof.Step], Option[Deferred[Proof.Step, ProofContext]], ProofContext) = (Nil, None, ctx) private def initPre(asn: CAssertion, uniqueName: String): List[Proof.Step] = { val phi = asn.phi @@ -96,7 +96,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val postEx = cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)).toMap val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) - Result(steps, List((Some(deferred), ctx1))) + Result(steps, List((Nil, Some(deferred), ctx1))) case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => val pre = f.pre.translate val post = f.post.translate @@ -108,7 +108,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont /** Control flow */ case SuslikProofStep.Branch(cond, _) => - val childContexts = List(ctx.copy(numSubgoals = ctx.numSubgoals + 1), ctx) + val childContexts = List(ctx, ctx) Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferred)) case SuslikProofStep.Open(sapp, freshExistentials, freshParamArgs, selectors) => val exSub = freshExistentials.translate @@ -121,22 +121,20 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont } val csapp = sapp.translate val cselectors = selectors.map(_.translate) - val branchSteps = clauses.flatMap { case (_, existentials, asn) => + val branchSteps = clauses.map { case (_, existentials, asn) => List( Proof.ElimExistential(existentials), Proof.ElimExistential(asn.heapVars) - ) ++ initPre(asn, csapp.uniqueName) ++ List(Proof.Shelve) + ) ++ initPre(asn, csapp.uniqueName) } - val parentGoalShelves = (1 to ctx.numSubgoals).map(_ => Proof.Shelve).toList - val steps = List(Proof.Open(cselectors, csapp)) ++ branchSteps ++ parentGoalShelves ++ List(Proof.Unshelve) + val steps = List(Proof.Open(cselectors, csapp)) val numClauses = pred.clauses.length val childCtxs = clauses.map { case (idx, _, asn) => val newApps = asn.sigma.apps.map(a => a -> a).toMap - val numSubgoals = numClauses - idx + ctx.numSubgoals - ctx.copy(appAliases = ctx.appAliases ++ newApps, numSubgoals = numSubgoals) + ctx.copy(appAliases = ctx.appAliases ++ newApps) } - Result(steps, childCtxs.map(withNoDeferred)) + Result(steps, branchSteps.zip(childCtxs).map { case (s, c) => (s, None, c) }) /** Program statements */ case SuslikProofStep.Read(ghostFrom, ghostTo, Load(to, _, from, offset)) => @@ -232,7 +230,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val unfoldings = ctx.unfoldings + (csapp -> constructor) val ctx1 = ctx.copy(appAliases = appAliases, unfoldings = unfoldings) - Result(List(), List((Some(deferred), ctx1))) + Result(List(), List((Nil, Some(deferred), ctx1))) /** Terminals */ case SuslikProofStep.Inconsistency(label) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala index adcd0d88c..49e4fdb77 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -12,7 +12,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result object ProgramTranslator extends Translator[SuslikProofStep, HExpr, TranslationContext] { override def translate(step: SuslikProofStep, ctx: TranslationContext): Translator.Result[HExpr, TranslationContext] = { - val withNoDeferred = (None, ctx) + val withNoDeferred = (Nil, None, ctx) step match { case SuslikProofStep.Open(_, _, _, selectors) => val stmt = HIf(selectors.map(_.translate)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index 245b71602..f4e2d9f12 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -39,14 +39,14 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS private val no_deferreds: Option[Deferred] = None private val no_ops : List[StatementStep] = List() - def single_child_result_of(a: List[StatementStep], b : (Option[Deferred], VSTProgramContext)) = Result(a, List(b)) + def single_child_result_of(a: List[StatementStep], b : (List[StatementStep], Option[Deferred], VSTProgramContext)) = Result(a, List(b)) def no_child_result_of(a: List[StatementStep]) : Result[StatementStep, VSTProgramContext] = Result(a, List()) def with_no_op(implicit context: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = - Result(List(), List((None, context))) + Result(List(), List((Nil, None, context))) def to_heap(typing_context: Map[String, VSTCType], chunks: List[Heaplet]) = { @@ -109,16 +109,16 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS case _ => None }}) val new_context = VSTProgramContext(new_typing_context) - single_child_result_of(no_ops, (no_deferreds, new_context)) + single_child_result_of(no_ops, (Nil, no_deferreds, new_context)) case SuslikProofStep.Open(pred, fresh_vars, sbst, selectors) => val ops = CElif(selectors.map(v => ProofSpecTranslation.translate_expression(ctx.typing_context)(v).asInstanceOf[CLangExpr])) val children = selectors.map(_ => { - (no_deferreds, ctx) + (Nil, no_deferreds, ctx) }) Result(List(ops), children) case SuslikProofStep.Branch(cond, bLabel) => val ops = CIf(ProofSpecTranslation.translate_expression(ctx.typing_context)(cond).asInstanceOf[CLangExpr]) - val children = List((no_deferreds, ctx), (no_deferreds, ctx)) + val children = List((Nil, no_deferreds, ctx), (Nil, no_deferreds, ctx)) Result(List(ops), children) case SuslikProofStep.Write(Store(Var(to), offset, base_expr)) => val expr = ProofSpecTranslation.translate_expression(ctx.typing_context)(base_expr).asInstanceOf[CLangExpr] @@ -126,24 +126,24 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS case Types.CoqPtrValType => CWriteLoc(to, expr, offset) case Types.CoqIntValType => CWriteInt(to, expr, offset) } - single_child_result_of(List(op), (no_deferreds, ctx)) + single_child_result_of(List(op), (Nil, no_deferreds, ctx)) case SuslikProofStep.Read(from_var, to_var, Load(Var(to),tpe, Var(from), offset)) => val (op, variable_type) = tpe match { case IntType => (CLoadInt(to, from, offset), CoqIntValType) case LocType => (CLoadLoc(to, from, offset), CoqPtrValType) } val new_context = ctx with_new_variable(to, variable_type) - single_child_result_of(List(op), (no_deferreds, new_context)) + single_child_result_of(List(op), (Nil, no_deferreds, new_context)) case SuslikProofStep.Call(subst, Call(Var(fname), args, _)) => val exprs = args.map(ProofSpecTranslation.translate_expression(ctx.typing_context)(_).asInstanceOf[CLangExpr]) val op = CCall(fname, exprs) - single_child_result_of(List(op), (no_deferreds, ctx)) + single_child_result_of(List(op), (Nil, no_deferreds, ctx)) case SuslikProofStep.Free(Free(Var(name)), size) => val op = CFree(name) - single_child_result_of(List(op), (no_deferreds, ctx)) + single_child_result_of(List(op), (Nil, no_deferreds, ctx)) case SuslikProofStep.Malloc(Var(_), Var(_), Malloc(Var(to), tpe, sz)) => val new_context = ctx with_new_variable (to, translate_type(tpe)) - single_child_result_of(List(CMalloc(to, sz)), (no_deferreds, new_context)) + single_child_result_of(List(CMalloc(to, sz)), (Nil, no_deferreds, new_context)) case SuslikProofStep.Inconsistency(label) => no_child_result_of(List(CSkip)) case SuslikProofStep.EmpRule(label) => no_child_result_of(List(CSkip)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index b30862b44..33709b757 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -79,9 +79,9 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl type Result = Translator.Result[VSTProofStep, VSTClientContext] private val no_deferreds: Option[Deferred] = None - private def with_no_deferreds(ctx: VSTClientContext) : (Option[Deferred], VSTClientContext) = (no_deferreds, ctx) + private def with_no_deferreds(ctx: VSTClientContext) : (List[VSTProofStep], Option[Deferred], VSTClientContext) = (Nil, no_deferreds, ctx) - def with_no_op(context: VSTClientContext): Result = Result(List(), List((None, context))) + def with_no_op(context: VSTClientContext): Result = Result(List(), List((Nil, None, context))) override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { value match { @@ -98,7 +98,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val steps : List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList (steps ++ List(Entailer), ctx) } - Result(List(), List((Some(deferreds), ctx))) + Result(List(), List((Nil, Some(deferreds), ctx))) /** Branching rules */ case SuslikProofStep.Branch(cond, bLabel) => diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala index ed3847e1b..64f8d0c9d 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/BasicEvaluator.scala @@ -9,24 +9,26 @@ import org.tygus.suslik.certification.traversal.Step.{DestStep, SourceStep} */ abstract class BasicEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] { override def run(tree: ProofTree[S], initialClientContext: C): ProofTree[D] = { - def visit(tree: ProofTree[S], deferredsStack: DeferredsStack[D,C], clientContext: C): ProofTree[D] = { + def visit(tree: ProofTree[S], parentSteps: List[D], deferredsStack: DeferredsStack[D,C], clientContext: C): ProofTree[D] = { val res = tree.step.translate[D, C](clientContext) if (tree.children.length != res.childParams.length) { throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") } val action = tree.step.deferredsAction - val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) - val steps = res.steps ++ newSteps - val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => + val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._3)) + val steps = parentSteps ++ res.steps ++ newSteps + val childSteps = res.childParams.map(_._1) + val childDeferredsStacks = res.childParams.map { case (_, newDeferred, _) => action.updateStack(deferredsStack, newDeferred) } - val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList - val childResults = next.map(Function.tupled(visit)) + val childResults = tree.children.zip(childSteps).zip(childDeferredsStacks).zip(childClientContexts).map { + case (((a, b), c), d) => visit(a, b, c, d) + } foldStepsIntoTree(steps, childResults, tree.label) } - visit(tree, Nil, initialClientContext) + visit(tree, Nil, Nil, initialClientContext) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala index 366d60ab5..7ad901737 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/StackEvaluator.scala @@ -9,26 +9,30 @@ import org.tygus.suslik.certification.traversal.Step._ */ abstract class StackEvaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] extends Evaluator[S,D,C] with TreeVisitor { type Result = ProofTree[D] - type Item = (ProofTree[S], DeferredsStack[D,C], C) + type Item = (ProofTree[S], List[D], DeferredsStack[D,C], C) - def run(tree: ProofTree[S], initialClientContext: C): ProofTree[D] = visit((tree, Nil, initialClientContext)) + def run(tree: ProofTree[S], initialClientContext: C): ProofTree[D] = + visit((tree, Nil, Nil, initialClientContext)) def process(item: Item): (List[Result] => Result, List[Item]) = { - val (tree, deferredsStack, clientContext) = item + val (tree, parentSteps, deferredsStack, clientContext) = item val res = tree.step.translate[D,C](clientContext) if (tree.children.length != res.childParams.length) { throw EvaluatorException(s"step ${tree.step.pp} has ${tree.children.length} children but translation returned results for ${res.childParams.length} children") } val action = tree.step.deferredsAction - val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._2)) - val steps = res.steps ++ newSteps - val childDeferredsStacks = res.childParams.map { case (newDeferred, _) => + val (newSteps, childClientContexts) = action.handleDeferreds(deferredsStack, clientContext, res.childParams.map(_._3)) + val steps = parentSteps ++ res.steps ++ newSteps + val childSteps = res.childParams.map(_._1) + val childDeferredsStacks = res.childParams.map { case (_, newDeferred, _) => action.updateStack(deferredsStack, newDeferred) } val k = (results: List[Result]) => foldStepsIntoTree(steps, results, tree.label) - val next = (tree.children, childDeferredsStacks, childClientContexts).zipped.toList + val next = tree.children.zip(childSteps).zip(childDeferredsStacks).zip(childClientContexts).map { + case (((a, b), c), d) => (a, b, c, d) + } (k, next) } } diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala index 3f692d4cc..4f56bc48e 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala @@ -8,5 +8,5 @@ trait Translator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { } object Translator { - case class Result[D <: DestStep, C <: ClientContext[D]](steps: List[D], childParams: List[(Option[Deferred[D,C]], C)]) + case class Result[D <: DestStep, C <: ClientContext[D]](steps: List[D], childParams: List[(List[D], Option[Deferred[D,C]], C)]) } \ No newline at end of file From 69f68c9d3d4ba2ef3f8e67328c2fab48d7c360e8 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sun, 21 Feb 2021 18:44:02 +0800 Subject: [PATCH 118/211] Listfree now works with evaluator --- .../source/SuslikProofStep.scala | 14 +- .../targets/vst/logic/ProofTerms.scala | 37 +- .../targets/vst/logic/VSTProofStep.scala | 11 +- .../translation/ProofSpecTranslation.scala | 8 +- .../targets/vst/translation/Translation.scala | 12 +- .../vst/translation/VSTProofTranslator.scala | 424 ++++++++++++------ 6 files changed, 338 insertions(+), 168 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index b5247cad1..9cacd4d24 100644 --- a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -99,7 +99,19 @@ case class AbduceCall( gamma: Gamma ) extends SuslikProofStep { override def deferredsAction: DeferredsAction = DeferredsAction.PushLayer - override def pp: String = s"AbduceCall({${new_vars.mkString(",")}}, ${sanitize(f_pre.pp)}, ${sanitize(callePost.pp)}, ${sanitize(call.pp)}, {${freshSub.mkString(",")}});" + override def pp: String = { + val new_vars_str = new_vars.map({case(Var(name), ty) => s"${name} -> ${ty.pp}"}).mkString(",") + val fresh_sub_str = freshSub.map({case (Var(name), Var(to)) => s"${name} -> ${to}"}).mkString(",") + val fresh_to_actual_sub = freshToActual.map({case (Var(name), expr) => s"${name} -> ${expr.pp}"}).mkString(",") + s"AbduceCall(" + + s"new_vars: {${new_vars_str}}, " + + s"freshSub: {${fresh_sub_str}}," + + s"freshToActual: {${fresh_to_actual_sub}}, " + + s"pre: ${sanitize(f_pre.pp)}, " + + s"post: ${sanitize(callePost.pp)}, " + + s"call: ${sanitize(call.pp)}" + + s");" + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 314f88724..9c077692c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,24 +1,45 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types._ +import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.{Ident, PrettyPrinting} object ProofTerms { - trait PureFormula extends PrettyPrinting + sealed trait PureFormula extends PrettyPrinting { + def subst(subst: Map[String, ProofCExpr]) = this match { + case IsValidPointerOrNull(expr) => + IsValidPointerOrNull(expr.subst(subst)) + case IsValidInt(expr) => + IsValidInt(expr.subst(subst)) + case IsTrueProp(expr) => + IsTrueProp(expr.subst(subst)) + case IsTrue(expr) => + IsTrue(expr.subst(subst)) + } + def rename (renaming: Map[String, String]) = { + this match { + case IsValidPointerOrNull(expr) => + IsValidPointerOrNull(expr.rename(renaming)) + case IsValidInt(expr) => IsValidInt(expr.rename(renaming)) + case IsTrueProp(expr) => IsTrueProp(expr.rename(renaming)) + case IsTrue(expr) => IsTrueProp(expr.rename(renaming)) + } + } + } /** predicate encoding that C-parameter (of type val) is a valid pointer */ - case class IsValidPointerOrNull(name: String) extends PureFormula { + case class IsValidPointerOrNull(expr: ProofCExpr) extends PureFormula { override def pp: String = - s"is_pointer_or_null(${name})" + s"is_pointer_or_null(${expr.pp})" } /** predicate encoding that C-parameter (of type val) is a valid int */ - case class IsValidInt(name: String) extends PureFormula { + case class IsValidInt(expr: ProofCExpr) extends PureFormula { override def pp: String = - s"ssl_is_valid_int(${name})" + s"ssl_is_valid_int(${expr.pp})" } /** prop predicate encoding that a given propositional expression is true @@ -54,9 +75,9 @@ object ProofTerms { */ case class FormalSpecification( name: Ident, - c_params: Seq[(Ident, VSTCType)], - formal_params: Seq[(Ident, VSTType)], - existensial_params: Seq[(Ident, VSTType)], + c_params: List[(Ident, VSTCType)], + formal_params: List[(Ident, VSTType)], + existensial_params: List[(Ident, VSTType)], precondition: FormalCondition, postcondition: FormalCondition, ) extends PrettyPrinting { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 0edc7732b..ecb268f07 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,7 +1,8 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.VSTType -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, PureFormula, VSTPredicate} import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -123,8 +124,8 @@ object VSTProofStep { override def pp: String = s"assert_PROP (isptr ${variable}). { entailer!. }" } - case class ForwardCall(args: List[Ident]) extends VSTProofStep { - override def pp: String = s"forward_call (${args.mkString(", ")})." + case class ForwardCall(args: List[ProofCExpr]) extends VSTProofStep { + override def pp: String = s"forward_call (${args.map(_.pp).mkString(", ")})." } case class Rename(old_name: Ident, new_name: Ident) extends VSTProofStep { @@ -147,6 +148,10 @@ object VSTProofStep { override def pp: String = s"let ssl_var := fresh in assert_PROP(${variable} = ${expr.pp}) as ssl_var; try rewrite ssl_var in *. { entailer!. }" } + case class AssertProp(expr: PureFormula) extends VSTProofStep { + override def pp: String = s"assert_PROP(${expr.pp}). { entailer!. }" + } + case object Qed extends VSTProofStep { override def pp: String = "" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 695547acf..5ab0684a9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -264,13 +264,13 @@ object ProofSpecTranslation { goal.pre.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => cType match { - case CoqIntValType => Some(IsValidInt(ident)) - case CoqPtrValType => Some(IsValidPointerOrNull(ident)) + case CoqIntValType => Some(IsValidInt(ProofCVar(ident, cType))) + case CoqPtrValType => Some(IsValidPointerOrNull(ProofCVar(ident, cType))) case _ => None } }) ++ formal_params.flatMap({ case (ident, ty) => ty match { - case CoqPtrValType => Some(IsValidPointerOrNull(ident)) - case CoqIntValType => Some(IsValidInt(ident)) + case CoqPtrValType => Some(IsValidPointerOrNull(ProofCVar(ident, ty))) + case CoqIntValType => Some(IsValidInt(ProofCVar(ident, ty))) case _ => None } }) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index ec82ba9ea..fea2c16c7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -43,9 +43,15 @@ object Translation { println(spec.pp) val pred_map = predicates.map(v => (v.name,v)).toMap - val steps = translate_proof(base_proof)(VSTProofTranslator(spec), VSTClientContext.make_context(pred_map)) - - val proof = Proof(proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep]) + val spec_map = Map(proc.name -> spec) + val proof_translator = VSTProofTranslator(spec) + val steps = translate_proof(base_proof)(proof_translator, VSTClientContext.make_context(pred_map, spec_map)) + + val proof = Proof( + proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep], + uses_free = proof_translator.contains_free, + uses_malloc = proof_translator.contains_malloc + ) VSTCertificate(proc.f.name, procedure, proof) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 506ea5529..ce481cf48 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -4,50 +4,108 @@ import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCVar} -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertPropSubst, Entailer, Exists, Forward, ForwardEntailer, ForwardIf, ForwardIfConstructor, Rename, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Entailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, IntrosTuple, Rename, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, SubstVar, Var} -import org.tygus.suslik.language.{Ident, SSLType} -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.language.{Ident, SSLType, Statements} +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{PendingCall, VSTClientContext} import org.tygus.suslik.language.Statements.Load -import scala.collection.immutable.Queue - object VSTProofTranslator { + /** + * Represents a pending function call. + * + * @param function_name name of the function being called. + * @param args arguments to function call + * @param existential_params existential paramters in result of function call (will be introduced after calling function) + * @param pre_conditions pre-conditions to function + */ + case class PendingCall( + function_name: String, + args: List[(String, VSTType)], + existential_params: List[(String, VSTType)], + pre_conditions: List[PureFormula] + ) + + /** + * Represents the context needed to translate a proof into a VST proof + * + * @param pred_map mapping of predicate names to their definitions + * @param spec_map mapping of function names to their specifications + * @param coq_context tracks the coq context as the proof progresses + * @param existential_context tracks the existentials and types + * @param variable_map tracks values for existentials + * @param call function calls + */ case class VSTClientContext( pred_map: Map[String, VSTPredicate], + spec_map: Map[String, FormalSpecification], coq_context: Map[String, VSTType], existential_context: Map[String, VSTType], - variable_map: Map[String, ProofCExpr] + variable_map: Map[String, ProofCExpr], + accumulated_renaming: Map[String, String], + call: Option[PendingCall] ) extends ClientContext[VSTProofStep] { - def typing_context: Map[String, VSTType] = coq_context + def is_existential_variable(from: String): Boolean = { + existential_context.contains(from) + } + + def with_queued_call(suspended_call: PendingCall) = { + VSTClientContext(pred_map, spec_map, coq_context, existential_context, variable_map, accumulated_renaming, Some(suspended_call)) + } + + def unqueue_call: (PendingCall, VSTClientContext) = + (call.get, VSTClientContext(pred_map, spec_map, coq_context, existential_context, variable_map, accumulated_renaming, None)) + + def typing_context: Map[String, VSTType] = coq_context ++ existential_context def find_predicate_with_name(pred: Ident): VSTPredicate = pred_map(pred) - def with_renaming(vl: (String, String)) : VSTClientContext = { + def with_renaming(vl: (String, String)): VSTClientContext = { val (from, to) = vl val renaming = Map(from -> to) - def true_name(vl : String) = renaming.getOrElse(vl,vl) - val new_coq_context = coq_context.map({case (name, ty) => (true_name(name), ty)}) - val new_existential_context = existential_context.map({case (name, ty) => (true_name(name), ty)}) + + def true_name(vl: String) = renaming.getOrElse(vl, vl) + + val new_coq_context = coq_context.map({ case (name, ty) => (true_name(name), ty) }) + val new_existential_context = existential_context.map({ case (name, ty) => (true_name(name), ty) }) val new_variable_map = variable_map.map { case (name, expr) => (true_name(name), expr.rename(renaming)) } - VSTClientContext(pred_map, new_coq_context, new_existential_context, variable_map) + val new_accumulated_renaming = accumulated_renaming.map({ case (from, to) => (from, true_name(to)) }) ++ renaming + val new_call = call match { + case None => None + case Some(PendingCall(f_name, args, existential_params, pre_conditions)) => + Some(PendingCall( + f_name, args.map(v => (true_name(v._1), v._2)), + existential_params.map(v => (true_name(v._1), v._2)), + pre_conditions.map(_.rename(renaming)))) + } + VSTClientContext(pred_map, spec_map, new_coq_context, new_existential_context, new_variable_map, new_accumulated_renaming, new_call) } + def renaming: Map[String, String] = accumulated_renaming + def with_existential_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, coq_context, existential_context ++ new_vars, variable_map) + VSTClientContext(pred_map, spec_map, coq_context, existential_context ++ new_vars, variable_map, accumulated_renaming, call) } - def resolve_existential(ident: Ident) : ProofCExpr = variable_map(ident) + def without_existentials_of(variables: List[String]): VSTClientContext = { + val new_existential_context = existential_context.filterNot(v => variables.contains(v._1)) + VSTClientContext(pred_map, spec_map, coq_context, new_existential_context, variable_map, accumulated_renaming, call) + } + + def resolve_existential(ident: Ident): ProofCExpr = { + val true_name = accumulated_renaming.getOrElse(ident, ident) + variable_map(true_name) + } def with_program_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { // Note: translate_predicate_param_type maps val (containing ints) -> Z, and val (containing ptrs) -> val. @@ -56,172 +114,240 @@ object VSTProofTranslator { // As such, both ghost and program variables will correctly have type Z in the coq context (as the precondition for // specifications asserts that their values are valid ints). val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) + VSTClientContext(pred_map, spec_map, coq_context ++ new_vars, existential_context, variable_map, accumulated_renaming, call) } def with_ghost_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, coq_context ++ new_vars, existential_context, variable_map) + VSTClientContext(pred_map, spec_map, coq_context ++ new_vars, existential_context, variable_map, accumulated_renaming, call) } def with_mapping_between(from: String, to: Expr): VSTClientContext = { val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) val subst = Map(from -> to_expr) - val new_variable_map = variable_map.map({case (name, vl) => (name, vl.subst(subst))}) ++ subst - VSTClientContext(pred_map, coq_context, existential_context, new_variable_map) + val new_variable_map = variable_map.map({ case (name, vl) => (name, vl.subst(subst)) }) ++ subst + val new_call = call.map({ + case PendingCall(f_name, args, eparams, preconds) => + PendingCall(f_name, args, eparams, preconds.map(v => v.subst(subst))) + }) + VSTClientContext(pred_map, spec_map, coq_context, existential_context, new_variable_map, accumulated_renaming, new_call) } } object VSTClientContext { - def make_context(pred_map: Map[String, VSTPredicate]): VSTClientContext = - VSTClientContext(pred_map, Map(), Map(), Map()) + def make_context(pred_map: Map[String, VSTPredicate], spec_map: Map[String, FormalSpecification]): VSTClientContext = + VSTClientContext(pred_map, spec_map, Map(), Map(), Map(), Map(), None) } + } + case class VSTProofTranslator(spec: FormalSpecification) extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { - type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] - type Result = Translator.Result[VSTProofStep, VSTClientContext] - private val no_deferreds: Option[Deferred] = None + type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] + type Result = Translator.Result[VSTProofStep, VSTClientContext] - private def with_no_deferreds(ctx: VSTClientContext) : (Option[Deferred], VSTClientContext) = (no_deferreds, ctx) + var contains_free: Boolean = false + var contains_malloc: Boolean = false - def with_no_op(context: VSTClientContext): Result = Result(List(), List((None, context))) + private val no_deferreds: Option[Deferred] = None - def unwrap_val_type(ty: VSTType) : VSTType = ty match { + private def with_no_deferreds(ctx: VSTClientContext): (Option[Deferred], VSTClientContext) = (no_deferreds, ctx) + + def with_no_op(context: VSTClientContext): Result = Result(List(), List((None, context))) + + def unwrap_val_type(ty: VSTType): VSTType = ty match { case Types.CoqPtrValType => Types.CoqPtrValType case Types.CoqIntValType => Types.CoqZType case v => v } override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { - value match { - /** Initialization */ - case SuslikProofStep.Init(goal) => - var ctx = clientContext - val program_vars = spec.c_params.toList.map {case (name, ty) => (name, unwrap_val_type(ty : VSTType))} - val ghost_vars = spec.formal_params.toList.map {case (name, ty) => (name, unwrap_val_type(ty))} - val existential_params = spec.existensial_params.toList - ctx = ctx with_program_variables_of program_vars - ctx = ctx with_ghost_variables_of ghost_vars - ctx = ctx with_existential_variables_of existential_params - println(goal.pp) - val existentials = spec.existensial_params - val deferreds : Deferred = (ctx: VSTClientContext) => { - val steps : List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList - (steps ++ List(Entailer), ctx) - } - Result(List(), List((Some(deferreds), ctx))) - - /** Branching rules */ - case SuslikProofStep.Branch(cond, bLabel) => - val cont = with_no_deferreds(clientContext) - Result(List(ForwardIf), List(cont, cont)) - - /** Call handling */ - case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => - - ??? - case SuslikProofStep.Call(subst, call) => ??? - - /** Proof Termination rules */ - case SuslikProofStep.EmpRule(label) => - - Result(List(ForwardEntailer), List()) - case SuslikProofStep.Inconsistency(label) => ??? - - case SuslikProofStep.Write(stmt) => - Result(List(Forward), List(with_no_deferreds(clientContext))) - case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - val ctx = clientContext with_renaming (ghost_from -> ghost_to) - val rename_step = Rename(ghost_from, ghost_to) - val read_step = Forward - Result(List(rename_step, read_step), List(with_no_deferreds(ctx))) - case SuslikProofStep.Free(stmt, size) => ??? - case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => ??? - - /** Substitutions and renaming */ - case SuslikProofStep.Pick(from, to_expr) => - val new_context = clientContext with_mapping_between(from.name, to_expr) - with_no_op(new_context) - - case SuslikProofStep.PureSynthesis(is_final, assignments) => - val new_context = - assignments.foldLeft(clientContext)({case (ctx, (from, to_expr)) => ctx with_mapping_between(from.name, to_expr)}) - with_no_op(new_context) - - case SuslikProofStep.PickArg(from, to) => ??? - - case SuslikProofStep.PickCard(from, to) => ??? - - case SuslikProofStep.SubstL(Var(from), to) => - // SubstL translated into `assert (from = to) as H; rewrite H in *` - // No changes to context - val from_type = clientContext.typing_context(from) - val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) - val step = AssertPropSubst(from, to_expr) - Result(List(step), List((with_no_deferreds(clientContext)))) - - case SuslikProofStep.SubstR(from, to) => ??? - - /** Predicate folding/unfolding */ - case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? - case SuslikProofStep.Open(pred, fresh_exists, fresh_params, selectors) => - // Open rules translated into forward_if. - { ... } - { ... } ... - { ... } - // each case branch intros two types of variables (and thus changes the coq context) - // - existential variables in the predicate - i.e lseg x s a = lseg_card_1 a' => exists ..., _ - // - cardinality variables within the - val existentials_sub = fresh_exists.map { case (var1, var2) => (var1.name, var2.name)} - val params_sub = fresh_params.map { case (var1 : Var, var2 : Var) => (var1.name, var2.name)} - val card_variable = pred.card.asInstanceOf[Var].name - val predicate_name = pred.pred - val predicate = clientContext.find_predicate_with_name(predicate_name) - val c_selectors = selectors.map(ProofSpecTranslation.translate_expression(clientContext.typing_context)(_)) - val clauses = predicate.clauses.toList.zip(c_selectors).map { - case ((constructor, body), expr) => + value match { + /** Initialization */ + case SuslikProofStep.Init(goal) => + var ctx = clientContext + val program_vars = spec.c_params.toList.map { case (name, ty) => (name, unwrap_val_type(ty: VSTType)) } + val ghost_vars = spec.formal_params.toList.map { case (name, ty) => (name, unwrap_val_type(ty)) } + ctx = ctx with_program_variables_of program_vars + ctx = ctx with_ghost_variables_of ghost_vars + // ctx = ctx with_existential_variables_of existential_params + println(goal.pp) + val existentials = spec.existensial_params + val deferreds: Deferred = (ctx: VSTClientContext) => { + val steps: List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList + (steps ++ (if (steps.nonEmpty) { + List(Entailer) + } else { + List() + }), ctx) + } + Result(List(), List((Some(deferreds), ctx))) + + /** Branching rules */ + case SuslikProofStep.Branch(cond, bLabel) => + val cont = with_no_deferreds(clientContext) + Result(List(ForwardIf), List(cont, cont)) + + /** Call handling */ + case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => + // Abduce call emits no immediate steps, but pus + var ctx = clientContext + val fun_spec = ctx.spec_map(f.name) + val renaming = freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) } + val function_params = fun_spec.params.map((pair) => (renaming(pair._1), pair._2)) + val function_result_existentials = fun_spec.existensial_params.map { case (name, ty) => (renaming(name), ty) } + val function_preconds = fun_spec.precondition.pure_constraints.map(_.rename(renaming)) + val suspended_call = PendingCall(f.name, function_params, function_result_existentials, function_preconds) + ctx = ctx with_existential_variables_of function_params + ctx = ctx with_queued_call suspended_call + // Use deferred to remove (translation-only) existentials produced by abduce-call + val deferred = (ctx: VSTClientContext) => { + (List(), ctx without_existentials_of function_params.map(_._1)) + } + Result(List(), List((Some(deferred), ctx))) + case SuslikProofStep.Call(subst, call) => + // retreive call from ctx + var (call, ctx) = clientContext.unqueue_call + // resolve arguments to function call + val call_args = call.args map (v => ctx.resolve_existential(v._1)) + val steps = { + // assert and solve pure precondition for call + val pre_steps = call.pre_conditions.map(v => AssertProp(v)) + pre_steps ++ List( + // run call rule + ForwardCall(call_args), + ) ++ + // intro existentials + (if (call.existential_params.nonEmpty) { + List(IntrosTuple(call.existential_params)) + } else { + List() + }) + } + // update context to have existential variables produced by call + ctx = ctx with_program_variables_of call.existential_params + Result(steps, List((None, ctx))) + + /** Proof Termination rules */ + case SuslikProofStep.EmpRule(label) => + + Result(List(ForwardEntailer), List()) + case SuslikProofStep.Inconsistency(label) => ??? + + case SuslikProofStep.Write(stmt) => + Result(List(Forward), List(with_no_deferreds(clientContext))) + case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => + val ctx = clientContext with_renaming (ghost_from -> ghost_to) + val rename_step = Rename(ghost_from, ghost_to) + val read_step = Forward + Result(List(rename_step, read_step), List(with_no_deferreds(ctx))) + case SuslikProofStep.Free(Statements.Free(Var(v)), size) => + this.contains_free = true + val step = Free(v, size) + Result(List(step), List(with_no_deferreds(clientContext))) + case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => + this.contains_malloc = true + ??? + + /** Substitutions and renaming */ + case SuslikProofStep.Pick(from, to_expr) => + val new_context = clientContext with_mapping_between(from.name, to_expr) + with_no_op(new_context) + + case SuslikProofStep.PureSynthesis(is_final, assignments) => + val new_context = + assignments.foldLeft(clientContext)({ case (ctx, (from, to_expr)) => ctx with_mapping_between(from.name, to_expr) }) + with_no_op(new_context) + + case SuslikProofStep.PickArg(from, to) => ??? + + case SuslikProofStep.PickCard(from, to) => ??? + + case SuslikProofStep.SubstL(Var(from), to) => + // SubstL translated into `assert (from = to) as H; rewrite H in *` + // No changes to context + val ctx = clientContext with_mapping_between(from, to) + val from_type = clientContext.typing_context(from) + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) + val step = AssertPropSubst(from, to_expr) + Result(List(step), List((with_no_deferreds(ctx)))) + + case SuslikProofStep.SubstR(Var(from), to) => + val from_type = clientContext.typing_context(from) + val ctx = clientContext with_mapping_between(from, to) + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) + val deferred = if (ctx.is_existential_variable(from)) { + None + } else { + Some((ctx: VSTClientContext) => { + val renaming = ctx.renaming + val updated_from = renaming.getOrElse(from, from) + val updated_to_expr = to_expr.rename(renaming) + val step = AssertPropSubst(updated_from, updated_to_expr) + (List(step), ctx) + }) + } + Result(List(), List((deferred, ctx))) + + /** Predicate folding/unfolding */ + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.Open(pred, fresh_exists, fresh_params, selectors) => + // Open rules translated into forward_if. - { ... } - { ... } ... - { ... } + // each case branch intros two types of variables (and thus changes the coq context) + // - existential variables in the predicate - i.e lseg x s a = lseg_card_1 a' => exists ..., _ + // - cardinality variables within the + val existentials_sub = fresh_exists.map { case (var1, var2) => (var1.name, var2.name) } + val params_sub = fresh_params.map { case (var1: Var, var2: Var) => (var1.name, var2.name) } + val card_variable = pred.card.asInstanceOf[Var].name + val predicate_name = pred.pred + val predicate = clientContext.find_predicate_with_name(predicate_name) + val c_selectors = selectors.map(ProofSpecTranslation.translate_expression(clientContext.typing_context)(_)) + val clauses = predicate.clauses.toList.zip(c_selectors).map { + case ((constructor, body), expr) => val existentials = predicate.constructor_existentials(constructor).map({ case (name, ty) => ((existentials_sub(name), ty)) }) val constructor_arg_existentials = constructor.constructor_args.map(existentials_sub(_)).map(v => (v, CoqCardType(predicate_name))) val renamed_body = body.rename(existentials_sub).rename(params_sub) - (constructor, constructor_arg_existentials, existentials, renamed_body, expr) - } - - val branches = clauses.map { - case (constructor, cons_intro_variables, intro_variables, body, selector) => - val intro_names = intro_variables.map(_._1) - val cons_intro_names = cons_intro_variables.map(_._1) - ((constructor, cons_intro_names), selector, intro_names) - } - val step = ForwardIfConstructor( - card_variable, - predicate, - branches - ) - val child_rules = clauses.map { - case (_, constructor_intro_gamma, intro_gamma, _, _) => - var ctx = clientContext - ctx = ctx with_ghost_variables_of constructor_intro_gamma ++ intro_gamma - (no_deferreds, ctx) - } - Result(List(step), child_rules) - - /** Misc. facts */ - case SuslikProofStep.NilNotLval(vars) => - // NilNotLval translated into `assert_prop (is_valid_pointer (var)) for var in vars` - // No changes to context - val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) - Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) - - /** Ignored rules */ - case SuslikProofStep.CheckPost(_, _) - | SuslikProofStep.WeakenPre(_) - | SuslikProofStep.HeapUnify(_) - | SuslikProofStep.HeapUnifyPointer(_, _) - | SuslikProofStep.StarPartial(_, _) - | SuslikProofStep.FrameUnfold(_, _) => - with_no_op(clientContext) - } + (constructor, constructor_arg_existentials, existentials, renamed_body, expr) + } + + val branches = clauses.map { + case (constructor, cons_intro_variables, intro_variables, body, selector) => + val intro_names = intro_variables.map(_._1) + val cons_intro_names = cons_intro_variables.map(_._1) + ((constructor, cons_intro_names), selector, intro_names) + } + val step = ForwardIfConstructor( + card_variable, + predicate, + branches + ) + val child_rules = clauses.map { + case (_, constructor_intro_gamma, intro_gamma, _, _) => + var ctx = clientContext + ctx = ctx with_ghost_variables_of constructor_intro_gamma ++ intro_gamma + (no_deferreds, ctx) + } + Result(List(step), child_rules) + + /** Misc. facts */ + case SuslikProofStep.NilNotLval(vars) => + // NilNotLval translated into `assert_prop (is_valid_pointer (var)) for var in vars` + // No changes to context + val valid_pointer_rules = vars.map({ case Var(name) => ValidPointer(name) }) + Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) + + /** Ignored rules */ + case SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.WeakenPre(_) + | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyPointer(_, _) + | SuslikProofStep.StarPartial(_, _) + | SuslikProofStep.FrameUnfold(_, _) => + with_no_op(clientContext) } } +} From f7997ef1de43f29fa0925a12ccc13b7d29bbeaa6 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sun, 21 Feb 2021 19:05:16 +0800 Subject: [PATCH 119/211] Listfree now works with evaluator --- .../certification/targets/vst/logic/ProofTerms.scala | 9 ++++++++- .../targets/vst/logic/VSTProofStep.scala | 11 +++++++---- .../targets/vst/translation/VSTProofTranslator.scala | 10 +++++----- .../certification/translation/GenericPredicate.scala | 6 ++++++ 4 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 59fc6964e..25ca7540f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -131,7 +131,14 @@ object ProofTerms { case class VSTPredicateClause(override val pure: List[Expressions.ProofCExpr], override val spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) - extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) + extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) { + def rename(renaming: Map[String, String]) = + VSTPredicateClause( + pure.map(_.rename(renaming)), + spatial.map(_.rename(renaming)), + sub_constructor.map({case (name, constructor) => (renaming.getOrElse(name,name), constructor.rename(renaming))}) + ) + } /** * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker * diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 9ec5e3640..51499e083 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.VSTType import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr -import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{CardConstructor, PureFormula, VSTPredicate} +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{PureFormula, VSTPredicate} import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf} import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} @@ -40,9 +40,12 @@ object VSTProofStep { def branch_strings(children : List[ProofTree[VSTProofStep]]): String = { val branches = this.branches.zip(children).map({ case ((clause, selector, args), value) => (clause, selector, args, value)}) - def constructor_prop(cons_name: Ident, cons: CardConstructor): String = cons match { - case CardNull => s"${card_variable} = ${cons_name}" - case CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + def constructor_prop(cons: CardConstructor): String = { + val cons_name = predicate.constructorName(cons) + cons match { + case CardNull => s"${card_variable} = ${cons_name}" + case CardOf(args) => s"exists ${args.mkString(" ")}, ${card_variable} = ${cons_name} ${args.mkString(" ")}" + } } val predicate_name = predicate.name branches match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index d93cc96f6..7c0b1e168 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -205,7 +205,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val deferred = (ctx: VSTClientContext) => { (List(), ctx without_existentials_of function_params.map(_._1)) } - Result(List(), List((Some(deferred), ctx))) + Result(List(), List((List(), Some(deferred), ctx))) case SuslikProofStep.Call(subst, call) => // retreive call from ctx var (call, ctx) = clientContext.unqueue_call @@ -227,7 +227,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl } // update context to have existential variables produced by call ctx = ctx with_program_variables_of call.existential_params - Result(steps, List((None, ctx))) + Result(steps, List((List(), None, ctx))) /** Proof Termination rules */ case SuslikProofStep.EmpRule(label) => @@ -288,7 +288,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl (List(step), ctx) }) } - Result(List(), List((deferred, ctx))) + Result(List(), List((List(), deferred, ctx))) /** Predicate folding/unfolding */ case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? @@ -305,7 +305,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val c_selectors = selectors.map(ProofSpecTranslation.translate_expression(clientContext.typing_context)(_)) val clauses = predicate.clauses.toList.zip(c_selectors).map { case ((constructor, body), expr) => - val existentials = predicate.constructor_existentials(constructor).map({ + val existentials = predicate.findExistentials(constructor)(body).map({ case (name, ty) => ((existentials_sub(name), ty)) }) val constructor_arg_existentials = @@ -329,7 +329,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case (_, constructor_intro_gamma, intro_gamma, _, _) => var ctx = clientContext ctx = ctx with_ghost_variables_of constructor_intro_gamma ++ intro_gamma - (no_deferreds, ctx) + (List(), no_deferreds, ctx) } Result(List(step), child_rules) diff --git a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala index 7e4538a18..d7e12b3f5 100644 --- a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala +++ b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala @@ -8,6 +8,12 @@ import org.tygus.suslik.language.{Ident, PrettyPrinting} * termination measures in Coq */ trait CardConstructor extends PrettyPrinting { + def rename(renaming: Map[String, String]) = this match { + case CardNull => CardNull + case CardOf(args) => CardOf(args.map(v => renaming.getOrElse(v,v))) + case _ => this + } + def constructor_args: List[Ident] = this match { case CardNull => Nil From b2c6b4ac70c2c1c03046eba4b8aa5b17704dda6c Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sun, 21 Feb 2021 20:12:02 +0800 Subject: [PATCH 120/211] began implementing a more refined rewriting lemma --- .../targets/vst/logic/ProofTerms.scala | 69 ++++++++++++------- .../targets/vst/logic/VSTProofStep.scala | 5 +- 2 files changed, 48 insertions(+), 26 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 25ca7540f..98d147df0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,8 +1,10 @@ package org.tygus.suslik.certification.targets.vst.logic +import org.tygus.suslik.certification.targets.htt.logic.Proof.UnfoldConstructor import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicateHelper.HelpUnfold import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, GenericPredicate, GenericPredicateClause} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -20,7 +22,8 @@ object ProofTerms { case IsTrue(expr) => IsTrue(expr.subst(subst)) } - def rename (renaming: Map[String, String]) = { + + def rename(renaming: Map[String, String]) = { this match { case IsValidPointerOrNull(expr) => IsValidPointerOrNull(expr.rename(renaming)) @@ -33,7 +36,7 @@ object ProofTerms { /** * Represents helper lemmas and operations that are required to make VST handle the predicate automatically - * */ + **/ trait VSTPredicateHelper extends PrettyPrinting /** predicate encoding that C-parameter (of type val) is a valid pointer */ @@ -49,7 +52,7 @@ object ProofTerms { } /** prop predicate encoding that a given propositional expression is true - * */ + **/ case class IsTrueProp(expr: Expressions.ProofCExpr) extends PureFormula { override def pp: String = { s"${expr.pp}" @@ -127,18 +130,19 @@ object ProofTerms { * @param pure are the pure assertions * @param spatial are the spatial assertions * @param sub_constructor are the subconstructors - * */ + **/ case class VSTPredicateClause(override val pure: List[Expressions.ProofCExpr], override val spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) - extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) { + extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) { def rename(renaming: Map[String, String]) = VSTPredicateClause( pure.map(_.rename(renaming)), spatial.map(_.rename(renaming)), - sub_constructor.map({case (name, constructor) => (renaming.getOrElse(name,name), constructor.rename(renaming))}) + sub_constructor.map({ case (name, constructor) => (renaming.getOrElse(name, name), constructor.rename(renaming)) }) ) } + /** * represents a VST inductive predicate defined in a format that satisfies Coq's termination checker * @@ -150,7 +154,7 @@ object ProofTerms { * @param name is the name of the predicate * @param params is the list of arguments to the predicate * @param clauses is the mapping from cardinality constructors to clauses - * */ + **/ case class VSTPredicate(override val name: Ident, override val params: List[(String, VSTType)], override val existentials: List[(String, VSTType)], @@ -160,6 +164,7 @@ object ProofTerms { /** returns any helper lemmas that need to be constructed for the helper */ def get_helpers: List[VSTPredicateHelper] = { val local_facts = VSTPredicateHelper.LocalFacts(this) + val unfolding_lemmas = clauses.map({case (constructor, clause) => HelpUnfold(this, constructor, clause)}) params.flatMap({ case (param, CoqPtrValType) => val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formalParams, param) @@ -170,7 +175,25 @@ object ProofTerms { }) ++ List( local_facts, VSTPredicateHelper.HintResolve(local_facts.lemma_name, "saturate_local") - ) + ) ++ unfolding_lemmas + } + + def pp_constructor_clause(constructor: CardConstructor, pclause: VSTPredicateClause) : String = { + val VSTPredicateClause(pure, spatial, _) = pclause + s"${ + val clause_existentials: List[(String, VSTType)] = findExistentials(constructor)(pclause) + val str = clause_existentials.map({ case (name, ty) => s" EX ${name} : ${ty.pp}," }).mkString("\n") + clause_existentials match { + case Nil => "" + case ::(_, _) => "\n" + str + "\n" + } + } ${ + (pure.map(v => s"!!${v.pp}") ++ + List((spatial match { + case Nil => List("emp") + case v => v.map(_.pp) + }).mkString(" * "))).mkString(" && ") + }" } /** pretty print the constructor */ @@ -182,19 +205,7 @@ object ProofTerms { s"| | ${constructorName(constructor)} ${ expandArgs(sub_constructor)(constructor.constructor_args) } => ${ - val clause_existentials: List[(String, VSTType)] = findExistentials(constructor)(pclause) - val str = clause_existentials.map({ case (name, ty) => s"| EX ${name} : ${ty.pp}," }).mkString("\n") - clause_existentials match { - case Nil => "" - case ::(_, _) => "\n" + str + "\n" - } - } ${ - (pure.map(v => s"!!${v.pp}") - ++ - List((spatial match { - case Nil => List("emp") - case v => v.map(_.pp) - }).mkString(" * "))).mkString(" && ") + pp_constructor_clause(constructor, pclause) }" }).mkString("\n") } @@ -210,7 +221,7 @@ object ProofTerms { * @param cons a constructor matching some clause of the predicate * @param pclause the corresponding clause of the predicate * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause - * */ + **/ def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet]): List[(String, VSTType)] = { val param_map = params.toMap val exist_map: Map[String, VSTType] = existentials.toMap @@ -247,6 +258,8 @@ object ProofTerms { .map((v: String) => (v, exist_map(v))).toList } } + + def unfold_lemma_name (cardConstructor: CardConstructor) = s"unfold_${constructorName(cardConstructor)}" } object VSTPredicateHelper { @@ -277,7 +290,7 @@ object ProofTerms { /** Converts a predicate clause into a clause that mutually exclusively matches the clause * * Note: !!ASSUMPTION!! We assume that the first pure term of the predicate mutually exclusively matches the clause - * */ + **/ def predicate_to_determininant_term(clause: VSTPredicateClause): String = clause.pure.head.pp @@ -304,6 +317,16 @@ object ProofTerms { } + case class HelpUnfold(predicate: VSTPredicate, cardConstructor: CardConstructor, pclause: VSTPredicateClause) extends VSTPredicateHelper { + override def pp: String = { + s"Lemma ${predicate.unfold_lemma_name(cardConstructor)} " + + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + + s"${cardConstructor.constructor_args.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} : " + + s"${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ + predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructor_args) + }) = ${predicate.pp_constructor_clause(cardConstructor, pclause)}. Proof. auto. Qed." + } + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 51499e083..9d0e3bff8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -155,10 +155,9 @@ object VSTProofStep { override def pp: String = "" } - case class Unfold(predicate: ProofTerms.VSTPredicate, args: Int, cardinality: Expressions.ProofCExpr) extends VSTProofStep { + case class UnfoldRewrite(rewrite_name: String) extends VSTProofStep { override def pp: String = - s"simpl (${predicate.name} ${List.iterate("_", args)(v => v).mkString(" ")} (${cardinality.pp})) at 1." - + s"rewrite ${rewrite_name} at 1." } case object ForwardEntailer extends VSTProofStep { From 93231f3c1ca13731c27705d1b8d8d6a42f245383 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 22 Feb 2021 13:25:02 +0800 Subject: [PATCH 121/211] good news and bad --- .../targets/vst/logic/Expressions.scala | 2 +- .../targets/vst/logic/ProofTerms.scala | 9 +- .../targets/vst/logic/VSTProofStep.scala | 2 +- .../vst/translation/VSTProofTranslator.scala | 163 +++++++++++++----- 4 files changed, 126 insertions(+), 50 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index d664ffe4f..3f6567fba 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -208,7 +208,7 @@ object Expressions { /** boolean constant in a VST proof */ case class ProofCBoolConst(value: Boolean) extends ProofCExpr with CLangExpr { - override def pp: String = value.toString + override def pp: String = s"(is_true ${value.toString})" } /** integer constant in a VST proof */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 98d147df0..96e88a7ae 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -5,8 +5,10 @@ import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicateHelper.HelpUnfold +import org.tygus.suslik.certification.targets.vst.translation.ProofSpecTranslation import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf, GenericPredicate, GenericPredicateClause} +import org.tygus.suslik.language.Expressions.Expr import org.tygus.suslik.language.{Ident, PrettyPrinting} object ProofTerms { @@ -135,6 +137,7 @@ object ProofTerms { override val spatial: List[VSTHeaplet], sub_constructor: Map[String, CardConstructor]) extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) { + def rename(renaming: Map[String, String]) = VSTPredicateClause( pure.map(_.rename(renaming)), @@ -161,6 +164,10 @@ object ProofTerms { override val clauses: Map[CardConstructor, VSTPredicateClause]) extends GenericPredicate[Expressions.ProofCExpr, VSTHeaplet, VSTType](name, params, existentials, clauses) { + def clause_by_selector(expr: Expr) = { + val translated_expr = ProofSpecTranslation.translate_expression(params.toMap)(expr) + clauses.find({case (constructor, clause) => clause.selector == translated_expr}).get + } /** returns any helper lemmas that need to be constructed for the helper */ def get_helpers: List[VSTPredicateHelper] = { val local_facts = VSTPredicateHelper.LocalFacts(this) @@ -199,7 +206,7 @@ object ProofTerms { /** pretty print the constructor */ def ppPredicate: String = { val predicate_definition = - s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductiveName}) : mpred := match self_card with + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductiveName}) {struct self_card} : mpred := match self_card with ${ clauses.map({ case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => s"| | ${constructorName(constructor)} ${ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 9d0e3bff8..87a21da8d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -97,7 +97,7 @@ object VSTProofStep { } case class IntrosTuple(variables: List[(Ident, VSTType)]) extends VSTProofStep { - variables match { + override def pp = variables match { case Nil => "" case ::((variable, _), Nil) => s"Intros ${variable}." diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 7c0b1e168..190013c95 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -2,16 +2,16 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types -import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, VSTType} -import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqPtrValType, VSTType} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Entailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, IntrosTuple, Rename, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Entailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, Intros, IntrosTuple, Malloc, Rename, UnfoldRewrite, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, SubstVar, Var} -import org.tygus.suslik.language.{Ident, SSLType, Statements} +import org.tygus.suslik.language.{Expressions, Ident, SSLType, Statements} import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{PendingCall, VSTClientContext} import org.tygus.suslik.language.Statements.Load @@ -47,26 +47,30 @@ object VSTProofTranslator { spec_map: Map[String, FormalSpecification], coq_context: Map[String, VSTType], existential_context: Map[String, VSTType], + ghost_existential_context: Map[String, VSTType], variable_map: Map[String, ProofCExpr], - accumulated_renaming: Map[String, String], + renamings: Map[String, String], call: Option[PendingCall] ) extends ClientContext[VSTProofStep] { def is_existential_variable(from: String): Boolean = { - existential_context.contains(from) + existential_context.contains(from) || ghost_existential_context.contains(from) } def with_queued_call(suspended_call: PendingCall) = { - VSTClientContext(pred_map, spec_map, coq_context, existential_context, variable_map, accumulated_renaming, Some(suspended_call)) + VSTClientContext(pred_map, spec_map, coq_context, existential_context, ghost_existential_context, variable_map, renamings, Some(suspended_call)) } def unqueue_call: (PendingCall, VSTClientContext) = - (call.get, VSTClientContext(pred_map, spec_map, coq_context, existential_context, variable_map, accumulated_renaming, None)) + (call.get, VSTClientContext(pred_map, spec_map, coq_context, existential_context, ghost_existential_context, variable_map, renamings, None)) - def typing_context: Map[String, VSTType] = coq_context ++ existential_context + def typing_context: Map[String, VSTType] = coq_context ++ ghost_existential_context ++ existential_context def find_predicate_with_name(pred: Ident): VSTPredicate = pred_map(pred) + def with_renaming(vl: Map[String,String]): VSTClientContext = + vl.foldLeft(this)({case (ctx, map) => ctx.with_renaming(map)}) + def with_renaming(vl: (String, String)): VSTClientContext = { val (from, to) = vl val renaming = Map(from -> to) @@ -74,11 +78,11 @@ object VSTProofTranslator { def true_name(vl: String) = renaming.getOrElse(vl, vl) val new_coq_context = coq_context.map({ case (name, ty) => (true_name(name), ty) }) + val new_ghost_context = ghost_existential_context.map({ case (name, ty) => (true_name(name), ty) }) val new_existential_context = existential_context.map({ case (name, ty) => (true_name(name), ty) }) val new_variable_map = variable_map.map { case (name, expr) => (true_name(name), expr.rename(renaming)) } - val new_accumulated_renaming = accumulated_renaming.map({ case (from, to) => (from, true_name(to)) }) ++ renaming val new_call = call match { case None => None case Some(PendingCall(f_name, args, existential_params, pre_conditions)) => @@ -87,58 +91,67 @@ object VSTProofTranslator { existential_params.map(v => (true_name(v._1), v._2)), pre_conditions.map(_.rename(renaming)))) } - VSTClientContext(pred_map, spec_map, new_coq_context, new_existential_context, new_variable_map, new_accumulated_renaming, new_call) + val new_renamings = renamings.map({case (from, to) => (from, true_name(to))}) ++ Map(from -> to) + VSTClientContext(pred_map, spec_map, new_coq_context, new_existential_context, new_ghost_context, new_variable_map, new_renamings, new_call) } - def renaming: Map[String, String] = accumulated_renaming - def with_existential_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, spec_map, coq_context, existential_context ++ new_vars, variable_map, accumulated_renaming, call) + VSTClientContext(pred_map, spec_map, coq_context, existential_context ++ new_vars, ghost_existential_context, variable_map, renamings, call) } + def without_existentials_of(variables: List[String]): VSTClientContext = { val new_existential_context = existential_context.filterNot(v => variables.contains(v._1)) - VSTClientContext(pred_map, spec_map, coq_context, new_existential_context, variable_map, accumulated_renaming, call) + VSTClientContext(pred_map, spec_map, coq_context, new_existential_context, ghost_existential_context, variable_map, renamings, call) + } + + def without_ghost_existentials_of(variables: List[String]): VSTClientContext = { + val new_ghost_context = ghost_existential_context.filterNot(v => variables.contains(v._1)) + VSTClientContext(pred_map, spec_map, coq_context, existential_context, new_ghost_context, variable_map, renamings, call) } def resolve_existential(ident: Ident): ProofCExpr = { - val true_name = accumulated_renaming.getOrElse(ident, ident) + val true_name = renamings.getOrElse(ident, ident) variable_map(true_name) } - def with_program_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { + def with_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { // Note: translate_predicate_param_type maps val (containing ints) -> Z, and val (containing ptrs) -> val. // This is valid because the ssl_open_context tactic called at the start of a proof in VST will unwrap any // variable x in the context where ssl_is_valid_int(x) is known into a term of type Z (preserving the name - i.e (x : Z)). // As such, both ghost and program variables will correctly have type Z in the coq context (as the precondition for // specifications asserts that their values are valid ints). val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, spec_map, coq_context ++ new_vars, existential_context, variable_map, accumulated_renaming, call) + VSTClientContext(pred_map, spec_map, coq_context ++ new_vars, existential_context, ghost_existential_context, variable_map, renamings, call) } - def with_ghost_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { + def with_ghost_existentials_of(variables: List[(String, VSTType)]): VSTClientContext = { val new_vars: Map[String, VSTType] = variables.toMap - VSTClientContext(pred_map, spec_map, coq_context ++ new_vars, existential_context, variable_map, accumulated_renaming, call) + VSTClientContext(pred_map, spec_map, coq_context, existential_context, ghost_existential_context ++ new_vars, variable_map, renamings, call) } - def with_mapping_between(from: String, to: Expr): VSTClientContext = { - val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to) + val target_type = typing_context.get(from) + val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to, target = target_type) + with_mapping_between(from, to_expr) + } + + def with_mapping_between(from: String, to_expr: ProofCExpr): VSTClientContext = { val subst = Map(from -> to_expr) val new_variable_map = variable_map.map({ case (name, vl) => (name, vl.subst(subst)) }) ++ subst val new_call = call.map({ case PendingCall(f_name, args, eparams, preconds) => PendingCall(f_name, args, eparams, preconds.map(v => v.subst(subst))) }) - VSTClientContext(pred_map, spec_map, coq_context, existential_context, new_variable_map, accumulated_renaming, new_call) + VSTClientContext(pred_map, spec_map, coq_context, existential_context, ghost_existential_context, new_variable_map, renamings, new_call) } } object VSTClientContext { def make_context(pred_map: Map[String, VSTPredicate], spec_map: Map[String, FormalSpecification]): VSTClientContext = - VSTClientContext(pred_map, spec_map, Map(), Map(), Map(), Map(), None) + VSTClientContext(pred_map, spec_map, Map(), Map(), Map(), Map(), Map(), None) } } @@ -169,18 +182,19 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl var ctx = clientContext val program_vars = spec.c_params.toList.map { case (name, ty) => (name, unwrap_val_type(ty: VSTType)) } val ghost_vars = spec.formal_params.toList.map { case (name, ty) => (name, unwrap_val_type(ty)) } - ctx = ctx with_program_variables_of program_vars - ctx = ctx with_ghost_variables_of ghost_vars - // ctx = ctx with_existential_variables_of existential_params - println(goal.pp) val existentials = spec.existensial_params + ctx = ctx with_variables_of program_vars + ctx = ctx with_variables_of ghost_vars + ctx = ctx with_existential_variables_of existentials + println(goal.pp) val deferreds: Deferred = (ctx: VSTClientContext) => { - val steps: List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)).toList + val steps: List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)) + val new_ctx = ctx.without_existentials_of (existentials.map(_._1)) (steps ++ (if (steps.nonEmpty) { List(Entailer) } else { List() - }), ctx) + }), new_ctx) } Result(List(), List((Nil, Some(deferreds), ctx))) @@ -199,13 +213,14 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val function_result_existentials = fun_spec.existensial_params.map { case (name, ty) => (renaming(name), ty) } val function_preconds = fun_spec.precondition.pure_constraints.map(_.rename(renaming)) val suspended_call = PendingCall(f.name, function_params, function_result_existentials, function_preconds) - ctx = ctx with_existential_variables_of function_params + ctx = ctx with_ghost_existentials_of function_params ctx = ctx with_queued_call suspended_call // Use deferred to remove (translation-only) existentials produced by abduce-call val deferred = (ctx: VSTClientContext) => { - (List(), ctx without_existentials_of function_params.map(_._1)) + (List(), ctx without_ghost_existentials_of function_params.map(_._1)) } Result(List(), List((List(), Some(deferred), ctx))) + case SuslikProofStep.Call(subst, call) => // retreive call from ctx var (call, ctx) = clientContext.unqueue_call @@ -226,29 +241,39 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl }) } // update context to have existential variables produced by call - ctx = ctx with_program_variables_of call.existential_params + ctx = ctx with_variables_of call.existential_params Result(steps, List((List(), None, ctx))) /** Proof Termination rules */ case SuslikProofStep.EmpRule(label) => - Result(List(ForwardEntailer), List()) + case SuslikProofStep.Inconsistency(label) => ??? case SuslikProofStep.Write(stmt) => Result(List(Forward), List(with_no_deferreds(clientContext))) case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - val ctx = clientContext with_renaming (ghost_from -> ghost_to) + val ctx = clientContext with_renaming ghost_from -> ghost_to val rename_step = Rename(ghost_from, ghost_to) val read_step = Forward Result(List(rename_step, read_step), List(with_no_deferreds(ctx))) + case SuslikProofStep.Free(Statements.Free(Var(v)), size) => this.contains_free = true val step = Free(v, size) Result(List(step), List(with_no_deferreds(clientContext))) - case SuslikProofStep.Malloc(ghostFrom, ghostTo, stmt) => + + case SuslikProofStep.Malloc(Var(ghostFrom), Var(ghostTo), Statements.Malloc(to, tpe, sz)) => this.contains_malloc = true - ??? + val new_var = (ghostTo, CoqPtrValType) + var ctx = clientContext + ctx = ctx with_variables_of List(new_var) + ctx = ctx with_mapping_between (ghostFrom, ProofCVar(ghostTo, CoqPtrValType)) + val steps = List( + Malloc(sz), + Intros(List(new_var)) + ) + Result(steps, List(with_no_deferreds(ctx))) /** Substitutions and renaming */ case SuslikProofStep.Pick(from, to_expr) => @@ -262,7 +287,25 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.PickArg(from, to) => ??? - case SuslikProofStep.PickCard(from, to) => ??? + case SuslikProofStep.PickCard(Var(from), to) => + // Pick card is a hack - not sure what the best way to do this is. + val card_type = clientContext.typing_context(from).asInstanceOf[CoqCardType] + val pred_type = card_type.pred_type + val predicate = clientContext.pred_map(pred_type) + val empty_constructor = predicate.clauses.head._1 + val second_constructor = predicate.clauses.toList(1)._1 + val card_expr = to match { + case Var(name) => ProofCVar(name, clientContext.typing_context(name)) + case Expressions.IntConst(v) if v == 0 => + ProofCCardinalityConstructor(pred_type, predicate.constructorName(empty_constructor), List()) + case Expressions.BinaryExpr(Expressions.OpPlus, Var(name), Expressions.IntConst(v)) if v == 1 => + ProofCCardinalityConstructor( + pred_type, predicate.constructorName(second_constructor), List( + ProofCVar(name, clientContext.typing_context(name)) + )) + } + var ctx = clientContext with_mapping_between (from, card_expr) + with_no_op(ctx) case SuslikProofStep.SubstL(Var(from), to) => // SubstL translated into `assert (from = to) as H; rewrite H in *` @@ -274,24 +317,50 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl Result(List(step), List((with_no_deferreds(ctx)))) case SuslikProofStep.SubstR(Var(from), to) => - val from_type = clientContext.typing_context(from) + val from_type = clientContext.typing_context.get(from) val ctx = clientContext with_mapping_between(from, to) - val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) - val deferred = if (ctx.is_existential_variable(from)) { + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = from_type) + val deferred = if (ctx is_existential_variable from) { None } else { Some((ctx: VSTClientContext) => { - val renaming = ctx.renaming - val updated_from = renaming.getOrElse(from, from) - val updated_to_expr = to_expr.rename(renaming) - val step = AssertPropSubst(updated_from, updated_to_expr) + val step = AssertPropSubst(from, to_expr) (List(step), ctx) }) } Result(List(), List((List(), deferred, ctx))) /** Predicate folding/unfolding */ - case SuslikProofStep.Close(app, selector, asn, fresh_exist) => ??? + case SuslikProofStep.Close(app, selector, asn, fresh_exist) => + var ctx = clientContext + val fresh_exist_renaming = fresh_exist.map({case (Var(from), Var(to)) => (from,to)}) + val predicate_name = app.pred + val cardinality_var = app.card.asInstanceOf[Var].name + val predicate = clientContext.pred_map(predicate_name) + val (constructor, predicate_clause) = predicate.clause_by_selector(selector) + val existentials = predicate.findExistentials(constructor)(predicate_clause).map({ + case (name,ty) => (fresh_exist_renaming(name), ty) + }) + val constructor_args = constructor.constructor_args.map(v => ProofCVar(fresh_exist_renaming(v), CoqCardType(predicate.name))) + ctx = ctx with_existential_variables_of existentials + ctx = ctx with_ghost_existentials_of (constructor_args.map(v => (v.name, v.typ))) + ctx = ctx with_mapping_between(cardinality_var, ProofCCardinalityConstructor( + predicate.name, + predicate.constructorName(constructor), + constructor_args + )) + val deferred_steps = (base_ctx: VSTClientContext) => { + var ctx = base_ctx + ctx = ctx without_existentials_of existentials.map(_._1) + ctx = ctx without_ghost_existentials_of constructor_args.map(_.name) + val unfold_step = UnfoldRewrite( + predicate.unfold_lemma_name(constructor) + ) + val existential_steps = existentials.map(v => Exists(ctx.resolve_existential(v._1))) + (List(unfold_step) ++ existential_steps ++ List(Entailer), ctx) + } + Result(List(), List((List(), Some(deferred_steps), ctx))) + case SuslikProofStep.Open(pred, fresh_exists, fresh_params, selectors) => // Open rules translated into forward_if. - { ... } - { ... } ... - { ... } // each case branch intros two types of variables (and thus changes the coq context) @@ -328,7 +397,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val child_rules = clauses.map { case (_, constructor_intro_gamma, intro_gamma, _, _) => var ctx = clientContext - ctx = ctx with_ghost_variables_of constructor_intro_gamma ++ intro_gamma + ctx = ctx with_variables_of constructor_intro_gamma ++ intro_gamma (List(), no_deferreds, ctx) } Result(List(step), child_rules) From 5bfee0ec4f1d8c6637fb47c46342ee324d772a0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 22 Feb 2021 18:16:20 +0800 Subject: [PATCH 122/211] Iris: WIP predicate generation --- .../certification/targets/iris/Iris.scala | 4 - .../targets/iris/IrisCertificate.scala | 21 ++- .../targets/iris/heaplang/Expressions.scala | 16 +- .../targets/iris/heaplang/Types.scala | 12 +- .../targets/iris/logic/Assertions.scala | 138 ++++++++++++++---- .../iris/translation/IrisTranslator.scala | 77 +++++++++- .../iris/translation/Translation.scala | 9 +- .../iris/translation/TranslationContext.scala | 4 +- .../targets/vst/logic/ProofTerms.scala | 14 +- .../vst/translation/VSTProofTranslator.scala | 2 +- .../translation/GenericPredicate.scala | 6 +- 11 files changed, 234 insertions(+), 69 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 08f6ca81f..88c407dd2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -6,7 +6,6 @@ import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException -import org.tygus.suslik.certification.targets.vst.translation.ProofSpecTranslation object Iris extends CertificationTarget { val name: String = "HTT" @@ -16,9 +15,6 @@ object Iris extends CertificationTarget { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) val cert = Translation.translate(root, proc)(env) - val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList - - val simplified = SuslikProofStep.of_certtree(root) println(s"Suslik Proof:\n ${SuslikPrinter.pp(simplified)}") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index cbbf86f47..57311a884 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -1,10 +1,10 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef -import org.tygus.suslik.certification.targets.iris.logic.Assertions.IFunSpec +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class IrisCertificate(name: String, funDef: HFunDef, funSpec: IFunSpec) extends Certificate { +case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec) extends Certificate { val target: CertificationTarget = Iris private val prelude = @@ -17,23 +17,27 @@ case class IrisCertificate(name: String, funDef: HFunDef, funSpec: IFunSpec) ext |Implicit Types Δ : envs PROP. |Set Default Proof Using "Type". | + |Definition null_loc : loc := {|loc_car := 0 |}. + |Definition nullptr : val := LitV (LitLoc null_loc). + | |Definition loc_at x lx := x = LitV (LitLoc lx). |Definition int_at x vx := x = LitV (LitInt vx). | |(* This is a less clever version of tac_and_destruct, which | does NOT break ↦ assertions into fractional assertions. *) - |Lemma tac_sep_destruct Δ i j1 j2 P P1 P2 Q : - | envs_lookup i Δ = Some (false, P) → - | (P -∗ P1 ∗ P2) → - | match envs_simple_replace i false (Esnoc (Esnoc Enil j1 P1) j2 P2) Δ with + |Lemma tac_sep_destruct Δ i p j1 j2 P P1 P2 Q : + | envs_lookup i Δ = Some (p, P) → + | (if p then IntoAnd true P P1 P2 else (P -∗ P1 ∗ P2)) → + | match envs_simple_replace i p (Esnoc (Esnoc Enil j1 P1) j2 P2) Δ with | | None => False | | Some Δ' => envs_entails Δ' Q | end → | envs_entails Δ Q. |Proof. | destruct (envs_simple_replace _ _ _ _) as [Δ'|] eqn:Hrep; last done. - | rewrite envs_entails_eq. intros H0 H1 H2. rewrite envs_simple_replace_sound //=. - | by rewrite /= right_id -(comm _ P1) H2 -H1 bi.wand_elim_r. + | rewrite envs_entails_eq. intros H H0 H1. rewrite envs_simple_replace_sound //=. destruct p. + | by rewrite (into_and _ P) /= right_id -(comm _ P1) bi.wand_elim_r. + | by rewrite /= right_id -(comm _ P1) H1 -H0 bi.wand_elim_r. |Qed. | |Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := @@ -87,6 +91,7 @@ case class IrisCertificate(name: String, funDef: HFunDef, funSpec: IFunSpec) ext def pp : String = { val b = new StringBuilder b.append(prelude) + b.append(preds.map(_.pp).mkString("\n")) b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index 68533b8cb..5ca0116a4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -23,10 +23,20 @@ object Expressions { case class HLitInt(value: Int) extends HLit(value.toString) + case class HLitOffset(off: Int) extends HLit(off.toString) { + override def pp: String = s"$off" + } + case class HLitBool(value: Boolean) extends HLit(value.toString) - case class HLitLoc(value: Int) extends HLit(value.toString) + case class HLitLoc(loc: Int) extends HLit(loc.toString) { + override def pp: String = if(loc == 0) "nullptr" else super.pp + } + case class HSetLiteral(elems: List[HExpr]) extends HExpr { + override def pp: String = + s"[${elems.map(_.pp).mkString("; ")}]" + } /** Variables */ case class HProgVar(name: String) extends HExpr { override def pp: String = s""""${name}"""" @@ -49,6 +59,10 @@ object Expressions { override def pp: String = "+" } + object HOpUnion extends HBinOp { + override def pp: String = "++" + } + object HOpMinus extends HBinOp { override def pp: String = "-" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala index 829537b58..3778f3835 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala @@ -7,9 +7,19 @@ object Types { sealed abstract class HType extends PrettyPrinting case class HIntType() extends HType { - override def pp: String = "int" + override def pp: String = "Z" } case class HLocType() extends HType { override def pp: String = "loc" } + case class HCardType(predType: String) extends HType { + override def pp: String = s"${predType}_card" + } + case class HUnknownType() extends HType { + override def pp: String = "_" + } + + case class HIntSetType() extends HType { + override def pp: String = "(list Z)" + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 28db8fd93..c9dbaac79 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -20,26 +20,6 @@ object Assertions { abstract class ISpecification extends PrettyPrinting - case class IPredicate(override val name: Ident, - override val params: List[(Ident, HType)], - override val existentials: List[(Ident, HType)], - override val clauses: Map[CardConstructor, IPredicateClause] - ) - extends GenericPredicate[IPureAssertion, ISpatialAssertion, HType](name, params, existentials, clauses) { - - def ppPredicate = "(* PREDICATE NOT IMPLEMENTED *)" - - /** - * For a given clause of the predicate and its associated constructor, - * return the list of existential variables used in the body of the clause - * - * @param cons a constructor matching some clause of the predicate - * @param pclause the corresponding clause of the predicate - * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause - * */ - override def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[IPureAssertion, ISpatialAssertion]): List[(Ident, HType)] = List() - } - case class ISpecLit(lit: HLit) extends IPureAssertion { override def pp: String = lit.pp } @@ -52,11 +32,21 @@ object Assertions { override def pp: String = s"${name}" } + case class ISetLiteral(elems: List[IPureAssertion]) extends IPureAssertion { + override def pp: String = + s"([${elems.map(_.pp).mkString("; ")}] : list Z)" + } + + case class ISpecUnaryExpr(op: HUnOp, expr: IPureAssertion) extends IPureAssertion { + override def pp: String = s"${op.pp} ${expr.pp}" + } + case class ISpecBinaryExpr(op: HBinOp, left: IPureAssertion, right: IPureAssertion) extends IPureAssertion { - override def pp: String = s"${left.pp} ${op.pp} ${right.pp}" + override def pp: String = s"(${left.pp} ${op.pp} ${right.pp})" override def ppAsPhi: String = op match { - case HOpLe | HOpLt | HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" + case HOpLe | HOpLt => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" + case HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})" case _ => ??? } } @@ -69,22 +59,32 @@ object Assertions { override def pp: String = s"${loc.pp} ↦ ${value.pp}" } + case class IPredApp(pred: String, args: Seq[IPureAssertion], card: IPureAssertion) extends ISpatialAssertion { + override def pp: String = + s"(${pred} ${(args ++ List(card)).map(_.pp).mkString(" ")})" + } + + case class IBlock() extends ISpatialAssertion { + override def pp: String = s"⌜True⌝" + } + case class IHeap(heaplets: Seq[ISpatialAssertion]) extends ISpatialAssertion { override def pp: String = s"${heaplets.map(_.pp).mkString(" ∗ ")}" } case class IAssertion(phi: IPureAssertion, sigma: ISpatialAssertion) extends PrettyPrinting { override def pp: String = { - val pure = if (phi.ppAsPhi.isEmpty) "" else s"⌜${phi.ppAsPhi}⌝ ∗ " - val whole = s"${pure}${sigma.pp}" + val pure = if (phi.ppAsPhi.isEmpty) "" else s"⌜${phi.ppAsPhi}⌝" + val spatial = sigma.pp + val whole = s"${pure}${if(pure.nonEmpty && spatial.nonEmpty) " ∗ " else ""}${sigma.pp}" if (whole.isEmpty) "True" else whole } } case class IFunSpec(fname: String, funArgs: Seq[(ISpecVar, HType)], - specUniversal: Seq[IQuantifiedVar], - specExistential: Seq[IQuantifiedVar], + specUniversal: Seq[(IQuantifiedVar, HType)], + specExistential: Seq[(IQuantifiedVar, HType)], pre: IAssertion, post: IAssertion ) extends ISpecification { @@ -100,12 +100,12 @@ object Assertions { val var_at = (v: ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" val postExist = if (specExistential.nonEmpty) - s"∃ ${specExistential.map(v => v.pp).mkString(" ")}, " + s"∃ ${specExistential.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, " else "" s""" |Lemma ${fname}_spec : - |∀ ${specUniversal.map(v => v.pp).mkString(" ")}, + |∀ ${specUniversal.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, |${funArgs.map(v => var_at(v._1, v._2)).mkString(" →\n")} → |{{{ ${pre.pp} }}} | ${fname} ${funArgs.map(v => v._1.pp).mkString(" ")} @@ -114,9 +114,85 @@ object Assertions { } } - class IPredicateClause(pure: List[IPureAssertion], - spatial: List[ISpatialAssertion], - subConstructor: Map[String, CardConstructor]) + case class IPredicateClause(override val pure: List[IPureAssertion], + override val spatial: List[ISpatialAssertion], + override val subConstructor: Map[String, CardConstructor]) extends GenericPredicateClause[IPureAssertion, ISpatialAssertion](pure, spatial, subConstructor) + case class IPredicate(override val name: Ident, + override val params: List[(Ident, HType)], + override val existentials: List[(Ident, HType)], + override val clauses: Map[CardConstructor, IPredicateClause]) + extends GenericPredicate[IPureAssertion, ISpatialAssertion, HType](name, params, existentials, clauses) { + + def ppPredicate: String = { + def ppConstructorClause(constr: CardConstructor, pclause: IPredicateClause): String = { + val IPredicateClause(pure, spatial, _) = pclause + val clause = IAssertion(IAnd(pure), IHeap(spatial)) + val ex = findExistentials(constr)(pclause) + val exStr = if (ex.nonEmpty) s"∃ ${ex.map({ case (name, ty) => s"($name : ${ty.pp})"}).mkString(" ")}, " else "" + s"${exStr}${clause.pp}" + } + + val predicate_definition = + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductiveName}) : iProp Σ := match self_card with + ${ + clauses.map({ case (constructor, pclause@IPredicateClause(_, _, subConstructor)) => + s"| | ${constructorName(constructor)} ${ + expandArgs(subConstructor)(constructor.constructorArgs) + } => ${ + ppConstructorClause(constructor, pclause) + }" + }).mkString("\n") + } + |end. + |""".stripMargin + s"${predicate_definition}" + } + + /** + * For a given clause of the predicate and its associated constructor, + * return the list of existential variables used in the body of the clause + * + * @param cons a constructor matching some clause of the predicate + * @param pclause the corresponding clause of the predicate + * @return the list of pairs of (variable, variable_type) of all the existential variables in this clause + * */ + override def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[IPureAssertion, ISpatialAssertion]): List[(Ident, HType)] = { + val paramMap = params.toMap + + // + val existMap = existentials.toMap + val cardMap = cons.constructorArgs + + pclause match { + case IPredicateClause(pure, spatial, subClauses) => + val clauseCardMap = (cardMap ++ subClauses.flatMap({ case (_, cons) => cons.constructorArgs })).toSet + + def pureExistentials(exp: IPureAssertion): Set[Ident] = exp match { + case ISpecVar(name) => { + paramMap.get(name) match { + // A variable that is neither (1) a parameter of the predicate nor (2) a cardinality IS an existential + case None if !clauseCardMap.contains(name) => Set(name) + case _ => Set() + } + } + case ISpecBinaryExpr(_, left, right) => pureExistentials(left) ++ pureExistentials(right) + case ISpecUnaryExpr(_, expr) => pureExistentials(expr) + case ISetLiteral(elems) => elems.flatMap(pureExistentials).toSet + case ISpecLit(_) => Set() + case _ => ??? + } + def spatialExistentials(heap: ISpatialAssertion): Set[Ident] = heap match { + case IPointsTo(loc, value) => pureExistentials(loc) ++ pureExistentials(value) + case IPredApp(_, args, _) => args.flatMap(pureExistentials).toSet + case IHeap(heaplets) => heaplets.flatMap(spatialExistentials).toSet + case IBlock() => Set() + case _ => ??? + } + (pure.flatMap(pureExistentials) ++ spatial.flatMap(spatialExistentials)).map(v => (v, existMap(v))) + } + } + } + } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 04c402e60..4888e3c58 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -1,12 +1,13 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HIntType, HLocType, HType} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HIntSetType, HIntType, HLocType, HType, HUnknownType} import org.tygus.suslik.certification.targets.iris.logic.Assertions._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.translation.{CardConstructor, PredicateTranslation} import org.tygus.suslik.language.Expressions._ import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} -import org.tygus.suslik.language.{IntType, LocType, SSLType} +import org.tygus.suslik.language.{CardType, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic._ @@ -27,6 +28,14 @@ object IrisTranslator { case _ => ??? } + implicit val overloadedBinOpTranslator: IrisTranslator[OverloadedBinOp, HBinOp] = (value, _) => value match { + case OpOverloadedEq => HOpEq + case OpOverloadedLeq => HOpLe + case OpOverloadedPlus => HOpUnion + case OpOverloadedStar => HOpMultiply + case _ => ??? + } + implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _) => el._1.translate(progVarTranslator) implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = (value, _) => value match { @@ -34,15 +43,20 @@ object IrisTranslator { case OpUnaryMinus => HOpUnaryMinus } - implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr, _) => { + implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr, ctx) => { def visit(expr: Expr): HExpr = expr match { case Var(name) => HProgVar(name) case IntConst(v) => HLitInt(v) case LocConst(v) => HLitLoc(v) case BoolConst(v) => HLitBool(v) + case SetLiteral(elems) => HSetLiteral(elems.map(_.translate(exprTranslator))) case UnaryExpr(op, arg) => HUnaryExpr(op.translate, visit(arg)) + // Hack for null-pointer comparison + case BinaryExpr(OpEq, v:Var, IntConst(value)) if ctx.get.gamma.get(v).contains(LocType) && value == 0 => + HBinaryExpr(HOpEq, visit(v), HLitLoc(0)) case BinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) case IfThenElse(cond, t, f) => HIfThenElse(visit(cond), visit(t), visit(f)) + case OverloadedBinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) case _ => ??? } visit(expr) @@ -61,6 +75,8 @@ object IrisTranslator { implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _) => value match { case IntType => HIntType() case LocType => HLocType() + case IntSetType => HIntSetType() + case CardType => HUnknownType() case _ => ??? } @@ -94,6 +110,8 @@ object IrisTranslator { case v: HProgVar => ctx.get.pts.getOrElse(v, v.translate(progVarToSpecVar)) case l: HLit => ISpecLit(l) case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx), right.translate(toSpecExpr, ctx)) + case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecExpr, ctx)) + case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecExpr, ctx))) case _ => ??? } } @@ -106,6 +124,8 @@ object IrisTranslator { case v: HProgVar => ctx.get.pts.getOrElse(v, ISpecLit(new HLit(v.name))) case l: HLit => ISpecLit(l) case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecVal, ctx), right.translate(toSpecVal, ctx)) + case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecVal, ctx)) + case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecVal, ctx))) case _ => ??? } } @@ -113,16 +133,22 @@ object IrisTranslator { implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx) => { val base = f.loc.translate.translate(toSpecExpr, ctx) - val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitLoc(f.offset))) else base + val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitOffset(f.offset))) else base val value = f.value.translate.translate(toSpecVal, ctx) IPointsTo(loc, value) } + implicit val predAppTranslator: IrisTranslator[SApp, IPredApp] = (pa, ctx) => { + assert(ctx.isDefined) + IPredApp(pa.pred, pa.args.map(_.translate.translate(toSpecExpr, ctx)), pa.card.translate.translate(toSpecExpr, ctx)) + } + implicit val heapleatTranslator: IrisTranslator[Heaplet, ISpatialAssertion] = (h, ctx) => { assert(ctx.isDefined) h match { case pt:PointsTo => pt.translate(pointsToTranslator, ctx) - case pa:SApp => IHeap(Seq()) + case bl:Block => IBlock() + case pa:SApp => pa.translate(predAppTranslator, ctx) case _ => ??? } } @@ -139,14 +165,49 @@ object IrisTranslator { implicit val goalToFunSpecTranslator: IrisTranslator[Goal, IFunSpec] = (g, ctx) => { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) + + // OLD: untyped universals and existentials + // val specUniversal = g.universals.map(_.translate.translate(progVarToSpecVar)).toSeq ++ quantifiedValues + // val specExistential = g.existentials.map(_.translate.translate(progVarToSpecVar)).toSeq + + // We "unwrap" every value to a l ↦ v form. quantifiedVal = v and takes the type of the value. val quantifiedValues = g.programVars.map( - v => v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)) + v => (v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)), g.gamma(v).translate) ) - val specUniversal = g.universals.map(_.translate.translate(progVarToSpecVar)).toSeq ++ quantifiedValues - val specExistential = g.existentials.map(_.translate.translate(progVarToSpecVar)).toSeq + // We quantify over all universals, ignoring the type of function arguments + val quantifiedLocs = g.universals.map(v => (v.translate.translate(progVarToSpecVar), + if(g.programVars.contains(v)) HUnknownType() else g.gamma(v).translate)) + val specUniversal = quantifiedLocs.toSeq ++ quantifiedValues + val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar), g.gamma(v).translate)).toSeq + val pre = g.pre.translate(assertionTranslator, ctx) val post = g.post.translate(assertionTranslator, ctx) IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar), x._2)), specUniversal, specExistential, pre, post) } + implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx) => { + assert(ctx.isDefined) + class IPredicateTranslation extends PredicateTranslation[IPureAssertion, ISpatialAssertion, HType, IPredicateClause, IPredicate] { + override def translatePredicateParamType(predName: String, ty: SSLType): HType = ty match { + case CardType => HCardType(predName) + case _ => ty.translate + } + + // TODO: should this be specExpr or specVal? + override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = expr.translate.translate(toSpecExpr, ctx) + + override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = + heaplets.map(_.translate(heapleatTranslator, ctx)) + + override def constructClause(pure: List[IPureAssertion], spatial: List[ISpatialAssertion], subConstructor: Map[String, CardConstructor]): IPredicateClause = { + IPredicateClause(pure, spatial, subConstructor) + } + + override def constructPred(name: Ident, params: List[(Ident, HType)], existentials: List[(Ident, HType)], clauses: Map[CardConstructor, IPredicateClause]): IPredicate = { + IPredicate(name, params, existentials, clauses) + } + } + new IPredicateTranslation().translatePredicate(ctx.get.env)(predicate) + } + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index ef157166d..4dda64d16 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -19,15 +19,18 @@ object Translation { val params = proc.formals.map(_.translate) // We have this "dummy" value to generate progToSpec for the actual context, ctx - val pre_ctx = Some(TranslationContext(node.goal.gamma, List().toMap)) + val pre_ctx = Some(TranslationContext(env, node.goal.gamma, List().toMap)) val progToSpec = params.map(p => (p, p.translate(progVarToSpecQuantifiedValue, pre_ctx))) - val ctx = TranslationContext(node.goal.gamma, progToSpec.toMap) + val ctx = TranslationContext(env, node.goal.gamma, progToSpec.toMap) + val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList + predicates.foreach(p => println(p.pp)) + val progTree = ProgramEvaluator.run(suslikTree, ctx) val funDef = HFunDef(proc.name, params, progTree) val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) - IrisCertificate(proc.name, funDef, funSpec) + IrisCertificate(proc.name, predicates, funDef, funSpec) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala index d9f1f539b..3b6d3470b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala @@ -3,6 +3,6 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HProgVar} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.logic.Gamma +import org.tygus.suslik.logic.{Environment, Gamma} -case class TranslationContext(gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar]) extends ClientContext[HExpr] \ No newline at end of file +case class TranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar]) extends ClientContext[HExpr] \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 98d147df0..de0ecc46d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -203,7 +203,7 @@ object ProofTerms { ${ clauses.map({ case (constructor, pclause@VSTPredicateClause(pure, spatial, sub_constructor)) => s"| | ${constructorName(constructor)} ${ - expandArgs(sub_constructor)(constructor.constructor_args) + expandArgs(sub_constructor)(constructor.constructorArgs) } => ${ pp_constructor_clause(constructor, pclause) }" @@ -225,10 +225,10 @@ object ProofTerms { def findExistentials(cons: CardConstructor)(pclause: GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet]): List[(String, VSTType)] = { val param_map = params.toMap val exist_map: Map[String, VSTType] = existentials.toMap - val card_map = cons.constructor_args + val card_map = cons.constructorArgs pclause match { case VSTPredicateClause(pure, spatial, sub_clauses) => - val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructor_args })).toSet + val clause_card_map = (card_map ++ sub_clauses.flatMap({ case (_, cons) => cons.constructorArgs })).toSet def to_variables(exp: Expressions.ProofCExpr): List[String] = exp match { case Expressions.ProofCVar(name, typ) => @@ -281,10 +281,10 @@ object ProofTerms { override def pp: String = { def constructor_to_equality_term(vl: String, cons: CardConstructor) = - if (cons.constructor_args.isEmpty) { + if (cons.constructorArgs.isEmpty) { s"${vl} = ${predicate.constructorName(cons)}" } else { - s"exists ${cons.constructor_args.mkString(" ")}, ${vl} = ${predicate.constructorName(cons)} ${cons.constructor_args.mkString(" ")}" + s"exists ${cons.constructorArgs.mkString(" ")}, ${vl} = ${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")}" } /** Converts a predicate clause into a clause that mutually exclusively matches the clause @@ -321,9 +321,9 @@ object ProofTerms { override def pp: String = { s"Lemma ${predicate.unfold_lemma_name(cardConstructor)} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + - s"${cardConstructor.constructor_args.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} : " + + s"${cardConstructor.constructorArgs.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} : " + s"${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ - predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructor_args) + predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructorArgs) }) = ${predicate.pp_constructor_clause(cardConstructor, pclause)}. Proof. auto. Qed." } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 7c0b1e168..55ec58a3b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -309,7 +309,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case (name, ty) => ((existentials_sub(name), ty)) }) val constructor_arg_existentials = - constructor.constructor_args.map(existentials_sub(_)).map(v => (v, CoqCardType(predicate_name))) + constructor.constructorArgs.map(existentials_sub(_)).map(v => (v, CoqCardType(predicate_name))) val renamed_body = body.rename(existentials_sub).rename(params_sub) (constructor, constructor_arg_existentials, existentials, renamed_body, expr) } diff --git a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala index d7e12b3f5..fdfe9ec16 100644 --- a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala +++ b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala @@ -14,7 +14,7 @@ trait CardConstructor extends PrettyPrinting { case _ => this } - def constructor_args: List[Ident] = + def constructorArgs: List[Ident] = this match { case CardNull => Nil case CardOf(args) => args @@ -70,7 +70,7 @@ abstract class GenericPredicate[Pure, Spatial, Type](val name: Ident, * @param n number of arguments */ def constructorByArg(n: Int): CardConstructor = - clauses.find({ case (constructor, clause) => constructor.constructor_args.length == n }).get._1 + clauses.find({ case (constructor, clause) => constructor.constructorArgs.length == n }).get._1 /** returns all instances of constructors and the bindings they expose */ def constructors: List[CardConstructor] = @@ -93,7 +93,7 @@ abstract class GenericPredicate[Pure, Spatial, Type](val name: Ident, idents.map(arg => sub_constructor.get(arg) match { case Some(constructor) => - s"(${constructorName(constructor)} ${expandArgs(sub_constructor)(constructor.constructor_args)} as ${arg})" + s"(${constructorName(constructor)} ${expandArgs(sub_constructor)(constructor.constructorArgs)} as ${arg})" case None => arg } ).mkString(" ") From 8f6f352ecbd6dffe7320aa9775e7b9ce48f74457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 22 Feb 2021 20:01:33 +0800 Subject: [PATCH 123/211] Iris: tactics to manage the context --- .../targets/iris/IrisCertificate.scala | 45 ++++++++----------- .../vst/translation/VSTProofTranslator.scala | 2 +- 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 57311a884..49679aa26 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -13,8 +13,6 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |From iris.heap_lang Require Import lang notation proofmode. |From Hammer Require Import Hammer. |Context `{!heapG Σ}. - |Context {PROP : bi}. - |Implicit Types Δ : envs PROP. |Set Default Proof Using "Type". | |Definition null_loc : loc := {|loc_car := 0 |}. @@ -23,25 +21,11 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Definition loc_at x lx := x = LitV (LitLoc lx). |Definition int_at x vx := x = LitV (LitInt vx). | - |(* This is a less clever version of tac_and_destruct, which - | does NOT break ↦ assertions into fractional assertions. *) - |Lemma tac_sep_destruct Δ i p j1 j2 P P1 P2 Q : - | envs_lookup i Δ = Some (p, P) → - | (if p then IntoAnd true P P1 P2 else (P -∗ P1 ∗ P2)) → - | match envs_simple_replace i p (Esnoc (Esnoc Enil j1 P1) j2 P2) Δ with - | | None => False - | | Some Δ' => envs_entails Δ' Q - | end → - | envs_entails Δ Q. - |Proof. - | destruct (envs_simple_replace _ _ _ _) as [Δ'|] eqn:Hrep; last done. - | rewrite envs_entails_eq. intros H H0 H1. rewrite envs_simple_replace_sound //=. destruct p. - | by rewrite (into_and _ P) /= right_id -(comm _ P1) bi.wand_elim_r. - | by rewrite /= right_id -(comm _ P1) H1 -H0 bi.wand_elim_r. - |Qed. + |Remove Hints fractional.into_sep_fractional fractional.into_sep_fractional_half : typeclass_instances. | + |(* This is exactly iAndDestruct in ltac_tactics.v, which is not exported. *) |Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := - | eapply tac_sep_destruct with H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) + | eapply tac_and_destruct with H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) | [pm_reflexivity | |pm_reduce; iSolveTC | |pm_reduce; @@ -52,13 +36,15 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | |Local Ltac iSplitAllHyps := | iStartProof; - | let rec go Hs := - | match Hs with [] => idtac | ?H :: ?Hs => + | let rec go Hs success := + | match Hs with + | [] => match success with | true => idtac | false => fail end + | | ?H :: ?Hs => | let Hn := iFresh in - | try iAndDestruct H as H Hn; go Hs end in + | (iAndDestruct H as H Hn; go Hs true) || go Hs success end in | match goal with | | |- envs_entails ?Δ _ => - | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs + | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs false | end. | |Local Ltac iFindApply := @@ -78,12 +64,17 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | | [H: bool_decide _ = _ |- _ ] => rewrite H | end. | + |Local Ltac printGoal := + | match goal with + | | [|- ?q] => idtac q + | end. + | |Local Ltac iSimplContext := - | wp_pures; iSplitAllHyps; iRewriteHyp; iSimpl in "# ∗"; iSimpl. + | (repeat wp_pures); iRewriteHyp; iSimpl in "# ∗"; iSimpl. | - |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. - |Ltac ssl_load := wp_load; wp_let. - |Ltac ssl_store := wp_store. + |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); repeat (iSimplContext; iSplitAllHyps). + |Ltac ssl_load := wp_load; wp_let; iSimplContext. + |Ltac ssl_store := wp_store; iSimplContext. |Ltac ssl_finish := by iFindApply; iFrame "% # ∗". | |""".stripMargin diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index fbdcabc4e..d7ccdfd3e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -341,7 +341,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val existentials = predicate.findExistentials(constructor)(predicate_clause).map({ case (name,ty) => (fresh_exist_renaming(name), ty) }) - val constructor_args = constructor.constructor_args.map(v => ProofCVar(fresh_exist_renaming(v), CoqCardType(predicate.name))) + val constructor_args = constructor.constructorArgs.map(v => ProofCVar(fresh_exist_renaming(v), CoqCardType(predicate.name))) ctx = ctx with_existential_variables_of existentials ctx = ctx with_ghost_existentials_of (constructor_args.map(v => (v.name, v.typ))) ctx = ctx with_mapping_between(cardinality_var, ProofCCardinalityConstructor( From dda272efdcdd753cf44e205ad535afdadb3bec04 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 22 Feb 2021 20:13:21 +0800 Subject: [PATCH 124/211] makes VST tree-copy, tree-free, list-free, list-copy, min2, swap, swap1, swap2, work. --- .../targets/vst/logic/Expressions.scala | 6 ++-- .../targets/vst/logic/ProofTerms.scala | 4 +-- .../targets/vst/logic/VSTProofStep.scala | 8 +++-- .../vst/translation/VSTProofTranslator.scala | 36 ++++++++++++++----- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 3f6567fba..0d5e75fc0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -90,11 +90,13 @@ object Expressions { /** Applies a substitution to an expression */ - def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { + def subst(mapping: Map[String, ProofCExpr], recurse: Boolean=true): ProofCExpr = this match { case expr@ProofCVar(name, _) => mapping.get(name) match { - case Some(value) => value.subst(mapping) + case Some(value) if recurse => value.subst(mapping) + case Some(value) => value case None => expr } + case expr@ProofCNullval => expr case expr@ProofCBoolConst(_) => expr case expr@ProofCIntConst(_) => expr case ProofCIntSetLiteral(elems) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 96e88a7ae..a55733516 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -327,9 +327,9 @@ object ProofTerms { case class HelpUnfold(predicate: VSTPredicate, cardConstructor: CardConstructor, pclause: VSTPredicateClause) extends VSTPredicateHelper { override def pp: String = { s"Lemma ${predicate.unfold_lemma_name(cardConstructor)} " + + s"${cardConstructor.constructor_args.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + - s"${cardConstructor.constructor_args.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} : " + - s"${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ + s": ${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructor_args) }) = ${predicate.pp_constructor_clause(cardConstructor, pclause)}. Proof. auto. Qed." } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 87a21da8d..5e9c30b0e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -155,9 +155,13 @@ object VSTProofStep { override def pp: String = "" } - case class UnfoldRewrite(rewrite_name: String) extends VSTProofStep { + case class UnfoldRewrite(rewrite_name: String, constructor_args: List[Option[ProofCExpr]]) extends VSTProofStep { + def pp_o_expr (expr: Option[ProofCExpr]) : String = expr match { + case None => "_" + case Some(expr) => expr.pp + } override def pp: String = - s"rewrite ${rewrite_name} at 1." + s"ssl_rewrite_last (${rewrite_name} ${constructor_args.map(pp_o_expr).mkString(" ")})." } case object ForwardEntailer extends VSTProofStep { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 190013c95..86fe17fc1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -12,7 +12,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, SubstVar, Var} import org.tygus.suslik.language.{Expressions, Ident, SSLType, Statements} -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{PendingCall, VSTClientContext} +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{PendingCall, VSTClientContext, normalize_renaming} import org.tygus.suslik.language.Statements.Load object VSTProofTranslator { @@ -133,7 +133,7 @@ object VSTProofTranslator { def with_mapping_between(from: String, to: Expr): VSTClientContext = { val target_type = typing_context.get(from) - val to_expr = ProofSpecTranslation.translate_expression(coq_context)(to, target = target_type) + val to_expr = ProofSpecTranslation.translate_expression(typing_context)(to, target = target_type) with_mapping_between(from, to_expr) } @@ -154,6 +154,16 @@ object VSTProofTranslator { VSTClientContext(pred_map, spec_map, Map(), Map(), Map(), Map(), Map(), None) } + def normalize_renaming(renaming: Map[String, String]) = { + var old_mapping = renaming + var mapping = old_mapping.map(v => (v._1, renaming.getOrElse(v._2, v._2))) + while(mapping != old_mapping) { + old_mapping = mapping + mapping = mapping.map(v => (v._1, renaming.getOrElse(v._2, v._2))) + } + mapping + } + } case class VSTProofTranslator(spec: FormalSpecification) extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { @@ -205,10 +215,13 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl /** Call handling */ case SuslikProofStep.AbduceCall(new_vars, f_pre, callePost, call, freshSub, freshToActual, f, gamma) => - // Abduce call emits no immediate steps, but pus + // Abduce call does two things + // - Adds a set of ghost existential variables (the parameters of the function) to the context + // - sets a pending function call on the context + // (After the abduce call has ended, we can eliminate the ghost existential variables, and use deferreds to do so) var ctx = clientContext val fun_spec = ctx.spec_map(f.name) - val renaming = freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) } + val renaming = normalize_renaming(freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) }) val function_params = fun_spec.params.map((pair) => (renaming(pair._1), pair._2)) val function_result_existentials = fun_spec.existensial_params.map { case (name, ty) => (renaming(name), ty) } val function_preconds = fun_spec.precondition.pure_constraints.map(_.rename(renaming)) @@ -269,6 +282,8 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl var ctx = clientContext ctx = ctx with_variables_of List(new_var) ctx = ctx with_mapping_between (ghostFrom, ProofCVar(ghostTo, CoqPtrValType)) + ctx = ctx with_renaming ghostFrom -> ghostTo + val steps = List( Malloc(sz), Intros(List(new_var)) @@ -333,9 +348,11 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl /** Predicate folding/unfolding */ case SuslikProofStep.Close(app, selector, asn, fresh_exist) => var ctx = clientContext - val fresh_exist_renaming = fresh_exist.map({case (Var(from), Var(to)) => (from,to)}) + val fresh_exist_renaming = normalize_renaming(fresh_exist.map({case (Var(from), Var(to)) => (from,to)})) val predicate_name = app.pred val cardinality_var = app.card.asInstanceOf[Var].name + val close_args = app.args.map(ProofSpecTranslation.translate_expression(ctx.typing_context)(_)) + val predicate = clientContext.pred_map(predicate_name) val (constructor, predicate_clause) = predicate.clause_by_selector(selector) val existentials = predicate.findExistentials(constructor)(predicate_clause).map({ @@ -351,12 +368,15 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl )) val deferred_steps = (base_ctx: VSTClientContext) => { var ctx = base_ctx - ctx = ctx without_existentials_of existentials.map(_._1) - ctx = ctx without_ghost_existentials_of constructor_args.map(_.name) val unfold_step = UnfoldRewrite( - predicate.unfold_lemma_name(constructor) + predicate.unfold_lemma_name(constructor), + constructor_args.map(v => Some(ctx.resolve_existential(v.name))) // ++ + // close_args.map(v => Some(v.rename(ctx.renamings).subst(ctx.variable_map, recurse=false))) ) val existential_steps = existentials.map(v => Exists(ctx.resolve_existential(v._1))) + + ctx = ctx without_existentials_of existentials.map(_._1) + ctx = ctx without_ghost_existentials_of constructor_args.map(_.name) (List(unfold_step) ++ existential_steps ++ List(Entailer), ctx) } Result(List(), List((List(), Some(deferred_steps), ctx))) From e568d2613d4bd9f5ff4deed4c6360dfbaed75daa Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 22 Feb 2021 20:15:28 +0800 Subject: [PATCH 125/211] makes VST tree-copy, tree-free, list-free, list-copy, min2, swap, swap1, swap2, work. --- .../suslik/certification/targets/vst/logic/ProofTerms.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index e748d4510..fbb4a5d93 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -327,10 +327,10 @@ object ProofTerms { case class HelpUnfold(predicate: VSTPredicate, cardConstructor: CardConstructor, pclause: VSTPredicateClause) extends VSTPredicateHelper { override def pp: String = { s"Lemma ${predicate.unfold_lemma_name(cardConstructor)} " + - s"${cardConstructor.constructor_args.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} " + + s"${cardConstructor.constructorArgs.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + s": ${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ - predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructor_args) + predicate.expandArgs(pclause.sub_constructor)(cardConstructor.constructorArgs) }) = ${predicate.pp_constructor_clause(cardConstructor, pclause)}. Proof. auto. Qed." } } From ef0044e8130ad9a2fdc3b8dacb08cef89f7d1378 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Feb 2021 23:46:47 +0800 Subject: [PATCH 126/211] HTT: More principled sslauto --- .../targets/htt/language/Expressions.scala | 17 ++--- .../targets/htt/logic/Proof.scala | 3 + .../targets/htt/logic/Sentences.scala | 12 +++- .../htt/translation/HTTTranslator.scala | 16 +++-- .../htt/translation/ProofContext.scala | 48 +++++++++++-- .../htt/translation/ProofTranslator.scala | 71 +++++++++++++++---- 6 files changed, 125 insertions(+), 42 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 83ea23508..b707983b7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -8,7 +8,6 @@ object Expressions { type CSubstVar = Map[CVar, CVar] sealed abstract class CExpr extends PrettyPrinting { - def isTrivial: Boolean = this == CBoolConst(true) def isCard: Boolean = this.vars.exists(_.isCard) def collect[R <: CExpr](p: CExpr => Boolean): Seq[R] = { @@ -87,11 +86,6 @@ object Expressions { def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]) def cardVars: Seq[CVar] = collect(_.isInstanceOf[CSApp]).flatMap {v: CSApp => v.card.vars} - - def conjuncts: Seq[CExpr] = this match { - case CBinaryExpr(COpAnd, left, right) => left.conjuncts ++ right.conjuncts - case _ => Seq(this) - } } case class CVar(name: String) extends CExpr { @@ -127,15 +121,12 @@ object Expressions { override def pp: String = op match { case COpSubset => s"@sub_mem nat_eqType (mem (${left.pp})) (mem (${right.pp}))" case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" - case _ => s"${left.pp} ${op.pp} ${right.pp}" + case _ => s"(${left.pp}) ${op.pp} (${right.pp})" } } case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { - override def pp: String = op match { - case COpNot => s"(${e.pp}) = false" - case _ => s"${op.pp} (${e.pp})" - } + override def pp: String = s"${op.pp} (${e.pp})" } case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { @@ -231,11 +222,11 @@ object Expressions { } object COpAnd extends CBinOp { - override def pp: String = "/\\" + override def pp: String = "&&" } object COpOr extends CBinOp { - override def pp: String = "\\/" + override def pp: String = "||" } object COpUnion extends CBinOp { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index 8d26ef54c..bbc414d87 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -136,6 +136,9 @@ object Proof { case class Branch(cond: CExpr) extends Step { override def pp: String = s"ssl_abduce_branch (${cond.pp})" } + case object FrameUnfold extends Step { + override def pp: String = "ssl_frame_unfold" + } case object Emp extends Step { override def pp: String = "ssl_emp" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index c10ce31ea..31adddf43 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -5,7 +5,15 @@ import org.tygus.suslik.certification.targets.htt.language.{CFormals, CGamma, Pr import org.tygus.suslik.certification.targets.htt.language.Types._ object Sentences { - case class CAssertion(phi: CExpr, sigma: CSFormula) extends PrettyPrinting { + case class CPFormula(conjuncts: Seq[CExpr]) extends PrettyPrinting { + override def pp: String = if (conjuncts.isEmpty) "true" else conjuncts.tail.foldLeft(conjuncts.head.pp) { case (s, e) => s"$s /\\ ${e.pp}" } + def isTrivial: Boolean = conjuncts.isEmpty + def subst(sigma: CSubst): CPFormula = CPFormula(conjuncts.map(_.subst(sigma))) + def vars: Seq[CVar] = conjuncts.flatMap(_.vars).distinct + def cardVars: Seq[CVar] = conjuncts.flatMap(_.cardVars).distinct + } + + case class CAssertion(phi: CPFormula, sigma: CSFormula) extends PrettyPrinting { override def pp: String = if (phi.isTrivial) sigma.pp else s"${phi.pp} /\\ ${sigma.pp}" def existentials(quantifiedVars: Seq[CVar]): Seq[CVar] = valueVars.diff(quantifiedVars) @@ -43,7 +51,7 @@ object Sentences { case CBinaryExpr(_, _, v:CVar) if cardVars.contains(v) => false case _ => true } - val phi1 = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce[CExpr] { case (c1, c2) => CBinaryExpr(COpAnd, c1, c2) } + val phi1 = CPFormula(conjuncts) this.copy(phi = phi1) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala index 9cef21f51..f51734135 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.Types._ import org.tygus.suslik.certification.targets.htt.logic.Proof -import org.tygus.suslik.certification.targets.htt.logic.Sentences.CAssertion +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CPFormula} import org.tygus.suslik.certification.targets.htt.program.Statements._ import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Expressions._ @@ -80,11 +80,11 @@ object HTTTranslator { implicit val pointsToTranslator: HTTTranslator[PointsTo, CPointsTo] = el => CPointsTo(el.loc.translate, el.offset, el.value.translate) implicit val asnTranslator: HTTTranslator[Assertion, CAssertion] = el => { - val conjuncts = el.phi.conjuncts.toSeq.map(c => c.translate.simplify).filterNot(_.isCard) - val f = (a1: CExpr, a2: CExpr) => CBinaryExpr(COpAnd, a1, a2) - val phi = if (conjuncts.isEmpty) CBoolConst(true) else conjuncts.reduce(f) - val sigma = el.sigma.translate - CAssertion(phi, sigma).removeCardConstraints + CAssertion(el.phi.translate, el.sigma.translate).removeCardConstraints + } + + implicit val pFormulaTranslator: HTTTranslator[PFormula, CPFormula] = el => { + CPFormula(el.conjuncts.toSeq.map(c => c.translate.simplify).filterNot(_.isCard)) } implicit val sFormulaTranslator: HTTTranslator[SFormula, CSFormula] = el => { @@ -110,6 +110,10 @@ object HTTTranslator { case (k, v) => k.translate -> v.translate } + implicit val substExprTranslator: HTTTranslator[Map[Expr, Expr], Map[CExpr, CExpr]] = _.map { + case (k, v) => k.translate -> v.translate + } + implicit val gammaTranslator: HTTTranslator[Map[Var, SSLType], Map[CVar, HTTType]] = _.map { case (k, v) => k.translate -> v.translate } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index f9dfb535d..a8b6411e5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -3,26 +3,38 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CSFormula, CSubst, CVar} import org.tygus.suslik.certification.targets.htt.language.Types.HTTType import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause, CInductivePredicate} -import org.tygus.suslik.certification.targets.htt.translation.ProofContext.{PredicateEnv, AppliedConstructor} -import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductivePredicate} +import org.tygus.suslik.certification.targets.htt.translation.ProofContext.{AppliedConstructor, PredicateEnv} +import org.tygus.suslik.certification.traversal.Evaluator.{ClientContext, EvaluatorException} +import scala.collection.immutable.ListMap import scala.collection.mutable.ListBuffer -case class ProofContext(predicates: PredicateEnv = Map.empty, - postEx: Map[CVar, (HTTType, CExpr)] = Map.empty, +case class ProofContext(// Map of predicates referenced by the spec + predicates: PredicateEnv = Map.empty, + // Post-condition existentials + postEx: ListMap[CVar, (HTTType, CExpr)] = ListMap.empty, + // Used to look up the latest form of a predicate application, after variable substitutions appAliases: Map[CSApp, CSApp] = Map.empty, + // Unfoldings that occur during evaluation, used to calculate heap existentials unfoldings: Map[CSApp, AppliedConstructor] = Map.empty, + // A nested proof goal; present during call abduction callGoal: Option[Proof.Goal] = None, + // Used to generate a unique name for a heap generated by a call nextCallId: Int = 0, - hints: ListBuffer[Hint] = new ListBuffer[Hint]) + // Hints accumulated during evaluation + hints: ListBuffer[Hint] = new ListBuffer[Hint], + // Current number of subgoals; used to calculate how many times to shelve/unshelve + numSubgoals: Int = 0, + // Used to track which post-condition predicate assertions remain to be solved + appsToSolve: Seq[CSApp] = Seq.empty) extends ClientContext[Proof.Step] { /** * Get existentials that result from unfolding an app; heap existentials are provided in * their maximally unfolded form (i.e., accounts for nested unfolds) */ - def unfoldedAppExistentials(app: CSApp): (Seq[CExpr], Seq[CSFormula]) = { + def existentialsAfterUnfolding(app: CSApp): (Seq[CExpr], Seq[CSFormula]) = { unfoldings.get(app) match { case Some(constructor) => val heapEx = constructor.asn.sigma.apps.map(unfoldedApp) @@ -31,6 +43,28 @@ case class ProofContext(predicates: PredicateEnv = Map.empty, } } + /** + * Get new apps that result from unfolding an app + */ + def newAppsAfterUnfolding(app: CSApp): Seq[CSApp] = { + unfoldings.get(app) match { + case Some(constructor) => constructor.asn.sigma.apps + case None => Nil + } + } + + /** + * Solve specified app and update the list of apps to solve + */ + def solveApp(app: CSApp, newApps: Seq[CSApp]): ProofContext = { + appsToSolve.indexOf(app) match { + case -1 => throw EvaluatorException(s"${app.pp} not found in list of predicate applications to solve") + case idx => + val (before, after) = appsToSolve.splitAt(idx) + this.copy(appsToSolve = before ++ newApps ++ after.tail) + } + } + /** * Get the latest version of an app, which may change if one of its argument variables is substituted */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index c26f90e9b..110f50f26 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -3,14 +3,17 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.language.Expressions.{CExpr, CSApp, CVar} import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductiveClause} +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CAssertion import org.tygus.suslik.certification.targets.htt.translation.ProofContext.AppliedConstructor import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Evaluator.Deferred +import org.tygus.suslik.certification.traversal.Evaluator.{Deferred, EvaluatorException} import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.language.Expressions.Var -import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} +import org.tygus.suslik.language.Statements.{Free, Load, Malloc, Store} +import org.tygus.suslik.logic.SApp + +import scala.collection.immutable.ListMap object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofContext] { private def withNoDeferred(ctx: ProofContext): (List[Proof.Step], Option[Deferred[Proof.Step, ProofContext]], ProofContext) = (Nil, None, ctx) @@ -54,6 +57,18 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont Result(steps, List(withNoDeferred(ctx1))) } + def handleShelving(app: CSApp, newApps: Seq[CSApp], ctx: ProofContext): (List[Proof.Step], List[Proof.Step]) = { + ctx.appsToSolve.indexOf(app) match { + case -1 => throw EvaluatorException(s"${app.pp} not found in list of predicate applications to solve") + case 0 => (Nil, Nil) + case idx => + val remainingGoals = ctx.appsToSolve.length - idx - 1 + newApps.length + ctx.numSubgoals + val preSteps = List.tabulate(idx)(_ => Proof.Shelve) + val postSteps = List.tabulate(remainingGoals)(_ => Proof.Shelve) ++ (if (idx + remainingGoals == 0) Nil else List(Proof.Unshelve)) + (preSteps, postSteps) + } + } + override def translate(value: SuslikProofStep, ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { value match { /** Initialization */ @@ -86,14 +101,16 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont }).toList val steps = valueEx :: heapEx ++ List(Proof.Auto) + val appsToSolve = cgoal.post.sigma.apps.map(ctx.currentAppAlias) + val ctx1 = ctx.copy(appsToSolve = appsToSolve) - (steps, ctx) + (steps, ctx1) } // initialize context with post-condition info and predicate hints, if any val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) - val postEx = cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)).toMap + val postEx = ListMap(cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)): _*) val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) Result(steps, List((Nil, Some(deferred), ctx1))) @@ -108,7 +125,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont /** Control flow */ case SuslikProofStep.Branch(cond, _) => - val childContexts = List(ctx, ctx) + val childContexts = List(ctx.copy(numSubgoals = ctx.numSubgoals + 1), ctx) Result(List(Proof.Branch(cond.translate)), childContexts.map(withNoDeferred)) case SuslikProofStep.Open(sapp, freshExistentials, freshParamArgs, selectors) => val exSub = freshExistentials.translate @@ -132,7 +149,8 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val numClauses = pred.clauses.length val childCtxs = clauses.map { case (idx, _, asn) => val newApps = asn.sigma.apps.map(a => a -> a).toMap - ctx.copy(appAliases = ctx.appAliases ++ newApps) + val numSubgoals = numClauses - idx + ctx.numSubgoals + ctx.copy(appAliases = ctx.appAliases ++ newApps, numSubgoals = numSubgoals) } Result(steps, branchSteps.zip(childCtxs).map { case (s, c) => (s, None, c) }) @@ -169,6 +187,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val post = callGoal.post.subst(sub) val universalGhosts = callGoal.universalGhosts.filterNot(_.isCard).map(_.subst(sub)) val existentials = callGoal.existentials.filterNot(_.isCard).map(_.substVar(sub)) + val preApps = pre.sigma.apps.map(ctx.currentAppAlias) val steps = List( // Move the part of the heap relevant to the call abduction to the beginning @@ -176,18 +195,21 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // Provide the necessary existentials so that the precondition of the call goal is met, // and then execute the call Proof.Call(universalGhosts), - Proof.Exists(pre.sigma.apps.map(ctx.currentAppAlias).map(ctx.unfoldedApp)) andThen, - Proof.Auto, + Proof.Exists(preApps.map(ctx.unfoldedApp)) andThen, + Proof.Auto + ) + + val childSteps = List( Proof.MoveToCtx(List(CVar(s"h_call$callId"))), // The postcondition of the call abduction becomes the precondition of the companion Proof.ElimExistential(existentials), - Proof.ElimExistential(post.heapVars), + Proof.ElimExistential(post.heapVars) ) ++ initPre(post, s"call$callId") ++ List(Proof.StoreValid) val newApps = post.sigma.apps.map(a => a -> a).toMap - val ctx1 = ctx.copy(callGoal = None, nextCallId = callId + 1, appAliases = ctx.appAliases ++ newApps) + val ctx1 = ctx.copy(appsToSolve = preApps, callGoal = None, nextCallId = callId + 1, appAliases = ctx.appAliases ++ newApps) - Result(steps, List(withNoDeferred(ctx1))) + Result(steps, List((childSteps, None, ctx1))) /** Substitutions and unfoldings */ case SuslikProofStep.Pick(from, to) => @@ -205,6 +227,21 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont case SuslikProofStep.SubstR(from, to) => val m = Map(from.translate -> to.translate) handleSubstitution(m, ctx) + case SuslikProofStep.FrameUnfold(_, h_post) => + h_post match { + case app:SApp => + val csapp = app.translate + val deferred = (ctx: ProofContext)=> { + val csapp1 = ctx.currentAppAlias(csapp) + val newApps = ctx.newAppsAfterUnfolding(csapp1) + val (preSteps, postSteps) = handleShelving(csapp1, newApps, ctx) + val ctx1 = ctx.solveApp(csapp1, newApps) + + (preSteps ++ List(Proof.FrameUnfold) ++ postSteps, ctx1) + } + Result(List(), List((Nil, Some(deferred), ctx))) + case _ => Result(List(), List(withNoDeferred(ctx))) + } case SuslikProofStep.Close(app, selector, asn, fresh_exist) => // build an instance of an invoked inductive clause with synthesis info val csapp = app.translate @@ -216,13 +253,19 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // create deferred to perform unfolding val deferred = (ctx: ProofContext) => { val csapp1 = ctx.currentAppAlias(csapp) - val (valueEx, heapEx) = ctx.unfoldedAppExistentials(csapp1) + val (valueEx, heapEx) = ctx.existentialsAfterUnfolding(csapp1) + + val newApps = ctx.newAppsAfterUnfolding(csapp1) + val (preSteps, postSteps) = handleShelving(csapp1, newApps, ctx) + val ctx1 = ctx.solveApp(csapp1, newApps) + val steps = List( Proof.UnfoldConstructor(constructor.idx) andThen, Proof.Exists(valueEx ++ heapEx) andThen, Proof.Auto ) - (steps, ctx) + + (preSteps ++ steps ++ postSteps, ctx1) } // update context with unfolding info From ea05612b0eeb091abd92a0d3d717e7bd02580a6b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Feb 2021 23:51:06 +0800 Subject: [PATCH 127/211] Capture unification info better --- .../certification/source/SuslikProofStep.scala | 14 +++++++++----- .../targets/htt/language/Expressions.scala | 14 ++++++++++++++ .../targets/htt/translation/ProofContext.scala | 14 ++++++++++++++ .../targets/htt/translation/ProofTranslator.scala | 6 ++++++ .../vst/translation/VSTProgramTranslator.scala | 1 + .../vst/translation/VSTProofTranslator.scala | 1 + .../org/tygus/suslik/synthesis/StmtProducer.scala | 5 ++++- .../suslik/synthesis/rules/UnificationRules.scala | 2 +- 8 files changed, 50 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index 9cacd4d24..1e327ac91 100644 --- a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -116,7 +116,7 @@ case class AbduceCall( /** unification of heap (ignores block/pure distinction) */ - case class HeapUnify(subst: Map[Var, Expr]) extends SuslikProofStep { + case class HeapUnify(subst: Map[Expr, Expr]) extends SuslikProofStep { override def pp: String = s"HeapUnify(${subst.mkString(",")});" } @@ -125,6 +125,10 @@ case class AbduceCall( override def pp: String = s"HeapUnifyPointer(${from.pp} -> ${to.pp});" } + case class HeapUnifyUnfold(preApp: SApp, postApp: SApp, subst: Map[Expr, Expr]) extends SuslikProofStep { + override def pp: String = s"HeapUnifyUnfold(${preApp.pp}, ${postApp.pp}, ${subst.mkString(",")})" + } + /** unfolds frame */ case class FrameUnfold(h_pre: Heaplet, h_post: Heaplet) extends SuslikProofStep { override def pp: String = s"FrameUnfold(${h_pre.pp}, ${h_post.pp});" @@ -315,7 +319,7 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyPure => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(UnificationProducer(_, _, subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) @@ -323,15 +327,15 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyUnfolding => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(UnificationProducer(preApp:SApp, postApp: SApp, subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { - case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) + case ::(head, Nil) => SuslikProofStep.HeapUnifyUnfold(preApp, postApp, subst) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() } case UnificationRules.HeapUnifyBlock => node.kont match { - case ChainedProducer(ChainedProducer(ChainedProducer(SubstMapProducer(subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => + case ChainedProducer(ChainedProducer(ChainedProducer(UnificationProducer(_, _, subst), IdProducer), HandleGuard(_)), ExtractHelper(_)) => node.children match { case ::(head, Nil) => SuslikProofStep.HeapUnify(subst) case ls => fail_with_bad_children(ls, 1) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index b707983b7..5fd18acd1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -65,6 +65,20 @@ object Expressions { this } + def substExpr(sigma: Map[CExpr, CExpr]): CExpr = sigma.get(this) match { + case Some(e) => e + case None => this match { + case v: CVar => v + case CBinaryExpr(op, left, right) => CBinaryExpr(op, left.substExpr(sigma), right.substExpr(sigma)) + case CUnaryExpr(op, arg) => CUnaryExpr(op, arg.substExpr(sigma)) + case CSetLiteral(elems) => CSetLiteral(elems.map(_.substExpr(sigma))) + case CIfThenElse(cond, left, right) => CIfThenElse(cond, left.substExpr(sigma), right.substExpr(sigma)) + case CSApp(pred, args, card) => CSApp(pred, args.map(_.substExpr(sigma)), card.substExpr(sigma)) + case CPointsTo(loc, offset, value) => CPointsTo(loc.substExpr(sigma), offset, value.substExpr(sigma)) + case _ => this + } + } + def simplify: CExpr = this match { case CBinaryExpr(op, left, right) => if (op == COpAnd) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index a8b6411e5..4129f0ca1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -94,6 +94,20 @@ case class ProofContext(// Map of predicates referenced by the spec this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) } + def withUnify(preApp: CSApp, postApp: CSApp, m: Map[CExpr, CExpr]): ProofContext = { + val m1 = m.toSeq.map(p => (p._2, p._1)).toMap + val postEx1 = postEx.map { case (k, v) => k -> (v._1, v._2.substExpr(m1)) } + val appAliases1 = appAliases + (postApp -> preApp) + val unfoldings1 = unfoldings.map { case (app, c) => + val app1 = if (app == postApp) preApp else app + val asn1 = c.asn.copy(sigma = c.asn.sigma.copy(apps = c.asn.sigma.apps.map(a => if (a == postApp) preApp else a))) + val existentials1 = c.existentials.map(_.substExpr(m1)) + val c1 = c.copy(existentials = existentials1, asn = asn1) + app1 -> c1 + } + this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) + } + def appsAffectedBySubst(m: Map[CVar, CExpr]): Map[CSApp, CSApp] = appAliases.foldLeft(Map[CSApp, CSApp]()) { case (affectedApps, (app, alias)) => if (app == alias) { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 110f50f26..9ddfa0727 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -227,6 +227,12 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont case SuslikProofStep.SubstR(from, to) => val m = Map(from.translate -> to.translate) handleSubstitution(m, ctx) + case SuslikProofStep.HeapUnifyUnfold(preApp, postApp, m) => + val cpreApp = preApp.translate + val cpostApp = postApp.translate + val ctx1 = ctx.withUnify(cpreApp, cpostApp, m.translate) + val steps = renameAppsStep(Map(cpostApp -> cpreApp)) + Result(steps, List((Nil, None, ctx1))) case SuslikProofStep.FrameUnfold(_, h_post) => h_post match { case app:SApp => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index f4e2d9f12..8289f4d30 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -95,6 +95,7 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS | SuslikProofStep.PickArg(_, _) | SuslikProofStep.AbduceCall(_, _, _, _, _, _, _, _) | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyUnfold(_, _, _) | SuslikProofStep.HeapUnifyPointer(_, _) | SuslikProofStep.FrameUnfold(_, _) | SuslikProofStep.WeakenPre(_) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 1795e0800..d5caec384 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -433,6 +433,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.CheckPost(_, _) | SuslikProofStep.WeakenPre(_) | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyUnfold(_, _, _) | SuslikProofStep.HeapUnifyPointer(_, _) | SuslikProofStep.StarPartial(_, _) | SuslikProofStep.FrameUnfold(_, _) => diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 803943d32..2eccb768a 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.synthesis -import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} +import org.tygus.suslik.language.Expressions.{Expr, ExprSubst, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements._ import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, PFormula, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} @@ -182,4 +182,7 @@ object StmtProducer { // Captures entailments emitted by SMT case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula) extends StmtProducer with Noop + + // Captures heap unifications + case class UnificationProducer(preHeaplet: Heaplet, postHeaplet: Heaplet, subst: ExprSubst) extends StmtProducer with Noop } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 3b763cd7b..4d2f8e822 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -49,7 +49,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils // val newGoal = goal.spawnChild(post = newPost, callGoal = newCallGoal) val newPost = Assertion(post.phi && subExpr, newPostSigma) val newGoal = goal.spawnChild(post = newPost) - val kont = SubstMapProducer(sub.asInstanceOf[SubstVar]) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) + val kont = UnificationProducer(t, s, sub) >> IdProducer >> HandleGuard(goal) >> ExtractHelper(goal) RuleResult(List(newGoal), kont, this, goal) } nubBy[RuleResult, Assertion](alternatives, sub => sub.subgoals.head.post) From b0b018dad480ad9db6af0a5352edfc9788caafef Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 22 Feb 2021 23:51:17 +0800 Subject: [PATCH 128/211] Add/fix benchmarks --- .../synthesis/certification/account/common.def | 3 +++ .../synthesis/certification/account/mk-acc.syn | 12 ++++++++++++ .../certification/tree/tree-flatten-acc.syn | 4 ++-- src/untitled.sc | 0 4 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 src/test/resources/synthesis/certification/account/common.def create mode 100644 src/test/resources/synthesis/certification/account/mk-acc.syn delete mode 100644 src/untitled.sc diff --git a/src/test/resources/synthesis/certification/account/common.def b/src/test/resources/synthesis/certification/account/common.def new file mode 100644 index 000000000..c93e0e6cb --- /dev/null +++ b/src/test/resources/synthesis/certification/account/common.def @@ -0,0 +1,3 @@ +predicate account(loc x, int id, int bal) { +| true => { [x, 2] ** x :-> id ** (x + 1) :-> bal } +} diff --git a/src/test/resources/synthesis/certification/account/mk-acc.syn b/src/test/resources/synthesis/certification/account/mk-acc.syn new file mode 100644 index 000000000..09adbff74 --- /dev/null +++ b/src/test/resources/synthesis/certification/account/mk-acc.syn @@ -0,0 +1,12 @@ +#. -b true +An account constructor + +### + +{ r :-> bal } + +void mk_acc (loc r, int id) + +{ r :-> x ** account(x, id, bal) } + +### diff --git a/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn b/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn index d808e986c..ac2caf6fe 100644 --- a/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn +++ b/src/test/resources/synthesis/certification/tree/tree-flatten-acc.syn @@ -3,12 +3,12 @@ should be able to flatten the tree into a list given a list accumulator #### { true ; tree(x, s) ** z :-> y ** sll(y, acc)} -void tree_flatten(loc x, loc z) +void tree_flatten_acc(loc x, loc z) { s1 =i s ++ acc ; z :-> t ** sll(t, s1)} #### -void tree_flatten (loc x, loc z) { +void tree_flatten_acc (loc x, loc z) { if (x == null) { } else { let v = *x; diff --git a/src/untitled.sc b/src/untitled.sc deleted file mode 100644 index e69de29bb..000000000 From d1042580672fe63eb7729cfa81c4a4abe660900c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 23 Feb 2021 11:01:29 +0800 Subject: [PATCH 129/211] HTT: Fix build --- .../certification/targets/htt/translation/ProofContext.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index 4129f0ca1..0b120bd83 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -88,7 +88,7 @@ case class ProofContext(// Map of predicates referenced by the spec * Update the current context with new substitutions */ def withSubst(m: Map[CVar, CExpr], affectedApps: Map[CSApp, CSApp]): ProofContext = { - val postEx1 = postEx.mapValues(v => (v._1, v._2.subst(m))) + val postEx1 = postEx.map { case (k, v) => k -> (v._1, v._2.subst(m)) } val appAliases1 = affectedApps.foldLeft(appAliases) { case (appAliases, (app, alias)) => appAliases + (app -> alias) + (alias -> alias) } val unfoldings1 = unfoldings.map { case (app, constructor) => app.subst(m) -> constructor.subst(m) } this.copy(postEx = postEx1, appAliases = appAliases1, unfoldings = unfoldings1) From d6b276877a52e8c078ef2638dd68cbd6388f4204 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Tue, 23 Feb 2021 16:29:19 +0800 Subject: [PATCH 130/211] Iris: WIP predicates --- .../targets/iris/IrisCertificate.scala | 11 ++++++----- .../targets/iris/heaplang/Expressions.scala | 2 +- .../targets/iris/logic/Assertions.scala | 9 +++++++-- .../targets/iris/translation/IrisTranslator.scala | 13 ++++++++++--- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 49679aa26..cfa7b00e1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -16,10 +16,9 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Set Default Proof Using "Type". | |Definition null_loc : loc := {|loc_car := 0 |}. - |Definition nullptr : val := LitV (LitLoc null_loc). | |Definition loc_at x lx := x = LitV (LitLoc lx). - |Definition int_at x vx := x = LitV (LitInt vx). + |Definition Z_at x vx := x = LitV (LitInt vx). | |Remove Hints fractional.into_sep_fractional fractional.into_sep_fractional_half : typeclass_instances. | @@ -60,7 +59,7 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Local Ltac iRewriteHyp := | repeat match goal with | | [H: loc_at _ _ |- _ ] => rewrite H - | | [H: int_at _ _ |- _ ] => rewrite H + | | [H: Z_at _ _ |- _ ] => rewrite H | | [H: bool_decide _ = _ |- _ ] => rewrite H | end. | @@ -69,10 +68,12 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | | [|- ?q] => idtac q | end. | - |Local Ltac iSimplContext := + |Local Ltac iSimplNoSplit := | (repeat wp_pures); iRewriteHyp; iSimpl in "# ∗"; iSimpl. | - |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); repeat (iSimplContext; iSplitAllHyps). + |Local Ltac iSimplContext := iSimplNoSplit; try repeat iSplitAllHyps; iSimplNoSplit. + | + |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. |Ltac ssl_load := wp_load; wp_let; iSimplContext. |Ltac ssl_store := wp_store; iSimplContext. |Ltac ssl_finish := by iFindApply; iFrame "% # ∗". diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index 5ca0116a4..9ffa4d150 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -30,7 +30,7 @@ object Expressions { case class HLitBool(value: Boolean) extends HLit(value.toString) case class HLitLoc(loc: Int) extends HLit(loc.toString) { - override def pp: String = if(loc == 0) "nullptr" else super.pp + override def pp: String = if(loc == 0) "#null_loc" else super.pp } case class HSetLiteral(elems: List[HExpr]) extends HExpr { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index c9dbaac79..bb703f901 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -26,6 +26,8 @@ object Assertions { case class ISpecVar(name: String) extends IQuantifiedVar { override def pp: String = s"${name}" + + override def ppAsPhi: String = super.ppAsPhi } case class ISpecQuantifiedValue(name: String) extends IQuantifiedVar { @@ -39,14 +41,17 @@ object Assertions { case class ISpecUnaryExpr(op: HUnOp, expr: IPureAssertion) extends IPureAssertion { override def pp: String = s"${op.pp} ${expr.pp}" + + override def ppAsPhi: String = s"${op.pp} ${expr.ppAsPhi}" } case class ISpecBinaryExpr(op: HBinOp, left: IPureAssertion, right: IPureAssertion) extends IPureAssertion { override def pp: String = s"(${left.pp} ${op.pp} ${right.pp})" override def ppAsPhi: String = op match { - case HOpLe | HOpLt => s"bool_decide (${left.pp} ${op.pp} ${right.pp})%Z" - case HOpEq => s"bool_decide (${left.pp} ${op.pp} ${right.pp})" + case HOpLe | HOpLt => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})%Z" + case HOpUnion => s"(${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" + case HOpEq => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" case _ => ??? } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 4888e3c58..ea5deac6b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -47,7 +47,8 @@ object IrisTranslator { def visit(expr: Expr): HExpr = expr match { case Var(name) => HProgVar(name) case IntConst(v) => HLitInt(v) - case LocConst(v) => HLitLoc(v) + case LocConst(v) => + HLitLoc(v) case BoolConst(v) => HLitBool(v) case SetLiteral(elems) => HSetLiteral(elems.map(_.translate(exprTranslator))) case UnaryExpr(op, arg) => HUnaryExpr(op.translate, visit(arg)) @@ -134,7 +135,12 @@ object IrisTranslator { implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx) => { val base = f.loc.translate.translate(toSpecExpr, ctx) val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitOffset(f.offset))) else base - val value = f.value.translate.translate(toSpecVal, ctx) + // Sometimes we get PointsTo(Var(x), 0, LocConst(0)) when we should get an IntConst + val valToTranslate = f.value match { + case LocConst(v) if v == 0 => IntConst(0) + case _ => f.value + } + val value = valToTranslate.translate.translate(toSpecVal, ctx) IPointsTo(loc, value) } @@ -187,6 +193,7 @@ object IrisTranslator { implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx) => { assert(ctx.isDefined) + class IPredicateTranslation extends PredicateTranslation[IPureAssertion, ISpatialAssertion, HType, IPredicateClause, IPredicate] { override def translatePredicateParamType(predName: String, ty: SSLType): HType = ty match { case CardType => HCardType(predName) @@ -194,7 +201,7 @@ object IrisTranslator { } // TODO: should this be specExpr or specVal? - override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = expr.translate.translate(toSpecExpr, ctx) + override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = expr.translate.translate(toSpecVal, ctx) override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = heaplets.map(_.translate(heapleatTranslator, ctx)) From 6550f9c7ea0577fe19b20c19bd0bb9a34df14e6e Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 23 Feb 2021 20:37:07 +0800 Subject: [PATCH 131/211] adds support for auxiliary lemmas and adjusts heap unification lemmas --- .../targets/vst/clang/Statements.scala | 9 ++++-- .../targets/vst/logic/Proof.scala | 14 +++++++-- .../targets/vst/logic/ProofTerms.scala | 6 +++- .../targets/vst/logic/VSTProofStep.scala | 4 +-- .../translation/ProofSpecTranslation.scala | 30 +++++++++++-------- .../targets/vst/translation/Translation.scala | 15 ++++++++-- .../vst/translation/VSTProofTranslator.scala | 10 +++++-- .../org/tygus/suslik/logic/Declarations.scala | 9 ++++++ .../tygus/suslik/logic/SpatialFormulas.scala | 2 ++ .../org/tygus/suslik/parsing/SSLParser.scala | 2 +- .../synthesis/rules/UnificationRules.scala | 4 +++ 11 files changed, 77 insertions(+), 28 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index 6dcd7d980..998dcee9f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification.targets.vst.clang import org.tygus.suslik.certification.targets.vst.Types.VSTCType import org.tygus.suslik.certification.targets.vst.logic.Expressions.CLangExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.FormalSpecification import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.language.PrettyPrinting @@ -106,11 +107,11 @@ object Statements { case class CProcedureDefinition( name: String, params: Seq[(String, VSTCType)], - body: ProofTree[StatementStep] + body: ProofTree[StatementStep], + helper_specs: List[FormalSpecification] ) extends PrettyPrinting { val c_prelude = - """ - |#include + s"""#include | |extern void free(void *p); |extern void *malloc(size_t size); @@ -124,6 +125,8 @@ object Statements { |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z | + ${helper_specs.map(spec => s"|${spec.pp_as_c_decl}").mkString("\n")} + | |""".stripMargin override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 35f93dabc..c2b9ac09f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -7,7 +7,12 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTerms._ import org.tygus.suslik.language.PrettyPrinting -case class Proof(name: String, predicates: List[VSTPredicate], spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], uses_free: Boolean = false, uses_malloc: Boolean = false) extends PrettyPrinting { +case class Proof( + name: String, predicates: List[VSTPredicate], + spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], + helper_specs: Map[String, ProofTerms.FormalSpecification], + uses_free: Boolean = false, uses_malloc: Boolean = false + ) extends PrettyPrinting { /** prelude for Coq file */ private def coq_prelude = s""" @@ -53,8 +58,12 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. |""".stripMargin private def library_spec : String = s"""Definition Gprog : funspecs := - | ltac:(with_library prog [${name}_spec${ if (uses_free) {"; free_spec"} else {""}}${ + | ltac:(with_library prog [${name}_spec${ + if (uses_free) {"; free_spec"} else {""} + }${ if (uses_malloc) {"; malloc_spec"} else {""} + }${ + if(helper_specs.nonEmpty) {";" ++ helper_specs.map(v => v._2.spec_name).mkString("; ")} else {""} }]). |""".stripMargin @@ -63,6 +72,7 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. (if (uses_free) { free_defs + "\n" } else { "" }) + (if (uses_malloc) { malloc_defs + "\n" } else { "" }) + predicates.map(_.pp).mkString("\n") + "\n" + + helper_specs.values.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"+ library_spec + "\n" + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index fbb4a5d93..c0389e365 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -94,12 +94,16 @@ object ProofTerms { def params: List[(Ident, VSTType)] = (c_params ++ formal_params).toList + def pp_as_c_decl: String = s"extern void ${name}(${c_params.map({case (name, ctype) => s"${ctype.pp_as_ctype} ${name}"}).mkString(", ")});" + + def spec_name = s"${name}_spec" + override def pp: String = { val formal_args = formal_params.map({ case (var_name, var_type) => s"${var_name}: ${var_type.pp}" }) val c_args = c_params.map({ case (var_name, _) => s"${var_name}: val" }) val FormalCondition(pre_pure_constraints, pre_spatial_constraints) = precondition val FormalCondition(post_pure_constraints, post_spatial_constraints) = postcondition - s"""Definition ${name}_spec := + s"""Definition ${spec_name} := | DECLARE _${name} | WITH ${(c_args ++ formal_args).mkString(", ")} | PRE [ ${c_params.map({ case (_, var_type) => s"${as_vst_type(var_type)}" }).mkString(", ")} ] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 5e9c30b0e..8b5834ea4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -22,8 +22,8 @@ object VSTProofStep { - case object Entailer extends VSTProofStep { - override def pp: String = s"entailer!." + case object TentativeEntailer extends VSTProofStep { + override def pp: String = s"try entailer!." } /** diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 0d4287d52..9ba6647ce 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -225,35 +225,39 @@ object ProofSpecTranslation { } /** translates a Suslik function specification into a proof */ - def translate_conditions(name: String, c_params: List[(Ident, VSTCType)])(goal: Goal): (FormalSpecification, Map[Ident, VSTType]) = { - + def translate_conditions(env: Environment)(f: FunSpec): FormalSpecification = { + val name = f.name + val c_params = f.params.map({case (Var(name), ty) => ty match { + case IntType => (name, CoqIntValType) + case LocType => (name, CoqPtrValType) + }}) // collect all cardinality_params and their associated types - val cardinality_params: Map[String, CoqCardType] = (goal.pre.sigma.chunks ++ goal.post.sigma.chunks).flatMap({ + val cardinality_params: Map[String, CoqCardType] = (f.pre.sigma.chunks ++ f.post.sigma.chunks).flatMap({ case PointsTo(loc, offset, value) => None case Block(loc, sz) => None case SApp(pred, args, tag, Var(name)) => Some(name, CoqCardType(pred)) case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") }).toMap - + val gamma = f.gamma(env) val formal_params: List[(Ident, VSTType)] = { val c_param_set = c_params.map(_._1).toSet - goal.universals + f.universals .map({ case variable@Var(name) => if (cardinality_params.contains(name)) { (name, cardinality_params(name)) } else { - (name, translate_predicate_param_type(goal.gamma(variable))) + (name, translate_predicate_param_type(gamma(variable))) } }) .filterNot({ case (name, _) => c_param_set.contains(name) }).toList } val existential_params: List[(Ident, VSTType)] = - goal.existentials.map({ case variable@Var(name) => + f.existentials().map({ case variable@Var(name) => if (cardinality_params.contains(name)) { (name, cardinality_params(name)) } else { - (name, translate_predicate_param_type(goal.gamma(variable))) + (name, translate_predicate_param_type(gamma(variable))) } }).toList @@ -264,7 +268,7 @@ object ProofSpecTranslation { val precondition: FormalCondition = { val pure_conditions = - goal.pre.phi.conjuncts.map(v => translate_expression(context)(v)) + f.pre.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList ++ (c_params).flatMap({ case (ident, cType) => cType match { case CoqIntValType => Some(IsValidInt(ProofCVar(ident, cType))) @@ -278,23 +282,23 @@ object ProofSpecTranslation { } }) val spatial_conditions: List[VSTHeaplet] = - translate_heaplets(context)(goal.pre.sigma.chunks) + translate_heaplets(context)(f.pre.sigma.chunks) FormalCondition(pure_conditions, spatial_conditions) } val postcondition: FormalCondition = { val pure_conditions = - goal.post.phi.conjuncts.map(v => translate_expression(context)(v)) + f.post.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList val spatial_conditions = - translate_heaplets(context)(goal.post.sigma.chunks) + translate_heaplets(context)(f.post.sigma.chunks) // goal.post.sigma.chunks.map(translate_heaplet(context)).toList FormalCondition(pure_conditions, spatial_conditions) } (FormalSpecification( name, c_params, formal_params, existential_params, precondition, postcondition - ), context) + )) } /** translates suslik's inductive predicate into a format that is diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 7ba0b4954..0531f52b6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -33,24 +33,33 @@ object Translation { val base_proof = SuslikProofStep.of_certtree(root) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList predicates.foreach(p => println(p.pp)) + var f_gamma = proc.f.gamma(env) + println(f_gamma) + env.functions.foreach { + case (_, spec) => + val gamma = spec.gamma(env) + println(gamma) + } val params = proc.formals.map({case (Var(name), ty) => ty match { case LocType => (name, CoqPtrValType) case IntType => (name, CoqIntValType) }}) - val (spec, _) = ProofSpecTranslation.translate_conditions(proc.name, params)(root.goal) + val spec = ProofSpecTranslation.translate_conditions(env)(proc.f) + val helper_specs = env.functions.map {case (fname, spec) => (fname, ProofSpecTranslation.translate_conditions(env)(spec))} println(spec.pp) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) - val procedure = CProcedureDefinition(proc.name, params, program_body) + val procedure = CProcedureDefinition(proc.name, params, program_body, helper_specs.values.toList) println(procedure.pp) println(spec.pp) val pred_map = predicates.map(v => (v.name,v)).toMap - val spec_map = Map(proc.name -> spec) + val spec_map = Map(proc.name -> spec) ++ helper_specs val proof_translator = VSTProofTranslator(spec) val steps = translate_proof(base_proof)(proof_translator, VSTClientContext.make_context(pred_map, spec_map)) val proof = Proof( proc.f.name, predicates, spec, steps: ProofTree[VSTProofStep], + helper_specs, uses_free = proof_translator.contains_free, uses_malloc = proof_translator.contains_malloc ) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 1795e0800..bf75f7bdb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqPtrValT import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Entailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, Intros, IntrosTuple, Malloc, Rename, UnfoldRewrite, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, TentativeEntailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, Intros, IntrosTuple, Malloc, Rename, UnfoldRewrite, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} @@ -201,7 +201,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl val steps: List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)) val new_ctx = ctx.without_existentials_of (existentials.map(_._1)) (steps ++ (if (steps.nonEmpty) { - List(Entailer) + List(TentativeEntailer) } else { List() }), new_ctx) @@ -246,6 +246,10 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl // run call rule ForwardCall(call_args), ) ++ + // tentative entailer for complex calls + (if (call.function_name != spec.name) { + List(TentativeEntailer) + } else {List()}) ++ // intro existentials (if (call.existential_params.nonEmpty) { List(IntrosTuple(call.existential_params)) @@ -377,7 +381,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl ctx = ctx without_existentials_of existentials.map(_._1) ctx = ctx without_ghost_existentials_of constructor_args.map(_.name) - (List(unfold_step) ++ existential_steps ++ List(Entailer), ctx) + (List(unfold_step) ++ existential_steps ++ List(TentativeEntailer), ctx) } Result(List(), List((List(), Some(deferred_steps), ctx))) diff --git a/src/main/scala/org/tygus/suslik/logic/Declarations.scala b/src/main/scala/org/tygus/suslik/logic/Declarations.scala index f2a3afd9b..9170aba48 100644 --- a/src/main/scala/org/tygus/suslik/logic/Declarations.scala +++ b/src/main/scala/org/tygus/suslik/logic/Declarations.scala @@ -37,6 +37,12 @@ case class FunSpec(name: Ident, rType: SSLType, params: Formals, this.copy(pre = pre.resolveOverloading(gamma), post = post.resolveOverloading(gamma)) } + def gamma(env: Environment): Gamma = { + val gamma0 = params.toMap // initial environment: derived from the formals + val gamma = resolvePrePost(gamma0, env, pre, post) + gamma + } + def existentials() : List[Var] = { val params = this.params.map(_._1).toSet val formal_params = pre.ghosts(params) @@ -44,6 +50,9 @@ case class FunSpec(name: Ident, rType: SSLType, params: Formals, existentials.toList } + // Currently used universally qualtified variables: program variables and ghosts in pre + def universals : Set[Var] = pre.vars ++ this.params.map(v => v._1) + override def pp: String = { ("" + s"${rType.pp} " diff --git a/src/main/scala/org/tygus/suslik/logic/SpatialFormulas.scala b/src/main/scala/org/tygus/suslik/logic/SpatialFormulas.scala index fb1344125..21d9b9169 100644 --- a/src/main/scala/org/tygus/suslik/logic/SpatialFormulas.scala +++ b/src/main/scala/org/tygus/suslik/logic/SpatialFormulas.scala @@ -223,6 +223,8 @@ case class SFormula(chunks: List[Heaplet]) extends PrettyPrinting with HasExpres def isEmp: Boolean = chunks.isEmpty + def block_size (expr: Expr) = blocks find { case Block(loc,_) if loc == expr => true case _ => false } map (v => v.sz) + // Add h to chunks (multiset semantics) def **(h: Heaplet): SFormula = SFormula(h :: chunks) diff --git a/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala b/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala index 5e76191e6..95f414e2d 100644 --- a/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala +++ b/src/main/scala/org/tygus/suslik/parsing/SSLParser.scala @@ -156,7 +156,7 @@ class SSLParser extends StandardTokenParsers with SepLogicUtils { def varsDeclaration: Parser[Formals] = "[" ~> repsep(formal, ",") <~ "]" def goalFunctionSYN: Parser[FunSpec] = assertion ~ typeParser ~ ident ~ ("(" ~> repsep(formal, ",") <~ ")") ~ varsDeclaration.? ~ assertion ^^ { - case pre ~ tpe ~ name ~ formals ~ vars_decl ~ post => FunSpec(name, tpe, formals, pre, post, vars_decl.getOrElse(Nil)) + case pre ~ tpe ~ name ~ formals ~ vars_decl ~ post => FunSpec(name, tpe, formals, pre, post, vars_decl.getOrElse(Nil)) } def nonGoalFunction: Parser[FunSpec] = typeParser ~ ident ~ ("(" ~> repsep(formal, ",") <~ ")") ~ varsDeclaration.? ~ assertion ~ assertion ^^ { diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 3b763cd7b..76aa083a6 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -19,12 +19,15 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils val exceptionQualifier: String = "rule-unification" + abstract class HeapUnify extends SynthesisRule { def heapletFilter(h: Heaplet): Boolean // Do we have a chance to get rid of the relevant kind of heaplets by only unification and framing? def profilesMatch(pre: SFormula, post: SFormula, exact: Boolean): Boolean + + def apply(goal: Goal): Seq[RuleResult] = { val pre = goal.pre val post = goal.post @@ -82,6 +85,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils PointsTo(y, oy, _) <- postPtss if y.vars.exists(goal.isExistential) t@PointsTo(x, ox, _) <- prePtss + if post.sigma.block_size(y) == pre.sigma.block_size(x) if ox == oy if !postPtss.exists(sameLhs(t)) } yield (y -> x) From 49a8971b984f04361fcfe73ffe05c469021cc3ac Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 24 Feb 2021 11:25:02 +0800 Subject: [PATCH 132/211] dll-dupleton/singleton goes through --- .../targets/vst/logic/Expressions.scala | 19 ++++++++++++++++--- .../vst/translation/VSTProofTranslator.scala | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 0d5e75fc0..30e4d5cde 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -88,11 +88,24 @@ object Expressions { ProofCCardinalityConstructor(pred_type, name, args.map(_.rename(mapping))) } + def variables : List[ProofCVar] = this match { + case ProofCCardinalityConstructor(pred_type, name, args) => args.flatMap(_.variables) + case expr@ProofCVar(name, typ) => List(expr) + case ProofCBoolConst(value) => List() + case ProofCNullval => List() + case ProofCIntConst(value) => List() + case ProofCIntSetLiteral(elems) => elems.flatMap(_.variables) + case ProofCIfThenElse(cond, left, right) => cond.variables ++ left.variables ++ right.variables + case ProofCBinaryExpr(op, left, right) => left.variables ++ right.variables + case ProofCUnaryExpr(op, e) => e.variables + } /** Applies a substitution to an expression */ - def subst(mapping: Map[String, ProofCExpr], recurse: Boolean=true): ProofCExpr = this match { + def subst(mapping: Map[String, ProofCExpr]): ProofCExpr = this match { case expr@ProofCVar(name, _) => mapping.get(name) match { - case Some(value) if recurse => value.subst(mapping) + case Some(ProofCVar(o_name, _)) if name == o_name => expr + // avoid infinite recursion by refusing to subst on expressions that contain the name they were just subst'd with + case Some(value) if !value.variables.map(_.name).contains(name) => value.subst(mapping) case Some(value) => value case None => expr } @@ -227,7 +240,7 @@ object Expressions { /** set literal (encoded as set) in a VST proof */ case class ProofCIntSetLiteral(elems: List[ProofCExpr]) extends ProofCExpr { override def pp: String = - s"([${elems.map(_.pp).mkString("; ")}] : list Z)" + s"([${elems.map(_.pp_as_Z_value).mkString("; ")}] : list Z)" } /** encodes a ternary expression in a VST proof */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 9bfe99418..9be875dfa 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -113,7 +113,7 @@ object VSTProofTranslator { def resolve_existential(ident: Ident): ProofCExpr = { val true_name = renamings.getOrElse(ident, ident) - variable_map(true_name) + variable_map(true_name).subst(variable_map) } def with_variables_of(variables: List[(String, VSTType)]): VSTClientContext = { From e18968767f75dfddb7430d8090bcb12501065f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Wed, 24 Feb 2021 13:37:18 +0800 Subject: [PATCH 133/211] Iris: WIP predicate translation (will probably ditch this) --- .../targets/iris/IrisCertificate.scala | 29 ++-- .../targets/iris/heaplang/Types.scala | 14 +- .../targets/iris/logic/Assertions.scala | 43 +++++- .../iris/translation/IrisTranslator.scala | 145 +++++++++++------- .../iris/translation/TranslatableOps.scala | 6 +- .../iris/translation/Translation.scala | 4 +- .../iris/translation/TranslationContext.scala | 12 +- 7 files changed, 170 insertions(+), 83 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index cfa7b00e1..d8590cd89 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -33,17 +33,27 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | | _ => idtac | end]. | + |Local Ltac movePure := + | iStartProof; + | let rec go Hs := match Hs with [] => idtac | ?H :: ?Hs => (try iDestruct H as "%"); go Hs end in + | match goal with + | | |- envs_entails ?Δ _ => + | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs + | end. + | |Local Ltac iSplitAllHyps := | iStartProof; | let rec go Hs success := | match Hs with | [] => match success with | true => idtac | false => fail end - | | ?H :: ?Hs => - | let Hn := iFresh in - | (iAndDestruct H as H Hn; go Hs true) || go Hs success end in - | match goal with + | | ?H :: ?Hs => let Hn := iFresh in (iAndDestruct H as H Hn; go Hs true) || go Hs success + | end in + | repeat match goal with | | |- envs_entails ?Δ _ => - | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs false + | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs false + | end; + | repeat match goal with + | | [H: _ /\\ _ |- _ ] => destruct H | end. | |Local Ltac iFindApply := @@ -63,15 +73,10 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | | [H: bool_decide _ = _ |- _ ] => rewrite H | end. | - |Local Ltac printGoal := - | match goal with - | | [|- ?q] => idtac q - | end. - | |Local Ltac iSimplNoSplit := - | (repeat wp_pures); iRewriteHyp; iSimpl in "# ∗"; iSimpl. + | (repeat wp_pures); movePure; iRewriteHyp; iSimpl in "# ∗"; iSimpl. | - |Local Ltac iSimplContext := iSimplNoSplit; try repeat iSplitAllHyps; iSimplNoSplit. + |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. | |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. |Ltac ssl_load := wp_load; wp_let; iSimplContext. diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala index 3778f3835..42f80504a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Types.scala @@ -4,7 +4,19 @@ import org.tygus.suslik.language.PrettyPrinting object Types { - sealed abstract class HType extends PrettyPrinting + sealed abstract class HType extends PrettyPrinting { + def isValType: Boolean = this match { + case HValType() => true + case _ => false + } + } + case class HBoolType() extends HType { + override def pp: String = "bool" + } + + case class HValType() extends HType { + override def pp: String = "val" + } case class HIntType() extends HType { override def pp: String = "Z" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index bb703f901..7b7e01b30 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.iris.logic import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HLocType, HType} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HBoolType, HIntSetType, HIntType, HLocType, HType, HValType} import org.tygus.suslik.certification.translation.{CardConstructor, GenericPredicate, GenericPredicateClause} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -12,6 +12,7 @@ object Assertions { * program-level expressions to spec-level. */ abstract class IPureAssertion extends PrettyPrinting { def ppAsPhi: String = pp + def typ: HType } abstract class IQuantifiedVar extends IPureAssertion @@ -22,35 +23,60 @@ object Assertions { case class ISpecLit(lit: HLit) extends IPureAssertion { override def pp: String = lit.pp + + override def typ: HType = HValType() + } + + case class ISpecMakeVal(v: ISpecVar) extends IPureAssertion { + override def pp: String = s"#${v.pp}" + override def typ: HType = HValType() } - case class ISpecVar(name: String) extends IQuantifiedVar { + case class ISpecVar(name: String, typ: HType) extends IQuantifiedVar { override def pp: String = s"${name}" override def ppAsPhi: String = super.ppAsPhi } - case class ISpecQuantifiedValue(name: String) extends IQuantifiedVar { + case class ISpecQuantifiedValue(name: String, typ: HType) extends IQuantifiedVar { override def pp: String = s"${name}" } case class ISetLiteral(elems: List[IPureAssertion]) extends IPureAssertion { override def pp: String = - s"([${elems.map(_.pp).mkString("; ")}] : list Z)" + s"[${elems.map(_.pp).mkString("; ")}]" + + override def typ: HType = HIntSetType() } case class ISpecUnaryExpr(op: HUnOp, expr: IPureAssertion) extends IPureAssertion { override def pp: String = s"${op.pp} ${expr.pp}" override def ppAsPhi: String = s"${op.pp} ${expr.ppAsPhi}" + + override def typ: HType = op match { + case HOpUnaryMinus => HIntType() + case HOpNot => HBoolType() + case _ => ??? + } } case class ISpecBinaryExpr(op: HBinOp, left: IPureAssertion, right: IPureAssertion) extends IPureAssertion { override def pp: String = s"(${left.pp} ${op.pp} ${right.pp})" + override def typ: HType = op match { + case HOpEq | HOpLe | HOpLt => HBoolType() + case HOpUnion => HIntSetType() + case HOpPlus | HOpMinus | HOpMultiply => HIntType() + case HOpOffset => HLocType() + case _ => ??? + } + override def ppAsPhi: String = op match { case HOpLe | HOpLt => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})%Z" case HOpUnion => s"(${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" + case HOpEq if left.typ == HIntSetType() || right.typ == HIntSetType() => + s"${left.ppAsPhi} ${op.pp} ${right.ppAsPhi}" case HOpEq => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" case _ => ??? } @@ -58,6 +84,8 @@ object Assertions { case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { override def pp: String = s"${conjuncts.map(_.ppAsPhi).mkString(" ∧ ")}" + + override def typ: HType = HBoolType() } case class IPointsTo(loc: IPureAssertion, value: IPureAssertion) extends ISpatialAssertion { @@ -98,8 +126,8 @@ object Assertions { // TODO: make this use the general translation mechanism def getArgLitVal(v: ISpecVar, t: HType): ISpecQuantifiedValue = (v, t) match { - case (ISpecVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}") - case (ISpecVar(name), _) => ISpecQuantifiedValue(s"v${name}") + case (ISpecVar(name, t), HLocType()) => ISpecQuantifiedValue(s"l${name}", t) + case (ISpecVar(name, t), _) => ISpecQuantifiedValue(s"v${name}", t) } val var_at = (v: ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" @@ -175,7 +203,7 @@ object Assertions { val clauseCardMap = (cardMap ++ subClauses.flatMap({ case (_, cons) => cons.constructorArgs })).toSet def pureExistentials(exp: IPureAssertion): Set[Ident] = exp match { - case ISpecVar(name) => { + case ISpecVar(name, _) => { paramMap.get(name) match { // A variable that is neither (1) a parameter of the predicate nor (2) a cardinality IS an existential case None if !clauseCardMap.contains(name) => Set(name) @@ -185,6 +213,7 @@ object Assertions { case ISpecBinaryExpr(_, left, right) => pureExistentials(left) ++ pureExistentials(right) case ISpecUnaryExpr(_, expr) => pureExistentials(expr) case ISetLiteral(elems) => elems.flatMap(pureExistentials).toSet + case ISpecMakeVal(v) => pureExistentials(v) case ISpecLit(_) => Set() case _ => ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index ea5deac6b..c0dfe4230 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -1,24 +1,24 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HIntSetType, HIntType, HLocType, HType, HUnknownType} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HIntSetType, HIntType, HLocType, HType, HUnknownType, HValType} import org.tygus.suslik.certification.targets.iris.logic.Assertions._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.translation.{CardConstructor, PredicateTranslation} -import org.tygus.suslik.language.Expressions._ +import org.tygus.suslik.language.Expressions.{OpEq, _} import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} -import org.tygus.suslik.language.{CardType, Ident, IntSetType, IntType, LocType, SSLType} +import org.tygus.suslik.language.{CardType, Expressions, Ident, IntSetType, IntType, LocType, SSLType} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic._ trait IrisTranslator[From, To] { - def translate(value: From, ctx: Option[TranslationContext] = None): To + def translate(value: From, ctx: Option[TranslationContext] = None, target: Option[HType] = None): To } object IrisTranslator { case class TranslatorException(msg: String) extends Exception(msg) - implicit val binOpTranslator: IrisTranslator[BinOp, HBinOp] = (value, _) => value match { + implicit val binOpTranslator: IrisTranslator[BinOp, HBinOp] = (value, _, _) => value match { case OpPlus => HOpPlus case OpMinus => HOpMinus case OpMultiply => HOpMultiply @@ -28,7 +28,7 @@ object IrisTranslator { case _ => ??? } - implicit val overloadedBinOpTranslator: IrisTranslator[OverloadedBinOp, HBinOp] = (value, _) => value match { + implicit val overloadedBinOpTranslator: IrisTranslator[OverloadedBinOp, HBinOp] = (value, _, _) => value match { case OpOverloadedEq => HOpEq case OpOverloadedLeq => HOpLe case OpOverloadedPlus => HOpUnion @@ -36,19 +36,18 @@ object IrisTranslator { case _ => ??? } - implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _) => el._1.translate(progVarTranslator) + implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _, _) => el._1.translate(progVarTranslator) - implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = (value, _) => value match { + implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = (value, _, _) => value match { case OpNot => HOpNot case OpUnaryMinus => HOpUnaryMinus } - implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr, ctx) => { + implicit val exprTranslator: IrisTranslator[Expr, HExpr] = (expr, ctx, _) => { def visit(expr: Expr): HExpr = expr match { case Var(name) => HProgVar(name) case IntConst(v) => HLitInt(v) - case LocConst(v) => - HLitLoc(v) + case LocConst(v) => HLitLoc(v) case BoolConst(v) => HLitBool(v) case SetLiteral(elems) => HSetLiteral(elems.map(_.translate(exprTranslator))) case UnaryExpr(op, arg) => HUnaryExpr(op.translate, visit(arg)) @@ -63,17 +62,18 @@ object IrisTranslator { visit(expr) } - implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _) => HProgVar(pv.name) - implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, _) => ISpecVar(hv.name) - implicit val progVarToSpecQuantifiedValue: IrisTranslator[HProgVar, ISpecQuantifiedValue] = (pv, ctx) => { + implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _, _) => HProgVar(pv.name) + implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, ctx, _) => ISpecVar(hv.name, + ctx.get.hctx(hv.name)) + implicit val progVarToSpecQuantifiedValue: IrisTranslator[HProgVar, ISpecQuantifiedValue] = (pv, ctx, _) => { assert(ctx.isDefined) (pv, ctx.get.gamma(Var(pv.name)).translate) match { - case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}") - case (HProgVar(name), _) => ISpecQuantifiedValue(s"v${name}") + case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}", HLocType()) + case (HProgVar(name), t) => ISpecQuantifiedValue(s"v${name}", t) } } - implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _) => value match { + implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _, _) => value match { case IntType => HIntType() case LocType => HLocType() case IntSetType => HIntSetType() @@ -81,58 +81,83 @@ object IrisTranslator { case _ => ??? } + implicit val gammaTranslator: IrisTranslator[Gamma, Map[Ident, HType]] = (g, _ , _) => { + g.map({ case (v, t) => (v.name, t.translate) }) + } + implicit val mallocTranslator: IrisTranslator[Malloc, HStore] = - (stmt, _) => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) + (stmt, _, _) => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) implicit val loadTranslator: IrisTranslator[Load, HLet] = - (stmt, _) => { + (stmt, _, _) => { val baseVar = stmt.from.translate val fromAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) HLet(stmt.to.translate, HLoad(fromAddr), HLitUnit()) } implicit val storeTranslator: IrisTranslator[Store, HStore] = - (stmt, _) => { + (stmt, _, _) => { val baseVar = stmt.to.translate val toAddr = if (stmt.offset == 0) baseVar else HBinaryExpr(HOpOffset, baseVar, HLitLoc(stmt.offset)) HStore(toAddr, stmt.e.translate) } - implicit val callTranslator: IrisTranslator[Call, HCall] = (stmt, _) => HCall(stmt.fun.translate, stmt.args.map(_.translate)) + implicit val callTranslator: IrisTranslator[Call, HCall] = (stmt, _, _) => HCall(stmt.fun.translate, stmt.args.map(_.translate)) - implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, ctx) => { + implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, ctx, _) => { assert(ctx.isDefined) IAnd(f.conjuncts.map(_.translate.translate(toSpecExpr, ctx)).toSeq) } - // TODO: remove duplication between toSpecExpr and toSpecVal - implicit val toSpecExpr: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { + /*** + * FIXME: This is going to be a major source of bugs. + * This method performs a crappy version of type inference to figure out when something needs to be printed + * as a HeapLang value and when it needs to be printed as a raw Coq term + */ + implicit val toSpecExpr: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx, target) => { assert(ctx.isDefined) - expr match { - // Get the quantified value if it is quantified - case v: HProgVar => ctx.get.pts.getOrElse(v, v.translate(progVarToSpecVar)) - case l: HLit => ISpecLit(l) - case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx), right.translate(toSpecExpr, ctx)) - case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecExpr, ctx)) - case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecExpr, ctx))) - case _ => ??? + val c = ctx.get + + def shouldPrintAsVal(expr: HExpr):Boolean = expr match { + // heaplang does not support set values + case HProgVar(n) => !c.hctx.get(n).contains(HIntSetType()) + case HUnaryExpr(_, e) => shouldPrintAsVal(e) + case HBinaryExpr(HOpUnion, _, _) => false + case HBinaryExpr(_, left, right) => shouldPrintAsVal(left) && shouldPrintAsVal(right) + case _ => true } - } - implicit val toSpecVal: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { - assert(ctx.isDefined) + val prodVal = target.isDefined && target.get.isValType + val subTarget = if (prodVal && shouldPrintAsVal(expr)) target else None + expr match { - // Get the quantified value if it is quantified - // If it's a ghost, it has a non-value type (e.g. Z), so we have to make it into a value - case v: HProgVar => ctx.get.pts.getOrElse(v, ISpecLit(new HLit(v.name))) + case v: HProgVar => + val specVar = v.translate(progVarToSpecVar, ctx) + // Get the quantified value version of the var, if it exists, otherwise + // make the var into value by prepending # to it + ctx.get.pts.getOrElse(v, if (prodVal) ISpecMakeVal(specVar) else specVar) case l: HLit => ISpecLit(l) - case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecVal, ctx), right.translate(toSpecVal, ctx)) - case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecVal, ctx)) - case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecVal, ctx))) + case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx, subTarget), right.translate(toSpecExpr, ctx, subTarget)) + case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecExpr, ctx, subTarget)) + case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecExpr, ctx, subTarget))) case _ => ??? } } +// implicit val toSpecVal: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { +// assert(ctx.isDefined) +// expr match { +// // Get the quantified value if it is quantified +// // If it's a ghost, it has a non-value type (e.g. Z), so we have to make it into a value +// case v: HProgVar => ctx.get.pts.getOrElse(v, ISpecLit(new HLit(v.name))) +// case l: HLit => ISpecLit(l) +// case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecVal, ctx), right.translate(toSpecVal, ctx)) +// case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecVal, ctx)) +// case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecVal, ctx))) +// case _ => ??? +// } +// } + - implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx) => { + implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx, _) => { val base = f.loc.translate.translate(toSpecExpr, ctx) val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitOffset(f.offset))) else base // Sometimes we get PointsTo(Var(x), 0, LocConst(0)) when we should get an IntConst @@ -140,35 +165,35 @@ object IrisTranslator { case LocConst(v) if v == 0 => IntConst(0) case _ => f.value } - val value = valToTranslate.translate.translate(toSpecVal, ctx) + val value = valToTranslate.translate.translate(toSpecExpr, ctx, Some(HValType())) IPointsTo(loc, value) } - implicit val predAppTranslator: IrisTranslator[SApp, IPredApp] = (pa, ctx) => { + implicit val predAppTranslator: IrisTranslator[SApp, IPredApp] = (pa, ctx, _) => { assert(ctx.isDefined) IPredApp(pa.pred, pa.args.map(_.translate.translate(toSpecExpr, ctx)), pa.card.translate.translate(toSpecExpr, ctx)) } - implicit val heapleatTranslator: IrisTranslator[Heaplet, ISpatialAssertion] = (h, ctx) => { + implicit val heapleatTranslator: IrisTranslator[Heaplet, ISpatialAssertion] = (h, ctx, _) => { assert(ctx.isDefined) h match { case pt:PointsTo => pt.translate(pointsToTranslator, ctx) - case bl:Block => IBlock() + case _:Block => IBlock() case pa:SApp => pa.translate(predAppTranslator, ctx) case _ => ??? } } - implicit val sigmaTranslator: IrisTranslator[SFormula, ISpatialAssertion] = (f, ctx) => { + implicit val sigmaTranslator: IrisTranslator[SFormula, ISpatialAssertion] = (f, ctx, _) => { IHeap(f.chunks.map(_.translate(heapleatTranslator, ctx))) } - implicit val assertionTranslator: IrisTranslator[Assertion, IAssertion] = (f, ctx) => { + implicit val assertionTranslator: IrisTranslator[Assertion, IAssertion] = (f, ctx, _) => { assert(ctx.isDefined) IAssertion(f.phi.translate(phiTranslator, ctx), f.sigma.translate(sigmaTranslator, ctx)) } - implicit val goalToFunSpecTranslator: IrisTranslator[Goal, IFunSpec] = (g, ctx) => { + implicit val goalToFunSpecTranslator: IrisTranslator[Goal, IFunSpec] = (g, ctx, _) => { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) @@ -181,17 +206,17 @@ object IrisTranslator { v => (v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)), g.gamma(v).translate) ) // We quantify over all universals, ignoring the type of function arguments - val quantifiedLocs = g.universals.map(v => (v.translate.translate(progVarToSpecVar), - if(g.programVars.contains(v)) HUnknownType() else g.gamma(v).translate)) + val quantifiedLocs = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), + if(g.programVars.contains(v)) HValType() else g.gamma(v).translate)) val specUniversal = quantifiedLocs.toSeq ++ quantifiedValues - val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar), g.gamma(v).translate)).toSeq + val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)).toSeq val pre = g.pre.translate(assertionTranslator, ctx) val post = g.post.translate(assertionTranslator, ctx) - IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar), x._2)), specUniversal, specExistential, pre, post) + IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), specUniversal, specExistential, pre, post) } - implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx) => { + implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx, _) => { assert(ctx.isDefined) class IPredicateTranslation extends PredicateTranslation[IPureAssertion, ISpatialAssertion, HType, IPredicateClause, IPredicate] { @@ -200,11 +225,15 @@ object IrisTranslator { case _ => ty.translate } - // TODO: should this be specExpr or specVal? - override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = expr.translate.translate(toSpecVal, ctx) + override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = { + val predCtx = Some(TranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + expr.translate.translate(toSpecExpr, predCtx, Some(HValType())) + } - override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = - heaplets.map(_.translate(heapleatTranslator, ctx)) + override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = { + val predCtx = Some(TranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + heaplets.map(_.translate(heapleatTranslator, predCtx)) + } override def constructClause(pure: List[IPureAssertion], spatial: List[ISpatialAssertion], subConstructor: Map[String, CardConstructor]): IPredicateClause = { IPredicateClause(pure, spatial, subConstructor) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala index edaa832ba..71684aa5f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala @@ -1,9 +1,11 @@ package org.tygus.suslik.certification.targets.iris.translation +import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType + object TranslatableOps { implicit class Translatable[A](value: A) { - def translate[B](implicit translator: IrisTranslator[A,B], ctx: Option[TranslationContext] = None): B = { - translator.translate(value, ctx) + def translate[B](implicit translator: IrisTranslator[A,B], ctx: Option[TranslationContext] = None, target: Option[HType] = None): B = { + translator.translate(value, ctx, target) } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 4dda64d16..7a3e9cdde 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -19,10 +19,10 @@ object Translation { val params = proc.formals.map(_.translate) // We have this "dummy" value to generate progToSpec for the actual context, ctx - val pre_ctx = Some(TranslationContext(env, node.goal.gamma, List().toMap)) + val pre_ctx = Some(TranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) val progToSpec = params.map(p => (p, p.translate(progVarToSpecQuantifiedValue, pre_ctx))) - val ctx = TranslationContext(env, node.goal.gamma, progToSpec.toMap) + val ctx = TranslationContext(env, node.goal.gamma, progToSpec.toMap, node.goal.gamma.translate) val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList predicates.foreach(p => println(p.pp)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala index 3b6d3470b..1be4137b7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala @@ -1,8 +1,18 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HProgVar} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.language.Ident import org.tygus.suslik.logic.{Environment, Gamma} -case class TranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar]) extends ClientContext[HExpr] \ No newline at end of file +/*** + * + * @param env + * @param gamma + * @param pts mapping between program and quantified spec variables + * @param hctx heaplang type context used for predicates + */ + +case class TranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar], hctx: Map[Ident, HType]) extends ClientContext[HExpr] \ No newline at end of file From f0cc30661ed85cd5b4f8f543076e8aa89190421d Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 25 Feb 2021 09:49:03 +0800 Subject: [PATCH 134/211] fixes printing of predicate applications --- .../targets/vst/logic/Expressions.scala | 1 - .../targets/vst/logic/Formulae.scala | 19 +++++++--- .../targets/vst/logic/ProofTerms.scala | 11 ++++-- .../targets/vst/logic/VSTProofStep.scala | 6 +++- .../translation/ProofSpecTranslation.scala | 36 +++++++++---------- .../targets/vst/translation/Translation.scala | 8 ++--- .../vst/translation/VSTProofTranslator.scala | 26 +++++++++----- 7 files changed, 66 insertions(+), 41 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala index 30e4d5cde..ecc7cdfcc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Expressions.scala @@ -209,7 +209,6 @@ object Expressions { override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${pred_type}_card)" } - /** a variable in a VST proof */ case class ProofCVar(name: String, typ: VSTType) extends ProofCExpr with CLangExpr { override def pp: String = typ match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala index f454c6fd0..05939f79e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Formulae.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.vst.logic +import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation.TranslationException import org.tygus.suslik.language.PrettyPrinting @@ -12,13 +14,13 @@ object Formulae { def rename(renaming: Map[String, String]): VSTHeaplet = this match { case CDataAt(loc, elems) => CDataAt(loc.rename(renaming), elems.map(_.rename(renaming))) - case CSApp(pred, args, card) => CSApp(pred, args.map(_.rename(renaming)), card.rename(renaming)) + case CSApp(pred, args, card) => CSApp(pred, args.map(v => (v._1.rename(renaming), v._2)), card.rename(renaming)) } def subst(mapping: Map[String, ProofCExpr]): VSTHeaplet = this match { case CDataAt(loc, elems) => CDataAt(loc.subst(mapping), elems.map(_.subst(mapping))) - case CSApp(pred, args, card) => CSApp(pred, args.map(_.subst(mapping)), card.subst(mapping)) + case CSApp(pred, args, card) => CSApp(pred, args.map(v => (v._1.subst(mapping), v._2)), card.subst(mapping)) } } @@ -41,9 +43,18 @@ object Formulae { * @param args arguments * @param card cardinality of call * */ - case class CSApp(pred: String, var args: Seq[Expressions.ProofCExpr], card: Expressions.ProofCExpr) extends VSTHeaplet { + case class CSApp(pred: String, args: Seq[(Expressions.ProofCExpr, VSTType)], card: Expressions.ProofCExpr) extends VSTHeaplet { + + def pp_pred_arg(v: (ProofCExpr, VSTType)): String = v._2 match { + case Types.CoqPtrValType => v._1.pp_as_c_value + case Types.CoqIntValType => v._1.pp_as_c_value + case Types.CoqZType => v._1.pp_as_Z_value + case _ =>v._1.pp + } + override def pp: String = - s"(${pred} ${(args ++ List(card)).map(_.pp).mkString(" ")})" + s"(${pred} ${((args).map(v => pp_pred_arg(v)) ++ List(card.pp)).mkString(" ")})" + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index c0389e365..1c74d05d8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,6 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.htt.logic.Proof.UnfoldConstructor +import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet @@ -190,20 +191,24 @@ object ProofTerms { } def pp_constructor_clause(constructor: CardConstructor, pclause: VSTPredicateClause) : String = { + val clause_existentials: List[(String, VSTType)] = findExistentials(constructor)(pclause) val VSTPredicateClause(pure, spatial, _) = pclause s"${ - val clause_existentials: List[(String, VSTType)] = findExistentials(constructor)(pclause) val str = clause_existentials.map({ case (name, ty) => s" EX ${name} : ${ty.pp}," }).mkString("\n") clause_existentials match { case Nil => "" case ::(_, _) => "\n" + str + "\n" } } ${ + ((clause_existentials.flatMap(v => v._2 match { + case Types.CoqZType => Some(s"!!(Int.min_signed <= ${v._1} <= Int.max_signed)") + case _ => None + })) ++ (pure.map(v => s"!!${v.pp}") ++ List((spatial match { case Nil => List("emp") case v => v.map(_.pp) - }).mkString(" * "))).mkString(" && ") + }).mkString(" * ")))).mkString(" && ") }" } @@ -261,7 +266,7 @@ object ProofTerms { case Formulae.CDataAt(loc, elems) => to_variables(loc) ++ elems.flatMap(elem => to_variables(elem)) case Formulae.CSApp(pred, args, card) => - args.flatMap(to_variables).toList + args.flatMap(v => to_variables(v._1)).toList } (pure.flatMap(to_variables) ++ spatial.flatMap(to_variables_heap): List[String]) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 8b5834ea4..daba79bde 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.vst.Types.VSTType -import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCIfThenElse} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{PureFormula, VSTPredicate} import org.tygus.suslik.certification.translation.{CardConstructor, CardNull, CardOf} import org.tygus.suslik.certification.traversal.Step.DestStep @@ -90,6 +90,10 @@ object VSTProofStep { override def pp: String = s"forward." } + case class ForwardTernary(ternary_expr: ProofCIfThenElse) extends VSTProofStep { + override def pp: String = s"ssl_forward_write_ternary (${ternary_expr.pp_as_c_value});\ntry (forward; entailer!; ssl_reflect_boolean)." + } + case class Intros(variables: List[(Ident, VSTType)]) extends VSTProofStep { override def pp: String = { s"Intros ${variables.map(_._1).mkString(" ")}." diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 9ba6647ce..7bf1b94f6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -105,6 +105,14 @@ object ProofSpecTranslation { case Expressions.OpUnaryMinus => ProofCOpUnaryMinus } ProofCUnaryExpr(top, translate_expression(context)(arg)) + case Expressions.BinaryExpr(Expressions.OpAnd, left, right) => + val left_expr = translate_expression(context)(left) + val right_expr = translate_expression(context)(right) + ProofCBinaryExpr(ProofCOpAnd, left_expr, right_expr) + case Expressions.BinaryExpr(Expressions.OpOr, left, right) => + val left_expr = translate_expression(context)(left) + val right_expr = translate_expression(context)(right) + ProofCBinaryExpr(ProofCOpOr, left_expr, right_expr) case Expressions.BinaryExpr(op, left, right) => val left_expr = translate_expression(context)(left) val type_of_expr = type_expr(left_expr) @@ -145,7 +153,7 @@ object ProofSpecTranslation { * * This function performs the translation between suslik's encoding and VST's encoding */ - def translate_heaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { + def translate_heaplets(context: Map[Ident, VSTType])(predicate_param_map: Map[Ident, List[VSTType]])(heaplets: List[Heaplet]): List[VSTHeaplet] = { val initial_map: Map[Ident, (List[PointsTo], Option[Block])] = Map.empty // we first build up a mapping from pointer variables @@ -170,7 +178,7 @@ object ProofSpecTranslation { } (updated_map, acc: List[CSApp]) case SApp(pred, args, tag, card) => - (map, (List(CSApp(pred, args.map(v => translate_expression((context))(v)), translate_expression(context)(card))) ++ acc) + (map, (List(CSApp(pred, args.zip(predicate_param_map(pred)).map({ case (exp, ty) => (translate_expression((context))(exp), ty) }), translate_expression(context)(card))) ++ acc) ) } }) @@ -206,18 +214,6 @@ object ProofSpecTranslation { blocks.map(_.asInstanceOf[VSTHeaplet]) ++ apps.map(_.asInstanceOf[VSTHeaplet]) } - def translate_assertion(context: Map[Ident, VSTType])(assertion: Assertion): FormalCondition = assertion match { - case Assertion(phi, sigma) => { - val pure_conditions = - phi.conjuncts.map(v => translate_expression(context)(v)) - .map(IsTrueProp).toList - - val spatial_conditions: List[VSTHeaplet] = - translate_heaplets(context)(sigma.chunks) - - FormalCondition(pure_conditions, spatial_conditions) - } - } def proof_type_of_c_type(cType: VSTType): VSTCType = cType match { case cType: Types.VSTCType => cType @@ -225,7 +221,7 @@ object ProofSpecTranslation { } /** translates a Suslik function specification into a proof */ - def translate_conditions(env: Environment)(f: FunSpec): FormalSpecification = { + def translate_conditions(env: Environment)(pred_type_map: Map[Ident, List[VSTType]])(f: FunSpec): FormalSpecification = { val name = f.name val c_params = f.params.map({case (Var(name), ty) => ty match { case IntType => (name, CoqIntValType) @@ -282,7 +278,7 @@ object ProofSpecTranslation { } }) val spatial_conditions: List[VSTHeaplet] = - translate_heaplets(context)(f.pre.sigma.chunks) + translate_heaplets(context)(pred_type_map)(f.pre.sigma.chunks) FormalCondition(pure_conditions, spatial_conditions) } @@ -291,7 +287,7 @@ object ProofSpecTranslation { f.post.phi.conjuncts.map(v => translate_expression(context)(v)) .map(IsTrueProp).toList val spatial_conditions = - translate_heaplets(context)(f.post.sigma.chunks) + translate_heaplets(context)(pred_type_map)(f.post.sigma.chunks) // goal.post.sigma.chunks.map(translate_heaplet(context)).toList FormalCondition(pure_conditions, spatial_conditions) } @@ -341,8 +337,10 @@ object ProofSpecTranslation { override def translateExpression(context: Map[Ident, VSTType])(expr: Expressions.Expr): ProofCExpr = translate_expression(context)(expr) - override def translateHeaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = - translate_heaplets(context)(heaplets) + override def translateHeaplets(context: Map[Ident, VSTType])(heaplets: List[Heaplet]): List[VSTHeaplet] = { + val predicate_param_map: Map[Ident, List[VSTType]] = Map(predicate.name -> predicate.params.map(v => translatePredicateParamType(predicate.name, v._2))) + translate_heaplets(context)(predicate_param_map)(heaplets) + } override def constructClause(pure: List[ProofCExpr], spatial: List[VSTHeaplet], subConstructor: Map[String, CardConstructor]): VSTPredicateClause = { VSTPredicateClause(pure, spatial, subConstructor) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 0531f52b6..c1e91adc5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -32,7 +32,8 @@ object Translation { def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { val base_proof = SuslikProofStep.of_certtree(root) val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList - predicates.foreach(p => println(p.pp)) + val pred_map = predicates.map(v => (v.name,v)).toMap + val pred_type_map = predicates.map(v => (v.name, v.params.map(_._2))).toMap var f_gamma = proc.f.gamma(env) println(f_gamma) env.functions.foreach { @@ -44,15 +45,14 @@ object Translation { case LocType => (name, CoqPtrValType) case IntType => (name, CoqIntValType) }}) - val spec = ProofSpecTranslation.translate_conditions(env)(proc.f) - val helper_specs = env.functions.map {case (fname, spec) => (fname, ProofSpecTranslation.translate_conditions(env)(spec))} + val spec = ProofSpecTranslation.translate_conditions(env)(pred_type_map)(proc.f) + val helper_specs = env.functions.map {case (fname, spec) => (fname, ProofSpecTranslation.translate_conditions(env)(pred_type_map)(spec))} println(spec.pp) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body, helper_specs.values.toList) println(procedure.pp) println(spec.pp) - val pred_map = predicates.map(v => (v.name,v)).toMap val spec_map = Map(proc.name -> spec) ++ helper_specs val proof_translator = VSTProofTranslator(spec) val steps = translate_proof(base_proof)(proof_translator, VSTClientContext.make_context(pred_map, spec_map)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 9be875dfa..a6775efb1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -3,10 +3,10 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqPtrValType, VSTType} -import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, TentativeEntailer, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, Free, Intros, IntrosTuple, Malloc, Rename, UnfoldRewrite, ValidPointer} +import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, ForwardTernary, Free, Intros, IntrosTuple, Malloc, Rename, TentativeEntailer, UnfoldRewrite, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} @@ -267,8 +267,17 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.Inconsistency(label) => ??? - case SuslikProofStep.Write(stmt) => - Result(List(Forward), List(with_no_deferreds(clientContext))) + case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => + e match { + // ternary expressions need to be special cased + case e@Expressions.IfThenElse(cond, left, right) => // ternary + val ternary_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(e).asInstanceOf[ProofCIfThenElse] + Result(List(ForwardTernary(ternary_expr), Forward), List(with_no_deferreds(clientContext))) + case _ => + // otherwise, it is a normal write + Result(List(Forward), List(with_no_deferreds(clientContext))) + } + case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => val ctx = clientContext with_renaming ghost_from -> ghost_to val rename_step = Rename(ghost_from, ghost_to) @@ -391,7 +400,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl // - existential variables in the predicate - i.e lseg x s a = lseg_card_1 a' => exists ..., _ // - cardinality variables within the val existentials_sub = fresh_exists.map { case (var1, var2) => (var1.name, var2.name) } - val params_sub = fresh_params.map { case (var1: Var, var2: Var) => (var1.name, var2.name) } + //val params_sub = fresh_params.map { case (var1: Var, var2: Var) => (var1.name, var2.name) } val card_variable = pred.card.asInstanceOf[Var].name val predicate_name = pred.pred val predicate = clientContext.find_predicate_with_name(predicate_name) @@ -403,12 +412,11 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl }) val constructor_arg_existentials = constructor.constructorArgs.map(existentials_sub(_)).map(v => (v, CoqCardType(predicate_name))) - val renamed_body = body.rename(existentials_sub).rename(params_sub) - (constructor, constructor_arg_existentials, existentials, renamed_body, expr) + (constructor, constructor_arg_existentials, existentials, expr) } val branches = clauses.map { - case (constructor, cons_intro_variables, intro_variables, body, selector) => + case (constructor, cons_intro_variables, intro_variables, selector) => val intro_names = intro_variables.map(_._1) val cons_intro_names = cons_intro_variables.map(_._1) ((constructor, cons_intro_names), selector, intro_names) @@ -419,7 +427,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl branches ) val child_rules = clauses.map { - case (_, constructor_intro_gamma, intro_gamma, _, _) => + case (_, constructor_intro_gamma, intro_gamma, _) => var ctx = clientContext ctx = ctx with_variables_of constructor_intro_gamma ++ intro_gamma (List(), no_deferreds, ctx) From 62984bc56c74c6906c8b56351e2d5a186daa0322 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 25 Feb 2021 14:04:02 +0800 Subject: [PATCH 135/211] ssl_append goes through --- .../suslik/certification/targets/vst/logic/VSTProofStep.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index daba79bde..128405cea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -23,7 +23,7 @@ object VSTProofStep { case object TentativeEntailer extends VSTProofStep { - override def pp: String = s"try entailer!." + override def pp: String = s"ssl_entailer." } /** From fed56a87d340e4ac5d940878ce8eaa1a8d85683a Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 25 Feb 2021 14:14:13 +0800 Subject: [PATCH 136/211] HTT: Print hint equalities as Prop not bool --- .../targets/htt/language/Expressions.scala | 12 +++++++++++- .../certification/targets/htt/logic/Hint.scala | 4 ++-- .../certification/targets/htt/logic/Proof.scala | 6 +++--- .../targets/htt/translation/ProofTranslator.scala | 2 +- .../certification/targets/vst/logic/ProofTerms.scala | 2 +- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 5fd18acd1..4f86912f8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -100,6 +100,8 @@ object Expressions { def vars: Seq[CVar] = collect(_.isInstanceOf[CVar]) def cardVars: Seq[CVar] = collect(_.isInstanceOf[CSApp]).flatMap {v: CSApp => v.card.vars} + + def ppProp: String = pp } case class CVar(name: String) extends CExpr { @@ -137,6 +139,11 @@ object Expressions { case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" case _ => s"(${left.pp}) ${op.pp} (${right.pp})" } + + override def ppProp: String = op match { + case COpEq => s"(${left.pp}) ${op.ppProp} (${right.pp})" + case _ => pp + } } case class CUnaryExpr(op: CUnOp, e: CExpr) extends CExpr { @@ -201,7 +208,9 @@ object Expressions { object COpUnaryMinus extends CUnOp - sealed abstract class CBinOp extends PrettyPrinting + sealed abstract class CBinOp extends PrettyPrinting { + def ppProp: String = pp + } object COpImplication extends CBinOp { override def pp: String = "->" @@ -220,6 +229,7 @@ object Expressions { } object COpEq extends CBinOp { + override def ppProp: String = "=" override def pp: String = "==" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 895d9f6b8..7c78f9845 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -51,8 +51,8 @@ object Hint { case class Hypothesis(args: Set[CVar], ctx: Set[CExpr], goal: CExpr) { val name = s"pure$freshHintId" def pp: String = { - val ctxStr = ctx.map(_.pp).mkString(" -> ") - val goalStr = goal.pp + val ctxStr = ctx.map(_.ppProp).mkString(" -> ") + val goalStr = goal.ppProp val hypStr = if (ctx.isEmpty) goalStr else s"$ctxStr -> $goalStr" val argsStr = if (args.isEmpty) "" else s"${args.map(_.pp).mkString(" ")} " s"Lemma $name $argsStr: $hypStr. Admitted.\n${ppResolve(name)}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala index bbc414d87..4ff67f0d6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Proof.scala @@ -85,8 +85,8 @@ object Proof { case object Auto extends Step { override def pp: String = "sslauto" } - case class UnfoldConstructor(idx: Int) extends Step { - override def pp: String = s"unfold_constructor $idx" + case class Close(idx: Int) extends Step { + override def pp: String = s"ssl_close $idx" } case class Write(to: CVar, offset: Int = 0, e: CExpr) extends Step { override def pp: String = { @@ -134,7 +134,7 @@ object Proof { override def pp: String = "ssl_ghostelim_post" } case class Branch(cond: CExpr) extends Step { - override def pp: String = s"ssl_abduce_branch (${cond.pp})" + override def pp: String = s"ssl_branch (${cond.pp})" } case object FrameUnfold extends Step { override def pp: String = "ssl_frame_unfold" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 9ddfa0727..7cb15a68e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -266,7 +266,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val ctx1 = ctx.solveApp(csapp1, newApps) val steps = List( - Proof.UnfoldConstructor(constructor.idx) andThen, + Proof.Close(constructor.idx) andThen, Proof.Exists(valueEx ++ heapEx) andThen, Proof.Auto ) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 1c74d05d8..0c0dd459d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst.logic -import org.tygus.suslik.certification.targets.htt.logic.Proof.UnfoldConstructor +import org.tygus.suslik.certification.targets.htt.logic.Proof.Close import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types._ import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr From 14ce35cb18d439f45258674886481a22fc413693 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Thu, 25 Feb 2021 15:50:20 +0800 Subject: [PATCH 137/211] dll_append goes through - feature freeze --- .../targets/vst/logic/ProofTerms.scala | 18 ++++++++++-- .../vst/translation/VSTProofTranslator.scala | 28 +++++++++++++------ .../synthesis/certification/ints/max.syn | 12 ++++++++ 3 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 src/test/resources/synthesis/certification/ints/max.syn diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 0c0dd459d..1cd1b4444 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.vst.logic import org.tygus.suslik.certification.targets.htt.logic.Proof.Close import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types._ -import org.tygus.suslik.certification.targets.vst.logic.Expressions.ProofCExpr +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCExpr, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.Formulae.VSTHeaplet import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicateHelper.HelpUnfold import org.tygus.suslik.certification.targets.vst.translation.ProofSpecTranslation @@ -169,6 +169,12 @@ object ProofTerms { override val clauses: Map[CardConstructor, VSTPredicateClause]) extends GenericPredicate[Expressions.ProofCExpr, VSTHeaplet, VSTType](name, params, existentials, clauses) { + /** + * Returns a list of the local facts for this predicate + * @return + */ + def pure_constraints : List[PureFormula] = VSTPredicateHelper.LocalFacts(this).pure_constraints + def clause_by_selector(expr: Expr) = { val translated_expr = ProofSpecTranslation.translate_expression(params.toMap)(expr) clauses.find({case (constructor, clause) => clause.selector == translated_expr}).get @@ -324,11 +330,19 @@ object ProofTerms { | ${predicate.name} ${predicate.formalParams.mkString(" ")}|-- !!(${ ( predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ - predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) + pure_constraints.map(_.pp) +// predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) ).mkString("/\\") }). Proof. Admitted.""".stripMargin } + def pure_constraints : List[PureFormula] = predicate.params.flatMap({ + case (name, ty) => ty match { + case Types.CoqPtrValType => Some(IsValidPointerOrNull(ProofCVar(name, ty))) + case _ => None + } + }) + def lemma_name: String = s"${predicate.name}_local_factsP" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index a6775efb1..adc07e28f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqPtrValType, VSTType} import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} -import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep +import org.tygus.suslik.certification.targets.vst.logic.{Formulae, VSTProofStep} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, ForwardTernary, Free, Intros, IntrosTuple, Malloc, Rename, TentativeEntailer, UnfoldRewrite, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result @@ -24,12 +24,14 @@ object VSTProofTranslator { * @param args arguments to function call * @param existential_params existential paramters in result of function call (will be introduced after calling function) * @param pre_conditions pre-conditions to function + * @param post_constraints list of constraints enforced by function after execution */ case class PendingCall( function_name: String, args: List[(String, VSTType)], existential_params: List[(String, VSTType)], - pre_conditions: List[PureFormula] + pre_conditions: List[PureFormula], + post_constraints: List[PureFormula] ) /** @@ -85,11 +87,13 @@ object VSTProofTranslator { } val new_call = call match { case None => None - case Some(PendingCall(f_name, args, existential_params, pre_conditions)) => + case Some(PendingCall(f_name, args, existential_params, pre_conditions, post_constraints)) => Some(PendingCall( f_name, args.map(v => (true_name(v._1), v._2)), existential_params.map(v => (true_name(v._1), v._2)), - pre_conditions.map(_.rename(renaming)))) + pre_conditions.map(_.rename(renaming)), + post_constraints.map(_.rename(renaming)) + )) } val new_renamings = renamings.map({case (from, to) => (from, true_name(to))}) ++ Map(from -> to) VSTClientContext(pred_map, spec_map, new_coq_context, new_existential_context, new_ghost_context, new_variable_map, new_renamings, new_call) @@ -141,8 +145,8 @@ object VSTProofTranslator { val subst = Map(from -> to_expr) val new_variable_map = variable_map.map({ case (name, vl) => (name, vl.subst(subst)) }) ++ subst val new_call = call.map({ - case PendingCall(f_name, args, eparams, preconds) => - PendingCall(f_name, args, eparams, preconds.map(v => v.subst(subst))) + case PendingCall(f_name, args, eparams, preconds, postconds) => + PendingCall(f_name, args, eparams, preconds.map(v => v subst subst), postconds.map(v => v subst subst)) }) VSTClientContext(pred_map, spec_map, coq_context, existential_context, ghost_existential_context, new_variable_map, renamings, new_call) } @@ -222,10 +226,18 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl var ctx = clientContext val fun_spec = ctx.spec_map(f.name) val renaming = normalize_renaming(freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) }) + + val post_constraints = fun_spec.postcondition.spatial_constraints.flatMap({ + case Formulae.CSApp(pred, args, card) => + val predicate = ctx.pred_map(pred) + val param_map = predicate.params.zip(args).map({case ((name, _), (expr, _)) => (name, expr.rename(renaming))}).toMap + predicate.pure_constraints.map(v => v.subst(param_map)) + case _ => List() + }) val function_params = fun_spec.params.map((pair) => (renaming(pair._1), pair._2)) val function_result_existentials = fun_spec.existensial_params.map { case (name, ty) => (renaming(name), ty) } val function_preconds = fun_spec.precondition.pure_constraints.map(_.rename(renaming)) - val suspended_call = PendingCall(f.name, function_params, function_result_existentials, function_preconds) + val suspended_call = PendingCall(f.name, function_params, function_result_existentials, function_preconds, post_constraints) ctx = ctx with_ghost_existentials_of function_params ctx = ctx with_queued_call suspended_call // Use deferred to remove (translation-only) existentials produced by abduce-call @@ -255,7 +267,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl List(IntrosTuple(call.existential_params)) } else { List() - }) + }) ++ (call.post_constraints.map(v => AssertProp(v))) } // update context to have existential variables produced by call ctx = ctx with_variables_of call.existential_params diff --git a/src/test/resources/synthesis/certification/ints/max.syn b/src/test/resources/synthesis/certification/ints/max.syn new file mode 100644 index 000000000..39cbd2d27 --- /dev/null +++ b/src/test/resources/synthesis/certification/ints/max.syn @@ -0,0 +1,12 @@ +#. -b true +maximum of two integers + +### + +{ r :-> 0 } + +void max (loc r, int x, int y) + +{ x <= m /\ y <= m; r :-> m } + +### From 4940a6e1e4a26a7db248fa504e36f4b52a401314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Thu, 25 Feb 2021 17:03:21 +0800 Subject: [PATCH 138/211] Iris: generate lemmas for predicate unfolding & learning cardinalities from selectors --- .../targets/iris/IrisCertificate.scala | 2 + .../targets/iris/heaplang/Expressions.scala | 4 + .../targets/iris/logic/Assertions.scala | 101 ++++++++++++++---- .../iris/translation/IrisTranslator.scala | 66 +++++------- 4 files changed, 117 insertions(+), 56 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index d8590cd89..02d3a866a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -89,6 +89,8 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe val b = new StringBuilder b.append(prelude) b.append(preds.map(_.pp).mkString("\n")) + b.append("\n") + b.append(preds.flatMap(_.getHelpers).map(_.pp).mkString("\n")) b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index 9ffa4d150..c2cb69b05 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -75,6 +75,10 @@ object Expressions { override def pp: String = "=" } + object HOpSetEq extends HBinOp { + override def pp: String = "=" + } + object HOpLt extends HBinOp { override def pp: String = "<" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 7b7e01b30..998765c59 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -12,6 +12,7 @@ object Assertions { * program-level expressions to spec-level. */ abstract class IPureAssertion extends PrettyPrinting { def ppAsPhi: String = pp + def ppAsBool: String = ppAsPhi def typ: HType } @@ -24,6 +25,9 @@ object Assertions { case class ISpecLit(lit: HLit) extends IPureAssertion { override def pp: String = lit.pp + // Don't print the # at the beginning + override def ppAsPhi: String = pp.substring(1) + override def typ: HType = HValType() } @@ -50,7 +54,7 @@ object Assertions { } case class ISpecUnaryExpr(op: HUnOp, expr: IPureAssertion) extends IPureAssertion { - override def pp: String = s"${op.pp} ${expr.pp}" + override def pp: String = s"(${op.pp} ${expr.pp})" override def ppAsPhi: String = s"${op.pp} ${expr.ppAsPhi}" @@ -61,9 +65,29 @@ object Assertions { } } + case class ISpecIfThenElse(cond: IPureAssertion, trueBranch: IPureAssertion, falseBranch: IPureAssertion) extends IPureAssertion { + override def pp: String = s"if ${cond.pp} then ${trueBranch.pp} else ${falseBranch.pp}" + override def ppAsPhi: String = s"if ${cond.ppAsBool} then ${trueBranch.ppAsPhi} else ${falseBranch.ppAsPhi}" + + override def typ: HType = trueBranch.typ + } + case class ISpecBinaryExpr(op: HBinOp, left: IPureAssertion, right: IPureAssertion) extends IPureAssertion { override def pp: String = s"(${left.pp} ${op.pp} ${right.pp})" + override def ppAsBool: String = op match { + case HOpLe => s"(Z.leb ${left.ppAsPhi} ${right.ppAsPhi})" + case HOpLe => s"(Z.ltb ${left.ppAsPhi} ${right.ppAsPhi})" + case _ => ppAsPhi + } + + override def ppAsPhi: String = op match { + case HOpLe | HOpLt | HOpPlus | HOpMinus | HOpMultiply + if left.typ == HIntType() || right.typ == HIntType() + => s"(${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})%Z" + case _ => s"(${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" + } + override def typ: HType = op match { case HOpEq | HOpLe | HOpLt => HBoolType() case HOpUnion => HIntSetType() @@ -71,15 +95,6 @@ object Assertions { case HOpOffset => HLocType() case _ => ??? } - - override def ppAsPhi: String = op match { - case HOpLe | HOpLt => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})%Z" - case HOpUnion => s"(${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" - case HOpEq if left.typ == HIntSetType() || right.typ == HIntSetType() => - s"${left.ppAsPhi} ${op.pp} ${right.ppAsPhi}" - case HOpEq => s"bool_decide (${left.ppAsPhi} ${op.pp} ${right.ppAsPhi})" - case _ => ??? - } } case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { @@ -158,17 +173,63 @@ object Assertions { override val clauses: Map[CardConstructor, IPredicateClause]) extends GenericPredicate[IPureAssertion, ISpatialAssertion, HType](name, params, existentials, clauses) { - def ppPredicate: String = { - def ppConstructorClause(constr: CardConstructor, pclause: IPredicateClause): String = { - val IPredicateClause(pure, spatial, _) = pclause - val clause = IAssertion(IAnd(pure), IHeap(spatial)) - val ex = findExistentials(constr)(pclause) - val exStr = if (ex.nonEmpty) s"∃ ${ex.map({ case (name, ty) => s"($name : ${ty.pp})"}).mkString(" ")}, " else "" - s"${exStr}${clause.pp}" + abstract class IPredicateHelper extends PrettyPrinting + + case class HelpUnfold(predicate: IPredicate, cardConstructor: CardConstructor, pclause: IPredicateClause) extends IPredicateHelper { + override def pp: String = { + s"Lemma ${lemmaName} " + + s"${cardConstructor.constructorArgs.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} " + + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + + s":\n${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ + predicate.expandArgs(pclause.subConstructor)(cardConstructor.constructorArgs) + }) = (${predicate.ppConstructorClause(cardConstructor, pclause)})%I.\nProof. auto. Qed.\n" + } + + def lemmaName: String = s"${constructorName(cardConstructor)}_open" + } + + /*** See the health warnings attached to LocalFacts. The same apply. */ + case class HelpCard(predicate: IPredicate, cardConstructor: CardConstructor, pclause: IPredicateClause) extends IPredicateHelper { + override def pp: String = { + def ppPred: String = s"${predicate.name} ${predicate.params.map(_._1).mkString(" ")}" + + def ppEqualityTerm(cons: CardConstructor): String = + if (cons.constructorArgs.isEmpty) { + s"${ppPred} ${cardinalityParam} = ${ppPred} ${predicate.constructorName(cons)}" + } else { + s"∃ ${cons.constructorArgs.mkString(" ")}, ${ppPred} ${cardinalityParam} = ${ppPred} (${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")})" + } + + s"Lemma ${lemmaName} " + + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + + s"${cardinalityParam}:\n" + + s"${pclause.selector.ppAsPhi} -> ${ppEqualityTerm(cardConstructor)}.\n" + + s"Proof. Admitted.\n" } + def lemmaName: String = s"${constructorName(cardConstructor)}_learn" + + } + + val cardinalityParam: String = "self_card" + + def getHelpers: List[IPredicateHelper] = { + val cardLemmas = clauses.map({ case (constructor, clause) => HelpCard(this, constructor, clause )}) + val unfoldingLemmas = clauses.map({ case (constructor, clause) => HelpUnfold(this, constructor, clause )}) + cardLemmas.toList ++ unfoldingLemmas.toList + } + + def ppConstructorClause(constr: CardConstructor, pclause: IPredicateClause): String = { + val IPredicateClause(pure, spatial, _) = pclause + val clause = IAssertion(IAnd(pure), IHeap(spatial)) + val ex = findExistentials(constr)(pclause) + val exStr = if (ex.nonEmpty) s"∃ ${ex.map({ case (name, ty) => s"($name : ${ty.pp})"}).mkString(" ")}, " else "" + s"${exStr}${clause.pp}" + } + + def ppPredicate: String = { val predicate_definition = - s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (self_card: ${inductiveName}) : iProp Σ := match self_card with + s"""Fixpoint ${name} ${params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} (${cardinalityParam}: ${inductiveName}) { struct self_card } : iProp Σ := match self_card with ${ clauses.map({ case (constructor, pclause@IPredicateClause(_, _, subConstructor)) => s"| | ${constructorName(constructor)} ${ @@ -212,6 +273,7 @@ object Assertions { } case ISpecBinaryExpr(_, left, right) => pureExistentials(left) ++ pureExistentials(right) case ISpecUnaryExpr(_, expr) => pureExistentials(expr) + case ISpecIfThenElse(cond, left, right) => pureExistentials(cond) ++ pureExistentials(left) ++ pureExistentials(right) case ISetLiteral(elems) => elems.flatMap(pureExistentials).toSet case ISpecMakeVal(v) => pureExistentials(v) case ISpecLit(_) => Set() @@ -224,7 +286,8 @@ object Assertions { case IBlock() => Set() case _ => ??? } - (pure.flatMap(pureExistentials) ++ spatial.flatMap(spatialExistentials)).map(v => (v, existMap(v))) + (pure.flatMap(pureExistentials) ++ spatial.flatMap(spatialExistentials)).distinct.map(v => (v, existMap(v))) + } } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index c0dfe4230..0d6fcf885 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -25,18 +25,12 @@ object IrisTranslator { case OpEq | OpBoolEq => HOpEq case OpLeq => HOpLe case OpLt => HOpLt + case OpSetEq => HOpSetEq + case OpUnion => HOpUnion case _ => ??? } - implicit val overloadedBinOpTranslator: IrisTranslator[OverloadedBinOp, HBinOp] = (value, _, _) => value match { - case OpOverloadedEq => HOpEq - case OpOverloadedLeq => HOpLe - case OpOverloadedPlus => HOpUnion - case OpOverloadedStar => HOpMultiply - case _ => ??? - } - - implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _, _) => el._1.translate(progVarTranslator) + implicit val paramTranslator: IrisTranslator[(Var, SSLType), HProgVar] = (el, _, _) => el._1.translate implicit val unopTranslator: IrisTranslator[UnOp, HUnOp] = (value, _, _) => value match { case OpNot => HOpNot @@ -49,17 +43,21 @@ object IrisTranslator { case IntConst(v) => HLitInt(v) case LocConst(v) => HLitLoc(v) case BoolConst(v) => HLitBool(v) - case SetLiteral(elems) => HSetLiteral(elems.map(_.translate(exprTranslator))) + case SetLiteral(elems) => HSetLiteral(elems.map(_.translate(exprTranslator, ctx))) case UnaryExpr(op, arg) => HUnaryExpr(op.translate, visit(arg)) // Hack for null-pointer comparison case BinaryExpr(OpEq, v:Var, IntConst(value)) if ctx.get.gamma.get(v).contains(LocType) && value == 0 => HBinaryExpr(HOpEq, visit(v), HLitLoc(0)) case BinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) case IfThenElse(cond, t, f) => HIfThenElse(visit(cond), visit(t), visit(f)) - case OverloadedBinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) +// case OverloadedBinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) case _ => ??? } - visit(expr) + + // FIXME: we should probably guarantee that the context gets passed also during program translation + // (it does get passed during spec translation, I think) + val e = if (ctx.isDefined) expr.resolveOverloading(ctx.get.hctx.translate) else expr + visit(e) } implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _, _) => HProgVar(pv.name) @@ -81,12 +79,24 @@ object IrisTranslator { case _ => ??? } + implicit val reverseTypeTranslator: IrisTranslator[HType, SSLType] = (value, _, _) => value match { + case HIntType() => IntType + case HLocType() => LocType + case HIntSetType() => IntSetType + case HCardType(_) | HUnknownType() => CardType + case _ => ??? + } + implicit val gammaTranslator: IrisTranslator[Gamma, Map[Ident, HType]] = (g, _ , _) => { g.map({ case (v, t) => (v.name, t.translate) }) } - implicit val mallocTranslator: IrisTranslator[Malloc, HStore] = - (stmt, _, _) => HStore(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit())) + implicit val reverseGammaTranslator: IrisTranslator[Map[Ident, HType], Gamma] = (h, _, _) => { + h.map( {case (v, t) => (Var(v), t.translate)}) + } + + implicit val mallocTranslator: IrisTranslator[Malloc, HLet] = + (stmt, _, _) => HLet(stmt.to.translate, HAllocN(HLitInt(stmt.sz), HLitUnit()), HLitUnit()) implicit val loadTranslator: IrisTranslator[Load, HLet] = (stmt, _, _) => { val baseVar = stmt.from.translate @@ -138,40 +148,26 @@ object IrisTranslator { case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx, subTarget), right.translate(toSpecExpr, ctx, subTarget)) case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecExpr, ctx, subTarget)) case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecExpr, ctx, subTarget))) + case HIfThenElse(cond, left, right) => ISpecIfThenElse(cond.translate(toSpecExpr, ctx, subTarget), left.translate(toSpecExpr, ctx, subTarget), right.translate(toSpecExpr, ctx, subTarget)) case _ => ??? } } -// implicit val toSpecVal: IrisTranslator[HExpr, IPureAssertion] = (expr, ctx) => { -// assert(ctx.isDefined) -// expr match { -// // Get the quantified value if it is quantified -// // If it's a ghost, it has a non-value type (e.g. Z), so we have to make it into a value -// case v: HProgVar => ctx.get.pts.getOrElse(v, ISpecLit(new HLit(v.name))) -// case l: HLit => ISpecLit(l) -// case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecVal, ctx), right.translate(toSpecVal, ctx)) -// case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecVal, ctx)) -// case HSetLiteral(elems) => ISetLiteral(elems.map(_.translate(toSpecVal, ctx))) -// case _ => ??? -// } -// } - - implicit val pointsToTranslator: IrisTranslator[PointsTo, IPointsTo] = (f, ctx, _) => { - val base = f.loc.translate.translate(toSpecExpr, ctx) + val base = f.loc.translate(exprTranslator, ctx).translate(toSpecExpr, ctx) val loc = if (f.offset > 0) ISpecBinaryExpr(HOpOffset, base, ISpecLit(HLitOffset(f.offset))) else base // Sometimes we get PointsTo(Var(x), 0, LocConst(0)) when we should get an IntConst val valToTranslate = f.value match { case LocConst(v) if v == 0 => IntConst(0) case _ => f.value } - val value = valToTranslate.translate.translate(toSpecExpr, ctx, Some(HValType())) + val value = valToTranslate.translate(exprTranslator, ctx).translate(toSpecExpr, ctx, Some(HValType())) IPointsTo(loc, value) } implicit val predAppTranslator: IrisTranslator[SApp, IPredApp] = (pa, ctx, _) => { assert(ctx.isDefined) - IPredApp(pa.pred, pa.args.map(_.translate.translate(toSpecExpr, ctx)), pa.card.translate.translate(toSpecExpr, ctx)) + IPredApp(pa.pred, pa.args.map(_.translate(exprTranslator, ctx).translate(toSpecExpr, ctx)), pa.card.translate(exprTranslator, ctx).translate(toSpecExpr, ctx)) } implicit val heapleatTranslator: IrisTranslator[Heaplet, ISpatialAssertion] = (h, ctx, _) => { @@ -197,10 +193,6 @@ object IrisTranslator { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) - // OLD: untyped universals and existentials - // val specUniversal = g.universals.map(_.translate.translate(progVarToSpecVar)).toSeq ++ quantifiedValues - // val specExistential = g.existentials.map(_.translate.translate(progVarToSpecVar)).toSeq - // We "unwrap" every value to a l ↦ v form. quantifiedVal = v and takes the type of the value. val quantifiedValues = g.programVars.map( v => (v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)), g.gamma(v).translate) @@ -227,7 +219,7 @@ object IrisTranslator { override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = { val predCtx = Some(TranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) - expr.translate.translate(toSpecExpr, predCtx, Some(HValType())) + expr.translate(exprTranslator, predCtx).translate(toSpecExpr, predCtx) } override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = { From d5b17444e8fc6b77ec09a9115063429d33522f53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Thu, 25 Feb 2021 18:52:05 +0800 Subject: [PATCH 139/211] Iris: skeleton for generating proofs --- .../targets/iris/IrisCertificate.scala | 16 ++++-- .../targets/iris/logic/Assertions.scala | 4 +- .../targets/iris/logic/IProofStep.scala | 33 ++++++++++++ .../iris/translation/IrisTranslator.scala | 6 +-- .../iris/translation/ProgramEvaluator.scala | 9 ---- .../iris/translation/ProgramTranslator.scala | 13 +++-- .../iris/translation/ProofTranslator.scala | 54 +++++++++++++++++++ .../iris/translation/TranslatableOps.scala | 2 +- .../iris/translation/Translation.scala | 22 ++++++-- .../iris/translation/TranslationContext.scala | 18 ------- .../synthesis/certification/ints/swap1.syn | 15 ------ 11 files changed, 134 insertions(+), 58 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala delete mode 100644 src/test/resources/synthesis/certification/ints/swap1.syn diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 02d3a866a..99a6569db 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -1,10 +1,14 @@ package org.tygus.suslik.certification.targets.iris +import org.tygus.suslik.certification.targets.htt.logic.ProofPrinter import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} +import org.tygus.suslik.certification.targets.iris.logic.IProofStep +import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePrinter +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec) extends Certificate { +case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { val target: CertificationTarget = Iris private val prelude = @@ -68,9 +72,10 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | |Local Ltac iRewriteHyp := | repeat match goal with - | | [H: loc_at _ _ |- _ ] => rewrite H + | | [H: loc_at ?x ?rx |- _ ] => rewrite H; clear x H; rename rx into x | | [H: Z_at _ _ |- _ ] => rewrite H | | [H: bool_decide _ = _ |- _ ] => rewrite H + | | [H : _ = _ |- _ ]=> rewrite H | end. | |Local Ltac iSimplNoSplit := @@ -79,10 +84,13 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. | |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. + |Ltac ssl_let := wp_let. |Ltac ssl_load := wp_load; wp_let; iSimplContext. |Ltac ssl_store := wp_store; iSimplContext. + |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. |Ltac ssl_finish := by iFindApply; iFrame "% # ∗". | + | |""".stripMargin def pp : String = { @@ -94,7 +102,9 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) - b.append("Proof.\nAdmitted.\n") + b.append("Proof.\n") + b.append(ProofTreePrinter.pp(proof)) + b.append("Qed.\n") b.toString() } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 998765c59..27c05173f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -77,7 +77,7 @@ object Assertions { override def ppAsBool: String = op match { case HOpLe => s"(Z.leb ${left.ppAsPhi} ${right.ppAsPhi})" - case HOpLe => s"(Z.ltb ${left.ppAsPhi} ${right.ppAsPhi})" + case HOpLt => s"(Z.ltb ${left.ppAsPhi} ${right.ppAsPhi})" case _ => ppAsPhi } @@ -129,7 +129,7 @@ object Assertions { } } - case class IFunSpec(fname: String, + case class IFunSpec(fname: Ident, funArgs: Seq[(ISpecVar, HType)], specUniversal: Seq[(IQuantifiedVar, HType)], specExistential: Seq[(IQuantifiedVar, HType)], diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala new file mode 100644 index 000000000..a31315007 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -0,0 +1,33 @@ +package org.tygus.suslik.certification.targets.iris.logic + +import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} +import org.tygus.suslik.certification.traversal.Step.DestStep + +sealed abstract class IProofStep extends DestStep {} + +object IProofStep { + + implicit object ProofTreePrinter extends ProofTreePrinter[IProofStep] { + override def pp(tree: ProofTree[IProofStep]): String = tree.step match { + case _ => tree.step.pp ++ "\n" ++ tree.children.map(pp).mkString("\n") + } + } + +} + +case object IInit extends IProofStep { + override def pp: String = "ssl_begin." +} + +case object ILoad extends IProofStep { + override def pp: String = "ssl_load." +} + +case object IStore extends IProofStep { + override def pp: String = "ssl_store." +} + +case object IEmp extends IProofStep { + override def pp: String = "ssl_finish." +} + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 0d6fcf885..7482df7be 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -12,7 +12,7 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic._ trait IrisTranslator[From, To] { - def translate(value: From, ctx: Option[TranslationContext] = None, target: Option[HType] = None): To + def translate(value: From, ctx: Option[ProgramTranslationContext] = None, target: Option[HType] = None): To } object IrisTranslator { @@ -218,12 +218,12 @@ object IrisTranslator { } override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = { - val predCtx = Some(TranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + val predCtx = Some(ProgramTranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) expr.translate(exprTranslator, predCtx).translate(toSpecExpr, predCtx) } override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = { - val predCtx = Some(TranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + val predCtx = Some(ProgramTranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) heaplets.map(_.translate(heapleatTranslator, predCtx)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala deleted file mode 100644 index 74263d59a..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramEvaluator.scala +++ /dev/null @@ -1,9 +0,0 @@ -package org.tygus.suslik.certification.targets.iris.translation - -import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HExpr -import org.tygus.suslik.certification.traversal.StackEvaluator - -object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, TranslationContext] { - val translator = ProgramTranslator -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala index 49e4fdb77..ca5be5319 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -1,17 +1,24 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset, HProgVar} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.language.Ident +import org.tygus.suslik.logic.{Environment, Gamma} + +case class ProgramTranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar], hctx: Map[Ident, HType]) extends ClientContext[HExpr] /** * Extract a HeapLang program directly from the SSL proof. */ -object ProgramTranslator extends Translator[SuslikProofStep, HExpr, TranslationContext] { +object ProgramTranslator extends Translator[SuslikProofStep, HExpr, ProgramTranslationContext] { - override def translate(step: SuslikProofStep, ctx: TranslationContext): Translator.Result[HExpr, TranslationContext] = { + override def translate(step: SuslikProofStep, ctx: ProgramTranslationContext): Translator.Result[HExpr, ProgramTranslationContext] = { val withNoDeferred = (Nil, None, ctx) step match { case SuslikProofStep.Open(_, _, _, selectors) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala new file mode 100644 index 000000000..e1dadc231 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -0,0 +1,54 @@ +package org.tygus.suslik.certification.targets.iris.translation + +import org.tygus.suslik.certification.source.SuslikProofStep +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} +import org.tygus.suslik.certification.targets.iris.logic.{IEmp, IInit, ILoad, IProofStep, IStore} +import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.language.Expressions.Var +import org.tygus.suslik.language.{Ident, Statements} +import org.tygus.suslik.language.Statements.Load + +case class IProofContext( +// predMap: Map[Ident, IPredicate], +// specMap: Map[Ident, IFunSpec] + ) extends ClientContext[IProofStep] + +case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, IProofStep, IProofContext] { + type Deferred = Evaluator.Deferred[IProofStep, IProofContext] + type Result = Translator.Result[IProofStep, IProofContext] + + private val noDeferreds: Option[Deferred] = None + + private def withNoDeferreds(ctx: IProofContext) : (List[IProofStep], Option[Deferred], IProofContext) = (Nil, noDeferreds, ctx) + + def withNoOp(context: IProofContext): Result = Result(List(), List((Nil, None, context))) + + override def translate(value: SuslikProofStep, clientContext: IProofContext): Result = { + value match { + case SuslikProofStep.Init(goal) => Result(List(IInit), List(withNoDeferreds(clientContext))) + + case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) + case SuslikProofStep.NilNotLval(vars) => withNoOp(clientContext) + + case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => + Result(List(ILoad), List(withNoDeferreds(clientContext))) + + case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => + Result(List(IStore), List(withNoDeferreds(clientContext))) + + /** Ignored rules */ + case SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.WeakenPre(_) + | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyUnfold(_, _, _) + | SuslikProofStep.HeapUnifyPointer(_, _) + | SuslikProofStep.StarPartial(_, _) + | SuslikProofStep.FrameUnfold(_, _) => + withNoOp(clientContext) + + case _ => ??? + } + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala index 71684aa5f..bef46d89d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslatableOps.scala @@ -4,7 +4,7 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType object TranslatableOps { implicit class Translatable[A](value: A) { - def translate[B](implicit translator: IrisTranslator[A,B], ctx: Option[TranslationContext] = None, target: Option[HType] = None): B = { + def translate[B](implicit translator: IrisTranslator[A,B], ctx: Option[ProgramTranslationContext] = None, target: Option[HType] = None): B = { translator.translate(value, ctx, target) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 7a3e9cdde..83043b48d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -3,11 +3,22 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.IrisCertificate -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HFunDef} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IFunSpec +import org.tygus.suslik.certification.targets.iris.logic.IProofStep import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator._ +import org.tygus.suslik.certification.traversal.{StackEvaluator, Translator} + +object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramTranslationContext] { + val translator: Translator[SuslikProofStep, HExpr, ProgramTranslationContext] = ProgramTranslator +} + +case class ProofEvaluator(spec: IFunSpec) extends StackEvaluator[SuslikProofStep, IProofStep, IProofContext] { + val translator: Translator[SuslikProofStep, IProofStep, IProofContext] = ProofTranslator(spec) +} object Translation { @@ -19,10 +30,10 @@ object Translation { val params = proc.formals.map(_.translate) // We have this "dummy" value to generate progToSpec for the actual context, ctx - val pre_ctx = Some(TranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) + val pre_ctx = Some(ProgramTranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) val progToSpec = params.map(p => (p, p.translate(progVarToSpecQuantifiedValue, pre_ctx))) - val ctx = TranslationContext(env, node.goal.gamma, progToSpec.toMap, node.goal.gamma.translate) + val ctx = ProgramTranslationContext(env, node.goal.gamma, progToSpec.toMap, node.goal.gamma.translate) val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList predicates.foreach(p => println(p.pp)) @@ -31,6 +42,9 @@ object Translation { val funDef = HFunDef(proc.name, params, progTree) val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) - IrisCertificate(proc.name, predicates, funDef, funSpec) + val proofCtx = IProofContext() + val proofTree = ProofEvaluator(funSpec).run(suslikTree, proofCtx) + + IrisCertificate(proc.name, predicates, funDef, funSpec, proofTree) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala deleted file mode 100644 index 1be4137b7..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/TranslationContext.scala +++ /dev/null @@ -1,18 +0,0 @@ -package org.tygus.suslik.certification.targets.iris.translation - -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HProgVar} -import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType -import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar -import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.language.Ident -import org.tygus.suslik.logic.{Environment, Gamma} - -/*** - * - * @param env - * @param gamma - * @param pts mapping between program and quantified spec variables - * @param hctx heaplang type context used for predicates - */ - -case class TranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar], hctx: Map[Ident, HType]) extends ClientContext[HExpr] \ No newline at end of file diff --git a/src/test/resources/synthesis/certification/ints/swap1.syn b/src/test/resources/synthesis/certification/ints/swap1.syn deleted file mode 100644 index cc2a62880..000000000 --- a/src/test/resources/synthesis/certification/ints/swap1.syn +++ /dev/null @@ -1,15 +0,0 @@ -should be able to synthesize a non-trivial swap program - -### - -{true; x :-> a ** y :-> c ** z :-> b ** t :-> q } -void swap1 (loc x, loc z, loc y, loc t) -{ true; x :-> c ** z :-> b ** t :-> q ** y :-> 41 } - -### - -void swap1 (loc x, loc z, loc y, loc t) { - let c = *y; - *x = c; - *y = 41; -} \ No newline at end of file From d349264d5808ee26b5abebf74713a19d80dc2e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Fri, 26 Feb 2021 18:42:58 +0800 Subject: [PATCH 140/211] Iris: int proofs go through! :D --- .../targets/iris/IrisCertificate.scala | 9 +- .../targets/iris/logic/Assertions.scala | 34 ++++- .../targets/iris/logic/IProofStep.scala | 65 +++++++- .../iris/translation/IrisTranslator.scala | 11 +- .../iris/translation/ProofTranslator.scala | 143 ++++++++++++++---- .../iris/translation/Translation.scala | 7 +- 6 files changed, 220 insertions(+), 49 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 99a6569db..fe9b27922 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -15,6 +15,8 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe s"""From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. |From iris.heap_lang Require Import lang notation proofmode. + |From iris_string_ident Require Import ltac2_string_ident. + | |From Hammer Require Import Hammer. |Context `{!heapG Σ}. |Set Default Proof Using "Type". @@ -73,7 +75,7 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Local Ltac iRewriteHyp := | repeat match goal with | | [H: loc_at ?x ?rx |- _ ] => rewrite H; clear x H; rename rx into x - | | [H: Z_at _ _ |- _ ] => rewrite H + | | [H: Z_at ?x ?rx |- _ ] => rewrite H; clear x H; rename rx into x | | [H: bool_decide _ = _ |- _ ] => rewrite H | | [H : _ = _ |- _ ]=> rewrite H | end. @@ -83,13 +85,12 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. | - |Ltac ssl_begin := iIntros; (wp_rec; repeat wp_let); iSimplContext. + |Ltac ssl_begin := (wp_rec; repeat wp_let); iSimplContext. |Ltac ssl_let := wp_let. |Ltac ssl_load := wp_load; wp_let; iSimplContext. |Ltac ssl_store := wp_store; iSimplContext. |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. - |Ltac ssl_finish := by iFindApply; iFrame "% # ∗". - | + |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; try iPureIntro; try lia; done. | |""".stripMargin diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 27c05173f..dd75721fc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.iris.logic +import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HBoolType, HIntSetType, HIntType, HLocType, HType, HValType} import org.tygus.suslik.certification.translation.{CardConstructor, GenericPredicate, GenericPredicateClause} @@ -14,11 +15,25 @@ object Assertions { def ppAsPhi: String = pp def ppAsBool: String = ppAsPhi def typ: HType + + def conjuncts: Seq[IPureAssertion] = throw TranslationException("Called conjuncts on IPureAssertion not of type IAnd.") + + // TODO: implement this + def subst(s: Map[Ident, IPureAssertion]): IPureAssertion = this match { + case expr => expr + } + } + + abstract class IQuantifiedVar extends IPureAssertion { + def name: Ident + + def originalName: Ident = throw TranslationException("Called originalName on IQuantifiedVar not of type ISpecQuantifiedValue.") } - abstract class IQuantifiedVar extends IPureAssertion + abstract class ISpatialAssertion extends PrettyPrinting { - abstract class ISpatialAssertion extends PrettyPrinting + def heaplets: Seq[ISpatialAssertion] = throw TranslationException("Called heaplets on ISpatialAssertion not of type IHeap.") + } abstract class ISpecification extends PrettyPrinting @@ -42,7 +57,7 @@ object Assertions { override def ppAsPhi: String = super.ppAsPhi } - case class ISpecQuantifiedValue(name: String, typ: HType) extends IQuantifiedVar { + case class ISpecQuantifiedValue(name: String, override val originalName: Ident, typ: HType) extends IQuantifiedVar { override def pp: String = s"${name}" } @@ -97,10 +112,11 @@ object Assertions { } } - case class IAnd(conjuncts: Seq[IPureAssertion]) extends IPureAssertion { + case class IAnd(override val conjuncts: Seq[IPureAssertion]) extends IPureAssertion { override def pp: String = s"${conjuncts.map(_.ppAsPhi).mkString(" ∧ ")}" override def typ: HType = HBoolType() + } case class IPointsTo(loc: IPureAssertion, value: IPureAssertion) extends ISpatialAssertion { @@ -116,7 +132,7 @@ object Assertions { override def pp: String = s"⌜True⌝" } - case class IHeap(heaplets: Seq[ISpatialAssertion]) extends ISpatialAssertion { + case class IHeap(override val heaplets: Seq[ISpatialAssertion]) extends ISpatialAssertion { override def pp: String = s"${heaplets.map(_.pp).mkString(" ∗ ")}" } @@ -132,6 +148,7 @@ object Assertions { case class IFunSpec(fname: Ident, funArgs: Seq[(ISpecVar, HType)], specUniversal: Seq[(IQuantifiedVar, HType)], + artificialUniversal: Seq[(IQuantifiedVar, HType)], specExistential: Seq[(IQuantifiedVar, HType)], pre: IAssertion, post: IAssertion @@ -141,8 +158,8 @@ object Assertions { // TODO: make this use the general translation mechanism def getArgLitVal(v: ISpecVar, t: HType): ISpecQuantifiedValue = (v, t) match { - case (ISpecVar(name, t), HLocType()) => ISpecQuantifiedValue(s"l${name}", t) - case (ISpecVar(name, t), _) => ISpecQuantifiedValue(s"v${name}", t) + case (ISpecVar(name, t), HLocType()) => ISpecQuantifiedValue(s"l${name}", name, t) + case (ISpecVar(name, t), _) => ISpecQuantifiedValue(s"v${name}", name, t) } val var_at = (v: ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" @@ -151,9 +168,10 @@ object Assertions { s"∃ ${specExistential.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, " else "" + val universal = specUniversal ++ artificialUniversal s""" |Lemma ${fname}_spec : - |∀ ${specUniversal.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, + |∀ ${universal.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, |${funArgs.map(v => var_at(v._1, v._2)).mkString(" →\n")} → |{{{ ${pre.pp} }}} | ${fname} ${funArgs.map(v => v._1.pp).mkString(" ")} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index a31315007..f96f5c7c6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.iris.logic +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPureAssertion import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.DestStep +import org.tygus.suslik.language.{Ident, PrettyPrinting} sealed abstract class IProofStep extends DestStep {} @@ -15,6 +17,55 @@ object IProofStep { } +case class ICoqName(name: Ident) extends PrettyPrinting { + override def pp: String = s"${name}" +} + +sealed abstract class IIntroPattern extends PrettyPrinting + +case class IIdent(name: Ident) extends IIntroPattern { + override def pp: String = s"${name}" +} + +case class IPure(name: Ident) extends IIntroPattern { + override def pp: String = s"%${name}" +} + +case class IIntuitionistic(pattern: IIntroPattern) extends IIntroPattern { + override def pp: String = s"#${pattern.pp}" +} + +case class IPatDestruct(patterns: Seq[IIntroPattern]) extends IIntroPattern { + override def pp: String = { + val pats = s"${patterns.map(_.pp).mkString(" & ")}" + if (patterns.length > 1) s"($pats)" else pats + } +} + +case class IPatList(patterns: Seq[IIntroPattern]) extends IIntroPattern { + override def pp: String = s"${patterns.map(_.pp).mkString(" ")}" +} + +case class IIntros(coq: Seq[ICoqName], iris: IIntroPattern) extends IProofStep { + override def pp: String = { + val coqStr = if (coq.nonEmpty) s"(${coq.map(_.pp).mkString(" ")})" else "" + val irisStr = if (iris.pp.nonEmpty) s""""${iris.pp}"""" else "" + s"iIntros $coqStr $irisStr." + } +} + +case class IExists(e: IPureAssertion) extends IProofStep { + override def pp: String = s"iExists ${e.pp}." +} + +case object IFindApply extends IProofStep { + override def pp: String = "iFindApply." +} + +case object IRewriteHyp extends IProofStep { + override def pp: String = "iRewriteHyp." +} + case object IInit extends IProofStep { override def pp: String = "ssl_begin." } @@ -27,7 +78,19 @@ case object IStore extends IProofStep { override def pp: String = "ssl_store." } -case object IEmp extends IProofStep { +case object IBegin extends IProofStep { + override def pp: String = "ssl_begin." +} + +case class IIf(hyp: ICoqName) extends IProofStep { + override def pp: String = s"ssl_if ${hyp.pp}." +} + +case object IFinish extends IProofStep { override def pp: String = "ssl_finish." } +case object IEmp extends IProofStep { + override def pp: String = "try wp_pures." +} + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 7482df7be..74c4aa651 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -66,8 +66,8 @@ object IrisTranslator { implicit val progVarToSpecQuantifiedValue: IrisTranslator[HProgVar, ISpecQuantifiedValue] = (pv, ctx, _) => { assert(ctx.isDefined) (pv, ctx.get.gamma(Var(pv.name)).translate) match { - case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}", HLocType()) - case (HProgVar(name), t) => ISpecQuantifiedValue(s"v${name}", t) + case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}", name, HLocType()) + case (HProgVar(name), t) => ISpecQuantifiedValue(s"v${name}", name, t) } } @@ -194,18 +194,17 @@ object IrisTranslator { val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) // We "unwrap" every value to a l ↦ v form. quantifiedVal = v and takes the type of the value. - val quantifiedValues = g.programVars.map( + val artificialUniversal = g.programVars.map( v => (v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)), g.gamma(v).translate) ) // We quantify over all universals, ignoring the type of function arguments - val quantifiedLocs = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), + val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), if(g.programVars.contains(v)) HValType() else g.gamma(v).translate)) - val specUniversal = quantifiedLocs.toSeq ++ quantifiedValues val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)).toSeq val pre = g.pre.translate(assertionTranslator, ctx) val post = g.post.translate(assertionTranslator, ctx) - IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), specUniversal, specExistential, pre, post) + IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), specUniversal.toSeq, artificialUniversal, specExistential, pre, post) } implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx, _) => { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index e1dadc231..ba035d9e9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -1,19 +1,58 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} -import org.tygus.suslik.certification.targets.iris.logic.{IEmp, IInit, ILoad, IProofStep, IStore} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HType, HUnknownType} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate, IPureAssertion} +import org.tygus.suslik.certification.targets.iris.logic._ +import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} -import org.tygus.suslik.language.Expressions.Var -import org.tygus.suslik.language.{Ident, Statements} +import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements.Load +import org.tygus.suslik.language.{Ident, Statements} + case class IProofContext( -// predMap: Map[Ident, IPredicate], -// specMap: Map[Ident, IFunSpec] - ) extends ClientContext[IProofStep] + var counter: Integer = 0, + baseTranslationContext: ProgramTranslationContext, + predMap: Map[Ident, IPredicate], + specMap: Map[Ident, IFunSpec], + // irisSpatial: Map[Ident, ISpatialAssertion], + coqTypingCtx: Map[Ident, HType], + varMap: Map[Ident, IPureAssertion] + ) extends ClientContext[IProofStep] { + + def freshHypName(): String = { + counter += 1 + s"iH${counter}" + } + + def typingContext: Map[Ident, HType] = coqTypingCtx + + def withVariablesTypes(variables: Map[Ident, HType]): IProofContext = { + this.copy(coqTypingCtx = coqTypingCtx ++ variables) + } + + def withMappingBetween(from: Ident, to: Expr): IProofContext = { + val targetType = typingContext.get(from) + val translationCtx = Some(baseTranslationContext.copy(pts = Map.empty, hctx = typingContext)) + val toExpr = to.translate.translate(IrisTranslator.toSpecExpr, translationCtx, targetType) + withMappingBetween(from, toExpr) + } + + def withMappingBetween(from: Ident, to: IPureAssertion): IProofContext = { + val s = Map(from -> to) + val newVarMap = varMap.map({ case (name, vl) => (name, vl.subst(s)) }) ++ s + this.copy(varMap = newVarMap) + } + + def resolveExistential(ident: Ident): IPureAssertion = { + // TODO: renamings + val name = ident + varMap(name).subst(varMap) + } +} case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, IProofStep, IProofContext] { type Deferred = Evaluator.Deferred[IProofStep, IProofContext] @@ -21,34 +60,80 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val noDeferreds: Option[Deferred] = None - private def withNoDeferreds(ctx: IProofContext) : (List[IProofStep], Option[Deferred], IProofContext) = (Nil, noDeferreds, ctx) + override def translate(value: SuslikProofStep, clientContext: IProofContext): Result = value match { + case SuslikProofStep.Init(_) => + var ctx = clientContext + val coqHyps = + spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ + spec.artificialUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ + // We only use the loc_at / Z_at lemmas for rewriting, so we don't give them names + spec.artificialUniversal.map({ case (v, _) => ICoqName("?") }) ++ + // Iris specific thing + List(ICoqName("ϕ")) - def withNoOp(context: IProofContext): Result = Result(List(), List((Nil, None, context))) + val pureIntro = spec.pre.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) + val spatialIntro = spec.pre.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) + // TODO: add spatial assertions to context && rewrite them accordingly + val irisHyps = IPatList(List(IPatDestruct(pureIntro ++ spatialIntro), IIdent("Post"))) + val intro = IIntros(coqHyps, irisHyps) + + // We modify the context to account for the loc_at / Z_at rewriting. + // All the artificialUniversals that show up in the spec will be renamed by IBegin, + // e.g. (l : val) with (lr : loc) (lr is artificial) will result in (l : loc). + val typeOf = spec.artificialUniversal.map({ case (v, t) => (v.originalName, t) }).toMap + val programVars = spec.specUniversal.map({ case (v, _) => (v.name, typeOf.getOrElse(v.name, HUnknownType())) }) + val existentials = spec.specExistential.map({ case (v, t) => (v.name, t) }) + ctx = ctx withVariablesTypes programVars.toMap + ctx = ctx withVariablesTypes existentials.toMap + + val steps = List(intro, IBegin) + + val deferred: Deferred = (ctx: IProofContext) => { + val instantiate = existentials.map({ case (v, _) => IExists(ctx resolveExistential v) }) + (List(IFindApply) ++ instantiate ++ List(IFinish), ctx) + } + + Result(steps, List((Nil, Some(deferred), ctx))) - override def translate(value: SuslikProofStep, clientContext: IProofContext): Result = { - value match { - case SuslikProofStep.Init(goal) => Result(List(IInit), List(withNoDeferreds(clientContext))) + case SuslikProofStep.Branch(_, _) => + val cont = withNoDeferreds(clientContext) + val step = IIf(ICoqName(clientContext.freshHypName())) + Result(List(step), List(cont, cont)) - case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) - case SuslikProofStep.NilNotLval(vars) => withNoOp(clientContext) + case SuslikProofStep.Pick(Var(fromName), to) => + val newCtx = clientContext withMappingBetween(fromName, to) + withNoOp(newCtx) - case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - Result(List(ILoad), List(withNoDeferreds(clientContext))) + case SuslikProofStep.PureSynthesis(_, assignments) => + val newCtx = + assignments.foldLeft(clientContext)({ case (ctx, (Var(fromName), to)) => ctx withMappingBetween(fromName, to) }) + withNoOp(newCtx) - case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => - Result(List(IStore), List(withNoDeferreds(clientContext))) + /** Statements */ + case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => + Result(List(ILoad), List(withNoDeferreds(clientContext))) - /** Ignored rules */ - case SuslikProofStep.CheckPost(_, _) - | SuslikProofStep.WeakenPre(_) - | SuslikProofStep.HeapUnify(_) - | SuslikProofStep.HeapUnifyUnfold(_, _, _) - | SuslikProofStep.HeapUnifyPointer(_, _) - | SuslikProofStep.StarPartial(_, _) - | SuslikProofStep.FrameUnfold(_, _) => - withNoOp(clientContext) + case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => + Result(List(IStore), List(withNoDeferreds(clientContext))) - case _ => ??? - } + case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) + case SuslikProofStep.NilNotLval(vars) => withNoOp(clientContext) // TODO: add assumption + + + /** Ignored rules */ + case SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.WeakenPre(_) + | SuslikProofStep.HeapUnify(_) + | SuslikProofStep.HeapUnifyUnfold(_, _, _) + | SuslikProofStep.HeapUnifyPointer(_, _) + | SuslikProofStep.StarPartial(_, _) + | SuslikProofStep.FrameUnfold(_, _) => + withNoOp(clientContext) + + case _ => ??? } + + private def withNoDeferreds(ctx: IProofContext): (List[IProofStep], Option[Deferred], IProofContext) = (Nil, noDeferreds, ctx) + + def withNoOp(context: IProofContext): Result = Result(List(), List((Nil, None, context))) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 83043b48d..33d6278c8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -42,7 +42,12 @@ object Translation { val funDef = HFunDef(proc.name, params, progTree) val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) - val proofCtx = IProofContext() + + val predMap = predicates.map(p => (p.name, p)).toMap + // TODO: add support for helper functions + val specMap = List((funSpec.fname, funSpec)).toMap + + val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty) val proofTree = ProofEvaluator(funSpec).run(suslikTree, proofCtx) IrisCertificate(proc.name, predicates, funDef, funSpec, proofTree) From 8c2b5458d0a781c30d32416b72bc9e7bce265efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 13:55:11 +0800 Subject: [PATCH 141/211] Iris: open rule --- .../targets/iris/IrisCertificate.scala | 3 +- .../targets/iris/heaplang/Expressions.scala | 2 +- .../targets/iris/logic/Assertions.scala | 11 +-- .../targets/iris/logic/IProofStep.scala | 47 +++++++++- .../iris/translation/ProofTranslator.scala | 92 ++++++++++++++----- 5 files changed, 123 insertions(+), 32 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index fe9b27922..1bcc33030 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -84,13 +84,14 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe | (repeat wp_pures); movePure; iRewriteHyp; iSimpl in "# ∗"; iSimpl. | |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. + |Ltac dispatchPure := iRewriteHyp; try lia; try sauto; done. | |Ltac ssl_begin := (wp_rec; repeat wp_let); iSimplContext. |Ltac ssl_let := wp_let. |Ltac ssl_load := wp_load; wp_let; iSimplContext. |Ltac ssl_store := wp_store; iSimplContext. |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. - |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; try iPureIntro; try lia; done. + |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; dispatchPure. | |""".stripMargin diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index c2cb69b05..273ae4f5b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -131,7 +131,7 @@ object Expressions { override def pp: String = s"${lhs.pp} <- ${rhs.pp}" } case class HFree(e: HExpr) extends HExpr { - override def pp: String = s"Free ${e.pp}" + override def pp: String = s"Free (${e.pp})" } case class HAlloc(e: HExpr) extends HExpr { override def pp: String = s"Alloc ${e.pp}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index dd75721fc..0197207b5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -195,7 +195,7 @@ object Assertions { case class HelpUnfold(predicate: IPredicate, cardConstructor: CardConstructor, pclause: IPredicateClause) extends IPredicateHelper { override def pp: String = { - s"Lemma ${lemmaName} " + + s"Lemma ${predicate.openLemmaName(cardConstructor)} " + s"${cardConstructor.constructorArgs.map(v => s"(${v} : ${predicate.inductiveName})").mkString(" ")} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + s":\n${predicate.name} ${predicate.params.map(_._1).mkString(" ")} (${predicate.constructorName(cardConstructor)} ${ @@ -203,9 +203,11 @@ object Assertions { }) = (${predicate.ppConstructorClause(cardConstructor, pclause)})%I.\nProof. auto. Qed.\n" } - def lemmaName: String = s"${constructorName(cardConstructor)}_open" } + def openLemmaName(cardConstructor: CardConstructor): String = s"${constructorName(cardConstructor)}_open" + def learnLemmaName(cardConstructor: CardConstructor): String = s"${constructorName(cardConstructor)}_learn" + /*** See the health warnings attached to LocalFacts. The same apply. */ case class HelpCard(predicate: IPredicate, cardConstructor: CardConstructor, pclause: IPredicateClause) extends IPredicateHelper { override def pp: String = { @@ -218,15 +220,12 @@ object Assertions { s"∃ ${cons.constructorArgs.mkString(" ")}, ${ppPred} ${cardinalityParam} = ${ppPred} (${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")})" } - s"Lemma ${lemmaName} " + + s"Lemma ${predicate.learnLemmaName(cardConstructor)} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + s"${cardinalityParam}:\n" + s"${pclause.selector.ppAsPhi} -> ${ppEqualityTerm(cardConstructor)}.\n" + s"Proof. Admitted.\n" } - - def lemmaName: String = s"${constructorName(cardConstructor)}_learn" - } val cardinalityParam: String = "self_card" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index f96f5c7c6..279d1ee5d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -1,6 +1,8 @@ package org.tygus.suslik.certification.targets.iris.logic -import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPureAssertion +import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IPredicate, IPureAssertion} +import org.tygus.suslik.certification.translation.CardConstructor import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -37,8 +39,9 @@ case class IIntuitionistic(pattern: IIntroPattern) extends IIntroPattern { case class IPatDestruct(patterns: Seq[IIntroPattern]) extends IIntroPattern { override def pp: String = { - val pats = s"${patterns.map(_.pp).mkString(" & ")}" - if (patterns.length > 1) s"($pats)" else pats + val nonEmptyPats = patterns.filterNot(_.pp.isEmpty) + val patsStr = s"${nonEmptyPats.map(_.pp).mkString(" & ")}" + if (nonEmptyPats.length > 1) s"($patsStr)" else patsStr } } @@ -54,10 +57,39 @@ case class IIntros(coq: Seq[ICoqName], iris: IIntroPattern) extends IProofStep { } } +case class IDestruct(hypName: IIdent, coq: Seq[ICoqName], iris: IIntroPattern) extends IProofStep { + override def pp: String = { + val coqStr = if (coq.nonEmpty) s"(${coq.map(_.pp).mkString(" ")})" else "" + val irisStr = if (iris.pp.nonEmpty) s""""${iris.pp}"""" else "" + s"""iDestruct "${hypName.pp}" as $coqStr $irisStr.""" + } +} + +case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExistentials: Seq[(Ident, HType)]) extends IProofStep { + override def pp: String = { + val learn = pred.learnLemmaName(constructor) + val open = pred.openLemmaName(constructor) + val tactic = if (constrExistentials.isEmpty) { + s""" + |erewrite $learn; try by dispatchPure. + |rewrite $open.""".stripMargin + } else { + s""" + |edestruct $learn as [${constrExistentials.map(v => v._1).mkString(" ")} ->]; try by dispatchPure. + |rewrite $open.""".stripMargin + } + tactic + } +} + case class IExists(e: IPureAssertion) extends IProofStep { override def pp: String = s"iExists ${e.pp}." } +case class IRenameSelect(pat: String, into: IIntroPattern) extends IProofStep { + override def pp: String = s"""iRename select ($pat)%I into "${into.pp}".""" +} + case object IFindApply extends IProofStep { override def pp: String = "iFindApply." } @@ -86,6 +118,15 @@ case class IIf(hyp: ICoqName) extends IProofStep { override def pp: String = s"ssl_if ${hyp.pp}." } +case class IDebug(msg: String) extends IProofStep { + override def pp: String = s"(* $msg *)" +} + +case object IFree extends IProofStep { + override def pp: String = "ssl_free." +} + + case object IFinish extends IProofStep { override def pp: String = "ssl_finish." } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index ba035d9e9..e64c5472e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HType, HUnknownType} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HType, HUnknownType} import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate, IPureAssertion} import org.tygus.suslik.certification.targets.iris.logic._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable @@ -18,7 +18,6 @@ case class IProofContext( baseTranslationContext: ProgramTranslationContext, predMap: Map[Ident, IPredicate], specMap: Map[Ident, IFunSpec], - // irisSpatial: Map[Ident, ISpatialAssertion], coqTypingCtx: Map[Ident, HType], varMap: Map[Ident, IPureAssertion] ) extends ClientContext[IProofStep] { @@ -28,7 +27,9 @@ case class IProofContext( s"iH${counter}" } - def typingContext: Map[Ident, HType] = coqTypingCtx + def translationCtx: Option[ProgramTranslationContext] = { + Some(baseTranslationContext.copy(pts = Map.empty, hctx = typingContext)) + } def withVariablesTypes(variables: Map[Ident, HType]): IProofContext = { this.copy(coqTypingCtx = coqTypingCtx ++ variables) @@ -36,11 +37,12 @@ case class IProofContext( def withMappingBetween(from: Ident, to: Expr): IProofContext = { val targetType = typingContext.get(from) - val translationCtx = Some(baseTranslationContext.copy(pts = Map.empty, hctx = typingContext)) val toExpr = to.translate.translate(IrisTranslator.toSpecExpr, translationCtx, targetType) withMappingBetween(from, toExpr) } + def typingContext: Map[Ident, HType] = coqTypingCtx + def withMappingBetween(from: Ident, to: IPureAssertion): IProofContext = { val s = Map(from -> to) val newVarMap = varMap.map({ case (name, vl) => (name, vl.subst(s)) }) ++ s @@ -60,16 +62,16 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val noDeferreds: Option[Deferred] = None - override def translate(value: SuslikProofStep, clientContext: IProofContext): Result = value match { + override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = value match { case SuslikProofStep.Init(_) => - var ctx = clientContext + var ctx = clientCtx val coqHyps = spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ - spec.artificialUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ - // We only use the loc_at / Z_at lemmas for rewriting, so we don't give them names - spec.artificialUniversal.map({ case (v, _) => ICoqName("?") }) ++ - // Iris specific thing - List(ICoqName("ϕ")) + spec.artificialUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ + // We only use the loc_at / Z_at lemmas for rewriting, so we don't give them names + spec.artificialUniversal.map({ case (v, _) => ICoqName("?") }) ++ + // Iris specific thing + List(ICoqName("ϕ")) val pureIntro = spec.pre.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) val spatialIntro = spec.pre.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) @@ -87,37 +89,85 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I ctx = ctx withVariablesTypes existentials.toMap val steps = List(intro, IBegin) - val deferred: Deferred = (ctx: IProofContext) => { val instantiate = existentials.map({ case (v, _) => IExists(ctx resolveExistential v) }) (List(IFindApply) ++ instantiate ++ List(IFinish), ctx) } - Result(steps, List((Nil, Some(deferred), ctx))) + case SuslikProofStep.Open(predApp, freshExists, freshParams, selectors) => + val ctx = clientCtx + val app = predApp.translate(IrisTranslator.predAppTranslator, ctx.translationCtx) + val appHyp = ctx.freshHypName() + val appHypIdent = IIdent(appHyp) + val steps = List ( + // Give the heaplet containing the predicate application a fresh name, appHyp + IRenameSelect(app.pp, appHypIdent), + IIf(ICoqName(s"Cond_${appHyp}")) + ) + val existentialsSub = freshExists.map { case (from, to) => (from.name, to.name) } + val predName = predApp.pred + val pred = ctx.predMap(predName) + val childRules = pred.clauses.map { + case (constructor, body) => + val existentials = pred.findExistentials(constructor)(body).map({ case (name, ty) => (existentialsSub(name), ty) }) + val constructorExistentials = constructor.constructorArgs.map(existentialsSub(_)).map(v => (v, HCardType(predName))) + val coqIntro = existentials.map({ case (name, _) => ICoqName(name) }) + val pureIntro = body.pure.map(_ => IPure(ctx.freshHypName())) + val spatialIntro = body.spatial.map(_ => IIdent(ctx.freshHypName())) + val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) + // Open and destruct the predicate appropriately in each clause + val immed = List( + IOpenCard(pred, constructor, constructorExistentials), + IDestruct(appHypIdent, coqIntro, irisHyps) + ) + val newCtx = ctx withVariablesTypes (existentials ++ constructorExistentials).toMap + (immed, noDeferreds, newCtx) + } + Result(steps, childRules.toList) + + case s:SuslikProofStep.AbduceCall => + // TODO: actually implement + Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + + case s:SuslikProofStep.Call => + // TODO: actually implement + Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + + // TODO: actually implement + case s:SuslikProofStep.SubstL => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + case s:SuslikProofStep.SubstR => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + + case SuslikProofStep.Free(_, _) => + // TODO: actually implement + val step = IFree + Result(List(step), List(withNoDeferreds(clientCtx))) + + case SuslikProofStep.Branch(_, _) => - val cont = withNoDeferreds(clientContext) - val step = IIf(ICoqName(clientContext.freshHypName())) + val cont = withNoDeferreds(clientCtx) + val step = IIf(ICoqName(clientCtx.freshHypName())) Result(List(step), List(cont, cont)) case SuslikProofStep.Pick(Var(fromName), to) => - val newCtx = clientContext withMappingBetween(fromName, to) + val newCtx = clientCtx withMappingBetween(fromName, to) withNoOp(newCtx) case SuslikProofStep.PureSynthesis(_, assignments) => val newCtx = - assignments.foldLeft(clientContext)({ case (ctx, (Var(fromName), to)) => ctx withMappingBetween(fromName, to) }) + assignments.foldLeft(clientCtx)({ case (ctx, (Var(fromName), to)) => ctx withMappingBetween(fromName, to) }) withNoOp(newCtx) /** Statements */ case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - Result(List(ILoad), List(withNoDeferreds(clientContext))) + // TODO: rename ghosts + Result(List(ILoad), List(withNoDeferreds(clientCtx))) case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => - Result(List(IStore), List(withNoDeferreds(clientContext))) + Result(List(IStore), List(withNoDeferreds(clientCtx))) case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) - case SuslikProofStep.NilNotLval(vars) => withNoOp(clientContext) // TODO: add assumption + case SuslikProofStep.NilNotLval(vars) => withNoOp(clientCtx) // TODO: add assumption /** Ignored rules */ @@ -128,7 +178,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I | SuslikProofStep.HeapUnifyPointer(_, _) | SuslikProofStep.StarPartial(_, _) | SuslikProofStep.FrameUnfold(_, _) => - withNoOp(clientContext) + withNoOp(clientCtx) case _ => ??? } From d78e1e5cfc29ca700edb84b8866d9b0f82800db5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 15:22:08 +0800 Subject: [PATCH 142/211] Iris: open rule and free --- .../targets/iris/IrisCertificate.scala | 5 +++-- .../targets/iris/logic/IProofStep.scala | 9 +++++++++ .../iris/translation/ProofTranslator.scala | 15 ++++++++++----- .../targets/iris/translation/Translation.scala | 7 +++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 1bcc33030..dc64ad600 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -8,7 +8,7 @@ import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePri import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { +case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proofStr: String) extends Certificate { val target: CertificationTarget = Iris private val prelude = @@ -90,6 +90,7 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe |Ltac ssl_let := wp_let. |Ltac ssl_load := wp_load; wp_let; iSimplContext. |Ltac ssl_store := wp_store; iSimplContext. + |Ltac ssl_free := wp_free; wp_pures; iSimplContext. |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; dispatchPure. | @@ -105,7 +106,7 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe b.append("\n") b.append(funSpec.pp) b.append("Proof.\n") - b.append(ProofTreePrinter.pp(proof)) + b.append(proofStr) b.append("Qed.\n") b.toString() } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 279d1ee5d..99cdd2c36 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -65,6 +65,15 @@ case class IDestruct(hypName: IIdent, coq: Seq[ICoqName], iris: IIntroPattern) e } } +case class ILob(hypName: IIdent, coq: Seq[ICoqName]) extends IProofStep { + override def pp: String = { + // iLöb as "IH" forall (x s _alpha_514 ϕ). + val coqStr = s"(${coq.map(_.pp).mkString(" ")})" + s"""iLöb as "${hypName.pp}" forall $coqStr.""" + } + +} + case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExistentials: Seq[(Ident, HType)]) extends IProofStep { override def pp: String = { val learn = pred.learnLemmaName(constructor) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index e64c5472e..df9ab1d60 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -62,6 +62,10 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val noDeferreds: Option[Deferred] = None + private val irisPhi: String = "ϕ" + private val irisPost: String = "Post" + private val irisSelf: String = spec.fname + override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = value match { case SuslikProofStep.Init(_) => var ctx = clientCtx @@ -88,11 +92,13 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I ctx = ctx withVariablesTypes programVars.toMap ctx = ctx withVariablesTypes existentials.toMap - val steps = List(intro, IBegin) val deferred: Deferred = (ctx: IProofContext) => { val instantiate = existentials.map({ case (v, _) => IExists(ctx resolveExistential v) }) (List(IFindApply) ++ instantiate ++ List(IFinish), ctx) } + + val lobInduction = ILob(IIdent(irisSelf), spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ List(ICoqName(irisPhi))) + val steps = List(intro, IRewriteHyp, lobInduction, IBegin) Result(steps, List((Nil, Some(deferred), ctx))) case SuslikProofStep.Open(predApp, freshExists, freshParams, selectors) => @@ -138,10 +144,9 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I case s:SuslikProofStep.SubstL => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) case s:SuslikProofStep.SubstR => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) - case SuslikProofStep.Free(_, _) => - // TODO: actually implement - val step = IFree - Result(List(step), List(withNoDeferreds(clientCtx))) + case SuslikProofStep.Free(_, sz) => + val steps = (0 until sz).map(_ => IFree).toList + Result(steps, List(withNoDeferreds(clientCtx))) case SuslikProofStep.Branch(_, _) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 33d6278c8..a3fd3e68a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -6,6 +6,7 @@ import org.tygus.suslik.certification.targets.iris.IrisCertificate import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HFunDef} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IFunSpec import org.tygus.suslik.certification.targets.iris.logic.IProofStep +import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePrinter import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -48,8 +49,10 @@ object Translation { val specMap = List((funSpec.fname, funSpec)).toMap val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty) - val proofTree = ProofEvaluator(funSpec).run(suslikTree, proofCtx) + val proofStr = + try { ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) } + catch { case e => s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } - IrisCertificate(proc.name, predicates, funDef, funSpec, proofTree) + IrisCertificate(proc.name, predicates, funDef, funSpec, proofStr) } } \ No newline at end of file From a66a634b860d30089ba2be41610cc94732e4cd2d Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 27 Feb 2021 15:25:24 +0800 Subject: [PATCH 143/211] Add benchmarking harness --- .../suslik/certification/Certificate.scala | 7 +- .../certification/CertificationTarget.scala | 1 + .../suslik/certification/Predicate.scala | 7 + .../source/SuslikProofStep.scala | 2 +- .../certification/targets/htt/HTT.scala | 15 ++ .../targets/htt/HTTCertificate.scala | 25 +-- .../htt/HTTCertificationBenchmarks.scala | 19 ++ .../targets/htt/logic/Sentences.scala | 3 +- .../targets/htt/translation/Translation.scala | 2 +- .../certification/targets/iris/Iris.scala | 3 +- .../targets/iris/IrisCertificate.scala | 9 +- .../certification/targets/vst/VST.scala | 13 +- .../targets/vst/VSTCertificate.scala | 7 +- .../translation/GenericPredicate.scala | 3 +- .../synthesis/CertificationBenchmarks.scala | 163 ++++++++++++++++++ .../synthesis/SynthesisRunnerUtil.scala | 6 +- 16 files changed, 234 insertions(+), 51 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/Predicate.scala create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala create mode 100644 src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 5142d6098..2f9b76f36 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -1,15 +1,12 @@ package org.tygus.suslik.certification -case class CertificateOutput(filename: Option[String], name: String, body: String) +case class CertificateOutput(filename: String, name: String, body: String, isProof: Boolean = true) /** * A generic interface for certificates, to be implemented by all * certification backends */ trait Certificate { - /** returns the exported filename for a string */ - def getFileName(name: String) : String = name + target.suffix - + val predicates: List[Predicate] val target: CertificationTarget def outputs: List[CertificateOutput] -// def fileName: String = name + target.suffix } diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 1b412932e..8ca855597 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -14,4 +14,5 @@ trait CertificationTarget { val name: String val suffix: String def certify(proc: Procedure, env: Environment): Certificate + def mkDefs(predicates: List[Predicate]): String } diff --git a/src/main/scala/org/tygus/suslik/certification/Predicate.scala b/src/main/scala/org/tygus/suslik/certification/Predicate.scala new file mode 100644 index 000000000..dada4d0e2 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/Predicate.scala @@ -0,0 +1,7 @@ +package org.tygus.suslik.certification + +import org.tygus.suslik.language.PrettyPrinting + +trait Predicate extends PrettyPrinting { + val name: String +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index 1e327ac91..a2f8330fc 100644 --- a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -582,7 +582,7 @@ case class AbduceCall( val initStep = Init(node.goal) val initItem = Item(initStep, None, Nil, Nil) val res = forward(node, List(initItem)) - Console.println(SuslikPrinter.pp(res)) +// Console.println(SuslikPrinter.pp(res)) res } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 4e12c036d..6050c1873 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -9,9 +9,24 @@ import org.tygus.suslik.logic.Environment object HTT extends CertificationTarget { val name: String = "HTT" val suffix: String = ".v" + val prelude = + """From mathcomp + |Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. + |From fcsl + |Require Import prelude pred pcm unionmap heap. + |From HTT + |Require Import stmod stsep stlog stlogR. + |From SSL + |Require Import core. + | + |""".stripMargin def certify(proc: Procedure, env: Environment): HTTCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) Translation.translate(root, proc)(env) } + + def mkDefs(predicates: List[Predicate]): String = { + s"$prelude\n${predicates.map(_.pp).mkString("\n\n")}" + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 0d86e99e8..12732931a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -1,34 +1,21 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} -import org.tygus.suslik.certification.targets.htt.logic.Sentences.CFunSpec +import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CFunSpec, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.program.Program -import org.tygus.suslik.certification.targets.htt.translation.ProofContext.PredicateEnv import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { +case class HTTCertificate(name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') - // Import Coq dependencies - private val prelude = - """From mathcomp - |Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. - |From fcsl - |Require Import prelude pred pcm unionmap heap. - |From HTT - |Require Import stmod stsep stlog stlogR. - |From SSL - |Require Import core. - | - |""".stripMargin - def pp: String = { val builder = new StringBuilder - builder.append(prelude) - preds.values.foreach(pred => builder.append(pred.pp + "\n")) + builder.append(HTT.prelude) + builder.append("Load common.\n\n") + if (hints.nonEmpty) { builder.append(hints.map(_.pp).mkString("\n")) builder.append("\n\n") @@ -48,6 +35,6 @@ case class HTTCertificate(name: String, preds: PredicateEnv, spec: CFunSpec, aux builder.toString } - override def outputs: List[CertificateOutput] = List(CertificateOutput(None, sanitize(name), pp)) + override def outputs: List[CertificateOutput] = List(CertificateOutput(s"${sanitize(name)}.v", sanitize(name), pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala new file mode 100644 index 000000000..57ada16c8 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala @@ -0,0 +1,19 @@ +package org.tygus.suslik.certification.targets.htt + +import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.synthesis.CertificationBenchmarks + +object HTTCertificationBenchmarks extends CertificationBenchmarks { + val target: CertificationTarget = HTT + + def main(args: Array[String]): Unit = { + initLog() + runAllTestsFromDir("certification/ints") + runAllTestsFromDir("certification/sll-bounds") + runAllTestsFromDir("certification/sll") + runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/srtl") + runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/bst") + } +} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala index 31adddf43..c8d1f99a5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Sentences.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.htt.logic +import org.tygus.suslik.certification.Predicate import org.tygus.suslik.certification.targets.htt.language.Expressions._ import org.tygus.suslik.certification.targets.htt.language.{CFormals, CGamma, PrettyPrinting} import org.tygus.suslik.certification.targets.htt.language.Types._ @@ -58,7 +59,7 @@ object Sentences { case class CInductiveClause(pred: String, idx: Int, selector: CExpr, asn: CAssertion, existentials: Seq[CVar]) extends PrettyPrinting - case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends PrettyPrinting { + case class CInductivePredicate(name: String, params: CFormals, clauses: Seq[CInductiveClause], gamma: CGamma) extends Predicate with PrettyPrinting { val paramVars: Seq[CVar] = params.map(_._1) override def pp: String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 095c0551e..f03c9c819 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -47,7 +47,7 @@ object Translation { val progBody = ProgramEvaluator.run(suslikTree, ProgramContext()) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) - HTTCertificate(cproc.name, cpreds, spec, auxSpecs, proof, cproc, hints) + HTTCertificate(cproc.name, cpreds.values.toList, spec, auxSpecs, proof, cproc, hints) } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 88c407dd2..bd0cebca9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.translation.Translation -import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, CertificationTarget, Predicate} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException @@ -21,4 +21,5 @@ object Iris extends CertificationTarget { cert } + override def mkDefs(predicates: List[Predicate]): String = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index fe9b27922..f886a0ff1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -1,6 +1,5 @@ package org.tygus.suslik.certification.targets.iris -import org.tygus.suslik.certification.targets.htt.logic.ProofPrinter import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} import org.tygus.suslik.certification.targets.iris.logic.IProofStep @@ -8,7 +7,7 @@ import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePri import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} -case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { +case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { val target: CertificationTarget = Iris private val prelude = @@ -97,9 +96,9 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe def pp : String = { val b = new StringBuilder b.append(prelude) - b.append(preds.map(_.pp).mkString("\n")) + b.append(predicates.map(_.pp).mkString("\n")) b.append("\n") - b.append(preds.flatMap(_.getHelpers).map(_.pp).mkString("\n")) + b.append(predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")) b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) @@ -109,5 +108,5 @@ case class IrisCertificate(name: String, preds: List[IPredicate], funDef: HFunDe b.toString() } - override def outputs: List[CertificateOutput] = List(CertificateOutput(None, name, pp)) + override def outputs: List[CertificateOutput] = List(CertificateOutput(s"$name.v", name, pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index cb77c2b9b..aa9bf5309 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -1,15 +1,8 @@ package org.tygus.suslik.certification.targets.vst -import java.nio.file.Paths -import java.io.{File, PrintWriter} - -import org.tygus.suslik.certification.{CertTree, Certificate, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, Certificate, CertificationTarget, Predicate} import org.tygus.suslik.language.Statements -import org.tygus.suslik.language.Statements.Statement -import org.tygus.suslik.logic.{Environment, FunSpec, FunctionEnv, PredicateEnv, Preprocessor, Program} -import org.tygus.suslik.parsing.SSLParser -import org.tygus.suslik.synthesis.{SynConfig, SynthesisException, SynthesisRunner} -import org.tygus.suslik.util.SynStats +import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.certification.targets.vst.translation.Translation @@ -23,8 +16,8 @@ object VST extends CertificationTarget { CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) Translation.translate(root, proc, env) - } + def mkDefs(predicates: List[Predicate]): String = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index 71a3d2c3a..6cd309b5e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -1,15 +1,16 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, Predicate} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Proof case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate { override val target: CertificationTarget = VST + override val predicates: List[Predicate] = Proof.predicates override def outputs: List[CertificateOutput] = List( - CertificateOutput(Some(name + ".c"), name, CProcedureDefinition.pp), - CertificateOutput(None, "verif_" + name, Proof.pp) + CertificateOutput(name + ".c", name, CProcedureDefinition.pp, false), + CertificateOutput("verif_" + name + ".v", "verif_" + name, Proof.pp) ) } diff --git a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala index fdfe9ec16..7098ae097 100644 --- a/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala +++ b/src/main/scala/org/tygus/suslik/certification/translation/GenericPredicate.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.translation +import org.tygus.suslik.certification.Predicate import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -45,7 +46,7 @@ abstract class GenericPredicate[Pure, Spatial, Type](val name: Ident, val params: List[(Ident, Type)], val existentials: List[(Ident, Type)], val clauses: Map[CardConstructor, GenericPredicateClause[Pure, Spatial]]) - extends PrettyPrinting { + extends Predicate with PrettyPrinting { // When extending GenericPredicate, you should implement these methods def ppPredicate: String diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala new file mode 100644 index 000000000..cb4cdd5fb --- /dev/null +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -0,0 +1,163 @@ +package org.tygus.suslik.synthesis + +import java.io.{File, FileWriter, PrintWriter} +import java.nio.file.{Files, Paths} + +import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.language.Statements +import org.tygus.suslik.logic.Environment +import org.tygus.suslik.logic.Preprocessor.preprocessProgram +import org.tygus.suslik.parsing.SSLParser +import org.tygus.suslik.report.ProofTraceCert +import org.tygus.suslik.report.StopWatch.timed +import org.tygus.suslik.util.{SynStatUtil, SynStats} + +import scala.sys.process._ + +abstract class CertificationBenchmarks extends SynthesisRunnerUtil { + val target: CertificationTarget + val tempDir: File = Files.createTempDirectory("suslik-").toFile + val defFilename: String = "common.v" + val statsFile: File = new File("cert-stats.csv") + val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (regular)", "Synthesis Time (with certification)", "Proof Time", "Spec Size", "Proof Size").mkString(", ") + "\n" + + def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { + val res = params.inputFormat match { + case `dotSyn` => parser.parseGoalSYN(text) + case `dotSus` => parser.parseGoalSUS(text) + } + if (!res.successful) { + throw SynthesisException(s"Failed to parse the input:\n$res") + } + + val prog = res.get + val (specs, predEnv, funcEnv, body) = preprocessProgram(prog, params) + + if (specs.lengthCompare(1) != 0) { + throw SynthesisException("Expected a single synthesis goal") + } + + val spec = specs.head + val env = Environment(predEnv, funcEnv, params, new SynStats(params.timeOut)) + val synthesizer = createSynthesizer(env) + + env.stats.start() + val sresult = synthesizer.synthesizeProc(spec, env, body) + val duration = env.stats.duration + + sresult._1 match { + case Nil => + throw SynthesisException(s"Failed to synthesise:\n$sresult") + case procs => + synthesizer.trace match { + case trace:ProofTraceCert => + CertTree.fromTrace(trace) + case _ => + } + (procs, env, duration) + } + } + + override def runAllTestsFromDir(dir: String): Unit = { + println(s"==========Benchmark Group $dir==========\n") + val path = List(rootDir, dir).mkString(File.separator) + val testDir = new File(path) + if (testDir.exists() && testDir.isDirectory) { + print(s"Retrieving definitions and specs from ${testDir.getName}...") + // Get definitions + val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) + // Get specs + val tests = testDir.listFiles.filter(f => f.isFile + && (f.getName.endsWith(s".$testExtension") || + f.getName.endsWith(s".$sketchExtension"))).toList + println("done!") + + val parser = new SSLParser + println(s"\nSynthesizing ${tests.length} test cases...") + val synResults = for (f <- tests) yield { + val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) + println(s"$testName:") + val fullInput = List(defs, in).mkString("\n") + // Synthesize normally + print(s" synthesizing normally...") + val (_, _, durationNoCert) = synthesizeOne(fullInput, parser, params) + println(s"done! ($durationNoCert ms)") + // Synthesize with cert + print(s" synthesizing in certification mode...") + val (res, env, durationWithCert) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false, certTarget = target, certDest = tempDir)) + println(s"done! ($durationWithCert ms)") + // Generate certificate + print(s" generating certificate...") + val cert = target.certify(res.head, env) + println("done!") + (testName, cert, durationNoCert, durationWithCert) + } + + println(s"Successfully synthesized ${tests.length} tests.") + + // Serialize definitions + print(s"\nWriting definitions to file $defFilename...") + val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList + val definitions = target.mkDefs(predicates) + serialize(tempDir, defFilename, definitions) + println("done!") + print(s"Compiling $defFilename...") + compileProof(tempDir, defFilename) + println("done!") + + println(s"\nGenerating statistics...") + for ((testName, cert, durationNoCert, durationWithCert) <- synResults) { + println(s"$testName:") + for (o <- cert.outputs) yield { + print(s" Writing certificate output to file ${o.filename}...") + serialize(tempDir, o.filename, o.body) + println("done!") + if (o.isProof) { + print(s" Checking proof size...") + val (specSize, proofSize) = checkProofSize(tempDir, o.filename) + println(s"done! (spec: $specSize, proof: $proofSize)") + print(s" Compiling proof...") + val (res, duration) = timed (compileProof(tempDir, o.filename)) + if (res == 0) { + println(s"done! ($duration ms)") + logStat(testName, o.filename, durationNoCert, durationWithCert, duration, specSize, proofSize) + } else { + throw SynthesisException(s"Failed to verify ${o.filename}!") + } + } + } + } + + println(s"\nStatistics written to: ${statsFile.getCanonicalPath}") + println(s"Proofs and definitions written to: $tempDir\n\n") + } + } + + private def serialize(dir: File, filename: String, body: String): Unit = { + val file = Paths.get(dir.getCanonicalPath, filename).toFile + new PrintWriter(file) { write(body); close() } + } + + private def checkProofSize(dir: File, filename: String): (Int, Int) = { + val cmd = Seq("coqwc", filename) + val proofSizes = Process(cmd, dir).!! + val Array(specSize, proofSize, _, _) = proofSizes.split('\n')(1).trim.split("\\s+") + (specSize.toInt, proofSize.toInt) + } + + private def compileProof(dir: File, filename: String): Int = { + val cmd = Seq("coqc", "-w", "none", filename) + Process(cmd, dir).! + } + + def initLog(): Unit = { + if (statsFile.exists()) statsFile.delete() + statsFile.createNewFile() + SynStatUtil.using(new FileWriter(statsFile, true))(_.write(statsHeader)) + } + + private def logStat(name: String, filename: String, synDurationNoCert: Long, synDurationWithCert: Long, proofDuration: Long, specSize: Int, proofSize: Int): Unit = { + val data = s"$name, $filename, $synDurationNoCert, $synDurationWithCert, $proofDuration, $specSize, $proofSize\n" + SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) + } +} diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index cf25c3fa8..b7db79e6f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -237,15 +237,13 @@ trait SynthesisRunnerUtil { if (params.certDest == null) { testPrintln(s"\n$targetName certificate:", Console.MAGENTA) certificate.outputs.foreach({case CertificateOutput(filename, name, body) => - val outname = filename.getOrElse(certificate.getFileName(name)) - testPrintln(s"File ${outname}:\n", Console.MAGENTA) + testPrintln(s"File $filename:\n", Console.MAGENTA) testPrintln(s"$body") }) } else { certificate.outputs.foreach({ case CertificateOutput(ofname, name, body) => - val filename = ofname.getOrElse(certificate.getFileName(name)) - val path = Paths.get(params.certDest.getCanonicalPath, filename).toFile + val path = Paths.get(params.certDest.getCanonicalPath, ofname).toFile new PrintWriter(path) { write(body); close() } testPrintln(s"\n$targetName certificate exported to $path", Console.MAGENTA) }) From 9e07849f52dbefce2727e35cf734e9ddad4b0a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 15:27:54 +0800 Subject: [PATCH 144/211] Iris: fix printing predicate application in specs --- .../suslik/certification/targets/iris/logic/Assertions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 0197207b5..ef14b11fe 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -125,7 +125,7 @@ object Assertions { case class IPredApp(pred: String, args: Seq[IPureAssertion], card: IPureAssertion) extends ISpatialAssertion { override def pp: String = - s"(${pred} ${(args ++ List(card)).map(_.pp).mkString(" ")})" + s"(${pred} ${(args.map(_.ppAsPhi) ++ List(card.pp)).mkString(" ")})" } case class IBlock() extends ISpatialAssertion { From 0ae1e31e1f46f9c495019c8ee0286c9c6526132c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 27 Feb 2021 16:24:02 +0800 Subject: [PATCH 145/211] Specify custom compilation behavior for C files --- .../suslik/certification/Certificate.scala | 28 +++++++++++++- .../targets/htt/HTTCertificate.scala | 4 +- .../targets/iris/IrisCertificate.scala | 4 +- .../targets/vst/VSTCertificate.scala | 6 +-- .../synthesis/CertificationBenchmarks.scala | 37 +++++++++---------- .../synthesis/SynthesisRunnerUtil.scala | 15 ++++---- 6 files changed, 59 insertions(+), 35 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 2f9b76f36..d3801a382 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -1,6 +1,32 @@ package org.tygus.suslik.certification -case class CertificateOutput(filename: String, name: String, body: String, isProof: Boolean = true) +import java.io.File + +import scala.sys.process.Process + +abstract class CertificateOutput { + val filename: String + val name: String + val body: String + val isProof: Boolean = false + def compile(dir: File): Int +} + +case class ClangOutput(filename: String, name: String, body: String) extends CertificateOutput { + def compile(dir: File): Int = { + val cmd = Seq("clightgen", filename, "&&", "coqc", s"$name.v") // TODO: correct? + Process(cmd, dir).! + } +} + +case class CoqOutput(filename: String, name: String, body: String) extends CertificateOutput { + override val isProof: Boolean = true + def compile(dir: File): Int = { + val cmd = Seq("coqc", "-w", filename) + Process(cmd, dir).! + } +} + /** * A generic interface for certificates, to be implemented by all * certification backends diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 12732931a..54557b35e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CFunSpec, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.program.Program -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case class HTTCertificate(name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { val target: CertificationTarget = HTT @@ -35,6 +35,6 @@ case class HTTCertificate(name: String, predicates: List[CInductivePredicate], s builder.toString } - override def outputs: List[CertificateOutput] = List(CertificateOutput(s"${sanitize(name)}.v", sanitize(name), pp)) + override def outputs: List[CertificateOutput] = List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index f886a0ff1..9a0e45114 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.targets.iris.logic.IProofStep import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePrinter import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { val target: CertificationTarget = Iris @@ -108,5 +108,5 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H b.toString() } - override def outputs: List[CertificateOutput] = List(CertificateOutput(s"$name.v", name, pp)) + override def outputs: List[CertificateOutput] = List(CoqOutput(s"$name.v", name, pp)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index 6cd309b5e..a2b0d8079 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, Predicate} +import org.tygus.suslik.certification.{ClangOutput, Certificate, CertificateOutput, CertificationTarget, CoqOutput, Predicate} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Proof @@ -10,7 +10,7 @@ case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinitio override def outputs: List[CertificateOutput] = List( - CertificateOutput(name + ".c", name, CProcedureDefinition.pp, false), - CertificateOutput("verif_" + name + ".v", "verif_" + name, Proof.pp) + ClangOutput(name + ".c", name, CProcedureDefinition.pp), + CoqOutput("verif_" + name + ".v", "verif_" + name, Proof.pp) ) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index cb4cdd5fb..94cb5dbca 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -19,7 +19,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val tempDir: File = Files.createTempDirectory("suslik-").toFile val defFilename: String = "common.v" val statsFile: File = new File("cert-stats.csv") - val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (regular)", "Synthesis Time (with certification)", "Proof Time", "Spec Size", "Proof Size").mkString(", ") + "\n" + val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (sec)", "Proof Time (sec)", "Spec Size", "Proof Size").mkString(", ") + "\n" def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { val res = params.inputFormat match { @@ -64,9 +64,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val testDir = new File(path) if (testDir.exists() && testDir.isDirectory) { print(s"Retrieving definitions and specs from ${testDir.getName}...") - // Get definitions val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) - // Get specs val tests = testDir.listFiles.filter(f => f.isFile && (f.getName.endsWith(s".$testExtension") || f.getName.endsWith(s".$sketchExtension"))).toList @@ -78,24 +76,19 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) println(s"$testName:") val fullInput = List(defs, in).mkString("\n") - // Synthesize normally - print(s" synthesizing normally...") - val (_, _, durationNoCert) = synthesizeOne(fullInput, parser, params) - println(s"done! ($durationNoCert ms)") - // Synthesize with cert + print(s" synthesizing in certification mode...") - val (res, env, durationWithCert) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false, certTarget = target, certDest = tempDir)) - println(s"done! ($durationWithCert ms)") - // Generate certificate + val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false, certTarget = target, certDest = tempDir)) + println(s"done! (${fmtTime(synDuration)} s)") + print(s" generating certificate...") val cert = target.certify(res.head, env) println("done!") - (testName, cert, durationNoCert, durationWithCert) + (testName, cert, synDuration) } println(s"Successfully synthesized ${tests.length} tests.") - // Serialize definitions print(s"\nWriting definitions to file $defFilename...") val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList val definitions = target.mkDefs(predicates) @@ -106,7 +99,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println("done!") println(s"\nGenerating statistics...") - for ((testName, cert, durationNoCert, durationWithCert) <- synResults) { + for ((testName, cert, synDuration) <- synResults) { println(s"$testName:") for (o <- cert.outputs) yield { print(s" Writing certificate output to file ${o.filename}...") @@ -117,13 +110,17 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val (specSize, proofSize) = checkProofSize(tempDir, o.filename) println(s"done! (spec: $specSize, proof: $proofSize)") print(s" Compiling proof...") - val (res, duration) = timed (compileProof(tempDir, o.filename)) + val (res, proofDuration) = timed (compileProof(tempDir, o.filename)) if (res == 0) { - println(s"done! ($duration ms)") - logStat(testName, o.filename, durationNoCert, durationWithCert, duration, specSize, proofSize) + println(s"done! (${fmtTime(proofDuration)} s)") + logStat(testName, o.filename, synDuration, proofDuration, specSize, proofSize) } else { throw SynthesisException(s"Failed to verify ${o.filename}!") } + } else { + print(s" Compiling output...") + o.compile(tempDir) + println("done!") } } } @@ -156,8 +153,10 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { SynStatUtil.using(new FileWriter(statsFile, true))(_.write(statsHeader)) } - private def logStat(name: String, filename: String, synDurationNoCert: Long, synDurationWithCert: Long, proofDuration: Long, specSize: Int, proofSize: Int): Unit = { - val data = s"$name, $filename, $synDurationNoCert, $synDurationWithCert, $proofDuration, $specSize, $proofSize\n" + private def logStat(name: String, filename: String, synDuration: Long, proofDuration: Long, specSize: Int, proofSize: Int): Unit = { + val data = s"$name, $filename, ${fmtTime(synDuration)}, ${fmtTime(proofDuration)}, $specSize, $proofSize\n" SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) } + + private def fmtTime(ms: Long): String = "%.1f".format(ms.toDouble / 1000) } diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index b7db79e6f..9433d9973 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -236,16 +236,15 @@ trait SynthesisRunnerUtil { val certificate = certTarget.certify(procs.head, env) if (params.certDest == null) { testPrintln(s"\n$targetName certificate:", Console.MAGENTA) - certificate.outputs.foreach({case CertificateOutput(filename, name, body) => - testPrintln(s"File $filename:\n", Console.MAGENTA) - testPrintln(s"$body") + certificate.outputs.foreach(o => { + testPrintln(s"File ${o.filename}:\n", Console.MAGENTA) + testPrintln(s"${o.body}") }) } else { - certificate.outputs.foreach({ - case CertificateOutput(ofname, name, body) => - val path = Paths.get(params.certDest.getCanonicalPath, ofname).toFile - new PrintWriter(path) { write(body); close() } - testPrintln(s"\n$targetName certificate exported to $path", Console.MAGENTA) + certificate.outputs.foreach(o => { + val path = Paths.get(params.certDest.getCanonicalPath, o.filename).toFile + new PrintWriter(path) { write(o.body); close() } + testPrintln(s"\n$targetName certificate exported to $path", Console.MAGENTA) }) } } From 0e29678fb866432071b2354ae5d33af53698ac2b Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 27 Feb 2021 16:36:00 +0800 Subject: [PATCH 146/211] Use cert output interface consistently --- .../tygus/suslik/certification/Certificate.scala | 2 +- .../synthesis/CertificationBenchmarks.scala | 15 +++++---------- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index d3801a382..356fe878b 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -22,7 +22,7 @@ case class ClangOutput(filename: String, name: String, body: String) extends Cer case class CoqOutput(filename: String, name: String, body: String) extends CertificateOutput { override val isProof: Boolean = true def compile(dir: File): Int = { - val cmd = Seq("coqc", "-w", filename) + val cmd = Seq("coqc", "-w", "none", filename) Process(cmd, dir).! } } diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 94cb5dbca..d44a6c4b3 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.{Files, Paths} -import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, CertificationTarget, CoqOutput} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor.preprocessProgram @@ -91,11 +91,11 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { print(s"\nWriting definitions to file $defFilename...") val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList - val definitions = target.mkDefs(predicates) - serialize(tempDir, defFilename, definitions) + val defFile = CoqOutput(defFilename, "common", target.mkDefs(predicates)) + serialize(tempDir, defFilename, defFile.body) println("done!") print(s"Compiling $defFilename...") - compileProof(tempDir, defFilename) + defFile.compile(tempDir) println("done!") println(s"\nGenerating statistics...") @@ -110,7 +110,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val (specSize, proofSize) = checkProofSize(tempDir, o.filename) println(s"done! (spec: $specSize, proof: $proofSize)") print(s" Compiling proof...") - val (res, proofDuration) = timed (compileProof(tempDir, o.filename)) + val (res, proofDuration) = timed (o.compile(tempDir)) if (res == 0) { println(s"done! (${fmtTime(proofDuration)} s)") logStat(testName, o.filename, synDuration, proofDuration, specSize, proofSize) @@ -142,11 +142,6 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { (specSize.toInt, proofSize.toInt) } - private def compileProof(dir: File, filename: String): Int = { - val cmd = Seq("coqc", "-w", "none", filename) - Process(cmd, dir).! - } - def initLog(): Unit = { if (statsFile.exists()) statsFile.delete() statsFile.createNewFile() From 71c671d009fa0e7d379dd53831460328f499421b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 16:39:17 +0800 Subject: [PATCH 147/211] Iris: WIP calls --- .../targets/iris/logic/Assertions.scala | 8 +++ .../targets/iris/logic/IProofStep.scala | 4 ++ .../iris/translation/ProofTranslator.scala | 53 +++++++++++++------ .../iris/translation/Translation.scala | 9 ++-- 4 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index ef14b11fe..22290dfb6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -22,6 +22,14 @@ object Assertions { def subst(s: Map[Ident, IPureAssertion]): IPureAssertion = this match { case expr => expr } + + def rename(s: Map[Ident, Ident]): IPureAssertion = this match { + case expr@ISpecVar(old, t) => s.get(old) match { + case Some(newName) => ISpecVar(newName, t) + case None => expr + } + case expr => expr + } } abstract class IQuantifiedVar extends IPureAssertion { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 99cdd2c36..4d3d6cfde 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -99,6 +99,10 @@ case class IRenameSelect(pat: String, into: IIntroPattern) extends IProofStep { override def pp: String = s"""iRename select ($pat)%I into "${into.pp}".""" } +case class IRename(oldName: ICoqName, newName: ICoqName) extends IProofStep { + override def pp: String = s"try rename ${oldName.pp} into ${newName.pp}." +} + case object IFindApply extends IProofStep { override def pp: String = "iFindApply." } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index df9ab1d60..d7cfa037c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -13,13 +13,18 @@ import org.tygus.suslik.language.Statements.Load import org.tygus.suslik.language.{Ident, Statements} +case class PendingCall( + functionName: Ident, + ) + case class IProofContext( var counter: Integer = 0, baseTranslationContext: ProgramTranslationContext, predMap: Map[Ident, IPredicate], specMap: Map[Ident, IFunSpec], coqTypingCtx: Map[Ident, HType], - varMap: Map[Ident, IPureAssertion] + varMap: Map[Ident, IPureAssertion], + renamings: Map[Ident, Ident] ) extends ClientContext[IProofStep] { def freshHypName(): String = { @@ -49,10 +54,21 @@ case class IProofContext( this.copy(varMap = newVarMap) } + def withRenaming(ren: (Ident, Ident)): IProofContext = { + val (from, to) = ren + val s = Map(from -> to) + def trueName(v: Ident): Ident = s.getOrElse(v, v) + + val newCoqTypingCtx = coqTypingCtx.map({ case (v, ty) => (trueName(v), ty) }) + val newVarMap = varMap.map { case (v, expr) => (trueName(v), expr.rename(s)) } + val newRenamings = renamings.map{ case (from, to) => (from, trueName(to)) } ++ s + + this.copy(coqTypingCtx = newCoqTypingCtx, varMap = newVarMap, renamings = newRenamings) + } + def resolveExistential(ident: Ident): IPureAssertion = { - // TODO: renamings - val name = ident - varMap(name).subst(varMap) + val trueName = renamings.getOrElse(ident, ident) + varMap(trueName).subst(varMap) } } @@ -66,7 +82,8 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val irisPost: String = "Post" private val irisSelf: String = spec.fname - override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = value match { + override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = + value match { case SuslikProofStep.Init(_) => var ctx = clientCtx val coqHyps = @@ -79,7 +96,6 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val pureIntro = spec.pre.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) val spatialIntro = spec.pre.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) - // TODO: add spatial assertions to context && rewrite them accordingly val irisHyps = IPatList(List(IPatDestruct(pureIntro ++ spatialIntro), IIdent("Post"))) val intro = IIntros(coqHyps, irisHyps) @@ -87,9 +103,9 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I // All the artificialUniversals that show up in the spec will be renamed by IBegin, // e.g. (l : val) with (lr : loc) (lr is artificial) will result in (l : loc). val typeOf = spec.artificialUniversal.map({ case (v, t) => (v.originalName, t) }).toMap - val programVars = spec.specUniversal.map({ case (v, _) => (v.name, typeOf.getOrElse(v.name, HUnknownType())) }) + val universals = spec.specUniversal.map({ case (v, _) => (v.name, typeOf.getOrElse(v.name, HUnknownType())) }) val existentials = spec.specExistential.map({ case (v, t) => (v.name, t) }) - ctx = ctx withVariablesTypes programVars.toMap + ctx = ctx withVariablesTypes universals.toMap ctx = ctx withVariablesTypes existentials.toMap val deferred: Deferred = (ctx: IProofContext) => { @@ -132,9 +148,16 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I } Result(steps, childRules.toList) - case s:SuslikProofStep.AbduceCall => - // TODO: actually implement - Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) +// // TODO: actually implement +// case SuslikProofStep.Close(app, selector, _, freshExist) => +// Result(List(IDebug(value.pp)), List(withNoDeferreds(clientCtx))) + + // TODO: actually implement + case SuslikProofStep.AbduceCall(_, precondition, _, _, freshSub, _, f, _) => + val ctx = clientCtx + val funSpec = ctx.specMap(f.name) + + Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) case s:SuslikProofStep.Call => // TODO: actually implement @@ -148,7 +171,6 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val steps = (0 until sz).map(_ => IFree).toList Result(steps, List(withNoDeferreds(clientCtx))) - case SuslikProofStep.Branch(_, _) => val cont = withNoDeferreds(clientCtx) val step = IIf(ICoqName(clientCtx.freshHypName())) @@ -164,9 +186,10 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I withNoOp(newCtx) /** Statements */ - case SuslikProofStep.Read(Var(ghost_from), Var(ghost_to), Load(to, _, from, offset)) => - // TODO: rename ghosts - Result(List(ILoad), List(withNoDeferreds(clientCtx))) + case SuslikProofStep.Read(Var(ghostFrom), Var(ghostTo), Load(to, _, from, offset)) => + val ctx = clientCtx withRenaming ghostFrom -> ghostTo + val rename = IRename(ICoqName(ghostFrom), ICoqName(ghostTo)) + Result(List(rename, ILoad), List(withNoDeferreds(ctx))) case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => Result(List(IStore), List(withNoDeferreds(clientCtx))) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index a3fd3e68a..fdf2ddf79 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -48,10 +48,13 @@ object Translation { // TODO: add support for helper functions val specMap = List((funSpec.fname, funSpec)).toMap - val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty) + val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty, Map.empty) val proofStr = - try { ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) } - catch { case e => s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } + try { + ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) + } + catch { case e => throw e + s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } IrisCertificate(proc.name, predicates, funDef, funSpec, proofStr) } From 7a52c92d67c358d06721e23ef8098c9532bd65f4 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 27 Feb 2021 16:56:09 +0800 Subject: [PATCH 148/211] Track proof gen time --- .../synthesis/CertificationBenchmarks.scala | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index d44a6c4b3..41cc08101 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -16,10 +16,9 @@ import scala.sys.process._ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val target: CertificationTarget - val tempDir: File = Files.createTempDirectory("suslik-").toFile val defFilename: String = "common.v" val statsFile: File = new File("cert-stats.csv") - val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (sec)", "Proof Time (sec)", "Spec Size", "Proof Size").mkString(", ") + "\n" + val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (sec)", "Proof Generation Time (sec)", "Proof Checking Time (sec)", "Spec Size", "Proof Size").mkString(", ") + "\n" def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { val res = params.inputFormat match { @@ -43,7 +42,6 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { env.stats.start() val sresult = synthesizer.synthesizeProc(spec, env, body) - val duration = env.stats.duration sresult._1 match { case Nil => @@ -54,6 +52,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { CertTree.fromTrace(trace) case _ => } + val duration = env.stats.duration (procs, env, duration) } } @@ -62,6 +61,8 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"==========Benchmark Group $dir==========\n") val path = List(rootDir, dir).mkString(File.separator) val testDir = new File(path) + val tempDir = Files.createTempDirectory("suslik-").toFile + println(s"Initialized output directory at: ${tempDir.getCanonicalPath}\n") if (testDir.exists() && testDir.isDirectory) { print(s"Retrieving definitions and specs from ${testDir.getName}...") val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) @@ -82,9 +83,9 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"done! (${fmtTime(synDuration)} s)") print(s" generating certificate...") - val cert = target.certify(res.head, env) + val (cert, proofGenDuration) = timed(target.certify(res.head, env)) println("done!") - (testName, cert, synDuration) + (testName, cert, synDuration, proofGenDuration) } println(s"Successfully synthesized ${tests.length} tests.") @@ -94,12 +95,12 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val defFile = CoqOutput(defFilename, "common", target.mkDefs(predicates)) serialize(tempDir, defFilename, defFile.body) println("done!") - print(s"Compiling $defFilename...") + print(s"Compiling definitions...") defFile.compile(tempDir) println("done!") println(s"\nGenerating statistics...") - for ((testName, cert, synDuration) <- synResults) { + for ((testName, cert, synDuration, proofGenDuration) <- synResults) { println(s"$testName:") for (o <- cert.outputs) yield { print(s" Writing certificate output to file ${o.filename}...") @@ -110,10 +111,10 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val (specSize, proofSize) = checkProofSize(tempDir, o.filename) println(s"done! (spec: $specSize, proof: $proofSize)") print(s" Compiling proof...") - val (res, proofDuration) = timed (o.compile(tempDir)) + val (res, proofCheckDuration) = timed (o.compile(tempDir)) if (res == 0) { - println(s"done! (${fmtTime(proofDuration)} s)") - logStat(testName, o.filename, synDuration, proofDuration, specSize, proofSize) + println(s"done! (${fmtTime(proofCheckDuration)} s)") + logStat(testName, o.filename, synDuration, proofGenDuration, proofCheckDuration, specSize, proofSize) } else { throw SynthesisException(s"Failed to verify ${o.filename}!") } @@ -126,7 +127,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { } println(s"\nStatistics written to: ${statsFile.getCanonicalPath}") - println(s"Proofs and definitions written to: $tempDir\n\n") + println(s"View generated proofs and definitions at: $tempDir\n\n") } } @@ -148,8 +149,8 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { SynStatUtil.using(new FileWriter(statsFile, true))(_.write(statsHeader)) } - private def logStat(name: String, filename: String, synDuration: Long, proofDuration: Long, specSize: Int, proofSize: Int): Unit = { - val data = s"$name, $filename, ${fmtTime(synDuration)}, ${fmtTime(proofDuration)}, $specSize, $proofSize\n" + private def logStat(name: String, filename: String, synDuration: Long, proofGenDuration: Long, proofCheckDuration: Long, specSize: Int, proofSize: Int): Unit = { + val data = s"$name, $filename, ${fmtTime(synDuration)}, ${fmtTime(proofGenDuration)}, ${fmtTime(proofCheckDuration)}, $specSize, $proofSize\n" SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) } From 641303af9d81d3182dad992d86717289f6741f9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 16:53:21 +0800 Subject: [PATCH 149/211] Iris: make Iris specs more like SSL specs --- .../targets/iris/heaplang/Expressions.scala | 6 ++--- .../targets/iris/logic/Assertions.scala | 14 ++--------- .../iris/translation/IrisTranslator.scala | 23 ++++--------------- .../iris/translation/ProofTranslator.scala | 9 +------- .../iris/translation/Translation.scala | 4 ++-- 5 files changed, 12 insertions(+), 44 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index 273ae4f5b..cfcdcac48 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -128,16 +128,16 @@ object Expressions { override def pp: String = s"! (${e.pp})" } case class HStore(lhs: HExpr, rhs: HExpr) extends HExpr { - override def pp: String = s"${lhs.pp} <- ${rhs.pp}" + override def pp: String = s"(${lhs.pp}) <- (${rhs.pp})" } case class HFree(e: HExpr) extends HExpr { override def pp: String = s"Free (${e.pp})" } case class HAlloc(e: HExpr) extends HExpr { - override def pp: String = s"Alloc ${e.pp}" + override def pp: String = s"Alloc (${e.pp})" } case class HAllocN(n: HExpr, e: HExpr) extends HExpr { - override def pp: String = s"AllocN ${n.pp} ${e.pp}" + override def pp: String = s"AllocN (${n.pp}) (${e.pp})" } case class HCall(name: HExpr, params: Seq[HExpr]) extends HExpr { override def pp: String = s"${name.pp} ${params.map(p => p.pp).mkString(" ")}" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 22290dfb6..d25926389 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -156,33 +156,23 @@ object Assertions { case class IFunSpec(fname: Ident, funArgs: Seq[(ISpecVar, HType)], specUniversal: Seq[(IQuantifiedVar, HType)], - artificialUniversal: Seq[(IQuantifiedVar, HType)], specExistential: Seq[(IQuantifiedVar, HType)], pre: IAssertion, post: IAssertion ) extends ISpecification { override def pp: String = { - // TODO: make this use the general translation mechanism - def getArgLitVal(v: ISpecVar, t: HType): ISpecQuantifiedValue = - (v, t) match { - case (ISpecVar(name, t), HLocType()) => ISpecQuantifiedValue(s"l${name}", name, t) - case (ISpecVar(name, t), _) => ISpecQuantifiedValue(s"v${name}", name, t) - } - - val var_at = (v: ISpecVar, t: HType) => s"${t.pp}_at ${v.pp} ${getArgLitVal(v, t).pp}" val postExist = if (specExistential.nonEmpty) s"∃ ${specExistential.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, " else "" - val universal = specUniversal ++ artificialUniversal + val universal = specUniversal s""" |Lemma ${fname}_spec : |∀ ${universal.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, - |${funArgs.map(v => var_at(v._1, v._2)).mkString(" →\n")} → |{{{ ${pre.pp} }}} - | ${fname} ${funArgs.map(v => v._1.pp).mkString(" ")} + | ${fname} ${funArgs.map(v => s"#${v._1.pp}").mkString(" ")} |{{{ RET #(); ${postExist}${post.pp} }}}. |""".stripMargin } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 74c4aa651..119946dfa 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -61,15 +61,7 @@ object IrisTranslator { } implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _, _) => HProgVar(pv.name) - implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, ctx, _) => ISpecVar(hv.name, - ctx.get.hctx(hv.name)) - implicit val progVarToSpecQuantifiedValue: IrisTranslator[HProgVar, ISpecQuantifiedValue] = (pv, ctx, _) => { - assert(ctx.isDefined) - (pv, ctx.get.gamma(Var(pv.name)).translate) match { - case (HProgVar(name), HLocType()) => ISpecQuantifiedValue(s"l${name}", name, HLocType()) - case (HProgVar(name), t) => ISpecQuantifiedValue(s"v${name}", name, t) - } - } + implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, ctx, _) => ISpecVar(hv.name, ctx.get.hctx(hv.name)) implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _, _) => value match { case IntType => HIntType() @@ -141,9 +133,7 @@ object IrisTranslator { expr match { case v: HProgVar => val specVar = v.translate(progVarToSpecVar, ctx) - // Get the quantified value version of the var, if it exists, otherwise - // make the var into value by prepending # to it - ctx.get.pts.getOrElse(v, if (prodVal) ISpecMakeVal(specVar) else specVar) + if (prodVal) ISpecMakeVal(specVar) else specVar case l: HLit => ISpecLit(l) case HBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.translate(toSpecExpr, ctx, subTarget), right.translate(toSpecExpr, ctx, subTarget)) case HUnaryExpr(op, expr) => ISpecUnaryExpr(op, expr.translate(toSpecExpr, ctx, subTarget)) @@ -193,18 +183,13 @@ object IrisTranslator { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) - // We "unwrap" every value to a l ↦ v form. quantifiedVal = v and takes the type of the value. - val artificialUniversal = g.programVars.map( - v => (v.translate.translate(progVarToSpecQuantifiedValue, Some(ctx.get)), g.gamma(v).translate) - ) // We quantify over all universals, ignoring the type of function arguments - val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), - if(g.programVars.contains(v)) HValType() else g.gamma(v).translate)) + val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)) val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)).toSeq val pre = g.pre.translate(assertionTranslator, ctx) val post = g.post.translate(assertionTranslator, ctx) - IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), specUniversal.toSeq, artificialUniversal, specExistential, pre, post) + IFunSpec(g.fname, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), specUniversal.toSeq, specExistential, pre, post) } implicit val predicateTranslator: IrisTranslator[InductivePredicate, IPredicate] = (predicate, ctx, _) => { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index d7cfa037c..82238758f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -88,9 +88,6 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I var ctx = clientCtx val coqHyps = spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ - spec.artificialUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ - // We only use the loc_at / Z_at lemmas for rewriting, so we don't give them names - spec.artificialUniversal.map({ case (v, _) => ICoqName("?") }) ++ // Iris specific thing List(ICoqName("ϕ")) @@ -99,11 +96,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val irisHyps = IPatList(List(IPatDestruct(pureIntro ++ spatialIntro), IIdent("Post"))) val intro = IIntros(coqHyps, irisHyps) - // We modify the context to account for the loc_at / Z_at rewriting. - // All the artificialUniversals that show up in the spec will be renamed by IBegin, - // e.g. (l : val) with (lr : loc) (lr is artificial) will result in (l : loc). - val typeOf = spec.artificialUniversal.map({ case (v, t) => (v.originalName, t) }).toMap - val universals = spec.specUniversal.map({ case (v, _) => (v.name, typeOf.getOrElse(v.name, HUnknownType())) }) + val universals = spec.specUniversal.map({ case (v, t) => (v.name, t) }) val existentials = spec.specExistential.map({ case (v, t) => (v.name, t) }) ctx = ctx withVariablesTypes universals.toMap ctx = ctx withVariablesTypes existentials.toMap diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index fdf2ddf79..bb9b7de6c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -32,7 +32,7 @@ object Translation { // We have this "dummy" value to generate progToSpec for the actual context, ctx val pre_ctx = Some(ProgramTranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) - val progToSpec = params.map(p => (p, p.translate(progVarToSpecQuantifiedValue, pre_ctx))) + val progToSpec = params.map(p => (p, p.translate(progVarToSpecVar, pre_ctx))) val ctx = ProgramTranslationContext(env, node.goal.gamma, progToSpec.toMap, node.goal.gamma.translate) val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList @@ -53,7 +53,7 @@ object Translation { try { ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) } - catch { case e => throw e + catch { case e => s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } IrisCertificate(proc.name, predicates, funDef, funSpec, proofStr) From 6c781dc169b6d17f6deb0e2f060bdae58f19f25f Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 27 Feb 2021 18:11:35 +0800 Subject: [PATCH 150/211] Refine type of predicate argument to mkDefs --- .../suslik/certification/Certificate.scala | 5 ++-- .../certification/CertificationTarget.scala | 29 +++++++++++++++++-- .../certification/targets/htt/HTT.scala | 27 ++++++++++------- .../targets/htt/HTTCertificate.scala | 6 ++-- .../htt/HTTCertificationBenchmarks.scala | 2 +- .../certification/targets/iris/Iris.scala | 9 ++++-- .../targets/iris/IrisCertificate.scala | 6 ++-- .../certification/targets/vst/VST.scala | 11 ++++--- .../targets/vst/VSTCertificate.scala | 8 ++--- .../tygus/suslik/synthesis/SynConfig.scala | 3 +- .../suslik/synthesis/SynthesisRunner.scala | 9 +++--- .../synthesis/SynthesisRunnerUtil.scala | 5 ++-- .../targets/htt/HTTCertificationTests.scala | 2 +- .../targets/vst/VSTCertificationTests.scala | 2 +- 14 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 356fe878b..93ddb3418 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -31,8 +31,7 @@ case class CoqOutput(filename: String, name: String, body: String) extends Certi * A generic interface for certificates, to be implemented by all * certification backends */ -trait Certificate { - val predicates: List[Predicate] - val target: CertificationTarget +trait Certificate[T <: CertificationTarget, P <: Predicate] { + val predicates: List[P] def outputs: List[CertificateOutput] } diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 8ca855597..43db5bb44 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -1,5 +1,8 @@ package org.tygus.suslik.certification +import org.tygus.suslik.certification.targets.htt.HTT +import org.tygus.suslik.certification.targets.iris.Iris +import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment @@ -11,8 +14,30 @@ trait CertificationTarget { /** uniquely identifies the certification target - users can specify * this certification target from the CLI passing `name` via the * `certTarget` flag */ + type T <: CertificationTarget + type P <: Predicate val name: String val suffix: String - def certify(proc: Procedure, env: Environment): Certificate - def mkDefs(predicates: List[Predicate]): String + def certify(proc: Procedure, env: Environment): Certificate[T,P] + def mkDefs(predicates: List[P]): String +} + +object CertificationTarget { + def fromString(s: String): CertificationTarget = s match { + case "htt" => HTT() + case "vst" => VST() + case "iris" => Iris() + case _ => NoCert + } + + object NoCert extends CertificationTarget { + override type T = Nothing + override type P = Nothing + val name: String = "" + override val suffix: String = "" + + override def certify(proc: Procedure, env: Environment): Certificate[T,P] = ??? + + override def mkDefs(predicates: List[P]): String = ??? + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 6050c1873..bd6a6cf9f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -1,14 +1,29 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification._ +import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate import org.tygus.suslik.certification.targets.htt.translation.Translation import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment -object HTT extends CertificationTarget { +case class HTT() extends CertificationTarget { + type T = HTT + type P = CInductivePredicate val name: String = "HTT" val suffix: String = ".v" + + def certify(proc: Procedure, env: Environment): HTTCertificate = { + val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) + Translation.translate(root, proc)(env) + } + + def mkDefs(predicates: List[CInductivePredicate]): String = { + s"${HTT.prelude}\n${predicates.map(_.pp).mkString("\n\n")}" + } +} + +object HTT { val prelude = """From mathcomp |Require Import ssreflect ssrbool ssrnat eqtype seq ssrfun. @@ -21,12 +36,4 @@ object HTT extends CertificationTarget { | |""".stripMargin - def certify(proc: Procedure, env: Environment): HTTCertificate = { - val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - Translation.translate(root, proc)(env) - } - - def mkDefs(predicates: List[Predicate]): String = { - s"$prelude\n${predicates.map(_.pp).mkString("\n\n")}" - } -} +} \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 54557b35e..8333892bc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -3,11 +3,9 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CFunSpec, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.program.Program -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} - -case class HTTCertificate(name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate { - val target: CertificationTarget = HTT +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CoqOutput} +case class HTTCertificate(name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate[HTT, CInductivePredicate] { // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala index 57ada16c8..2edcdcbd1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala @@ -4,7 +4,7 @@ import org.tygus.suslik.certification.CertificationTarget import org.tygus.suslik.synthesis.CertificationBenchmarks object HTTCertificationBenchmarks extends CertificationBenchmarks { - val target: CertificationTarget = HTT + val target: CertificationTarget = HTT() def main(args: Array[String]): Unit = { initLog() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index bd0cebca9..884e6ef8b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -1,13 +1,16 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPredicate import org.tygus.suslik.certification.targets.iris.translation.Translation -import org.tygus.suslik.certification.{CertTree, CertificationTarget, Predicate} +import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException -object Iris extends CertificationTarget { +case class Iris() extends CertificationTarget { + type T = Iris + type P = IPredicate val name: String = "HTT" val suffix: String = ".v" @@ -21,5 +24,5 @@ object Iris extends CertificationTarget { cert } - override def mkDefs(predicates: List[Predicate]): String = ??? + override def mkDefs(predicates: List[IPredicate]): String = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 9a0e45114..1463c7e11 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -5,11 +5,9 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.targets.iris.logic.IProofStep import org.tygus.suslik.certification.targets.iris.logic.IProofStep.ProofTreePrinter import org.tygus.suslik.certification.traversal.ProofTree -import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} - -case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate { - val target: CertificationTarget = Iris +import org.tygus.suslik.certification.{Certificate, CertificateOutput, CoqOutput} +case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proof: ProofTree[IProofStep]) extends Certificate[Iris, IPredicate] { private val prelude = s"""From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index aa9bf5309..f2b21b021 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -1,16 +1,19 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{CertTree, Certificate, CertificationTarget, Predicate} +import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation -object VST extends CertificationTarget { +case class VST() extends CertificationTarget { + type T = VST + type P = VSTPredicate override val name: String = "VST" override val suffix: String = ".v" - override def certify(proc: Statements.Procedure, env: Environment): Certificate = { + override def certify(proc: Statements.Procedure, env: Environment): VSTCertificate = { // retrieve the search tree val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) @@ -18,6 +21,6 @@ object VST extends CertificationTarget { Translation.translate(root, proc, env) } - def mkDefs(predicates: List[Predicate]): String = ??? + def mkDefs(predicates: List[VSTPredicate]): String = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index a2b0d8079..74bde15ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -1,12 +1,12 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{ClangOutput, Certificate, CertificateOutput, CertificationTarget, CoqOutput, Predicate} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, ClangOutput, CoqOutput} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Proof +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate { - override val target: CertificationTarget = VST - override val predicates: List[Predicate] = Proof.predicates +case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate[VST, VSTPredicate] { + override val predicates: List[VSTPredicate] = Proof.predicates override def outputs: List[CertificateOutput] = List( diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala index d2470d723..82fedc6d7 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.synthesis import java.io.File import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.certification.CertificationTarget.NoCert import org.tygus.suslik.language.PrettyPrinting import scala.concurrent.duration.Deadline @@ -50,7 +51,7 @@ case class SynConfig( printTree: Boolean = false, treeDest: File = null, // Certification - certTarget: CertificationTarget = null, + certTarget: CertificationTarget = NoCert, certDest: File = null, // Internal (not directly settable through CLI) inputFormat: InputFormat = dotSyn, diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 0461b70dd..e6116bfd0 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.synthesis import java.io.File import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.certification.CertificationTarget.NoCert import org.tygus.suslik.certification.targets._ import org.tygus.suslik.report.Log import org.tygus.suslik.util.SynLogLevels @@ -105,10 +106,10 @@ object SynthesisRunner extends SynthesisRunnerUtil { implicit val certTargetRead: scopt.Read[CertificationTarget] = scopt.Read.reads { - case "htt" => htt.HTT - case "vst" => vst.VST - case "iris" => iris.Iris - case _ => ??? + case "htt" => htt.HTT() + case "vst" => vst.VST() + case "iris" => iris.Iris() + case _ => NoCert } private def uncurryLens[A,B,C](lens: scalaz.Lens[A, B])(f: C => B => B) = diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index 9433d9973..c12b8a3f6 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -4,6 +4,7 @@ import java.io.{File, PrintWriter} import java.nio.file.Paths import org.tygus.suslik.LanguageUtils +import org.tygus.suslik.certification.CertificationTarget.NoCert import org.tygus.suslik.certification.{CertTree, CertificateOutput} import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor._ @@ -110,7 +111,7 @@ trait SynthesisRunnerUtil { new ReplaySynthesis(env.config) else new PhasedSynthesis(env.config) - val trace : ProofTrace = if (env.config.certTarget != null) new ProofTraceCert() else { + val trace : ProofTrace = if (env.config.certTarget != NoCert) new ProofTraceCert() else { env.config.traceToJsonFile match { case None => ProofTraceNone case Some(file) => new ProofTraceJson(file) @@ -230,7 +231,7 @@ trait SynthesisRunnerUtil { testPrintln(sresult._2.getExpansionChoices.mkString("\n")) testPrintln("-----------------------------------------------------") } - if (params.certTarget != null) { + if (params.certTarget != NoCert) { val certTarget = params.certTarget val targetName = certTarget.name val certificate = certTarget.certify(procs.head, env) diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala index 38d81517b..79578c283 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala @@ -19,7 +19,7 @@ class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUt override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = it(s"certifies that it $desc") { - synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = HTT, certDest = certRoot)) + synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = HTT(), certDest = certRoot)) val fname = testName.split('/').last // Find the path to the certificated that was written out to the temp directory diff --git a/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala index b820720bc..820ade52a 100644 --- a/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala +++ b/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala @@ -18,7 +18,7 @@ class VSTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUt synthesizeFromSpec( testName, in, out, params.copy( - assertSuccess = false, certTarget = VST, + assertSuccess = false, certTarget = VST(), certDest = certRoot )) val fname = testName.split('/').last From 3a6e147b39fe12d28601d75e0c0195d344dbe57b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 18:55:08 +0800 Subject: [PATCH 151/211] Iris: first attempt for calls --- .../targets/iris/logic/Assertions.scala | 84 +++++++++++++++---- .../targets/iris/logic/IProofStep.scala | 7 +- .../iris/translation/ProofTranslator.scala | 46 +++++++--- .../iris/translation/Translation.scala | 2 +- 4 files changed, 113 insertions(+), 26 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index d25926389..e22658a4f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -6,6 +6,8 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HBoolType, HI import org.tygus.suslik.certification.translation.{CardConstructor, GenericPredicate, GenericPredicateClause} import org.tygus.suslik.language.{Ident, PrettyPrinting} +import scala.annotation.tailrec + object Assertions { /** Unlike HTT, which encodes programs in a shallow embedding, Iris has a deep embedding of programs. @@ -18,9 +20,29 @@ object Assertions { def conjuncts: Seq[IPureAssertion] = throw TranslationException("Called conjuncts on IPureAssertion not of type IAnd.") - // TODO: implement this + def variables: Set[ISpecVar] = this match { + case expr@ISpecVar(_, _) => Set(expr) + case ISetLiteral(elems) => elems.flatMap(_.variables).toSet + case ISpecIfThenElse(cond, left, right) => cond.variables ++ left.variables ++ right.variables + case ISpecBinaryExpr(_, left, right) => left.variables ++ right.variables + case ISpecUnaryExpr(_, e) => e.variables + case _ => Set() + } + def subst(s: Map[Ident, IPureAssertion]): IPureAssertion = this match { - case expr => expr + case expr@ISpecVar(oldName, t) => s.get(oldName) match { + case Some(ISpecVar(name, _)) if name == oldName => expr + // avoid infinite recursion by refusing to subst on expressions that contain the name they were just subst'd with + case Some(value) if !value.variables.map(_.name).contains(oldName) => value.subst(s) + case Some(value) => value + case None => expr + } + case ISetLiteral(elems) => ISetLiteral(elems.map(_.subst(s))) + case ISpecIfThenElse(cond, left, right) => ISpecIfThenElse(cond.subst(s), left.subst(s), right.subst(s)) + case ISpecBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.subst(s), right.subst(s)) + case ISpecUnaryExpr(op, e) => ISpecUnaryExpr(op, e.subst(s)) + + case _ => ??? } def rename(s: Map[Ident, Ident]): IPureAssertion = this match { @@ -28,19 +50,18 @@ object Assertions { case Some(newName) => ISpecVar(newName, t) case None => expr } - case expr => expr + case ISetLiteral(elems) => ISetLiteral(elems.map(_.rename(s))) + case ISpecIfThenElse(cond, left, right) => ISpecIfThenElse(cond.rename(s), left.rename(s), right.rename(s)) + case ISpecBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.rename(s), right.rename(s)) + case ISpecUnaryExpr(op, e) => ISpecUnaryExpr(op, e.rename(s)) + case IAnd(elems) => IAnd(elems.map(_.rename(s))) + + case _ => ??? } } abstract class IQuantifiedVar extends IPureAssertion { def name: Ident - - def originalName: Ident = throw TranslationException("Called originalName on IQuantifiedVar not of type ISpecQuantifiedValue.") - } - - abstract class ISpatialAssertion extends PrettyPrinting { - - def heaplets: Seq[ISpatialAssertion] = throw TranslationException("Called heaplets on ISpatialAssertion not of type IHeap.") } abstract class ISpecification extends PrettyPrinting @@ -65,10 +86,6 @@ object Assertions { override def ppAsPhi: String = super.ppAsPhi } - case class ISpecQuantifiedValue(name: String, override val originalName: Ident, typ: HType) extends IQuantifiedVar { - override def pp: String = s"${name}" - } - case class ISetLiteral(elems: List[IPureAssertion]) extends IPureAssertion { override def pp: String = s"[${elems.map(_.pp).mkString("; ")}]" @@ -127,6 +144,25 @@ object Assertions { } + abstract class ISpatialAssertion extends PrettyPrinting { + def heaplets: Seq[ISpatialAssertion] = throw TranslationException("Called heaplets on ISpatialAssertion not of type IHeap.") + + def rename(s: Map[Ident, Ident]): ISpatialAssertion = this match { + case IPointsTo(loc, value) => IPointsTo(loc.rename(s), value.rename(s)) + case IPredApp(pred, args, card) => IPredApp(pred, args.map(_.rename(s)), card.rename(s)) + case IHeap(heaplets) => IHeap(heaplets.map(_.rename(s))) + case IBlock() => IBlock() + } + + def subst(s: Map[Ident, IPureAssertion]): ISpatialAssertion = this match { + case IPointsTo(loc, value) => IPointsTo(loc.subst(s), value.subst(s)) + case IPredApp(pred, args, card) => IPredApp(pred, args.map(_.subst(s)), card.subst(s)) + case IHeap(heaplets) => IHeap(heaplets.map(_.subst(s))) + case IBlock() => IBlock() + } + } + + case class IPointsTo(loc: IPureAssertion, value: IPureAssertion) extends ISpatialAssertion { override def pp: String = s"${loc.pp} ↦ ${value.pp}" } @@ -151,6 +187,9 @@ object Assertions { val whole = s"${pure}${if(pure.nonEmpty && spatial.nonEmpty) " ∗ " else ""}${sigma.pp}" if (whole.isEmpty) "True" else whole } + + def subst(s: Map[Ident, IPureAssertion]): IAssertion = IAssertion(phi.subst(s), sigma.subst(s)) + def rename(s: Map[Ident, Ident]): IAssertion = IAssertion(phi.rename(s), sigma.rename(s)) } case class IFunSpec(fname: Ident, @@ -176,6 +215,23 @@ object Assertions { |{{{ RET #(); ${postExist}${post.pp} }}}. |""".stripMargin } + +// // TODO: are the casts legit? +// def subst(s: Map[Ident, IPureAssertion]): IFunSpec = { +// val args = funArgs.map({ case (v, t) => ((ISpecVar) (v.subst(s)), t) }) +// val uni = specUniversal.map({ case (v, t) => ((ISpecVar) (v.subst(s)), t) }) +// val exi = specExistential.map({ case (v, t) => ((ISpecVar) (v.subst(s)), t) }) +// IFunSpec(fname, args, uni, exi, pre.subst(s), post.subst(s)) +// } + + def rename(s: Map[Ident, Ident]): IFunSpec = { + val args = funArgs.map { case (v, t) => (v.rename(s).asInstanceOf[ISpecVar], t) } + val uni = specUniversal.map { case (v, t) => (v.rename(s).asInstanceOf[ISpecVar], t) } + val exi = specExistential.map { case (v, t) => (v.rename(s).asInstanceOf[ISpecVar], t) } + val pr = pre.rename(s) + val po = post.rename(s) + IFunSpec(fname, args, uni, exi, pr, po) + } } case class IPredicateClause(override val pure: List[IPureAssertion], diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 4d3d6cfde..99ce57a1c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -61,7 +61,8 @@ case class IDestruct(hypName: IIdent, coq: Seq[ICoqName], iris: IIntroPattern) e override def pp: String = { val coqStr = if (coq.nonEmpty) s"(${coq.map(_.pp).mkString(" ")})" else "" val irisStr = if (iris.pp.nonEmpty) s""""${iris.pp}"""" else "" - s"""iDestruct "${hypName.pp}" as $coqStr $irisStr.""" + if (coqStr.isEmpty && irisStr.isEmpty) "" + else s"""iDestruct "${hypName.pp}" as $coqStr $irisStr.""" } } @@ -91,6 +92,10 @@ case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExist } } +case class IWpApply(applyName: String, exs: Seq[IPureAssertion]) extends IProofStep { + override def pp: String = + s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "[$$]").""" +} case class IExists(e: IPureAssertion) extends IProofStep { override def pp: String = s"iExists ${e.pp}." } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 82238758f..50cf4a17a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -2,9 +2,10 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HType, HUnknownType} -import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate, IPureAssertion} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate, IPureAssertion, ISpecVar} import org.tygus.suslik.certification.targets.iris.logic._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.normalize_renaming import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} @@ -24,7 +25,8 @@ case class IProofContext( specMap: Map[Ident, IFunSpec], coqTypingCtx: Map[Ident, HType], varMap: Map[Ident, IPureAssertion], - renamings: Map[Ident, Ident] + renamings: Map[Ident, Ident], + pendingCall: Option[IFunSpec] ) extends ClientContext[IProofStep] { def freshHypName(): String = { @@ -54,6 +56,10 @@ case class IProofContext( this.copy(varMap = newVarMap) } + def withQueuedCall(fs: IFunSpec): IProofContext = this.copy(pendingCall = Some(fs)) + + def unqueueCall(): (IFunSpec, IProofContext) = (pendingCall.get, this.copy(pendingCall = None)) + def withRenaming(ren: (Ident, Ident)): IProofContext = { val (from, to) = ren val s = Map(from -> to) @@ -80,6 +86,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val irisPhi: String = "ϕ" private val irisPost: String = "Post" + private val irisRet = "Ret" private val irisSelf: String = spec.fname override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = @@ -93,7 +100,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val pureIntro = spec.pre.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) val spatialIntro = spec.pre.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) - val irisHyps = IPatList(List(IPatDestruct(pureIntro ++ spatialIntro), IIdent("Post"))) + val irisHyps = IPatList(List(IPatDestruct(pureIntro ++ spatialIntro), IIdent(irisPost))) val intro = IIntros(coqHyps, irisHyps) val universals = spec.specUniversal.map({ case (v, t) => (v.name, t) }) @@ -145,16 +152,35 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I // case SuslikProofStep.Close(app, selector, _, freshExist) => // Result(List(IDebug(value.pp)), List(withNoDeferreds(clientCtx))) - // TODO: actually implement - case SuslikProofStep.AbduceCall(_, precondition, _, _, freshSub, _, f, _) => - val ctx = clientCtx + case SuslikProofStep.AbduceCall(_, _, _, _, freshSub, _, f, _) => + var ctx = clientCtx val funSpec = ctx.specMap(f.name) - + val s = normalize_renaming(freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) }) + val newSpec = funSpec.rename(s) + ctx = ctx withQueuedCall(newSpec) Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) - case s:SuslikProofStep.Call => - // TODO: actually implement - Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + case SuslikProofStep.Call(subst, Statements.Call(Var(funName), _, _)) => + val (spec, ctx) = clientCtx.unqueueCall() + val applyName = if (funName == irisSelf) s""""$irisSelf"""" else s"${funName}_spec" + val instantiate = spec.specUniversal.map { case (ISpecVar(name, _), _) => subst.getOrElse(Var(name), Var(name)) } + .map(e => e.translate.translate(IrisTranslator.toSpecExpr, ctx.translationCtx)) + + val retExistentials = spec.specExistential.map { case (v, _) => ICoqName(v.name) } + val pureIntro = spec.post.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) + val spatialIntro = spec.post.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) + val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) + + // TODO: need to identify heaps for wp_apply by name? + val steps = List( + IDebug(value.pp), + IWpApply(applyName, instantiate), + IIntros(Seq(), IIdent(irisRet)), + IDestruct(IIdent(irisRet), retExistentials, irisHyps), + IEmp + ) + + Result(steps, List(withNoDeferreds(ctx))) // TODO: actually implement case s:SuslikProofStep.SubstL => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index bb9b7de6c..7f36902ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -48,7 +48,7 @@ object Translation { // TODO: add support for helper functions val specMap = List((funSpec.fname, funSpec)).toMap - val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty, Map.empty) + val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty, Map.empty, None) val proofStr = try { ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) From 60204b0c6b92f5c4527dfca1d95fcd21941a07ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 19:57:32 +0800 Subject: [PATCH 152/211] Iris: proof translation Close (WIP) --- .../iris/translation/ProofTranslator.scala | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 50cf4a17a..0d5863bdf 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -148,9 +148,26 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I } Result(steps, childRules.toList) -// // TODO: actually implement -// case SuslikProofStep.Close(app, selector, _, freshExist) => -// Result(List(IDebug(value.pp)), List(withNoDeferreds(clientCtx))) + // TODO: actually implement + case SuslikProofStep.Close(app, selector, _, freshExist) => + var ctx = clientCtx + val ren = normalize_renaming(freshExist.map { case (Var(from), Var(to)) => (from, to) }) + val pred = clientCtx.predMap(app.pred) + val cardVar = app.card.asInstanceOf[Var].name + val tctx = clientCtx.baseTranslationContext.copy(hctx = pred.params.toMap) + val translSelector = selector.translate.translate(IrisTranslator.toSpecExpr, Some(tctx)) + val (constructor, clause) = pred.clauses.find { case (_, cl) => cl.selector == translSelector }.get + val existentials = pred.findExistentials(constructor)(clause).map( { case (name, ty) => (ren(name), ty) }) + + + + println(translSelector) + + +// val (constructor, predClause) = + + + Result(List(IDebug(value.pp)), List(withNoDeferreds(clientCtx))) case SuslikProofStep.AbduceCall(_, _, _, _, freshSub, _, f, _) => var ctx = clientCtx @@ -179,11 +196,12 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I IDestruct(IIdent(irisRet), retExistentials, irisHyps), IEmp ) - Result(steps, List(withNoDeferreds(ctx))) // TODO: actually implement - case s:SuslikProofStep.SubstL => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + case SuslikProofStep.SubstL(Var(from), to) => + val ctx = clientCtx withMappingBetween (from, to) + Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) case s:SuslikProofStep.SubstR => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) case SuslikProofStep.Free(_, sz) => From dfaa4232ea5aa0f7d8fa8bbbffcaab87ac9f7d63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sat, 27 Feb 2021 20:03:58 +0800 Subject: [PATCH 153/211] Iris: proof translation Close (WIP) --- .../suslik/certification/targets/iris/IrisCertificate.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index fa9a99094..6f6ab91f7 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { - val target: CertificationTarget = Iris + val target: CertificationTarget = Iris() private val prelude = s"""From iris.program_logic Require Export weakestpre. From cbaebd31e5c8b1e532d071044d11a842269d94f1 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 27 Feb 2021 18:36:54 +0800 Subject: [PATCH 154/211] vst bcnehmarks --- .../suslik/certification/Certificate.scala | 2 +- .../vst/VSTCertificationBenchmarks.scala | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 93ddb3418..8440f3801 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -14,7 +14,7 @@ abstract class CertificateOutput { case class ClangOutput(filename: String, name: String, body: String) extends CertificateOutput { def compile(dir: File): Int = { - val cmd = Seq("clightgen", filename, "&&", "coqc", s"$name.v") // TODO: correct? + val cmd = Seq("clightgen", "-normalize", filename, "&&", "coqc", s"$name.v") // TODO: correct? Process(cmd, dir).! } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala new file mode 100644 index 000000000..424ed5bd1 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -0,0 +1,20 @@ +package org.tygus.suslik.certification.targets.vst + +import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.synthesis.CertificationBenchmarks + +object VSTCertificationBenchmarks extends CertificationBenchmarks { + + val target: CertificationTarget = VST + + def main(args: Array[String]): Unit = { + initLog() + runAllTestsFromDir("certification/ints") + runAllTestsFromDir("certification/sll-bounds") + runAllTestsFromDir("certification/sll") + runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/srtl") + runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/bst") + } +} From cbfc17fe036f39add35e8d76d1a634c0799caa64 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 27 Feb 2021 21:12:13 +0800 Subject: [PATCH 155/211] made benchmarks cool --- .../suslik/certification/Certificate.scala | 38 +++++++- .../certification/CertificationTarget.scala | 12 ++- .../certification/targets/htt/HTT.scala | 4 +- .../certification/targets/iris/Iris.scala | 4 +- .../certification/targets/vst/VST.scala | 15 +++- .../targets/vst/VSTCertificate.scala | 20 +++-- .../vst/VSTCertificationBenchmarks.scala | 2 +- .../targets/vst/clang/Statements.scala | 88 +++++++++++++++---- .../targets/vst/logic/Proof.scala | 67 +++++++++++--- .../synthesis/CertificationBenchmarks.scala | 15 ++-- 10 files changed, 212 insertions(+), 53 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 8440f3801..228c45a1d 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -2,6 +2,7 @@ package org.tygus.suslik.certification import java.io.File +import scala.Seq import scala.sys.process.Process abstract class CertificateOutput { @@ -12,13 +13,39 @@ abstract class CertificateOutput { def compile(dir: File): Int } +case class ClangHeaderOutput(filename: String, name: String, body: String) extends CertificateOutput { + def compile (dir: File) : Int = { 0 } +} + +/** + * represents a synthesized C program with an additional compilation unit + * @param filename + * @param name + * @param body + * @param other_files + */ +case class ClangOutputWithCompilationUnit(filename: String, name: String, body: String, other_files: List[String]) extends CertificateOutput { + def compile(dir: File): Int = { + def to_cmd (cmd: Seq[String]) = Process(cmd, dir) + val build_cmd = to_cmd(Seq("clightgen", "-normalize") ++ other_files ++ Seq(filename)) + val verify_cmd = to_cmd(Seq("coqc", "-w", "none", s"${name}.v")) + build_cmd.#&&(verify_cmd).! + } +} + + case class ClangOutput(filename: String, name: String, body: String) extends CertificateOutput { def compile(dir: File): Int = { - val cmd = Seq("clightgen", "-normalize", filename, "&&", "coqc", s"$name.v") // TODO: correct? - Process(cmd, dir).! + def to_cmd (cmd: Seq[String]) = Process(cmd, dir) + val build_cmd = to_cmd(Seq("clightgen", "-normalize", filename)) + val verify_cmd = to_cmd(Seq("coqc", "-w", "none", s"${name}.v")) + println(to_cmd(Seq("which", "clightgen")).!!) + build_cmd.#&&(verify_cmd).! } } + + case class CoqOutput(filename: String, name: String, body: String) extends CertificateOutput { override val isProof: Boolean = true def compile(dir: File): Int = { @@ -33,5 +60,12 @@ case class CoqOutput(filename: String, name: String, body: String) extends Certi */ trait Certificate[T <: CertificationTarget, P <: Predicate] { val predicates: List[P] + + /** return a list of outputs */ def outputs: List[CertificateOutput] + + /** + * Return list of certification outputs, assuming that predicates in common_predicates have been exported to a common file + */ + def outputs_with_common_predicates(base_filename: String, common_predicates: List[P]): List[CertificateOutput] = outputs } diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 43db5bb44..388b60035 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -6,6 +6,7 @@ import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment + /** * A generic interface for certification targets. * The user can specify a target by setting the command-line flag `certTarget` to the target's `name` property. @@ -19,7 +20,14 @@ trait CertificationTarget { val name: String val suffix: String def certify(proc: Procedure, env: Environment): Certificate[T,P] - def mkDefs(predicates: List[P]): String + + /** + * Generate a list of outputs for the following predicates. + * @param base_filename - base name of common file + * @param predicates - list of predicates with common definitions + * @return - a list of synthesized files for predicate definitions + */ + def generate_common_definitions_of(base_filename: String, predicates: List[P]): List[CertificateOutput] } object CertificationTarget { @@ -38,6 +46,6 @@ object CertificationTarget { override def certify(proc: Procedure, env: Environment): Certificate[T,P] = ??? - override def mkDefs(predicates: List[P]): String = ??? + override def generate_common_definitions_of(base_filename: String, predicates: List[P]): List[CertificateOutput] = ??? } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index bd6a6cf9f..217b65613 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -18,8 +18,8 @@ case class HTT() extends CertificationTarget { Translation.translate(root, proc)(env) } - def mkDefs(predicates: List[CInductivePredicate]): String = { - s"${HTT.prelude}\n${predicates.map(_.pp).mkString("\n\n")}" + def generate_common_definitions_of(defFileName: String, predicates: List[CInductivePredicate]): List[CertificateOutput] = { + List(CoqOutput(defFileName ++ ".v", defFileName, s"${HTT.prelude}\n${predicates.map(_.pp).mkString("\n\n")}")) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 884e6ef8b..1671e257c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPredicate import org.tygus.suslik.certification.targets.iris.translation.Translation -import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException @@ -24,5 +24,5 @@ case class Iris() extends CertificationTarget { cert } - override def mkDefs(predicates: List[IPredicate]): String = ??? + override def generate_common_definitions_of(base_filename: String, predicates: List[IPredicate]): List[CertificateOutput] = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index f2b21b021..e5620cd49 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -1,9 +1,11 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException +import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition +import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation @@ -13,6 +15,8 @@ case class VST() extends CertificationTarget { override val name: String = "VST" override val suffix: String = ".v" + val common_coq_lib_name = "common" + override def certify(proc: Statements.Procedure, env: Environment): VSTCertificate = { // retrieve the search tree val root = @@ -21,6 +25,13 @@ case class VST() extends CertificationTarget { Translation.translate(root, proc, env) } - def mkDefs(predicates: List[VSTPredicate]): String = ??? + + def generate_common_definitions_of(base_filename: String, predicates: List[VSTPredicate]): List[CertificateOutput] = { + List( + CProcedureDefinition.common_c_header(base_filename), + CProcedureDefinition.common_c_file(base_filename), + Proof.common_predicates(base_filename, predicates) + ) + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index 74bde15ea..72ec8f735 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -8,9 +8,19 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate[VST, VSTPredicate] { override val predicates: List[VSTPredicate] = Proof.predicates - override def outputs: List[CertificateOutput] = - List( - ClangOutput(name + ".c", name, CProcedureDefinition.pp), - CoqOutput("verif_" + name + ".v", "verif_" + name, Proof.pp) - ) + override def outputs: List[CertificateOutput] = { + List( + ClangOutput(name + ".c", name, CProcedureDefinition.pp), + CoqOutput("verif_" + name + ".v", "verif_" + name, Proof.pp) + ) + } + + override def outputs_with_common_predicates(base_filename: String, common_predicates: List[VSTPredicate]): List[CertificateOutput] = + { + List( + ClangOutput(name + ".c", name, CProcedureDefinition.pp_with_common_defs(base_filename, common_predicates)), + CoqOutput("verif_" + name + ".v", "verif_" + name, Proof.pp_with_common_defs(base_filename, common_predicates)) + ) +} + } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala index 424ed5bd1..ac0b33372 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.synthesis.CertificationBenchmarks object VSTCertificationBenchmarks extends CertificationBenchmarks { - val target: CertificationTarget = VST + val target: CertificationTarget = VST() def main(args: Array[String]): Unit = { initLog() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala index 998dcee9f..819b001fd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/clang/Statements.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.certification.targets.vst.clang +import org.tygus.suslik.certification.{ClangHeaderOutput, ClangOutput} import org.tygus.suslik.certification.targets.vst.Types.VSTCType import org.tygus.suslik.certification.targets.vst.logic.Expressions.CLangExpr +import org.tygus.suslik.certification.targets.vst.logic.ProofTerms import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.FormalSpecification import org.tygus.suslik.certification.traversal.Step.DestStep import org.tygus.suslik.certification.traversal.{ProofTree, ProofTreePrinter} @@ -102,6 +104,59 @@ object Statements { } } + object CProcedureDefinition { + val common_defs = """typedef union sslval { + | int ssl_int; + | void *ssl_ptr; + |} *loc; + |#define READ_LOC(x,y) (*(x+y)).ssl_ptr + |#define READ_INT(x,y) (*(x+y)).ssl_int + |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z + |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z""".stripMargin + + + def common_c_file (base_name: String): ClangOutput = + ClangOutput( + filename=s"${base_name}.c", + name=base_name, + s"""#include + |#include "${base_name}.h" + | + |loc vst_keep_my_damn_definitions = NULL;""".stripMargin + ) + + def common_c_header (base_name: String): ClangHeaderOutput = + ClangHeaderOutput( + filename=s"${base_name}.h", + name=base_name, + s"""#ifndef COMMON_H + |#define COMMON_H + | + |${common_defs} + | + |#endif""".stripMargin + ) + + def c_prelude(common_defs_header_file: Option[String], helper_specs: List[FormalSpecification]) = + s"""#include + |${ + common_defs_header_file match { + case Some(header_file_name) => "#include \"" ++ header_file_name ++ ".h\"" + case None => "" + }} + | + |extern void free(void *p); + |extern void *malloc(size_t size); + | + |${common_defs_header_file match { + case Some(value) => "" // have common defs, no need to include + case None => common_defs + }} + | + ${helper_specs.map(spec => s"|${spec.pp_as_c_decl}").mkString("\n")} + | + |""".stripMargin + } /** Definition of a CProcedure */ case class CProcedureDefinition( @@ -110,24 +165,18 @@ object Statements { body: ProofTree[StatementStep], helper_specs: List[FormalSpecification] ) extends PrettyPrinting { - val c_prelude = - s"""#include - | - |extern void free(void *p); - |extern void *malloc(size_t size); - | - |typedef union sslval { - | int ssl_int; - | void *ssl_ptr; - |} *loc; - |#define READ_LOC(x,y) (*(x+y)).ssl_ptr - |#define READ_INT(x,y) (*(x+y)).ssl_int - |#define WRITE_LOC(x,y,z) (*(x+y)).ssl_ptr = z - |#define WRITE_INT(x,y,z) (*(x+y)).ssl_int = z - | - ${helper_specs.map(spec => s"|${spec.pp_as_c_decl}").mkString("\n")} - | - |""".stripMargin + + def pp_with_common_defs(base_filename: String, common_predicates: List[ProofTerms.VSTPredicate]): String = { + val body_string = ProofTreePrinter.pp(body) + val function_def = + s"void ${name}(${ + params.map({case (variable_name, variable_ty) => + s"${variable_ty.pp_as_ctype} ${variable_name}" + }).mkString(", ") + }) {\n${body_string}\n}\n" + CProcedureDefinition.c_prelude(Some(base_filename), helper_specs) + function_def + } + override def pp: String = { val body_string = ProofTreePrinter.pp(body) @@ -138,8 +187,9 @@ object Statements { }).mkString(", ") }) {\n${body_string}\n}\n" - c_prelude + function_def + CProcedureDefinition.c_prelude(None, helper_specs) + function_def } + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index c2b9ac09f..4a53d1aca 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -1,23 +1,19 @@ package org.tygus.suslik.certification.targets.vst.logic +import org.tygus.suslik.certification.CoqOutput import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.ProofTreePrinter import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.targets.vst.logic.ProofTerms._ import org.tygus.suslik.language.PrettyPrinting - -case class Proof( - name: String, predicates: List[VSTPredicate], - spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], - helper_specs: Map[String, ProofTerms.FormalSpecification], - uses_free: Boolean = false, uses_malloc: Boolean = false - ) extends PrettyPrinting { +object Proof { + def helper_name (base_name: String) = s"${base_name}_predicates" /** prelude for Coq file */ - private def coq_prelude = s""" + private def coq_prelude(import_names: List[String]) = s""" Require Import VST.floyd.proofauto. -Require Import $name. +${import_names.map(lib_name => s"Require Import $lib_name.").mkString("\n")} From SSL_VST Require Import core. Instance CompSpecs : compspecs. make_compspecs prog. Defined. Definition Vprog : varspecs. mk_varspecs prog. Defined. @@ -51,6 +47,27 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. | SEP(data_at_ Tsh t p). |""".stripMargin + def common_predicates(base_filename: String, predicates: List[VSTPredicate]) : CoqOutput = { + val body : String = + s"""${Proof.coq_prelude(List(base_filename))} + | + |${predicates.map(_.pp).mkString("\n") + "\n"} + | + |${predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n")} + |""".stripMargin + + CoqOutput(s"${helper_name(base_filename)}.v", helper_name(base_filename), body) + } + +} +case class Proof( + name: String, predicates: List[VSTPredicate], + spec: ProofTerms.FormalSpecification, steps: ProofTree[VSTProofStep], + helper_specs: Map[String, ProofTerms.FormalSpecification], + uses_free: Boolean = false, uses_malloc: Boolean = false + ) extends PrettyPrinting { + + /** prelude for the lemma */ private def lemma_prelude : String = s"""Lemma body_$name : semax_body Vprog Gprog f_$name ${name}_spec. @@ -68,9 +85,9 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. |""".stripMargin override def pp: String = { - coq_prelude + - (if (uses_free) { free_defs + "\n" } else { "" }) + - (if (uses_malloc) { malloc_defs + "\n" } else { "" }) + + Proof.coq_prelude(List(name)) + + (if (uses_free) { Proof.free_defs + "\n" } else { "" }) + + (if (uses_malloc) { Proof.malloc_defs + "\n" } else { "" }) + predicates.map(_.pp).mkString("\n") + "\n" + helper_specs.values.map(_.pp).mkString("\n") + "\n" + spec.pp + "\n" + @@ -82,6 +99,32 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. ProofTreePrinter.pp(steps) + "\n" + "Qed." } + def pp_with_common_defs(base_filename: String, common_predicates: List[VSTPredicate]): String = { + val defined_predicate_names = common_predicates.map(_.name).toSet + val remaining_predicates = predicates.filterNot(v => defined_predicate_names.contains(v.name)) + s"""${Proof.coq_prelude(List(Proof.helper_name(base_filename), name))} + | + |${if (uses_free) { Proof.free_defs + "\n" } else { "" }} + | + |${if (uses_malloc) { Proof.malloc_defs + "\n" } else { "" }} + | + |${remaining_predicates.map(_.pp).mkString("\n") + "\n"} + | + |${helper_specs.values.map(_.pp).mkString("\n") + "\n"} + | + |${spec.pp + "\n"} + | + |${remaining_predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"} + | + |${library_spec + "\n"} + |${lemma_prelude} + |start_function. + |ssl_open_context. + |${ProofTreePrinter.pp(steps) + "\n"} + |Qed.""".stripMargin + } + + } diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 41cc08101..29bbf676e 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -16,9 +16,9 @@ import scala.sys.process._ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val target: CertificationTarget - val defFilename: String = "common.v" val statsFile: File = new File("cert-stats.csv") val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (sec)", "Proof Generation Time (sec)", "Proof Checking Time (sec)", "Spec Size", "Proof Size").mkString(", ") + "\n" + val defFilename: String = "common" def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { val res = params.inputFormat match { @@ -90,19 +90,22 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"Successfully synthesized ${tests.length} tests.") - print(s"\nWriting definitions to file $defFilename...") val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList - val defFile = CoqOutput(defFilename, "common", target.mkDefs(predicates)) - serialize(tempDir, defFilename, defFile.body) + val defFiles = target.generate_common_definitions_of(defFilename, predicates) + defFiles.foreach { + case output => + print(s"\nWriting common definitions to file ${tempDir}/${output.filename}...") + serialize(tempDir, output.filename, output.body) + } println("done!") print(s"Compiling definitions...") - defFile.compile(tempDir) + defFiles.foreach(output => output.compile(tempDir)) println("done!") println(s"\nGenerating statistics...") for ((testName, cert, synDuration, proofGenDuration) <- synResults) { println(s"$testName:") - for (o <- cert.outputs) yield { + for (o <- cert.outputs_with_common_predicates(defFilename, predicates)) yield { print(s" Writing certificate output to file ${o.filename}...") serialize(tempDir, o.filename, o.body) println("done!") From 080ecba6f73ca36d610057b396bf390a7e9b3738 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 27 Feb 2021 21:28:18 +0800 Subject: [PATCH 156/211] updated tests & removed extraneous println --- .../scala/org/tygus/suslik/certification/Certificate.scala | 1 - .../targets/vst/VSTCertificationBenchmarks.scala | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 228c45a1d..b4c0ce277 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -39,7 +39,6 @@ case class ClangOutput(filename: String, name: String, body: String) extends Cer def to_cmd (cmd: Seq[String]) = Process(cmd, dir) val build_cmd = to_cmd(Seq("clightgen", "-normalize", filename)) val verify_cmd = to_cmd(Seq("coqc", "-w", "none", s"${name}.v")) - println(to_cmd(Seq("which", "clightgen")).!!) build_cmd.#&&(verify_cmd).! } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala index ac0b33372..5e74764c8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -9,12 +9,10 @@ object VSTCertificationBenchmarks extends CertificationBenchmarks { def main(args: Array[String]): Unit = { initLog() + runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/ints") runAllTestsFromDir("certification/sll-bounds") - runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/srtl") runAllTestsFromDir("certification/tree") - runAllTestsFromDir("certification/bst") } } From 126206385b4e89dfafd9e7813ca906c547e52ab6 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sat, 27 Feb 2021 21:45:44 +0800 Subject: [PATCH 157/211] make benchmarks robust --- .../targets/vst/VSTCertificate.scala | 4 +- .../targets/vst/logic/Proof.scala | 43 ++++++++----------- .../synthesis/CertificationBenchmarks.scala | 13 ++++-- 3 files changed, 28 insertions(+), 32 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index 72ec8f735..855cef458 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -1,6 +1,6 @@ package org.tygus.suslik.certification.targets.vst -import org.tygus.suslik.certification.{Certificate, CertificateOutput, ClangOutput, CoqOutput} +import org.tygus.suslik.certification.{Certificate, CertificateOutput, ClangOutput, ClangOutputWithCompilationUnit, CoqOutput} import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate @@ -18,7 +18,7 @@ case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinitio override def outputs_with_common_predicates(base_filename: String, common_predicates: List[VSTPredicate]): List[CertificateOutput] = { List( - ClangOutput(name + ".c", name, CProcedureDefinition.pp_with_common_defs(base_filename, common_predicates)), + ClangOutputWithCompilationUnit(name + ".c", name, CProcedureDefinition.pp_with_common_defs(base_filename, common_predicates), List(s"$base_filename.c")), CoqOutput("verif_" + name + ".v", "verif_" + name, Proof.pp_with_common_defs(base_filename, common_predicates)) ) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala index 4a53d1aca..21ac24e0e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/Proof.scala @@ -49,12 +49,10 @@ Definition Vprog : varspecs. mk_varspecs prog. Defined. def common_predicates(base_filename: String, predicates: List[VSTPredicate]) : CoqOutput = { val body : String = - s"""${Proof.coq_prelude(List(base_filename))} - | - |${predicates.map(_.pp).mkString("\n") + "\n"} - | - |${predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n")} - |""".stripMargin + s"${Proof.coq_prelude(List(base_filename))}\n\n"+ + s"${predicates.map(_.pp).mkString("\n") + "\n"}\n\n" + + s"${predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n")}" + CoqOutput(s"${helper_name(base_filename)}.v", helper_name(base_filename), body) } @@ -102,26 +100,19 @@ case class Proof( def pp_with_common_defs(base_filename: String, common_predicates: List[VSTPredicate]): String = { val defined_predicate_names = common_predicates.map(_.name).toSet val remaining_predicates = predicates.filterNot(v => defined_predicate_names.contains(v.name)) - s"""${Proof.coq_prelude(List(Proof.helper_name(base_filename), name))} - | - |${if (uses_free) { Proof.free_defs + "\n" } else { "" }} - | - |${if (uses_malloc) { Proof.malloc_defs + "\n" } else { "" }} - | - |${remaining_predicates.map(_.pp).mkString("\n") + "\n"} - | - |${helper_specs.values.map(_.pp).mkString("\n") + "\n"} - | - |${spec.pp + "\n"} - | - |${remaining_predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"} - | - |${library_spec + "\n"} - |${lemma_prelude} - |start_function. - |ssl_open_context. - |${ProofTreePrinter.pp(steps) + "\n"} - |Qed.""".stripMargin + s"${Proof.coq_prelude(List(Proof.helper_name(base_filename), name))}\n\n" + + s"${if (uses_free) { Proof.free_defs + "\n" } else { "" }}\n\n" + + s"${if (uses_malloc) { Proof.malloc_defs + "\n" } else { "" }}\n\n" + + s"${remaining_predicates.map(_.pp).mkString("\n") + "\n"}\n\n" + + s"${helper_specs.values.map(_.pp).mkString("\n") + "\n"}\n\n" + + s"${spec.pp + "\n"}\n\n" + + s"${remaining_predicates.flatMap(_.get_helpers).map(_.pp).mkString("\n") +"\n"}\n\n" + + s"${library_spec + "\n"}\n" + + s"${lemma_prelude}\n" + + "start_function.\n" + + "ssl_open_context.\n" + + s"${ProofTreePrinter.pp(steps) + "\n"}\n" + + "Qed." } diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 29bbf676e..6dfa21444 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -73,7 +73,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val parser = new SSLParser println(s"\nSynthesizing ${tests.length} test cases...") - val synResults = for (f <- tests) yield { + val raw_syn_results = for (f <- tests) yield { val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) println(s"$testName:") val fullInput = List(defs, in).mkString("\n") @@ -83,13 +83,18 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"done! (${fmtTime(synDuration)} s)") print(s" generating certificate...") - val (cert, proofGenDuration) = timed(target.certify(res.head, env)) - println("done!") - (testName, cert, synDuration, proofGenDuration) + try { + val (cert, proofGenDuration) = timed(target.certify(res.head, env)) + println("done!") + Some ((testName, cert, synDuration, proofGenDuration)) + } catch { + case _ => None + } } println(s"Successfully synthesized ${tests.length} tests.") + val synResults = raw_syn_results.flatten val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList val defFiles = target.generate_common_definitions_of(defFilename, predicates) defFiles.foreach { From 7c6b1454e9a2428b6de52b2c924aedade0c0ad86 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Sun, 28 Feb 2021 12:29:37 +0800 Subject: [PATCH 158/211] updated benchmarks to not die on a single proof failure --- .../org/tygus/suslik/synthesis/CertificationBenchmarks.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 6dfa21444..55de0e490 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -124,7 +124,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"done! (${fmtTime(proofCheckDuration)} s)") logStat(testName, o.filename, synDuration, proofGenDuration, proofCheckDuration, specSize, proofSize) } else { - throw SynthesisException(s"Failed to verify ${o.filename}!") + print(s"Failed to verify ${o.filename}!") } } else { print(s" Compiling output...") From c373372323ef8bbc0d7a8b9151e6920ca9d1ef15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sun, 28 Feb 2021 19:01:13 +0800 Subject: [PATCH 159/211] Iris: a lot more stuff working --- .../targets/iris/IrisCertificate.scala | 40 ++++++++- .../targets/iris/logic/Assertions.scala | 16 +++- .../targets/iris/logic/IProofStep.scala | 25 ++++-- .../iris/translation/IrisTranslator.scala | 14 ++- .../iris/translation/ProofTranslator.scala | 90 +++++++++++++++---- .../iris/translation/Translation.scala | 12 +-- 6 files changed, 163 insertions(+), 34 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 6f6ab91f7..4783948ab 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -77,11 +77,17 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H | end. | |Local Ltac iSimplNoSplit := - | (repeat wp_pures); movePure; iRewriteHyp; iSimpl in "# ∗"; iSimpl. + | (repeat wp_pures); movePure; iRewriteHyp. (* iSimpl in "# ∗"; iSimpl. *) | |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. |Ltac dispatchPure := iRewriteHyp; try lia; try sauto; done. | + |Ltac safeDispatchPure := + |lazymatch goal with + || [ |- (envs_entails _) _ ] => idtac + || _ => by dispatchPure + |end. + | |Ltac ssl_begin := (wp_rec; repeat wp_let); iSimplContext. |Ltac ssl_let := wp_let. |Ltac ssl_load := wp_load; wp_let; iSimplContext. @@ -90,6 +96,38 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; dispatchPure. | + |Ltac ssl_rewrite_term H term := + |let Htn := fresh in let Heqn := fresh in + |remember term as Htn eqn:Heqn; + |rewrite H in Heqn; rewrite Heqn; clear Htn Heqn. + | + |Ltac ssl_apply_to_heap tac := + |match goal with + || [ |- _ (_ ?H) ] => tac H + |end. + | + |Ltac apply_last_heap H tac := + | match H with + | | (?A ∗ ?B)%I => apply_last_heap B tac + | | (?A ∗ ?B)%I => tac B + | | (?A ∗ ?B)%I => apply_last_heap A tac + | | (?A ∗ ?B)%I => tac A + | | _ => fail "apply_last_heap failed on: "H + | end. + | + |Ltac pull_out_exist_internal H := + | lazymatch H with + | | ((bi_exist _) ∗ _)%I => rewrite bi.sep_exist_r + | | (_ ∗ (bi_exist ?Q))%I => rewrite bi.sep_exist_l + | | (?A ∗ ?B)%I => pull_out_exist_internal A || pull_out_exist_internal B + | | _ => fail + | end. + | + |Ltac pull_out_exist := ssl_apply_to_heap ltac:(fun H => pull_out_exist_internal H). + | + |Ltac sll_rewrite_last_heap lemma := ssl_apply_to_heap ltac:(fun H => + | apply_last_heap H ltac:(fun H1 => ssl_rewrite_term lemma H1)). + | |""".stripMargin def pp : String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index e22658a4f..08fa74dd4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.certification.targets.iris.logic import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HBoolType, HIntSetType, HIntType, HLocType, HType, HValType} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HBoolType, HCardType, HIntSetType, HIntType, HLocType, HType, HValType} import org.tygus.suslik.certification.translation.{CardConstructor, GenericPredicate, GenericPredicateClause} import org.tygus.suslik.language.{Ident, PrettyPrinting} @@ -26,6 +26,7 @@ object Assertions { case ISpecIfThenElse(cond, left, right) => cond.variables ++ left.variables ++ right.variables case ISpecBinaryExpr(_, left, right) => left.variables ++ right.variables case ISpecUnaryExpr(_, e) => e.variables + case ICardConstructor(predType, c, args) => args.flatMap(_.variables).toSet case _ => Set() } @@ -37,10 +38,13 @@ object Assertions { case Some(value) => value case None => expr } + case ISpecLit(_) => this case ISetLiteral(elems) => ISetLiteral(elems.map(_.subst(s))) case ISpecIfThenElse(cond, left, right) => ISpecIfThenElse(cond.subst(s), left.subst(s), right.subst(s)) case ISpecBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.subst(s), right.subst(s)) case ISpecUnaryExpr(op, e) => ISpecUnaryExpr(op, e.subst(s)) + case ICardConstructor(predType, c, args) =>ICardConstructor(predType, c, args.map(_.subst(s))) + case ISpecMakeVal(e) => ISpecMakeVal(e.subst(s).asInstanceOf[ISpecVar]) case _ => ??? } @@ -50,11 +54,14 @@ object Assertions { case Some(newName) => ISpecVar(newName, t) case None => expr } + case ISpecLit(_) => this case ISetLiteral(elems) => ISetLiteral(elems.map(_.rename(s))) case ISpecIfThenElse(cond, left, right) => ISpecIfThenElse(cond.rename(s), left.rename(s), right.rename(s)) case ISpecBinaryExpr(op, left, right) => ISpecBinaryExpr(op, left.rename(s), right.rename(s)) case ISpecUnaryExpr(op, e) => ISpecUnaryExpr(op, e.rename(s)) case IAnd(elems) => IAnd(elems.map(_.rename(s))) + case ICardConstructor(predType, c, args) =>ICardConstructor(predType, c, args.map(_.rename(s))) + case ISpecMakeVal(e) => ISpecMakeVal(e.rename(s).asInstanceOf[ISpecVar]) case _ => ??? } @@ -86,6 +93,13 @@ object Assertions { override def ppAsPhi: String = super.ppAsPhi } + case class ICardConstructor(predType: Ident, name: Ident, args: List[IPureAssertion]) extends IPureAssertion { + override def typ: HType = HCardType(predType) + + override def pp: String = s"(${name} ${args.map(_.pp).mkString(" ")} : ${predType}_card)" + override def ppAsPhi: String = s"(${name} ${args.map(_.ppAsPhi).mkString(" ")} : ${predType}_card)" + } + case class ISetLiteral(elems: List[IPureAssertion]) extends IPureAssertion { override def pp: String = s"[${elems.map(_.pp).mkString("; ")}]" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 99ce57a1c..fa375e2b1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -81,23 +81,27 @@ case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExist val open = pred.openLemmaName(constructor) val tactic = if (constrExistentials.isEmpty) { s""" - |erewrite $learn; try by dispatchPure. + |erewrite $learn; try by safeDispatchPure. |rewrite $open.""".stripMargin } else { + // TODO: existentials introduction s""" - |edestruct $learn as [${constrExistentials.map(v => v._1).mkString(" ")} ->]; try by dispatchPure. + |edestruct $learn as [${constrExistentials.map(v => v._1).mkString(" ")} ->]; try by safeDispatchPure. |rewrite $open.""".stripMargin } tactic } } -case class IWpApply(applyName: String, exs: Seq[IPureAssertion]) extends IProofStep { - override def pp: String = - s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "[$$]").""" +case class IWpApply(applyName: String, exs: Seq[IPureAssertion], toInstantiate: Integer) extends IProofStep { + override def pp: String = { + val inst = (0 until toInstantiate).map(_ => "[$]").mkString(" ") + s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "$inst").""" + } } + case class IExists(e: IPureAssertion) extends IProofStep { - override def pp: String = s"iExists ${e.pp}." + override def pp: String = s"iExists ${e.ppAsPhi}." } case class IRenameSelect(pat: String, into: IIntroPattern) extends IProofStep { @@ -140,6 +144,15 @@ case class IDebug(msg: String) extends IProofStep { override def pp: String = s"(* $msg *)" } +case class IMalloc(name: ICoqName, sz: Integer) extends IProofStep { + override def pp: String = + s"""|wp_alloc ${name.pp} as "?"; try by safeDispatchPure. + |wp_pures. + |do $sz try rewrite array_cons. iSplitAllHyps. try rewrite array_nil. + |try rewrite !loc_add_assoc !Z.add_1_r. + |""".stripMargin +} + case object IFree extends IProofStep { override def pp: String = "ssl_free." } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 119946dfa..252c32177 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -4,6 +4,7 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Expressions._ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HIntSetType, HIntType, HLocType, HType, HUnknownType, HValType} import org.tygus.suslik.certification.targets.iris.logic.Assertions._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException import org.tygus.suslik.certification.translation.{CardConstructor, PredicateTranslation} import org.tygus.suslik.language.Expressions.{OpEq, _} import org.tygus.suslik.language.Statements.{Call, Load, Malloc, Store} @@ -183,9 +184,18 @@ object IrisTranslator { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) + val cardinalityParams: Map[String, HCardType] = (g.pre.sigma.chunks ++ g.post.sigma.chunks).flatMap({ + case PointsTo(loc, offset, value) => None + case Block(loc, sz) => None + case SApp(pred, args, tag, Var(name)) => Some(name, HCardType(pred)) + case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") + }).toMap + + val newGamma = g.gamma.translate ++ cardinalityParams + // We quantify over all universals, ignoring the type of function arguments - val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)) - val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar, ctx), g.gamma(v).translate)).toSeq + val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), newGamma(v.name))) + val specExistential = g.existentials.map(v => (v.translate.translate(progVarToSpecVar, ctx), newGamma(v.name))).toSeq val pre = g.pre.translate(assertionTranslator, ctx) val post = g.post.translate(assertionTranslator, ctx) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 0d5863bdf..68a5e4077 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -1,8 +1,8 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HType, HUnknownType} -import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate, IPureAssertion, ISpecVar} +import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HLocType, HType, HUnknownType} +import org.tygus.suslik.certification.targets.iris.logic.Assertions.{ICardConstructor, IFunSpec, IPredicate, IPureAssertion, ISpecVar} import org.tygus.suslik.certification.targets.iris.logic._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.normalize_renaming @@ -11,7 +11,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.certification.traversal.{Evaluator, Translator} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements.Load -import org.tygus.suslik.language.{Ident, Statements} +import org.tygus.suslik.language.{Expressions, Ident, Statements} case class PendingCall( @@ -42,6 +42,11 @@ case class IProofContext( this.copy(coqTypingCtx = coqTypingCtx ++ variables) } + def removeFromCoqContext(variables: List[Ident]): IProofContext = { + val newCtx = coqTypingCtx.filterNot(v => variables.contains(v._1)) + this.copy(coqTypingCtx = newCtx) + } + def withMappingBetween(from: Ident, to: Expr): IProofContext = { val targetType = typingContext.get(from) val toExpr = to.translate.translate(IrisTranslator.toSpecExpr, translationCtx, targetType) @@ -69,7 +74,12 @@ case class IProofContext( val newVarMap = varMap.map { case (v, expr) => (trueName(v), expr.rename(s)) } val newRenamings = renamings.map{ case (from, to) => (from, trueName(to)) } ++ s - this.copy(coqTypingCtx = newCoqTypingCtx, varMap = newVarMap, renamings = newRenamings) + val newCall = pendingCall match { + case None => None + case Some(call) => Some(call.rename(s)) + } + + this.copy(coqTypingCtx = newCoqTypingCtx, varMap = newVarMap, renamings = newRenamings, pendingCall = newCall) } def resolveExistential(ident: Ident): IPureAssertion = { @@ -158,16 +168,24 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val translSelector = selector.translate.translate(IrisTranslator.toSpecExpr, Some(tctx)) val (constructor, clause) = pred.clauses.find { case (_, cl) => cl.selector == translSelector }.get val existentials = pred.findExistentials(constructor)(clause).map( { case (name, ty) => (ren(name), ty) }) - - - - println(translSelector) - - -// val (constructor, predClause) = - - - Result(List(IDebug(value.pp)), List(withNoDeferreds(clientCtx))) + val constructorArgs = constructor.constructorArgs.map(v => (ren(v), HCardType(pred.name))) + ctx = ctx withVariablesTypes existentials.toMap + ctx = ctx withVariablesTypes constructorArgs.toMap + ctx = ctx withMappingBetween(cardVar, + ICardConstructor( + pred.name, + pred.constructorName(constructor), + constructorArgs.map { case (v, t) => ISpecVar(v, t) } + )) + val deferred : Deferred = (baseCtx: IProofContext) => { + var ctx = baseCtx + val unfold = IDebug("Defer OF " + value.pp) + val existentialSteps = existentials.map(v => IExists(ctx.resolveExistential(v._1))) + ctx = ctx removeFromCoqContext existentials.map(_._1) + ctx = ctx removeFromCoqContext constructorArgs.map(_._1) + (List (unfold) ++ existentialSteps, ctx) + } + Result(List(IDebug(value.pp)), List((List(), Some(deferred), ctx))) case SuslikProofStep.AbduceCall(_, _, _, _, freshSub, _, f, _) => var ctx = clientCtx @@ -178,7 +196,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) case SuslikProofStep.Call(subst, Statements.Call(Var(funName), _, _)) => - val (spec, ctx) = clientCtx.unqueueCall() + var (spec, ctx) = clientCtx.unqueueCall() val applyName = if (funName == irisSelf) s""""$irisSelf"""" else s"${funName}_spec" val instantiate = spec.specUniversal.map { case (ISpecVar(name, _), _) => subst.getOrElse(Var(name), Var(name)) } .map(e => e.translate.translate(IrisTranslator.toSpecExpr, ctx.translationCtx)) @@ -187,22 +205,40 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val pureIntro = spec.post.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) val spatialIntro = spec.post.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) + val toInstantiate = spec.pre.phi.conjuncts.length + spec.pre.sigma.heaplets.length // TODO: need to identify heaps for wp_apply by name? val steps = List( IDebug(value.pp), - IWpApply(applyName, instantiate), + IWpApply(applyName, instantiate, toInstantiate), IIntros(Seq(), IIdent(irisRet)), IDestruct(IIdent(irisRet), retExistentials, irisHyps), IEmp ) + ctx = ctx withVariablesTypes spec.specExistential.map{ case (v, t) => (v.name, t) }.toMap + Result(steps, List(withNoDeferreds(ctx))) // TODO: actually implement case SuslikProofStep.SubstL(Var(from), to) => val ctx = clientCtx withMappingBetween (from, to) Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) - case s:SuslikProofStep.SubstR => Result(List(IDebug(s.pp)), List(withNoDeferreds(clientCtx))) + + case SuslikProofStep.SubstR(Var(from), to) => + val ctx = clientCtx withMappingBetween (from, to) + Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) + + + case SuslikProofStep.Malloc(Var(ghostFrom), Var(ghostTo), Statements.Malloc(_, _, sz)) => + val newVar = (ghostTo, HLocType()) + var ctx = clientCtx + ctx = ctx withVariablesTypes List(newVar).toMap + ctx = ctx withMappingBetween(ghostFrom, ISpecVar(ghostTo, HLocType())) + ctx = ctx withRenaming ghostFrom -> ghostTo + val steps = List ( + IMalloc(ICoqName(ghostTo), sz) + ) + Result(steps, List(withNoDeferreds(ctx))) case SuslikProofStep.Free(_, sz) => val steps = (0 until sz).map(_ => IFree).toList @@ -234,6 +270,24 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) case SuslikProofStep.NilNotLval(vars) => withNoOp(clientCtx) // TODO: add assumption + case SuslikProofStep.PickCard(Var(from), to) => + var cardType = clientCtx.typingContext(from).asInstanceOf[HCardType] + val predType = cardType.predType + val pred = clientCtx.predMap(predType) + val emptyConstr = pred.clauses.head._1 + val secondConstr = pred.clauses.toList(1)._1 + val cardExpr = to match { + case Var(name) => ISpecVar(name, HCardType(predType)) + case Expressions.IntConst(v) if v == 0 => + ICardConstructor(predType, pred.constructorName(emptyConstr), List()) + case Expressions.BinaryExpr(Expressions.OpPlus, Var(name), Expressions.IntConst(v)) if v == 1 => + ICardConstructor( + predType, pred.constructorName(secondConstr), List( + ISpecVar(name, HCardType(predType)) + )) + } + var ctx = clientCtx withMappingBetween (from, cardExpr) + withNoOp(ctx) /** Ignored rules */ case SuslikProofStep.CheckPost(_, _) @@ -244,8 +298,6 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I | SuslikProofStep.StarPartial(_, _) | SuslikProofStep.FrameUnfold(_, _) => withNoOp(clientCtx) - - case _ => ??? } private def withNoDeferreds(ctx: IProofContext): (List[IProofStep], Option[Deferred], IProofContext) = (Nil, noDeferreds, ctx) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 7f36902ea..9df565da2 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.CertTree -import org.tygus.suslik.certification.source.SuslikProofStep +import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.IrisCertificate import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HExpr, HFunDef} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IFunSpec @@ -29,6 +29,7 @@ object Translation { val suslikTree = SuslikProofStep.of_certtree(node) val params = proc.formals.map(_.translate) + println(SuslikPrinter.pp(suslikTree)) // We have this "dummy" value to generate progToSpec for the actual context, ctx val pre_ctx = Some(ProgramTranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) @@ -50,11 +51,12 @@ object Translation { val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty, Map.empty, None) val proofStr = - try { +// try { ProofTreePrinter.pp(ProofEvaluator(funSpec).run(suslikTree, proofCtx)) - } - catch { case e => - s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } +// } +// catch { case e => +// throw e +// s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } IrisCertificate(proc.name, predicates, funDef, funSpec, proofStr) } From d0a122671280529e4e8bd1dab4dd2a8438838ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sun, 28 Feb 2021 22:14:13 +0800 Subject: [PATCH 160/211] Iris: a lot MORE stuff working --- .../targets/iris/IrisCertificate.scala | 36 ++++++++++++------- .../targets/iris/logic/Assertions.scala | 2 ++ .../targets/iris/logic/IProofStep.scala | 35 +++++++++++++++--- .../iris/translation/ProofTranslator.scala | 13 ++++--- 4 files changed, 64 insertions(+), 22 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 4783948ab..fda81959b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -19,8 +19,9 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H | |Definition null_loc : loc := {|loc_car := 0 |}. | - |Definition loc_at x lx := x = LitV (LitLoc lx). - |Definition Z_at x vx := x = LitV (LitInt vx). + |Axiom NilNotLval: + | forall x v, + | x ↦ v -∗ x ↦ v ∗ ⌜x ≠ null_loc⌝. | |Remove Hints fractional.into_sep_fractional fractional.into_sep_fractional_half : typeclass_instances. | @@ -70,8 +71,6 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H | |Local Ltac iRewriteHyp := | repeat match goal with - | | [H: loc_at ?x ?rx |- _ ] => rewrite H; clear x H; rename rx into x - | | [H: Z_at ?x ?rx |- _ ] => rewrite H; clear x H; rename rx into x | | [H: bool_decide _ = _ |- _ ] => rewrite H | | [H : _ = _ |- _ ]=> rewrite H | end. @@ -94,11 +93,11 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H |Ltac ssl_store := wp_store; iSimplContext. |Ltac ssl_free := wp_free; wp_pures; iSimplContext. |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. - |Ltac ssl_finish := iRewriteHyp; iFrame "% # ∗"; dispatchPure. + |Ltac ssl_finish := iRewriteHyp; iFrame "# ∗"; try (try iPureIntro; try safeDispatchPure). | |Ltac ssl_rewrite_term H term := |let Htn := fresh in let Heqn := fresh in - |remember term as Htn eqn:Heqn; + |remember term as Htn eqn:Heqn in * at 1; |rewrite H in Heqn; rewrite Heqn; clear Htn Heqn. | |Ltac ssl_apply_to_heap tac := @@ -106,12 +105,12 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H || [ |- _ (_ ?H) ] => tac H |end. | - |Ltac apply_last_heap H tac := + |Ltac apply_first_heap H tac := | match H with - | | (?A ∗ ?B)%I => apply_last_heap B tac - | | (?A ∗ ?B)%I => tac B - | | (?A ∗ ?B)%I => apply_last_heap A tac + | | (?A ∗ ?B)%I => apply_first_heap A tac | | (?A ∗ ?B)%I => tac A + | | (?A ∗ ?B)%I => apply_first_heap B tac + | | (?A ∗ ?B)%I => tac B | | _ => fail "apply_last_heap failed on: "H | end. | @@ -123,10 +122,21 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H | | _ => fail | end. | - |Ltac pull_out_exist := ssl_apply_to_heap ltac:(fun H => pull_out_exist_internal H). + |Ltac pull_out_exist := repeat ssl_apply_to_heap ltac:(fun H => pull_out_exist_internal H). + | + |Ltac sll_rewrite_first_heap lemma := + | ssl_apply_to_heap ltac:(fun H => apply_first_heap H + | ltac:(fun H1 => ssl_rewrite_term lemma H1) || ssl_rewrite_term lemma H). | - |Ltac sll_rewrite_last_heap lemma := ssl_apply_to_heap ltac:(fun H => - | apply_last_heap H ltac:(fun H1 => ssl_rewrite_term lemma H1)). + |Ltac tac_except_post tac := + | lazymatch goal with + | | |- envs_entails ?Δ _ => + | let Hs := pm_eval (env_lookup (INamed "Post") (env_spatial Δ)) in + | let f := fresh in let feqn := fresh in + | lazymatch Hs with + | | Some((_ ?H) _) => remember H as f eqn:feqn; tac; (rewrite feqn; clear f feqn) + | end + | end. | |""".stripMargin diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 08fa74dd4..9d55e04fc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -325,6 +325,8 @@ object Assertions { }).mkString("\n") } |end. + | + |Opaque $name. |""".stripMargin s"${predicate_definition}" } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index fa375e2b1..90dd2cc27 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -75,6 +75,13 @@ case class ILob(hypName: IIdent, coq: Seq[ICoqName]) extends IProofStep { } +case class IUnfold(pred: IPredicate, constructor: CardConstructor) extends IProofStep { + override def pp: String = { + val open = pred.openLemmaName(constructor) + s"sll_rewrite_first_heap $open." + } +} + case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExistentials: Seq[(Ident, HType)]) extends IProofStep { override def pp: String = { val learn = pred.learnLemmaName(constructor) @@ -82,21 +89,39 @@ case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExist val tactic = if (constrExistentials.isEmpty) { s""" |erewrite $learn; try by safeDispatchPure. - |rewrite $open.""".stripMargin + |tac_except_post ltac:(rewrite $open).""".stripMargin } else { // TODO: existentials introduction s""" |edestruct $learn as [${constrExistentials.map(v => v._1).mkString(" ")} ->]; try by safeDispatchPure. - |rewrite $open.""".stripMargin + |tac_except_post ltac:(rewrite $open).""".stripMargin } tactic } } -case class IWpApply(applyName: String, exs: Seq[IPureAssertion], toInstantiate: Integer) extends IProofStep { +case object IPullOutExist extends IProofStep { + override def pp: String = s"pull_out_exist." +} + +case class INilNotVal(varName: Ident, hypName: String) extends IProofStep { + override def pp: String = + s""" + |iRename select (${varName} ↦ _)%I into "$hypName". + |iDestruct (NilNotLval with "$hypName") as "[$hypName %]". + |""".stripMargin +} + +case class IWpApply(applyName: String, exs: Seq[IPureAssertion], pureToInstantiate:Integer, spatialToInstantiate: Integer) extends IProofStep { override def pp: String = { - val inst = (0 until toInstantiate).map(_ => "[$]").mkString(" ") - s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "$inst").""" + val inst = { + (0 until pureToInstantiate).map(_ => "[]") ++ + (0 until spatialToInstantiate).map(_ => "[$]") + }.mkString(" ") + val after = (0 until pureToInstantiate).map(_ => "ssl_finish.").mkString("\n") + s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "$inst"). + |$after + |""".stripMargin } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 68a5e4077..1ecccb6d8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -179,11 +179,14 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I )) val deferred : Deferred = (baseCtx: IProofContext) => { var ctx = baseCtx - val unfold = IDebug("Defer OF " + value.pp) - val existentialSteps = existentials.map(v => IExists(ctx.resolveExistential(v._1))) + val unfold = IUnfold(pred, constructor) + val existentialSteps = existentials.flatMap(v => + List( + IPullOutExist, + IExists(ctx.resolveExistential(v._1)))) ctx = ctx removeFromCoqContext existentials.map(_._1) ctx = ctx removeFromCoqContext constructorArgs.map(_._1) - (List (unfold) ++ existentialSteps, ctx) + (List (unfold) ++ existentialSteps ++ List(IFinish), ctx) } Result(List(IDebug(value.pp)), List((List(), Some(deferred), ctx))) @@ -268,7 +271,9 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I Result(List(IStore), List(withNoDeferreds(clientCtx))) case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) - case SuslikProofStep.NilNotLval(vars) => withNoOp(clientCtx) // TODO: add assumption + case SuslikProofStep.NilNotLval(vars) => + val steps = vars.map { case Var(name) => INilNotVal(name, clientCtx.freshHypName() )} + Result(steps, List(withNoDeferreds(clientCtx))) case SuslikProofStep.PickCard(Var(from), to) => var cardType = clientCtx.typingContext(from).asInstanceOf[HCardType] From 721138706f43868478dd1fefe1d254ae9de785a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Sun, 28 Feb 2021 23:36:57 +0800 Subject: [PATCH 161/211] Iris: almost all benchmarks work --- .../targets/iris/logic/Assertions.scala | 6 ++-- .../targets/iris/logic/IProofStep.scala | 32 +++++++++++++++++-- .../iris/translation/ProofTranslator.scala | 19 +++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 9d55e04fc..c346ed088 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -283,15 +283,15 @@ object Assertions { def ppEqualityTerm(cons: CardConstructor): String = if (cons.constructorArgs.isEmpty) { - s"${ppPred} ${cardinalityParam} = ${ppPred} ${predicate.constructorName(cons)}" + s"${cardinalityParam} = ${predicate.constructorName(cons)}" } else { - s"∃ ${cons.constructorArgs.mkString(" ")}, ${ppPred} ${cardinalityParam} = ${ppPred} (${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")})" + s"∃ ${cons.constructorArgs.mkString(" ")}, ${cardinalityParam} = (${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")})" } s"Lemma ${predicate.learnLemmaName(cardConstructor)} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + s"${cardinalityParam}:\n" + - s"${pclause.selector.ppAsPhi} -> ${ppEqualityTerm(cardConstructor)}.\n" + + s"${ppPred} ${cardinalityParam} ⊢ ${ppPred} ${cardinalityParam} ∗ ⌜${pclause.selector.ppAsPhi} -> ${ppEqualityTerm(cardConstructor)}⌝.\n" + s"Proof. Admitted.\n" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 90dd2cc27..de93b1473 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -82,18 +82,44 @@ case class IUnfold(pred: IPredicate, constructor: CardConstructor) extends IProo } } -case class IOpenCard(pred: IPredicate, constructor: CardConstructor, constrExistentials: Seq[(Ident, HType)]) extends IProofStep { +case class IOpenCard(pred: IPredicate, + constructor: CardConstructor, + constrExistentials: Seq[(Ident, HType)], + appHypIdent: IIdent, + purePart: IPure, + ) extends IProofStep { + + def toDestructPattern(ls: List[String]): String = { + ls match { + case Nil => "" + case ::(x, Nil) => s"[$x]" + case ::(x, ::(y, Nil)) => s"[$x $y]" + case ::(x, xs) => s"[$x ${toDestructPattern(xs)}]" + } + +// (base, ls) match { +// case (None, :: (vara, :: (varb, rest))) => toDestructPattern(Some(s"[${varb} ${vara}]"))(rest) +// case (Some(base), ::((vara), rest)) => +// toDestructPattern(Some(s"[${vara} ${base}]"))(rest) +// case (Some(base), Nil) => base +// } + } override def pp: String = { val learn = pred.learnLemmaName(constructor) val open = pred.openLemmaName(constructor) val tactic = if (constrExistentials.isEmpty) { s""" - |erewrite $learn; try by safeDispatchPure. + |iDestruct ($learn with "${appHypIdent.pp}") as "[${appHypIdent.pp} ${purePart.pp}]". + |rewrite ${purePart.name}; last by safeDispatchPure. |tac_except_post ltac:(rewrite $open).""".stripMargin } else { // TODO: existentials introduction s""" - |edestruct $learn as [${constrExistentials.map(v => v._1).mkString(" ")} ->]; try by safeDispatchPure. + |iDestruct ($learn with "${appHypIdent.pp}") as "[${appHypIdent.pp} ${purePart.pp}]". + | + |edestruct ${purePart.name} as ${ + toDestructPattern(((constrExistentials.map(v => v._1)).toList ++ List("->"))) + }; first by safeDispatchPure. |tac_except_post ltac:(rewrite $open).""".stripMargin } tactic diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 1ecccb6d8..9c71700bb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -123,7 +123,10 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I (List(IFindApply) ++ instantiate ++ List(IFinish), ctx) } - val lobInduction = ILob(IIdent(irisSelf), spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) ++ List(ICoqName(irisPhi))) + val lobInduction = ILob(IIdent(irisSelf), + spec.specUniversal.map({ case (v, _) => ICoqName(v.name) }) + ++ pureIntro.map(v => ICoqName(v.name)) + ++ List(ICoqName(irisPhi))) val steps = List(intro, IRewriteHyp, lobInduction, IBegin) Result(steps, List((Nil, Some(deferred), ctx))) @@ -132,6 +135,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val app = predApp.translate(IrisTranslator.predAppTranslator, ctx.translationCtx) val appHyp = ctx.freshHypName() val appHypIdent = IIdent(appHyp) + val pureHyp = IPure(appHyp + "_eqn") val steps = List ( // Give the heaplet containing the predicate application a fresh name, appHyp IRenameSelect(app.pp, appHypIdent), @@ -150,7 +154,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) // Open and destruct the predicate appropriately in each clause val immed = List( - IOpenCard(pred, constructor, constructorExistentials), + IOpenCard(pred, constructor, constructorExistentials, appHypIdent, pureHyp), IDestruct(appHypIdent, coqIntro, irisHyps) ) val newCtx = ctx withVariablesTypes (existentials ++ constructorExistentials).toMap @@ -208,14 +212,17 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val pureIntro = spec.post.phi.conjuncts.map(_ => IPure(ctx.freshHypName())) val spatialIntro = spec.post.sigma.heaplets.map(_ => IIdent(ctx.freshHypName())) val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) - val toInstantiate = spec.pre.phi.conjuncts.length + spec.pre.sigma.heaplets.length + val pureInst = spec.pre.phi.conjuncts.length + val spatialInst = spec.pre.sigma.heaplets.length + + val ret = ctx.freshHypName() // TODO: need to identify heaps for wp_apply by name? val steps = List( IDebug(value.pp), - IWpApply(applyName, instantiate, toInstantiate), - IIntros(Seq(), IIdent(irisRet)), - IDestruct(IIdent(irisRet), retExistentials, irisHyps), + IWpApply(applyName, instantiate, pureInst, spatialInst), + IIntros(Seq(), IIdent(ret)), + IDestruct(IIdent(ret), retExistentials, irisHyps), IEmp ) ctx = ctx withVariablesTypes spec.specExistential.map{ case (v, t) => (v.name, t) }.toMap From b9550e9c4f089121052e145cf966e5f1fc1ce164 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 1 Mar 2021 09:53:02 +0800 Subject: [PATCH 162/211] rearranged order of tests --- .../targets/vst/VSTCertificationBenchmarks.scala | 9 ++++++--- .../targets/vst/translation/VSTProofTranslator.scala | 7 +++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala index 5e74764c8..e1257306a 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -9,10 +9,13 @@ object VSTCertificationBenchmarks extends CertificationBenchmarks { def main(args: Array[String]): Unit = { initLog() - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/ints") runAllTestsFromDir("certification/sll-bounds") - runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/bst") + runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/srtl") + runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/ints") + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index adc07e28f..868091ea0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -277,7 +277,8 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case SuslikProofStep.EmpRule(label) => Result(List(ForwardEntailer), List()) - case SuslikProofStep.Inconsistency(label) => ??? + case SuslikProofStep.Inconsistency(label) => + Result(List(ForwardEntailer), List()) case SuslikProofStep.Write(stmt@Statements.Store(to, offset, e)) => e match { @@ -325,7 +326,9 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl assignments.foldLeft(clientContext)({ case (ctx, (from, to_expr)) => ctx with_mapping_between(from.name, to_expr) }) with_no_op(new_context) - case SuslikProofStep.PickArg(from, to) => ??? + case SuslikProofStep.PickArg(Var(from), to) => + val new_ctx = clientContext with_mapping_between (from,to) + with_no_op(new_ctx) case SuslikProofStep.PickCard(Var(from), to) => // Pick card is a hack - not sure what the best way to do this is. From 1c082addf00f1d8cccbf2af7558165c1b05dd7b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 11:59:52 +0800 Subject: [PATCH 163/211] Iris: tree_flatten now works --- .../targets/iris/IrisCertificate.scala | 3 +- .../targets/iris/heaplang/Expressions.scala | 6 ++-- .../targets/iris/logic/Assertions.scala | 7 ++-- .../targets/iris/logic/IProofStep.scala | 10 ++++-- .../iris/translation/IrisTranslator.scala | 33 ++++++++++++++++--- .../iris/translation/ProgramTranslator.scala | 10 ++++-- .../iris/translation/ProofTranslator.scala | 15 +++++++-- .../iris/translation/Translation.scala | 19 ++++++----- 8 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index fda81959b..70893650b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -4,7 +4,7 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} -case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { +case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, helperSpecs: List[IFunSpec], funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { val target: CertificationTarget = Iris() private val prelude = @@ -146,6 +146,7 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H b.append(predicates.map(_.pp).mkString("\n")) b.append("\n") b.append(predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")) + b.append(helperSpecs.map(_.pp).mkString("\n")) b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala index cfcdcac48..6fa2124e3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/heaplang/Expressions.scala @@ -139,8 +139,10 @@ object Expressions { case class HAllocN(n: HExpr, e: HExpr) extends HExpr { override def pp: String = s"AllocN (${n.pp}) (${e.pp})" } - case class HCall(name: HExpr, params: Seq[HExpr]) extends HExpr { - override def pp: String = s"${name.pp} ${params.map(p => p.pp).mkString(" ")}" + case class HCall(name: HExpr, params: Seq[HExpr], selfCall: Boolean = true) extends HExpr { + override def pp: String = + // if it's a selfCall, we need to print the function name in quotes, otherwise no quotes + s"${if(selfCall) name.pp else name.asInstanceOf[HProgVar].name} ${params.map(p => p.pp).mkString(" ")}" } case class HFunDef(name: String, params: Seq[HProgVar], body: ProofTree[HExpr]) extends PrettyPrinting { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index c346ed088..024d7161e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -211,7 +211,8 @@ object Assertions { specUniversal: Seq[(IQuantifiedVar, HType)], specExistential: Seq[(IQuantifiedVar, HType)], pre: IAssertion, - post: IAssertion + post: IAssertion, + helper: Boolean=false ) extends ISpecification { override def pp: String = { @@ -221,8 +222,10 @@ object Assertions { else "" val universal = specUniversal + val prepend = if (helper) s"Axiom ${fname}: val.\n" else "" + prepend ++ s""" - |Lemma ${fname}_spec : + |${if (helper) "Axiom" else "Lemma"} ${fname}_spec : |∀ ${universal.map({ case (v, ty) => s"(${v.pp} : ${ty.pp})"}).mkString(" ")}, |{{{ ${pre.pp} }}} | ${fname} ${funArgs.map(v => s"#${v._1.pp}").mkString(" ")} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index de93b1473..3a72a15ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -138,14 +138,17 @@ case class INilNotVal(varName: Ident, hypName: String) extends IProofStep { |""".stripMargin } -case class IWpApply(applyName: String, exs: Seq[IPureAssertion], pureToInstantiate:Integer, spatialToInstantiate: Integer) extends IProofStep { +case class IWpApply(applyName: String, exs: Seq[IPureAssertion], + pureToInstantiate:Integer, + spatialToInstantiate: Integer, + applyLemma: Boolean=false) extends IProofStep { override def pp: String = { val inst = { (0 until pureToInstantiate).map(_ => "[]") ++ (0 until spatialToInstantiate).map(_ => "[$]") }.mkString(" ") val after = (0 until pureToInstantiate).map(_ => "ssl_finish.").mkString("\n") - s"""wp_apply ($applyName $$! ${exs.map(e => s"(${e.pp})").mkString(" ")} with "$inst"). + s"""wp_apply ($applyName ${if (applyLemma) "" else "$!"} ${exs.map(e => s"(${e.pp})").mkString(" ")} with "$inst"). |$after |""".stripMargin } @@ -192,7 +195,8 @@ case class IIf(hyp: ICoqName) extends IProofStep { } case class IDebug(msg: String) extends IProofStep { - override def pp: String = s"(* $msg *)" + override def pp: String = "" + // override def pp: String = s"(* $msg *)" } case class IMalloc(name: ICoqName, sz: Integer) extends IProofStep { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala index 252c32177..3aefcd727 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/IrisTranslator.scala @@ -52,7 +52,6 @@ object IrisTranslator { case BinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) case IfThenElse(cond, t, f) => HIfThenElse(visit(cond), visit(t), visit(f)) // case OverloadedBinaryExpr(op, left, right) => HBinaryExpr(op.translate, visit(left), visit(right)) - case _ => ??? } // FIXME: we should probably guarantee that the context gets passed also during program translation @@ -62,7 +61,8 @@ object IrisTranslator { } implicit val progVarTranslator: IrisTranslator[Var, HProgVar] = (pv, _, _) => HProgVar(pv.name) - implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, ctx, _) => ISpecVar(hv.name, ctx.get.hctx(hv.name)) + implicit val progVarToSpecVar: IrisTranslator[HProgVar, ISpecVar] = (hv, ctx, _) => + ISpecVar(hv.name, ctx.get.hctx(hv.name)) implicit val typeTranslator: IrisTranslator[SSLType, HType] = (value, _, _) => value match { case IntType => HIntType() @@ -107,7 +107,7 @@ object IrisTranslator { implicit val phiTranslator: IrisTranslator[PFormula, IPureAssertion] = (f, ctx, _) => { assert(ctx.isDefined) - IAnd(f.conjuncts.map(_.translate.translate(toSpecExpr, ctx)).toSeq) + IAnd(f.conjuncts.map(_.translate(exprTranslator, ctx).translate(toSpecExpr, ctx)).toSeq) } /*** @@ -180,6 +180,29 @@ object IrisTranslator { IAssertion(f.phi.translate(phiTranslator, ctx), f.sigma.translate(sigmaTranslator, ctx)) } + implicit val funSpecToFunSpecTranslator: IrisTranslator[FunSpec, IFunSpec] = (g, ctx, _) => { + assert(ctx.isDefined) + val params = g.params.map { case (v, t) => (v.translate, t.translate) } + + val cardinalityParams: Map[String, HCardType] = (g.pre.sigma.chunks ++ g.post.sigma.chunks).flatMap({ + case PointsTo(loc, offset, value) => None + case Block(loc, sz) => None + case SApp(pred, args, tag, Var(name)) => Some(name, HCardType(pred)) + case _ => throw TranslationException("ERR: Expecting all predicate applications to be abstract variables") + }).toMap + + val newGamma = g.gamma(ctx.get.env).translate ++ cardinalityParams + + // We quantify over all universals, ignoring the type of function arguments + val specUniversal = g.universals.map(v => (v.translate.translate(progVarToSpecVar, ctx), newGamma(v.name))) + val specExistential = g.existentials().map(v => (v.translate.translate(progVarToSpecVar, ctx), newGamma(v.name))).toSeq + + val pre = g.pre.translate(assertionTranslator, ctx) + val post = g.post.translate(assertionTranslator, ctx) + IFunSpec(g.name, params.map(x => (x._1.translate(progVarToSpecVar, ctx), x._2)), + specUniversal.toSeq, specExistential, pre, post, helper = true) + } + implicit val goalToFunSpecTranslator: IrisTranslator[Goal, IFunSpec] = (g, ctx, _) => { assert(ctx.isDefined) val params = g.programVars.map(v => (v.translate, g.gamma(v).translate)) @@ -212,12 +235,12 @@ object IrisTranslator { } override def translateExpression(context: Map[Ident, HType])(expr: Expr): IPureAssertion = { - val predCtx = Some(ProgramTranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + val predCtx = Some(ProgramTranslationContext(ctx.get.env, ctx.get.proc, predicate.params.toMap, Map.empty, context)) expr.translate(exprTranslator, predCtx).translate(toSpecExpr, predCtx) } override def translateHeaplets(context: Map[Ident, HType])(heaplets: List[Heaplet]): List[ISpatialAssertion] = { - val predCtx = Some(ProgramTranslationContext(ctx.get.env, predicate.params.toMap, Map.empty, context)) + val predCtx = Some(ProgramTranslationContext(ctx.get.env, ctx.get.proc, predicate.params.toMap, Map.empty, context)) heaplets.map(_.translate(heapleatTranslator, predCtx)) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala index ca5be5319..5c94a6137 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.iris.translation import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset, HProgVar} +import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.{HBinaryExpr, HCall, HExpr, HFree, HGuarded, HIf, HLitLoc, HLitUnit, HNoOp, HOpOffset, HProgVar} import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable @@ -9,9 +9,10 @@ import org.tygus.suslik.certification.traversal.Evaluator.ClientContext import org.tygus.suslik.certification.traversal.Translator import org.tygus.suslik.certification.traversal.Translator.Result import org.tygus.suslik.language.Ident +import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.{Environment, Gamma} -case class ProgramTranslationContext(env: Environment, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar], hctx: Map[Ident, HType]) extends ClientContext[HExpr] +case class ProgramTranslationContext(env: Environment, proc: Procedure, gamma: Gamma, pts: Map[HProgVar, IQuantifiedVar], hctx: Map[Ident, HType]) extends ClientContext[HExpr] /** * Extract a HeapLang program directly from the SSL proof. @@ -39,7 +40,10 @@ object ProgramTranslator extends Translator[SuslikProofStep, HExpr, ProgramTrans val v = stmt.v.translate val addr = (sz: Int) => if (sz == 0) v else HBinaryExpr(HOpOffset, v, HLitLoc(sz)) (0 until sz).map(i => HFree(addr(i))).toList - case SuslikProofStep.Call(_, stmt) => List(stmt.translate) + case SuslikProofStep.Call(_, stmt) => + val isSelfCall = stmt.fun.name == ctx.proc.name + val translated = stmt.translate.copy(selfCall = isSelfCall) + List(translated) case _ => List(HNoOp) } Result(stmts, List(withNoDeferred)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 9c71700bb..c7ad590ae 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -204,7 +204,8 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I case SuslikProofStep.Call(subst, Statements.Call(Var(funName), _, _)) => var (spec, ctx) = clientCtx.unqueueCall() - val applyName = if (funName == irisSelf) s""""$irisSelf"""" else s"${funName}_spec" + val isSelfCall = (funName == irisSelf) + val applyName = if (isSelfCall) s""""$irisSelf"""" else s"${funName}_spec" val instantiate = spec.specUniversal.map { case (ISpecVar(name, _), _) => subst.getOrElse(Var(name), Var(name)) } .map(e => e.translate.translate(IrisTranslator.toSpecExpr, ctx.translationCtx)) @@ -216,11 +217,15 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val spatialInst = spec.pre.sigma.heaplets.length val ret = ctx.freshHypName() - + // If we're self-calling, we destructed our precondition before doing iLob + // On the other hand, if we're using a spec, the precondition is a single spatial assertion + val apply = if (isSelfCall) + IWpApply(applyName, instantiate, pureInst, spatialInst) + else IWpApply(applyName, instantiate, 0, 1, applyLemma = true) // TODO: need to identify heaps for wp_apply by name? val steps = List( IDebug(value.pp), - IWpApply(applyName, instantiate, pureInst, spatialInst), + apply, IIntros(Seq(), IIdent(ret)), IDestruct(IIdent(ret), retExistentials, irisHyps), IEmp @@ -282,6 +287,10 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val steps = vars.map { case Var(name) => INilNotVal(name, clientCtx.freshHypName() )} Result(steps, List(withNoDeferreds(clientCtx))) +// case SuslikProofStep.PickArg(Var(from), to) => +// val newCtx = clientCtx withMappingBetween (from,to) +// withNoOp(newCtx) + case SuslikProofStep.PickCard(Var(from), to) => var cardType = clientCtx.typingContext(from).asInstanceOf[HCardType] val predType = cardType.predType diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 9df565da2..70f368056 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -32,10 +32,10 @@ object Translation { println(SuslikPrinter.pp(suslikTree)) // We have this "dummy" value to generate progToSpec for the actual context, ctx - val pre_ctx = Some(ProgramTranslationContext(env, node.goal.gamma, Map.empty, node.goal.gamma.translate)) - val progToSpec = params.map(p => (p, p.translate(progVarToSpecVar, pre_ctx))) + val pre_ctx = ProgramTranslationContext(env, proc, node.goal.gamma, Map.empty, node.goal.gamma.translate) + val progToSpec = params.map(p => (p, p.translate(progVarToSpecVar, Some(pre_ctx)))) - val ctx = ProgramTranslationContext(env, node.goal.gamma, progToSpec.toMap, node.goal.gamma.translate) + val ctx = pre_ctx.copy(pts = progToSpec.toMap) val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList predicates.foreach(p => println(p.pp)) @@ -44,11 +44,14 @@ object Translation { val funDef = HFunDef(proc.name, params, progTree) val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) - val predMap = predicates.map(p => (p.name, p)).toMap - // TODO: add support for helper functions - val specMap = List((funSpec.fname, funSpec)).toMap - + val helperSpecs = env.functions.map { case (fname, spec) => + val hPreCtx = ProgramTranslationContext(env, proc, spec.gamma(env), Map.empty, spec.gamma(env).translate) + val hProgToSpec = spec.params.map(_.translate).map(p => (p, p.translate(progVarToSpecVar, Some(hPreCtx)))) + val hCtx = hPreCtx.copy(pts = hProgToSpec.toMap) + (fname, spec.translate(funSpecToFunSpecTranslator, Some(hCtx))) + } + val specMap = Map(funSpec.fname -> funSpec) ++ helperSpecs val proofCtx = IProofContext(0, ctx, predMap, specMap, Map.empty, Map.empty, Map.empty, None) val proofStr = // try { @@ -58,6 +61,6 @@ object Translation { // throw e // s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } - IrisCertificate(proc.name, predicates, funDef, funSpec, proofStr) + IrisCertificate(proc.name, predicates, funDef, helperSpecs.values.toList, funSpec, proofStr) } } \ No newline at end of file From 39f2850f26b74a2bd1d5eb869a387ab50d9ba61e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 15:47:49 +0800 Subject: [PATCH 164/211] Iris: move tactics into separate repo --- .../targets/iris/IrisCertificate.scala | 121 +----------------- .../targets/iris/logic/IProofStep.scala | 5 +- .../iris/translation/ProofTranslator.scala | 9 +- 3 files changed, 8 insertions(+), 127 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 70893650b..a6f3a0e6b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -8,136 +8,19 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H val target: CertificationTarget = Iris() private val prelude = - s"""From iris.program_logic Require Export weakestpre. + s"""From SSL_Iris Require Import core. + |From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. |From iris.heap_lang Require Import lang notation proofmode. |From iris_string_ident Require Import ltac2_string_ident. - | |From Hammer Require Import Hammer. |Context `{!heapG Σ}. |Set Default Proof Using "Type". | - |Definition null_loc : loc := {|loc_car := 0 |}. - | |Axiom NilNotLval: | forall x v, | x ↦ v -∗ x ↦ v ∗ ⌜x ≠ null_loc⌝. | - |Remove Hints fractional.into_sep_fractional fractional.into_sep_fractional_half : typeclass_instances. - | - |(* This is exactly iAndDestruct in ltac_tactics.v, which is not exported. *) - |Local Tactic Notation "iAndDestruct" constr(H) "as" constr(H1) constr(H2) := - | eapply tac_and_destruct with H _ H1 H2 _ _ _; (* (i:=H) (j1:=H1) (j2:=H2) *) - | [pm_reflexivity - | |pm_reduce; iSolveTC - | |pm_reduce; - | lazymatch goal with - | | |- False => fail - | | _ => idtac - | end]. - | - |Local Ltac movePure := - | iStartProof; - | let rec go Hs := match Hs with [] => idtac | ?H :: ?Hs => (try iDestruct H as "%"); go Hs end in - | match goal with - | | |- envs_entails ?Δ _ => - | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs - | end. - | - |Local Ltac iSplitAllHyps := - | iStartProof; - | let rec go Hs success := - | match Hs with - | [] => match success with | true => idtac | false => fail end - | | ?H :: ?Hs => let Hn := iFresh in (iAndDestruct H as H Hn; go Hs true) || go Hs success - | end in - | repeat match goal with - | | |- envs_entails ?Δ _ => - | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs false - | end; - | repeat match goal with - | | [H: _ /\\ _ |- _ ] => destruct H - | end. - | - |Local Ltac iFindApply := - | iStartProof; - | let rec go Hs := - | match Hs with [] => idtac | ?H :: ?Hs => - | try iApply H; go Hs end in - | match goal with - | | |- envs_entails ?Δ _ => - | let Hs := eval cbv in (env_dom (env_spatial Δ)) in go Hs - | end. - | - |Local Ltac iRewriteHyp := - | repeat match goal with - | | [H: bool_decide _ = _ |- _ ] => rewrite H - | | [H : _ = _ |- _ ]=> rewrite H - | end. - | - |Local Ltac iSimplNoSplit := - | (repeat wp_pures); movePure; iRewriteHyp. (* iSimpl in "# ∗"; iSimpl. *) - | - |Local Ltac iSimplContext := iSimplNoSplit; try iSplitAllHyps; iSimplNoSplit. - |Ltac dispatchPure := iRewriteHyp; try lia; try sauto; done. - | - |Ltac safeDispatchPure := - |lazymatch goal with - || [ |- (envs_entails _) _ ] => idtac - || _ => by dispatchPure - |end. - | - |Ltac ssl_begin := (wp_rec; repeat wp_let); iSimplContext. - |Ltac ssl_let := wp_let. - |Ltac ssl_load := wp_load; wp_let; iSimplContext. - |Ltac ssl_store := wp_store; iSimplContext. - |Ltac ssl_free := wp_free; wp_pures; iSimplContext. - |Ltac ssl_if H := case_bool_decide as H; wp_if; iSimplContext. - |Ltac ssl_finish := iRewriteHyp; iFrame "# ∗"; try (try iPureIntro; try safeDispatchPure). - | - |Ltac ssl_rewrite_term H term := - |let Htn := fresh in let Heqn := fresh in - |remember term as Htn eqn:Heqn in * at 1; - |rewrite H in Heqn; rewrite Heqn; clear Htn Heqn. - | - |Ltac ssl_apply_to_heap tac := - |match goal with - || [ |- _ (_ ?H) ] => tac H - |end. - | - |Ltac apply_first_heap H tac := - | match H with - | | (?A ∗ ?B)%I => apply_first_heap A tac - | | (?A ∗ ?B)%I => tac A - | | (?A ∗ ?B)%I => apply_first_heap B tac - | | (?A ∗ ?B)%I => tac B - | | _ => fail "apply_last_heap failed on: "H - | end. - | - |Ltac pull_out_exist_internal H := - | lazymatch H with - | | ((bi_exist _) ∗ _)%I => rewrite bi.sep_exist_r - | | (_ ∗ (bi_exist ?Q))%I => rewrite bi.sep_exist_l - | | (?A ∗ ?B)%I => pull_out_exist_internal A || pull_out_exist_internal B - | | _ => fail - | end. - | - |Ltac pull_out_exist := repeat ssl_apply_to_heap ltac:(fun H => pull_out_exist_internal H). - | - |Ltac sll_rewrite_first_heap lemma := - | ssl_apply_to_heap ltac:(fun H => apply_first_heap H - | ltac:(fun H1 => ssl_rewrite_term lemma H1) || ssl_rewrite_term lemma H). - | - |Ltac tac_except_post tac := - | lazymatch goal with - | | |- envs_entails ?Δ _ => - | let Hs := pm_eval (env_lookup (INamed "Post") (env_spatial Δ)) in - | let f := fresh in let feqn := fresh in - | lazymatch Hs with - | | Some((_ ?H) _) => remember H as f eqn:feqn; tac; (rewrite feqn; clear f feqn) - | end - | end. - | |""".stripMargin def pp : String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index 3a72a15ea..cf69a10a8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -78,7 +78,7 @@ case class ILob(hypName: IIdent, coq: Seq[ICoqName]) extends IProofStep { case class IUnfold(pred: IPredicate, constructor: CardConstructor) extends IProofStep { override def pp: String = { val open = pred.openLemmaName(constructor) - s"sll_rewrite_first_heap $open." + s"ssl_rewrite_first_heap $open." } } @@ -195,8 +195,7 @@ case class IIf(hyp: ICoqName) extends IProofStep { } case class IDebug(msg: String) extends IProofStep { - override def pp: String = "" - // override def pp: String = s"(* $msg *)" + override def pp: String = s"(* $msg *)" } case class IMalloc(name: ICoqName, sz: Integer) extends IProofStep { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index c7ad590ae..7b1af5b77 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -192,7 +192,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I ctx = ctx removeFromCoqContext constructorArgs.map(_._1) (List (unfold) ++ existentialSteps ++ List(IFinish), ctx) } - Result(List(IDebug(value.pp)), List((List(), Some(deferred), ctx))) + Result(List(), List((List(), Some(deferred), ctx))) case SuslikProofStep.AbduceCall(_, _, _, _, freshSub, _, f, _) => var ctx = clientCtx @@ -200,7 +200,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I val s = normalize_renaming(freshSub.map { case (Var(name_from), Var(name_to)) => (name_from, name_to) }) val newSpec = funSpec.rename(s) ctx = ctx withQueuedCall(newSpec) - Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) + Result(List(), List(withNoDeferreds(ctx))) case SuslikProofStep.Call(subst, Statements.Call(Var(funName), _, _)) => var (spec, ctx) = clientCtx.unqueueCall() @@ -224,7 +224,6 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I else IWpApply(applyName, instantiate, 0, 1, applyLemma = true) // TODO: need to identify heaps for wp_apply by name? val steps = List( - IDebug(value.pp), apply, IIntros(Seq(), IIdent(ret)), IDestruct(IIdent(ret), retExistentials, irisHyps), @@ -237,11 +236,11 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I // TODO: actually implement case SuslikProofStep.SubstL(Var(from), to) => val ctx = clientCtx withMappingBetween (from, to) - Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) + Result(List(), List(withNoDeferreds(ctx))) case SuslikProofStep.SubstR(Var(from), to) => val ctx = clientCtx withMappingBetween (from, to) - Result(List(IDebug(value.pp)), List(withNoDeferreds(ctx))) + Result(List(), List(withNoDeferreds(ctx))) case SuslikProofStep.Malloc(Var(ghostFrom), Var(ghostTo), Statements.Malloc(_, _, sz)) => From 80273989e9f542e46b013af09a344ab49a17932c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 1 Mar 2021 16:20:43 +0800 Subject: [PATCH 165/211] HTT: Make SynthesisRunner run again --- .../certification/targets/htt/HTTCertificate.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index 8333892bc..a9340071b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -9,10 +9,16 @@ case class HTTCertificate(name: String, predicates: List[CInductivePredicate], s // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') - def pp: String = { + def pp: String = + s"""${HTT.prelude} + |${predicates.map(_.pp).mkString("\n\n")} + |$ppMain + |""".stripMargin + + def ppExternalDefs: String = s"${HTT.prelude}\nRequire Import common.\n\n$ppMain" + + private def ppMain: String = { val builder = new StringBuilder - builder.append(HTT.prelude) - builder.append("Load common.\n\n") if (hints.nonEmpty) { builder.append(hints.map(_.pp).mkString("\n")) @@ -33,6 +39,6 @@ case class HTTCertificate(name: String, predicates: List[CInductivePredicate], s builder.toString } - override def outputs: List[CertificateOutput] = List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), pp)) + override def outputs: List[CertificateOutput] = List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), ppExternalDefs)) } From 56ef76822ae504d96e0a6823b2ac9312fd9e9c1a Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 1 Mar 2021 16:26:24 +0800 Subject: [PATCH 166/211] VST:no more axioms!!! --- .../vst/VSTCertificationBenchmarks.scala | 9 ++-- .../targets/vst/logic/ProofTerms.scala | 48 +++++++++++++++++-- .../targets/vst/logic/VSTProofStep.scala | 2 +- .../translation/ProofSpecTranslation.scala | 20 +++++--- .../synthesis/CertificationBenchmarks.scala | 6 ++- 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala index e1257306a..9a4803144 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -9,13 +9,12 @@ object VSTCertificationBenchmarks extends CertificationBenchmarks { def main(args: Array[String]): Unit = { initLog() + runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/sll-bounds") + runAllTestsFromDir("certification/ints") runAllTestsFromDir("certification/bst") - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/tree") runAllTestsFromDir("certification/srtl") - runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/ints") - } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala index 1cd1b4444..d989210cd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/ProofTerms.scala @@ -143,6 +143,24 @@ object ProofTerms { sub_constructor: Map[String, CardConstructor]) extends GenericPredicateClause[Expressions.ProofCExpr, VSTHeaplet](pure, spatial, sub_constructor) { + def guaranteed_valid_pointers (existentials: List[(String, _)]) : Set[String] = { + def valid_ptr_expression (expr: ProofCExpr) : Set[String] = expr match { + case Expressions.ProofCBinaryExpr(Expressions.ProofCOpAnd, left, right) => valid_ptr_expression(left).union(valid_ptr_expression(right)) + case Expressions.ProofCBinaryExpr(Expressions.ProofCOpOr, left, right) => valid_ptr_expression(left).intersect(valid_ptr_expression(right)) + case Expressions.ProofCBinaryExpr(Expressions.ProofCOpPtrValEq, Expressions.ProofCVar(left, _), Expressions.ProofCNullval) => Set(left) + case Expressions.ProofCBinaryExpr(Expressions.ProofCOpPtrValEq, Expressions.ProofCNullval, Expressions.ProofCVar(right, _)) => Set(right) + case _ => Set() + } + val exists_names = existentials.map(v => v._1).toSet + val pure_valid_ptrs = pure.flatMap(valid_ptr_expression).toSet + val spatial_valid_ptrs = spatial.flatMap({ + case Formulae.CDataAt(ProofCVar(name, _), elems) => Some(name) + case _ => None + }).toSet + pure_valid_ptrs.union(spatial_valid_ptrs).diff(exists_names) + } + + def rename(renaming: Map[String, String]) = VSTPredicateClause( pure.map(_.rename(renaming)), @@ -169,6 +187,26 @@ object ProofTerms { override val clauses: Map[CardConstructor, VSTPredicateClause]) extends GenericPredicate[Expressions.ProofCExpr, VSTHeaplet, VSTType](name, params, existentials, clauses) { + + def valid_pointer_params : Set[String] = + clauses.map({ + case (constructor, clause) => + val existentials = findExistentials(constructor)(clause) + clause.guaranteed_valid_pointers(existentials) + }) match { + case ::(h : Set[String], t) => t.foldLeft(h)({ case ((acc: Set[String], st: Set[String])) => acc.intersect(st) }) + } + + def is_guaranteed_valid_pointer(name: String) : Boolean = valid_pointer_params.contains(name) + + def valid_params : List[(String, VSTType)] = params.flatMap { + case (name, ty) => ty match { + case Types.CoqPtrValType => + if(is_guaranteed_valid_pointer(name)) { Some ((name,ty)) } else { None } + case _ => Some((name, ty)) + } + } + /** * Returns a list of the local facts for this predicate * @return @@ -183,7 +221,7 @@ object ProofTerms { def get_helpers: List[VSTPredicateHelper] = { val local_facts = VSTPredicateHelper.LocalFacts(this) val unfolding_lemmas = clauses.map({case (constructor, clause) => HelpUnfold(this, constructor, clause)}) - params.flatMap({ + valid_params.flatMap({ case (param, CoqPtrValType) => val valid_lemma = VSTPredicateHelper.ValidPointer(name, this.formalParams, param) List( @@ -207,6 +245,7 @@ object ProofTerms { } } ${ ((clause_existentials.flatMap(v => v._2 match { + case Types.CoqPtrValType => Some(s"!!(is_pointer_or_null ${v._1})") case Types.CoqZType => Some(s"!!(Int.min_signed <= ${v._1} <= Int.max_signed)") case _ => None })) ++ @@ -288,7 +327,7 @@ object ProofTerms { case class ValidPointer(predicate: String, args: List[String], ptr: String) extends VSTPredicateHelper { override def pp: String = - s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. Admitted." + s"Lemma ${lemma_name} ${args.mkString(" ")}: ${predicate} ${args.mkString(" ")} |-- valid_pointer ${ptr}. Proof. destruct self_card; simpl; entailer; entailer!; eauto. Qed." def lemma_name: String = s"${predicate}_${ptr}_valid_pointerP" } @@ -331,12 +370,11 @@ object ProofTerms { ( predicate.clauses.toList.map({ case (cons, pred) => clause_fact(cons, pred) }) ++ pure_constraints.map(_.pp) -// predicate.params.flatMap({ case (param, CoqPtrValType) => Some(s"(is_pointer_or_null ${param})") case _ => None }) ).mkString("/\\") - }). Proof. Admitted.""".stripMargin + }).\n Proof. destruct self_card; simpl; entailer; saturate_local; apply prop_right; eauto. Qed.""".stripMargin } - def pure_constraints : List[PureFormula] = predicate.params.flatMap({ + def pure_constraints : List[PureFormula] = predicate.valid_params.flatMap({ case (name, ty) => ty match { case Types.CoqPtrValType => Some(IsValidPointerOrNull(ProofCVar(name, ty))) case _ => None diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala index 128405cea..a858051f1 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/logic/VSTProofStep.scala @@ -165,7 +165,7 @@ object VSTProofStep { case Some(expr) => expr.pp } override def pp: String = - s"ssl_rewrite_last (${rewrite_name} ${constructor_args.map(pp_o_expr).mkString(" ")})." + s"rewrite (${rewrite_name} ${constructor_args.map(pp_o_expr).mkString(" ")}) at 1." } case object ForwardEntailer extends VSTProofStep { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala index 7bf1b94f6..5e2f29490 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/ProofSpecTranslation.scala @@ -17,7 +17,7 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} /** translates suslik proof terms to VST compatible proof terms */ -object ProofSpecTranslation { +object ProofSpecTranslation { def to_ssl_context(gamma: Map[String, VSTType]): Gamma = { def to_ssl_type(ty: VSTType): SSLType = ty match { @@ -56,7 +56,7 @@ object ProofSpecTranslation { /** translate a suslik expression into a VST proof expression (note: this is not the same as a VST C expression, so can support terms like list comparisons etc.) - * */ + **/ def translate_expression(context: Map[Ident, VSTType])(expr: Expressions.Expr, target: Option[VSTType] = None): ProofCExpr = { def type_expr(left_1: ProofCExpr): VSTType = left_1.type_expr @@ -160,7 +160,7 @@ object ProofSpecTranslation { // to the declarations that relate to them // predicate applications are separated out unchanged // as these translate directly to vst - val (map: Map[Ident, (List[PointsTo], Option[Block])], apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = + val (map, rev_apps): (Map[Ident, (List[PointsTo], Option[Block])], List[CSApp]) = heaplets.foldLeft((initial_map, List(): List[CSApp]))({ case ((map, acc), ty: Heaplet) => ty match { @@ -183,6 +183,7 @@ object ProofSpecTranslation { } }) + val apps = rev_apps.reverse // having built the mapping, we then translate each (k,v) pair in this // mapping into a VST Data at declaration val blocks: List[CDataAt] = map.map({ case (var_nam, (points_to, o_block)) => @@ -223,10 +224,11 @@ object ProofSpecTranslation { /** translates a Suslik function specification into a proof */ def translate_conditions(env: Environment)(pred_type_map: Map[Ident, List[VSTType]])(f: FunSpec): FormalSpecification = { val name = f.name - val c_params = f.params.map({case (Var(name), ty) => ty match { + val c_params = f.params.map({ case (Var(name), ty) => ty match { case IntType => (name, CoqIntValType) case LocType => (name, CoqPtrValType) - }}) + } + }) // collect all cardinality_params and their associated types val cardinality_params: Map[String, CoqCardType] = (f.pre.sigma.chunks ++ f.post.sigma.chunks).flatMap({ case PointsTo(loc, offset, value) => None @@ -285,7 +287,11 @@ object ProofSpecTranslation { val postcondition: FormalCondition = { val pure_conditions = f.post.phi.conjuncts.map(v => translate_expression(context)(v)) - .map(IsTrueProp).toList + .map(IsTrueProp).toList ++ + existential_params.flatMap({ + case (name, CoqPtrValType) => Some(IsValidPointerOrNull(ProofCVar(name, CoqPtrValType))) + case _ => None + }) val spatial_conditions = translate_heaplets(context)(pred_type_map)(f.post.sigma.chunks) // goal.post.sigma.chunks.map(translate_heaplet(context)).toList @@ -326,7 +332,7 @@ object ProofSpecTranslation { * and matching on this - taking the first clause if the input is `lseg_card0` and the * second clause if the input is `lseg_card1 a` (and recursing on `a` * - * */ + **/ def translate_predicate(env: Environment)(predicate: InductivePredicate): VSTPredicate = { class VSTPredicateTranslation extends PredicateTranslation[ProofCExpr, VSTHeaplet, VSTType, VSTPredicateClause, VSTPredicate] { override def translatePredicateParamType(predName: String, ty: SSLType): VSTType = ty match { diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 55de0e490..9d43e8d58 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -88,7 +88,9 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println("done!") Some ((testName, cert, synDuration, proofGenDuration)) } catch { - case _ => None + case e => + println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") + None } } @@ -124,7 +126,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"done! (${fmtTime(proofCheckDuration)} s)") logStat(testName, o.filename, synDuration, proofGenDuration, proofCheckDuration, specSize, proofSize) } else { - print(s"Failed to verify ${o.filename}!") + println(s"ERR\n Failed to verify ${o.filename}!") } } else { print(s" Compiling output...") From 3a815a44a8cf87b95945f7d198253c20ff6a3adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 16:29:14 +0800 Subject: [PATCH 167/211] Iris: benchmark suite --- .../suslik/certification/Certificate.scala | 2 - .../certification/targets/iris/Iris.scala | 16 +++++-- .../targets/iris/IrisCertificate.scala | 48 ++++++++++++++----- .../iris/IrisCertificationBenchmarks.scala | 19 ++++++++ .../synthesis/CertificationBenchmarks.scala | 1 - 5 files changed, 67 insertions(+), 19 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index b4c0ce277..35620d204 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -1,8 +1,6 @@ package org.tygus.suslik.certification import java.io.File - -import scala.Seq import scala.sys.process.Process abstract class CertificateOutput { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 1671e257c..4e4cca213 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -3,10 +3,10 @@ package org.tygus.suslik.certification.targets.iris import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPredicate import org.tygus.suslik.certification.targets.iris.translation.Translation -import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget} +import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException +import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget, CoqOutput} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment -import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException case class Iris() extends CertificationTarget { type T = Iris @@ -14,7 +14,7 @@ case class Iris() extends CertificationTarget { val name: String = "HTT" val suffix: String = ".v" - def certify(proc: Procedure, env: Environment) : IrisCertificate = { + def certify(proc: Procedure, env: Environment): IrisCertificate = { val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) val cert = Translation.translate(root, proc)(env) @@ -24,5 +24,13 @@ case class Iris() extends CertificationTarget { cert } - override def generate_common_definitions_of(base_filename: String, predicates: List[IPredicate]): List[CertificateOutput] = ??? + override def generate_common_definitions_of(defFileName: String, predicates: List[IPredicate]): List[CertificateOutput] = { + def commonPredicates(predicates: List[IPredicate]): String = { + s"""${IrisCertificate.prelude(printAxioms = false)} + |${predicates.map(_.pp).mkString("\n")} + |${predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")} + |""".stripMargin + } + List(CoqOutput(defFileName ++ ".v", defFileName, commonPredicates(predicates))) + } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index a6f3a0e6b..6d55eec29 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -1,13 +1,12 @@ package org.tygus.suslik.certification.targets.iris +import org.tygus.suslik.certification.targets.iris.IrisCertificate.prelude import org.tygus.suslik.certification.targets.iris.heaplang.Expressions.HFunDef import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, IPredicate} import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} -case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, helperSpecs: List[IFunSpec], funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { - val target: CertificationTarget = Iris() - - private val prelude = +case object IrisCertificate { + def prelude(printAxioms: Boolean = true): String = { s"""From SSL_Iris Require Import core. |From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. @@ -16,20 +15,45 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H |From Hammer Require Import Hammer. |Context `{!heapG Σ}. |Set Default Proof Using "Type". - | - |Axiom NilNotLval: - | forall x v, - | x ↦ v -∗ x ↦ v ∗ ⌜x ≠ null_loc⌝. - | - |""".stripMargin + |""".stripMargin + (if (printAxioms) + s"""Axiom NilNotLval: + | forall x v, + | x ↦ v -∗ x ↦ v ∗ ⌜x ≠ null_loc⌝. + |""".stripMargin + else "") + } +} + +case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, auxSpecs: List[IFunSpec], funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { + val target: CertificationTarget = Iris() def pp : String = { val b = new StringBuilder - b.append(prelude) + b.append(prelude()) b.append(predicates.map(_.pp).mkString("\n")) b.append("\n") b.append(predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")) - b.append(helperSpecs.map(_.pp).mkString("\n")) + b.append(auxSpecs.map(_.pp).mkString("\n")) + b.append(funDef.pp) + b.append("\n") + b.append(funSpec.pp) + b.append("Proof.\n") + b.append(proofStr) + b.append("Qed.\n") + b.toString() + } + + def ppWithCommonDefs(baseFilename: String, excludedPreds: List[IPredicate]) : String = { + val exclPredNames = excludedPreds.map(p => p.name) + val predsToPrint = predicates.filterNot(p => exclPredNames.contains(p.name)) + + val b = new StringBuilder + b.append(prelude()) + b.append(s"Require Import $baseFilename.") + b.append(predsToPrint.map(_.pp).mkString("\n")) + b.append("\n") + b.append(predsToPrint.flatMap(_.getHelpers).map(_.pp).mkString("\n")) + b.append(auxSpecs.map(_.pp).mkString("\n")) b.append(funDef.pp) b.append("\n") b.append(funSpec.pp) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala new file mode 100644 index 000000000..1331a3db9 --- /dev/null +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala @@ -0,0 +1,19 @@ +package org.tygus.suslik.certification.targets.iris + +import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.synthesis.CertificationBenchmarks + +object IrisCertificationBenchmarks extends CertificationBenchmarks { + val target: CertificationTarget = Iris() + + def main(args: Array[String]): Unit = { + initLog() + runAllTestsFromDir("certification/ints") + runAllTestsFromDir("certification/sll-bounds") + runAllTestsFromDir("certification/sll") + runAllTestsFromDir("certification/dll") + runAllTestsFromDir("certification/srtl") + runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/bst") + } +} diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 9d43e8d58..62e6edc74 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -2,7 +2,6 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.{Files, Paths} - import org.tygus.suslik.certification.{CertTree, CertificationTarget, CoqOutput} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment From 716c514be4fabad483c420c40e59a5e0a46fc332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 16:51:46 +0800 Subject: [PATCH 168/211] Iris: benchmark suite --- .../org/tygus/suslik/certification/targets/iris/Iris.scala | 7 +++---- .../certification/targets/iris/IrisCertificate.scala | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 4e4cca213..5721ed3ce 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -26,10 +26,9 @@ case class Iris() extends CertificationTarget { override def generate_common_definitions_of(defFileName: String, predicates: List[IPredicate]): List[CertificateOutput] = { def commonPredicates(predicates: List[IPredicate]): String = { - s"""${IrisCertificate.prelude(printAxioms = false)} - |${predicates.map(_.pp).mkString("\n")} - |${predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")} - |""".stripMargin + s"${IrisCertificate.prelude(printAxioms = false)}\n" + + s"${predicates.map(_.pp).mkString("\n")}\n" + + s"${predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")}\n" } List(CoqOutput(defFileName ++ ".v", defFileName, commonPredicates(predicates))) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 6d55eec29..07c4913dc 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -64,4 +64,8 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H } override def outputs: List[CertificateOutput] = List(CoqOutput(s"$name.v", name, pp)) + + override def outputs_with_common_predicates(base_filename: String, common_predicates: List[IPredicate]): List[CertificateOutput] = + List(CoqOutput(s"$name.v", name, ppWithCommonDefs(base_filename, common_predicates))) + } From c0c057e784b7bfbe86b87d5ba077b1a53e808da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 17:10:12 +0800 Subject: [PATCH 169/211] Iris: benchmark suite --- .../tygus/suslik/certification/targets/iris/Iris.scala | 9 ++++++--- .../certification/targets/iris/IrisCertificate.scala | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 5721ed3ce..5272f322f 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -26,9 +26,12 @@ case class Iris() extends CertificationTarget { override def generate_common_definitions_of(defFileName: String, predicates: List[IPredicate]): List[CertificateOutput] = { def commonPredicates(predicates: List[IPredicate]): String = { - s"${IrisCertificate.prelude(printAxioms = false)}\n" + - s"${predicates.map(_.pp).mkString("\n")}\n" + - s"${predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")}\n" + s"${IrisCertificate.prelude(printAxioms = false, printContext = false)}\n" + + s"Section common.\n" + + "Context `{!heapG Σ}.\n" + + s"${predicates.map(_.pp).mkString("\n")}\n" + + s"${predicates.flatMap(_.getHelpers).map(_.pp).mkString("\n")}\n" + + "End common." } List(CoqOutput(defFileName ++ ".v", defFileName, commonPredicates(predicates))) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 07c4913dc..143b38d68 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -6,14 +6,14 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case object IrisCertificate { - def prelude(printAxioms: Boolean = true): String = { + def prelude(printAxioms: Boolean = true, printContext: Boolean = true): String = { s"""From SSL_Iris Require Import core. |From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. |From iris.heap_lang Require Import lang notation proofmode. |From iris_string_ident Require Import ltac2_string_ident. |From Hammer Require Import Hammer. - |Context `{!heapG Σ}. + |${if (printContext) "Context `{!heapG Σ}." else ""} |Set Default Proof Using "Type". |""".stripMargin + (if (printAxioms) s"""Axiom NilNotLval: From 4e1953caadf0de8f9c82f1008e5fd8664f8abe57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 17:17:00 +0800 Subject: [PATCH 170/211] Iris: benchmark suite --- .../org/tygus/suslik/certification/targets/iris/Iris.scala | 5 ++++- .../suslik/certification/targets/iris/logic/Assertions.scala | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 5272f322f..e1c0505b6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -26,7 +26,10 @@ case class Iris() extends CertificationTarget { override def generate_common_definitions_of(defFileName: String, predicates: List[IPredicate]): List[CertificateOutput] = { def commonPredicates(predicates: List[IPredicate]): String = { - s"${IrisCertificate.prelude(printAxioms = false, printContext = false)}\n" + + s"""From SSL_Iris Require Import core. + |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. + |From iris.heap_lang Require Import lang notation proofmode. + |""".stripMargin + s"Section common.\n" + "Context `{!heapG Σ}.\n" + s"${predicates.map(_.pp).mkString("\n")}\n" + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index 024d7161e..e00d8ca90 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -329,7 +329,7 @@ object Assertions { } |end. | - |Opaque $name. + |Global Opaque $name. |""".stripMargin s"${predicate_definition}" } From 6695443db4e86010c44ffa9b0cb37b7f2cf416ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Mon, 1 Mar 2021 17:45:10 +0800 Subject: [PATCH 171/211] Iris: benchmark suite ? --- .../tygus/suslik/certification/targets/iris/Iris.scala | 2 +- .../certification/targets/iris/IrisCertificate.scala | 8 ++++---- .../targets/iris/IrisCertificationBenchmarks.scala | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index e1c0505b6..10dc732c8 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -27,8 +27,8 @@ case class Iris() extends CertificationTarget { override def generate_common_definitions_of(defFileName: String, predicates: List[IPredicate]): List[CertificateOutput] = { def commonPredicates(predicates: List[IPredicate]): String = { s"""From SSL_Iris Require Import core. - |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. |From iris.heap_lang Require Import lang notation proofmode. + |From iris_string_ident Require Import ltac2_string_ident. |""".stripMargin + s"Section common.\n" + "Context `{!heapG Σ}.\n" + diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 143b38d68..565643326 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -6,14 +6,15 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case object IrisCertificate { - def prelude(printAxioms: Boolean = true, printContext: Boolean = true): String = { + def prelude(printAxioms: Boolean = true, importCommon: String = ""): String = { s"""From SSL_Iris Require Import core. |From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. |From iris.heap_lang Require Import lang notation proofmode. + |${importCommon} |From iris_string_ident Require Import ltac2_string_ident. |From Hammer Require Import Hammer. - |${if (printContext) "Context `{!heapG Σ}." else ""} + |Context `{!heapG Σ}. |Set Default Proof Using "Type". |""".stripMargin + (if (printAxioms) s"""Axiom NilNotLval: @@ -48,8 +49,7 @@ case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: H val predsToPrint = predicates.filterNot(p => exclPredNames.contains(p.name)) val b = new StringBuilder - b.append(prelude()) - b.append(s"Require Import $baseFilename.") + b.append(prelude(importCommon = s"Require Import $baseFilename.")) b.append(predsToPrint.map(_.pp).mkString("\n")) b.append("\n") b.append(predsToPrint.flatMap(_.getHelpers).map(_.pp).mkString("\n")) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala index 1331a3db9..6ca9714f9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala @@ -9,11 +9,11 @@ object IrisCertificationBenchmarks extends CertificationBenchmarks { def main(args: Array[String]): Unit = { initLog() runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/sll-bounds") runAllTestsFromDir("certification/sll") runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/srtl") runAllTestsFromDir("certification/tree") + runAllTestsFromDir("certification/sll-bounds") + runAllTestsFromDir("certification/srtl") runAllTestsFromDir("certification/bst") } } From ab758e2dce308ff0242d8239dc8dc0f9cc528334 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 1 Mar 2021 23:05:17 +0800 Subject: [PATCH 172/211] Multi-target benchmarks --- .../htt/HTTCertificationBenchmarks.scala | 2 +- .../iris/IrisCertificationBenchmarks.scala | 2 +- .../vst/VSTCertificationBenchmarks.scala | 2 +- .../synthesis/CertificationBenchmarks.scala | 203 ++++++++++++------ 4 files changed, 142 insertions(+), 67 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala index 2edcdcbd1..9e8d0e618 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala @@ -4,7 +4,7 @@ import org.tygus.suslik.certification.CertificationTarget import org.tygus.suslik.synthesis.CertificationBenchmarks object HTTCertificationBenchmarks extends CertificationBenchmarks { - val target: CertificationTarget = HTT() + val targets: List[CertificationTarget] = List(HTT()) def main(args: Array[String]): Unit = { initLog() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala index 6ca9714f9..c54e43f39 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala @@ -4,7 +4,7 @@ import org.tygus.suslik.certification.CertificationTarget import org.tygus.suslik.synthesis.CertificationBenchmarks object IrisCertificationBenchmarks extends CertificationBenchmarks { - val target: CertificationTarget = Iris() + val targets: List[CertificationTarget] = List(Iris()) def main(args: Array[String]): Unit = { initLog() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala index 9a4803144..ca85c8188 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.synthesis.CertificationBenchmarks object VSTCertificationBenchmarks extends CertificationBenchmarks { - val target: CertificationTarget = VST() + val targets: List[CertificationTarget] = List(VST()) def main(args: Array[String]): Unit = { initLog() diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 62e6edc74..0de5f343f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -2,21 +2,25 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.{Files, Paths} -import org.tygus.suslik.certification.{CertTree, CertificationTarget, CoqOutput} + +import org.tygus.suslik.certification.targets.htt.HTT +import org.tygus.suslik.certification.targets.vst.VST +import org.tygus.suslik.certification.targets.iris.Iris +import org.tygus.suslik.certification.{CertTree, CertificationTarget} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor.preprocessProgram import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.report.StopWatch.timed +import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} import scala.sys.process._ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { - val target: CertificationTarget + val targets: List[CertificationTarget] val statsFile: File = new File("cert-stats.csv") - val statsHeader: String = List("Benchmark Name", "Output Name", "Synthesis Time (sec)", "Proof Generation Time (sec)", "Proof Checking Time (sec)", "Spec Size", "Proof Size").mkString(", ") + "\n" val defFilename: String = "common" def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { @@ -56,12 +60,20 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { } } + override def createSynthesizer(env: Environment): Synthesis = { + val tactic = new PhasedSynthesis(env.config) + val trace = new ProofTraceCert() + new Synthesis(tactic, log, trace) + } + override def runAllTestsFromDir(dir: String): Unit = { - println(s"==========Benchmark Group $dir==========\n") + println(s"=========================================\n") + println(s" Benchmark Group: $dir\n") + println(s"=========================================\n") val path = List(rootDir, dir).mkString(File.separator) val testDir = new File(path) - val tempDir = Files.createTempDirectory("suslik-").toFile - println(s"Initialized output directory at: ${tempDir.getCanonicalPath}\n") + val tempDirNames = targets.map(t => t -> Files.createTempDirectory("suslik-").toFile).toMap + if (testDir.exists() && testDir.isDirectory) { print(s"Retrieving definitions and specs from ${testDir.getName}...") val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) @@ -71,72 +83,106 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println("done!") val parser = new SSLParser - println(s"\nSynthesizing ${tests.length} test cases...") - val raw_syn_results = for (f <- tests) yield { + + val testCases = for (f <- tests) yield { val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) - println(s"$testName:") - val fullInput = List(defs, in).mkString("\n") - - print(s" synthesizing in certification mode...") - val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false, certTarget = target, certDest = tempDir)) - println(s"done! (${fmtTime(synDuration)} s)") - - print(s" generating certificate...") - try { - val (cert, proofGenDuration) = timed(target.certify(res.head, env)) - println("done!") - Some ((testName, cert, synDuration, proofGenDuration)) - } catch { - case e => - println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") - None - } + (testName, in, params) } - println(s"Successfully synthesized ${tests.length} tests.") + val results = targets.map(target => { + val tempDir = tempDirNames(target) + println(s"\n****************************************\n") + println(s"Running benchmarks for certification target: ${target.name}") + println(s"Initialized output directory: ${tempDir.getCanonicalPath}\n") - val synResults = raw_syn_results.flatten - val predicates = synResults.flatMap(_._2.predicates).groupBy(_.name).map(_._2.head).toList - val defFiles = target.generate_common_definitions_of(defFilename, predicates) - defFiles.foreach { - case output => - print(s"\nWriting common definitions to file ${tempDir}/${output.filename}...") - serialize(tempDir, output.filename, output.body) - } - println("done!") - print(s"Compiling definitions...") - defFiles.foreach(output => output.compile(tempDir)) - println("done!") + println(s"Synthesizing ${tests.length} test cases...") - println(s"\nGenerating statistics...") - for ((testName, cert, synDuration, proofGenDuration) <- synResults) { - println(s"$testName:") - for (o <- cert.outputs_with_common_predicates(defFilename, predicates)) yield { - print(s" Writing certificate output to file ${o.filename}...") - serialize(tempDir, o.filename, o.body) - println("done!") - if (o.isProof) { - print(s" Checking proof size...") - val (specSize, proofSize) = checkProofSize(tempDir, o.filename) - println(s"done! (spec: $specSize, proof: $proofSize)") - print(s" Compiling proof...") - val (res, proofCheckDuration) = timed (o.compile(tempDir)) - if (res == 0) { - println(s"done! (${fmtTime(proofCheckDuration)} s)") - logStat(testName, o.filename, synDuration, proofGenDuration, proofCheckDuration, specSize, proofSize) - } else { - println(s"ERR\n Failed to verify ${o.filename}!") - } - } else { - print(s" Compiling output...") - o.compile(tempDir) + val synResults = for ((testName, in, params) <- testCases) yield { + println(s"$testName:") + val fullInput = List(defs, in).mkString("\n") + + print(s" synthesizing in certification mode...") + val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) + println(s"done! (${fmtTime(synDuration)} s)") + + print(s" generating certificate...") + try { + val (cert, proofGenDuration) = timed(target.certify(res.head, env)) println("done!") + (testName, synDuration, Some(cert, proofGenDuration)) + } catch { + case e => + println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") + (testName, synDuration, None) } } + + println(s"Successfully synthesized ${tests.length} tests.") + + val validCerts = synResults.map(_._3).filter(_.isDefined).map(_.get).map(_._1) + val predicates = validCerts.flatMap(_.predicates).groupBy(_.name).map(_._2.head).toList + val defFiles = target.generate_common_definitions_of(defFilename, predicates) + defFiles.foreach { + case output => + print(s"\nWriting common definitions to file $tempDir/${output.filename}...") + serialize(tempDir, output.filename, output.body) + } + println("done!") + print(s"Compiling definitions...") + defFiles.foreach(output => output.compile(tempDir)) + println("done!") + + println(s"\nGenerating statistics...") + val testToStats = synResults.map { case (testName, synDuration, certOpt) => certOpt match { + case None => + println(s"$testName:") + println(" No certificate was generated; skipping proof check...") + testName -> (synDuration, None, None, None) + case Some((cert, proofGenDuration)) => + println(s"$testName:") + + val outputs = cert.outputs_with_common_predicates(defFilename, predicates) + for (o <- outputs) yield { + print(s" Writing certificate output to file ${o.filename}...") + serialize(tempDir, o.filename, o.body) + println("done!") + if (!o.isProof) { + print(s" Compiling output...") + o.compile(tempDir) + println("done!") + } + } + + outputs.find(_.isProof) match { + case Some(proof) => + print(s" Checking proof size...") + val (specSize, proofSize) = checkProofSize(tempDir, proof.filename) + println(s"done! (spec: $specSize, proof: $proofSize)") + print(s" Compiling proof...") + val (res, proofCheckDuration) = timed (proof.compile(tempDir)) + if (res == 0) { + println(s"done! (${fmtTime(proofCheckDuration)} s)") + testName -> (synDuration, Some(proofGenDuration), Some(specSize, proofSize), Some(proofCheckDuration)) + } else { + println(s"ERR\n Failed to verify ${proof.filename}!") + testName -> (synDuration, Some(proofGenDuration), Some(specSize, proofSize), None) + } + case None => + println(s"Certificate was generated, but no proof output was found; skipping verification...") + testName -> (synDuration, Some(proofGenDuration), None, None) + } + }}.toMap + println(s"\n****************************************\n") + + target -> testToStats + }).toMap + + for ((testName, _, _) <- testCases) { + val resultsByTarget = targets.map(target => results(target)(testName)) + logStat(testName, resultsByTarget) } - println(s"\nStatistics written to: ${statsFile.getCanonicalPath}") - println(s"View generated proofs and definitions at: $tempDir\n\n") + println(s"\nStatistics written to: ${statsFile.getCanonicalPath}\n\n") } } @@ -155,13 +201,42 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { def initLog(): Unit = { if (statsFile.exists()) statsFile.delete() statsFile.createNewFile() + val topLevelHeader = List("") ++ targets.flatMap(t => List(t.name, "", "", "", "")) + val targetHeader = List("Synthesis Time (sec)", "Proof Generation Time (sec)", "Proof Checking Time (sec)", "Spec Size", "Proof Size") + val targetHeaders = targets.flatMap(_ => targetHeader) + val statsHeader = topLevelHeader.mkString(", ") + "\n" + (List("Benchmark Name") ++ targetHeaders).mkString(", ") + "\n" SynStatUtil.using(new FileWriter(statsFile, true))(_.write(statsHeader)) } - private def logStat(name: String, filename: String, synDuration: Long, proofGenDuration: Long, proofCheckDuration: Long, specSize: Int, proofSize: Int): Unit = { - val data = s"$name, $filename, ${fmtTime(synDuration)}, ${fmtTime(proofGenDuration)}, ${fmtTime(proofCheckDuration)}, $specSize, $proofSize\n" + private def logStat(name: String, targetResults: List[(Long, Option[Long], Option[(Int, Int)], Option[Long])]): Unit = { + def ppRes(res: (Long, Option[Long], Option[(Int, Int)], Option[Long])): String = { + val (synDuration, proofGenDuration, proofStats, proofCheckDuration) = res + val proofGenStr = proofGenDuration.map(fmtTime).getOrElse("-") + val proofStatsStr = proofStats match { + case Some((specSize, proofSize)) => List(specSize, proofSize).mkString(", ") + case None => "-, -, -" + } + val proofCheckStr = proofCheckDuration.map(fmtTime).getOrElse("-") + s"${fmtTime(synDuration)}, $proofGenStr, $proofCheckStr, $proofStatsStr" + } + val data = s"$name, ${targetResults.map(ppRes).mkString(", ")}\n" SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) } private def fmtTime(ms: Long): String = "%.1f".format(ms.toDouble / 1000) } + +object CertificationBenchmarks { + def main(args: Array[String]): Unit = { + val benchmarks = new CertificationBenchmarks { + override val targets: List[CertificationTarget] = List(HTT(), VST(), Iris()) + } + benchmarks.runAllTestsFromDir("certification/ints") + benchmarks.runAllTestsFromDir("certification/sll-bounds") + benchmarks.runAllTestsFromDir("certification/sll") + benchmarks.runAllTestsFromDir("certification/dll") + benchmarks.runAllTestsFromDir("certification/srtl") + benchmarks.runAllTestsFromDir("certification/tree") + benchmarks.runAllTestsFromDir("certification/bst") + } +} \ No newline at end of file From 37c21194deea882c9f97517563b725d4a0507018 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 2 Mar 2021 02:29:38 +0800 Subject: [PATCH 173/211] Extract certification benchmarks to separate dir --- .../synthesis/CertificationBenchmarks.scala | 12 +++---- .../certification-benchmarks/dll/common.def | 6 ++++ .../dll/dll-append.syn | 29 +++++++++++++++ .../dll/dll-singleton.syn | 18 ++++++++++ .../certification-benchmarks/ints/max.syn | 12 +++++++ .../certification-benchmarks/ints/min.syn | 20 +++++++++++ .../certification-benchmarks/ints/swap2.syn | 17 +++++++++ .../certification-benchmarks/ints/swap4.syn | 20 +++++++++++ .../sll-bounds/common.def | 5 +++ .../sll-bounds/sll-len.syn | 22 ++++++++++++ .../sll-bounds/sll-max.syn | 22 ++++++++++++ .../sll-bounds/sll-min.syn | 22 ++++++++++++ .../certification-benchmarks/sll/common.def | 4 +++ .../sll/sll-append.syn | 20 +++++++++++ .../certification-benchmarks/sll/sll-copy.syn | 26 ++++++++++++++ .../sll/sll-dupleton.syn | 17 +++++++++ .../certification-benchmarks/sll/sll-free.syn | 17 +++++++++ .../sll/sll-singleton.syn | 17 +++++++++ .../certification-benchmarks/tree/common.def | 15 ++++++++ .../tree/tree-copy.syn | 31 ++++++++++++++++ .../tree/tree-flatten.syn | 35 +++++++++++++++++++ .../tree/tree-free.syn | 20 +++++++++++ .../tree/tree-size.syn | 24 +++++++++++++ 23 files changed, 424 insertions(+), 7 deletions(-) create mode 100644 src/test/resources/synthesis/certification-benchmarks/dll/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks/dll/dll-append.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/dll/dll-singleton.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/ints/max.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/ints/min.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll-bounds/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-len.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-max.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-min.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/sll-append.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/sll-copy.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/sll-dupleton.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/sll-free.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/sll/sll-singleton.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/tree/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks/tree/tree-copy.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/tree/tree-flatten.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/tree/tree-free.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks/tree/tree-size.syn diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 0de5f343f..21d201b26 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -231,12 +231,10 @@ object CertificationBenchmarks { val benchmarks = new CertificationBenchmarks { override val targets: List[CertificationTarget] = List(HTT(), VST(), Iris()) } - benchmarks.runAllTestsFromDir("certification/ints") - benchmarks.runAllTestsFromDir("certification/sll-bounds") - benchmarks.runAllTestsFromDir("certification/sll") - benchmarks.runAllTestsFromDir("certification/dll") - benchmarks.runAllTestsFromDir("certification/srtl") - benchmarks.runAllTestsFromDir("certification/tree") - benchmarks.runAllTestsFromDir("certification/bst") + benchmarks.runAllTestsFromDir("certification-benchmarks/ints") + benchmarks.runAllTestsFromDir("certification-benchmarks/sll-bounds") + benchmarks.runAllTestsFromDir("certification-benchmarks/sll") + benchmarks.runAllTestsFromDir("certification-benchmarks/dll") + benchmarks.runAllTestsFromDir("certification-benchmarks/tree") } } \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/dll/common.def b/src/test/resources/synthesis/certification-benchmarks/dll/common.def new file mode 100644 index 000000000..9db84981b --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/dll/common.def @@ -0,0 +1,6 @@ +predicate dll(loc x, loc z, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => + { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> w ** (x + 2) :-> z ** dll(w, x, s1) } +} + diff --git a/src/test/resources/synthesis/certification-benchmarks/dll/dll-append.syn b/src/test/resources/synthesis/certification-benchmarks/dll/dll-append.syn new file mode 100644 index 000000000..b933bba99 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/dll/dll-append.syn @@ -0,0 +1,29 @@ +# -c 2 -f 1 + +doubly-linked list: append + +##### + +{true ; dll(x1, a, s1) ** dll(x2, b, s2) ** r :-> x2} +void dll_append (loc x1, loc r) +{s =i s1 ++ s2 ; dll(y, c, s) ** r :-> y } + +##### + +void dll_append (loc x1, loc r) { + if (x1 == null) { + } else { + let w = *(x1 + 1); + dll_append(w, r); + let y = *r; + if (y == null) { + *(x1 + 1) = null; + *r = x1; + } else { + *(y + 2) = x1; + *(x1 + 1) = y; + *r = x1; + } + } +} + diff --git a/src/test/resources/synthesis/certification-benchmarks/dll/dll-singleton.syn b/src/test/resources/synthesis/certification-benchmarks/dll/dll-singleton.syn new file mode 100644 index 000000000..cc1900b07 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/dll/dll-singleton.syn @@ -0,0 +1,18 @@ +#. -c 2 +doubly-linked list: construct a list with one element + +##### + +{ r :-> a } +void dll_singleton (int x, loc r) +{ elems =i {x} ; r :-> y ** dll(y, null, elems) } + +##### + +void dll_singleton (int x, loc r) { + let y = malloc(3); + *r = y; + *(y + 1) = 0; + *(y + 2) = 0; + *y = x; +} diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/max.syn b/src/test/resources/synthesis/certification-benchmarks/ints/max.syn new file mode 100644 index 000000000..39cbd2d27 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/ints/max.syn @@ -0,0 +1,12 @@ +#. -b true +maximum of two integers + +### + +{ r :-> 0 } + +void max (loc r, int x, int y) + +{ x <= m /\ y <= m; r :-> m } + +### diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/min.syn b/src/test/resources/synthesis/certification-benchmarks/ints/min.syn new file mode 100644 index 000000000..5b72a2cd5 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/ints/min.syn @@ -0,0 +1,20 @@ +#. -b true +minimum of two integers + +### + +{ true ; r :-> null } + +void min2(loc r, int x, int y) + +{ m <= x /\ m <= y; r :-> m } + +### + +void min2 (loc r, int x, int y) { + if (x <= y) { + *r = x; + } else { + *r = y; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn b/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn new file mode 100644 index 000000000..d8aab2c3c --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn @@ -0,0 +1,17 @@ +Swapping example +### + +{ x :-> a ** y :-> b } + +void swap(loc x, loc y) + +{ x :-> b ** y :-> a } + +### + +void swap (loc x, loc y) { + let a2 = *x; + let b2 = *y; + *x = b2; + *y = a2; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn b/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn new file mode 100644 index 000000000..f188b4785 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn @@ -0,0 +1,20 @@ +should be able to synthesize a more complex swap program + +### + +{true; x :-> a ** y :-> c ** z :-> b ** t :-> q } +void swap2 (loc x, loc z, loc y, loc t) +{ true; x :-> q ** z :-> c ** t :-> a ** y :-> b } + +### + +void swap2 (loc x, loc z, loc y, loc t) { + let a = *x; + let c = *y; + let b = *z; + let q = *t; + *x = q; + *y = b; + *z = c; + *t = a; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll-bounds/common.def b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/common.def new file mode 100644 index 000000000..a391b2cd8 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/common.def @@ -0,0 +1,5 @@ +predicate sll(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-len.syn b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-len.syn new file mode 100644 index 000000000..458109d1a --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-len.syn @@ -0,0 +1,22 @@ +singly-linked list: length + +##### + +{0 <= n ; r :-> a ** sll(x, n, lo, hi) } +void sll_len (loc x, loc r) +{true ; r :-> n ** sll(x, n, lo, hi) } + +##### + +void sll_len (loc x, loc r) {s + if (x == null) { + *r = 0; + } else { + let v = *x; + let n = *(x + 1); + sll_len(n, x); + let l = *x; + *r = 1 + l; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-max.syn b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-max.syn new file mode 100644 index 000000000..6dedebf36 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-max.syn @@ -0,0 +1,22 @@ +singly-linked list: max + +##### + +{true ; r :-> a ** sll(x, n, lo, hi) } +void sll_max (loc x, loc r) +{true ; r :-> hi ** sll(x, n, lo, hi) } + +##### + +void sll_max (loc x, loc r) { + if (x == null) { + *r = 0; + } else { + let v = *x; + let n = *(x + 1); + sll_max(n, x); + let h = *x; + *r = h <= v ? v : h; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-min.syn b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-min.syn new file mode 100644 index 000000000..19c32069f --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll-bounds/sll-min.syn @@ -0,0 +1,22 @@ +singly-linked list: min + +##### + +{true ; r :-> a ** sll(x, n, lo, hi) } +void sll_min (loc x, loc r) +{true ; r :-> lo ** sll(x, n, lo, hi) } + +##### + +void sll_min (loc x, loc r) { + if (x == 0) { + *r = 7; + } else { + let v = *x; + let n = *(x + 1); + sll_min(n, x); + let l = *x; + *r = v <= l ? v : l; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/common.def b/src/test/resources/synthesis/certification-benchmarks/sll/common.def new file mode 100644 index 000000000..da2578bf1 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/common.def @@ -0,0 +1,4 @@ +predicate sll(loc x, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/sll-append.syn b/src/test/resources/synthesis/certification-benchmarks/sll/sll-append.syn new file mode 100644 index 000000000..f11e5b9ad --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/sll-append.syn @@ -0,0 +1,20 @@ +singly-linked list: append + +##### + +{true ; sll(x1, s1) ** sll(x2, s2) ** r :-> x2} +void sll_append (loc x1, loc r) +{s =i s1 ++ s2 ; sll(y, s) ** r :-> y } + +##### + +void sll_append (loc x1, loc r) { + if (x1 == 0) { + } else { + let n = *(x1 + 1); + sll_append(n, r); + let y = *r; + *(x1 + 1) = y; + *r = x1; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/sll-copy.syn b/src/test/resources/synthesis/certification-benchmarks/sll/sll-copy.syn new file mode 100644 index 000000000..76503a7a3 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/sll-copy.syn @@ -0,0 +1,26 @@ +should be able to synthesize list copy + +##### + +{true ; r :-> x ** sll(x, s)} +void sll_copy(loc r) +{true ; r :-> y ** sll(x, s) ** sll(y, s) } + +##### + +void sll_copy (loc r) { + let x = *r; + if (x == 0) { + } else { + let v = *x; + let n = *(x + 1); + *x = n; + sll_copy(x); + let yx = *x; + let y = malloc(2); + *r = y; + *(y + 1) = yx; + *x = v; + *y = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/sll-dupleton.syn b/src/test/resources/synthesis/certification-benchmarks/sll/sll-dupleton.syn new file mode 100644 index 000000000..cbf9bb219 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/sll-dupleton.syn @@ -0,0 +1,17 @@ +# -c 3 +singly-linked list: construct a list with two elements + +##### + +{ true ; r :-> a } +void sll_dupleton (int x, int y, loc r) +{ elems =i {x, y} ; r :-> z ** sll(z, elems) } + +##### + +void sll_dupleton (int x, loc r) { + let y2 = malloc(2); + *y2 = x; + *(y2 + 1) = 0; + *r = y2; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/sll-free.syn b/src/test/resources/synthesis/certification-benchmarks/sll/sll-free.syn new file mode 100644 index 000000000..9fd7b35d2 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/sll-free.syn @@ -0,0 +1,17 @@ +should be able to deallocate a linked list +### + +{ true; sll(x, s) } + void sll_free(loc x) +{ true ; emp } + +### + +void sll_free (loc x) { + if (x == 0) { + } else { + let n = *(x + 1); + sll_free(n); + free(x); + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/sll/sll-singleton.syn b/src/test/resources/synthesis/certification-benchmarks/sll/sll-singleton.syn new file mode 100644 index 000000000..063fc8e2a --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/sll/sll-singleton.syn @@ -0,0 +1,17 @@ +#. -c 2 +singly-linked list: construct a list with one element + +##### + +{ true ; p :-> a } +void sll_singleton (int x, loc p) +{ elems =i {x} ; p :-> y ** sll(y, elems) } + +##### + +void sll_singleton (int x, loc p) { + let y = malloc(2); + *p = y; + *(y + 1) = 0; + *y = x; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/tree/common.def b/src/test/resources/synthesis/certification-benchmarks/tree/common.def new file mode 100644 index 000000000..80cd4fc04 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/tree/common.def @@ -0,0 +1,15 @@ +predicate tree(loc x, set s) { +| x == null => {s =i {}; emp} +| not (x == null) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} +} + +predicate treeN(loc x, int n) { +| x == null => { n == 0 ; emp } +| not (x == null) => { n == 1 + n1 + n2 /\ 0 <= n1 /\ 0 <= n2 ; + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** treeN(l, n1) ** treeN(r, n2)} +} + +predicate sll(loc x, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} diff --git a/src/test/resources/synthesis/certification-benchmarks/tree/tree-copy.syn b/src/test/resources/synthesis/certification-benchmarks/tree/tree-copy.syn new file mode 100644 index 000000000..89d7690f1 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/tree/tree-copy.syn @@ -0,0 +1,31 @@ +should be able to synthesize a tree copy (with elements) + +##### + +{true ; r :-> x ** tree(x, s)} +void tree_copy(loc r) +{true ; r :-> y ** tree(x, s) ** tree(y, s) } + +##### + +void tree_copy (loc r) { + let x = *r; + if (x == 0) { + } else { + let v = *x; + let l = *(x + 1); + let rx = *(x + 2); + *x = l; + tree_copy(x); + let yx = *x; + *r = rx; + tree_copy(r); + let yr = *r; + let y = malloc(3); + *r = y; + *(y + 1) = yx; + *(y + 2) = yr; + *x = v; + *y = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/tree/tree-flatten.syn b/src/test/resources/synthesis/certification-benchmarks/tree/tree-flatten.syn new file mode 100644 index 000000000..d8039fe0e --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/tree/tree-flatten.syn @@ -0,0 +1,35 @@ +should be able to flatten the tree into a list given an auxiliary function for list appending + +#### + +{true ; sll(x1, s1) ** sll(x2, s2) ** ret :-> x2} +void sll_append (loc x1, loc ret) +{s =i s1 ++ s2 ; sll(y, s) ** ret :-> y } + +{ true ; z :-> x ** tree(x, s)} +void tree_flatten(loc z) +{ true ; z :-> y ** sll(y, s)} + +#### + +void tree_flatten (loc z) { + let x = *z; + if (x == null) { + } else { + let v = *x; + let l = *(x + 1); + let r = *(x + 2); + *x = l; + tree_flatten(x); + *z = r; + tree_flatten(z); + let yz = *z; + sll_append(yz, x); + let yy = *x; + let y = malloc(2); + free(x); + *z = y; + *(y + 1) = yy; + *y = v; + } +} diff --git a/src/test/resources/synthesis/certification-benchmarks/tree/tree-free.syn b/src/test/resources/synthesis/certification-benchmarks/tree/tree-free.syn new file mode 100644 index 000000000..a290788aa --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/tree/tree-free.syn @@ -0,0 +1,20 @@ +should be able to free a tree + +#### + +{true ; tree(x, s) } + void tree_free(loc x) +{true ; emp } + +#### + +void tree_free (loc x) { + if (x == 0) { + } else { + let l2 = *(x + 1); + let r2 = *(x + 2); + tree_free(l2); + tree_free(r2); + free(x); + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks/tree/tree-size.syn b/src/test/resources/synthesis/certification-benchmarks/tree/tree-size.syn new file mode 100644 index 000000000..40224c02e --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks/tree/tree-size.syn @@ -0,0 +1,24 @@ +should be able to synthesize tree size + +##### + + +{0 <= n ; r :-> 0 ** treeN(x, n) } +void tree_size(loc x, loc r) +{true ; r :-> n ** treeN(x, n) } + +##### + +void tree_size (loc x, loc r) { + if (x == 0) { + } else { + let l = *(x + 1); + let rx = *(x + 2); + tree_size(l, r); + let n1 = *r; + *r = 0; + tree_size(rx, r); + let n = *r; + *r = 1 + n1 + n; + } +} \ No newline at end of file From 50b6aaea6ea0b4372ebd10a1554c7186f31c2ecc Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 2 Mar 2021 03:25:41 +0800 Subject: [PATCH 174/211] HTT: Revert to list equality and admit pure lemmas (todo: make togglable) --- .../certification/source/SuslikProofStep.scala | 6 +++--- .../suslik/certification/targets/htt/HTT.scala | 15 +++++++++++++++ .../targets/htt/language/Expressions.scala | 10 +++++++--- .../targets/htt/language/Types.scala | 2 +- .../certification/targets/htt/logic/Hint.scala | 16 +++++++++++----- .../htt/translation/ProofTranslator.scala | 6 +++--- .../synthesis/CertificationBenchmarks.scala | 1 + .../tygus/suslik/synthesis/StmtProducer.scala | 4 ++-- .../tygus/suslik/synthesis/rules/FailRules.scala | 2 +- 9 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala index a2f8330fc..83528b456 100644 --- a/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/source/SuslikProofStep.scala @@ -31,7 +31,7 @@ object SuslikProofStep { } /** solves a pure entailment with SMT */ - case class CheckPost(prePhi: PFormula, postPhi: PFormula) extends SuslikProofStep { + case class CheckPost(prePhi: PFormula, postPhi: PFormula, gamma: Gamma) extends SuslikProofStep { override def pp: String = s"CheckPost(${prePhi.pp}; ${postPhi.pp});" } @@ -211,8 +211,8 @@ case class AbduceCall( case _ => fail_with_bad_proof_structure() } case FailRules.CheckPost => node.kont match { - case ChainedProducer(PureEntailmentProducer(prePhi, postPhi), IdProducer) => node.children match { - case ::(head, Nil) => SuslikProofStep.CheckPost(prePhi, postPhi) + case ChainedProducer(PureEntailmentProducer(prePhi, postPhi, gamma), IdProducer) => node.children match { + case ::(head, Nil) => SuslikProofStep.CheckPost(prePhi, postPhi, gamma) case ls => fail_with_bad_children(ls, 1) } case _ => fail_with_bad_proof_structure() diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 217b65613..7cea8f204 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -33,6 +33,21 @@ object HTT { |Require Import stmod stsep stlog stlogR. |From SSL |Require Import core. + |From Hammer Require Import Hammer. + |(* Configure Hammer *) + |Unset Hammer Eprover. + |Unset Hammer Vampire. + |Add Search Blacklist "fcsl.". + |Add Search Blacklist "HTT.". + |Add Search Blacklist "Coq.ssr.ssrfun". + |Add Search Blacklist "mathcomp.ssreflect.ssrfun". + |Add Search Blacklist "mathcomp.ssreflect.bigop". + |Add Search Blacklist "mathcomp.ssreflect.choice". + |Add Search Blacklist "mathcomp.ssreflect.div". + |Add Search Blacklist "mathcomp.ssreflect.finfun". + |Add Search Blacklist "mathcomp.ssreflect.fintype". + |Add Search Blacklist "mathcomp.ssreflect.path". + |Add Search Blacklist "mathcomp.ssreflect.tuple". | |""".stripMargin diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index 4f86912f8..d3ffceff4 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -126,7 +126,7 @@ object Expressions { } case class CSetLiteral(elems: List[CExpr]) extends CExpr { - override def pp: String = if (elems.isEmpty) "nil" else s"[:: ${elems.map(_.pp).mkString("; ")}]" + override def pp: String = if (elems.isEmpty) "@nil nat" else s"[:: ${elems.map(_.pp).mkString("; ")}]" } case class CIfThenElse(cond: CExpr, left: CExpr, right: CExpr) extends CExpr { @@ -136,12 +136,13 @@ object Expressions { case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { override def pp: String = op match { case COpSubset => s"@sub_mem nat_eqType (mem (${left.pp})) (mem (${right.pp}))" - case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" +// case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" case _ => s"(${left.pp}) ${op.pp} (${right.pp})" } override def ppProp: String = op match { case COpEq => s"(${left.pp}) ${op.ppProp} (${right.pp})" + case COpSetEq => s"(${left.pp}) ${op.ppProp} (${right.pp})" case _ => pp } } @@ -263,7 +264,10 @@ object Expressions { object COpIn extends CBinOp - object COpSetEq extends CBinOp + object COpSetEq extends CBinOp { + override def ppProp: String = "=" + override def pp: String = "==" + } object COpSubset extends CBinOp { override def pp: String = "<=" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala index 497acb654..6f3190d31 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Types.scala @@ -42,6 +42,6 @@ object Types { } case object CCardType extends HTTType { - override def pp: String = "" + override def pp: String = "nat" } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 7c78f9845..ea8466da9 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.certification.targets.htt.logic import org.tygus.suslik.certification.targets.htt.language.Expressions._ -import org.tygus.suslik.certification.targets.htt.language.Types.CNatSeqType +import org.tygus.suslik.certification.targets.htt.language.Types.{CNatSeqType, HTTType} import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate import scala.annotation.tailrec @@ -32,7 +32,7 @@ object Hint { def pp: String = { val hyp = s"Lemma $name ${args.map(_.pp).mkString(" ")} : perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" - s"$hyp. Admitted.\n${ppResolve(name)}" + s"$hyp. intros; hammer. Qed.\n${ppResolve(name)}" } } @@ -46,7 +46,7 @@ object Hint { def pp: String = hypotheses.map(_.pp).mkString(".\n") + "." } - case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr]) extends Hint { + case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr], gamma: Map[CVar, HTTType]) extends Hint { val dbName: String = "ssl_pure" case class Hypothesis(args: Set[CVar], ctx: Set[CExpr], goal: CExpr) { val name = s"pure$freshHintId" @@ -54,8 +54,14 @@ object Hint { val ctxStr = ctx.map(_.ppProp).mkString(" -> ") val goalStr = goal.ppProp val hypStr = if (ctx.isEmpty) goalStr else s"$ctxStr -> $goalStr" - val argsStr = if (args.isEmpty) "" else s"${args.map(_.pp).mkString(" ")} " - s"Lemma $name $argsStr: $hypStr. Admitted.\n${ppResolve(name)}" + val argsStr = if (args.isEmpty) "" else { + val s = args.map(a => gamma.get(a) match { + case Some(tp) => s"(${a.pp} : ${tp.pp})" + case None => a.pp + }) + s"${s.mkString(" ")} " + } + s"Lemma $name $argsStr: $hypStr. intros; hammer. Qed.\n${ppResolve(name)}" } } private type ReachableMap = Map[CExpr, Set[CExpr]] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 7cb15a68e..c8f4c9cfd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -109,7 +109,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // initialize context with post-condition info and predicate hints, if any val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap - ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) +// ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) val postEx = ListMap(cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)): _*) val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) @@ -288,8 +288,8 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont Result(List(Proof.Emp andThen), Nil) /** Pure entailments */ - case SuslikProofStep.CheckPost(prePhi, postPhi) => - ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate)) + case SuslikProofStep.CheckPost(prePhi, postPhi, gamma) => + ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate), gamma.translate) Result(List(), List(withNoDeferred(ctx))) /** Ignored */ diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 21d201b26..fa361938f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -231,6 +231,7 @@ object CertificationBenchmarks { val benchmarks = new CertificationBenchmarks { override val targets: List[CertificationTarget] = List(HTT(), VST(), Iris()) } + benchmarks.initLog() benchmarks.runAllTestsFromDir("certification-benchmarks/ints") benchmarks.runAllTestsFromDir("certification-benchmarks/sll-bounds") benchmarks.runAllTestsFromDir("certification-benchmarks/sll") diff --git a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala index 2eccb768a..7d1789821 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/StmtProducer.scala @@ -2,7 +2,7 @@ package org.tygus.suslik.synthesis import org.tygus.suslik.language.Expressions.{Expr, ExprSubst, Subst, SubstVar, Var} import org.tygus.suslik.language.Statements._ -import org.tygus.suslik.logic.{FunSpec, Heaplet, InductiveClause, PFormula, SApp, SFormula} +import org.tygus.suslik.logic.{FunSpec, Gamma, Heaplet, InductiveClause, PFormula, SApp, SFormula} import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.synthesis.rules.RuleUtils @@ -181,7 +181,7 @@ object StmtProducer { case class AbduceCallProducer(f: FunSpec) extends StmtProducer with Noop // Captures entailments emitted by SMT - case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula) extends StmtProducer with Noop + case class PureEntailmentProducer(prePhi: PFormula, postPhi: PFormula, gamma: Gamma) extends StmtProducer with Noop // Captures heap unifications case class UnificationProducer(preHeaplet: Heaplet, postHeaplet: Heaplet, subst: ExprSubst) extends StmtProducer with Noop diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala index 43bd1d7f4..91fdf02dd 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/FailRules.scala @@ -46,7 +46,7 @@ object FailRules extends PureLogicUtils with SepLogicUtils with RuleUtils { else { val newPost = Assertion(exPost - PFormula(validExConjuncts), goal.post.sigma) val newGoal = goal.spawnChild(post = newPost) - List(RuleResult(List(newGoal), PureEntailmentProducer(goal.pre.phi, uniPost) >> IdProducer, this, goal)) + List(RuleResult(List(newGoal), PureEntailmentProducer(goal.pre.phi, uniPost, goal.gamma) >> IdProducer, this, goal)) } } From 05cbf0ab92a74c651e1ef79194117bc747fabf3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Tue, 2 Mar 2021 17:47:38 +0800 Subject: [PATCH 175/211] Iris: now axiom-free --- .../targets/iris/IrisCertificate.scala | 9 ++----- .../targets/iris/logic/Assertions.scala | 24 ++++++++++++++++++- .../targets/iris/logic/IProofStep.scala | 7 ++++++ .../iris/translation/ProofTranslator.scala | 13 +++++----- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 565643326..8c40b0e6d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -6,7 +6,7 @@ import org.tygus.suslik.certification.targets.iris.logic.Assertions.{IFunSpec, I import org.tygus.suslik.certification.{Certificate, CertificateOutput, CertificationTarget, CoqOutput} case object IrisCertificate { - def prelude(printAxioms: Boolean = true, importCommon: String = ""): String = { + def prelude(importCommon: String = ""): String = { s"""From SSL_Iris Require Import core. |From iris.program_logic Require Export weakestpre. |From iris.proofmode Require Export tactics coq_tactics ltac_tactics reduction. @@ -16,12 +16,7 @@ case object IrisCertificate { |From Hammer Require Import Hammer. |Context `{!heapG Σ}. |Set Default Proof Using "Type". - |""".stripMargin + (if (printAxioms) - s"""Axiom NilNotLval: - | forall x v, - | x ↦ v -∗ x ↦ v ∗ ⌜x ≠ null_loc⌝. - |""".stripMargin - else "") + |""".stripMargin } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala index e00d8ca90..11bb1e561 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/Assertions.scala @@ -291,11 +291,33 @@ object Assertions { s"∃ ${cons.constructorArgs.mkString(" ")}, ${cardinalityParam} = (${predicate.constructorName(cons)} ${cons.constructorArgs.mkString(" ")})" } + val appHyp = "P" + + def proofPerClause(constr: CardConstructor, pclause: IPredicateClause) = { + val coqHyps = findExistentials(constr)(pclause).map{ case (v, _) => ICoqName(v) } + val pureIntro = pclause.pure.map(_ => IPure("")) + val spatialIntro = pclause.spatial.map(_ => IIdent("?")) + val irisHyps = IPatDestruct(List(IPatDestruct(pureIntro), IPatDestruct(spatialIntro))) + val destruct = IDestruct(IIdent(appHyp), coqHyps, irisHyps) + val exists = IExistsMany(coqHyps) + val str = + s"""|- ${destruct.pp} + | iSplitL. + | ${exists.pp} iFrame. eauto. + | eauto.""".stripMargin + str + } + s"Lemma ${predicate.learnLemmaName(cardConstructor)} " + s"${predicate.params.map({ case (name, proofType) => s"(${name}: ${proofType.pp})" }).mkString(" ")} " + s"${cardinalityParam}:\n" + s"${ppPred} ${cardinalityParam} ⊢ ${ppPred} ${cardinalityParam} ∗ ⌜${pclause.selector.ppAsPhi} -> ${ppEqualityTerm(cardConstructor)}⌝.\n" + - s"Proof. Admitted.\n" + s"Proof.\n" + + s"Transparent ${predicate.name}.\n" + + s"""destruct ${cardinalityParam}; iIntros "$appHyp".\n""" + + s"${predicate.clauses.map{ case (constr, clause) => proofPerClause(constr, clause) }.mkString("\n")}\n" + + s"Global Opaque ${predicate.name}.\n" + + s"Qed.\n" } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala index cf69a10a8..21b849245 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/logic/IProofStep.scala @@ -154,6 +154,10 @@ case class IWpApply(applyName: String, exs: Seq[IPureAssertion], } } +case class IExistsMany(ex: List[ICoqName]) extends IProofStep { + override def pp: String = if (ex.isEmpty) "" else s"iExists ${ex.map(_.pp).mkString(", ")}." +} + case class IExists(e: IPureAssertion) extends IProofStep { override def pp: String = s"iExists ${e.ppAsPhi}." } @@ -211,6 +215,9 @@ case object IFree extends IProofStep { override def pp: String = "ssl_free." } +case object IMovePure extends IProofStep { + override def pp: String = "movePure." +} case object IFinish extends IProofStep { override def pp: String = "ssl_finish." diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala index 7b1af5b77..dab3f5c62 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala @@ -250,7 +250,8 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I ctx = ctx withMappingBetween(ghostFrom, ISpecVar(ghostTo, HLocType())) ctx = ctx withRenaming ghostFrom -> ghostTo val steps = List ( - IMalloc(ICoqName(ghostTo), sz) + IMalloc(ICoqName(ghostTo), sz), + IMovePure ) Result(steps, List(withNoDeferreds(ctx))) @@ -282,9 +283,9 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I Result(List(IStore), List(withNoDeferreds(clientCtx))) case SuslikProofStep.EmpRule(_) => Result(List(IEmp), List()) - case SuslikProofStep.NilNotLval(vars) => - val steps = vars.map { case Var(name) => INilNotVal(name, clientCtx.freshHypName() )} - Result(steps, List(withNoDeferreds(clientCtx))) + case SuslikProofStep.NilNotLval(_) => +// val steps = vars.map { case Var(name) => INilNotVal(name, clientCtx.freshHypName() )} + Result(List(), List(withNoDeferreds(clientCtx))) // case SuslikProofStep.PickArg(Var(from), to) => // val newCtx = clientCtx withMappingBetween (from,to) @@ -306,11 +307,11 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I ISpecVar(name, HCardType(predType)) )) } - var ctx = clientCtx withMappingBetween (from, cardExpr) + val ctx = clientCtx withMappingBetween(from, cardExpr) withNoOp(ctx) /** Ignored rules */ - case SuslikProofStep.CheckPost(_, _) + case SuslikProofStep.CheckPost(_, _, _) | SuslikProofStep.WeakenPre(_) | SuslikProofStep.HeapUnify(_) | SuslikProofStep.HeapUnifyUnfold(_, _, _) From 448c303d27b1e32eecbd939d36ed870426939d3e Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Tue, 2 Mar 2021 18:28:09 +0800 Subject: [PATCH 176/211] export script --- .../synthesis/CertificationBenchmarks.scala | 24 +++-- .../synthesis/CertificationExports.scala | 92 +++++++++++++++++++ 2 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index fa361938f..be6e2f5d1 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -18,7 +18,7 @@ import org.tygus.suslik.util.{SynStatUtil, SynStats} import scala.sys.process._ -abstract class CertificationBenchmarks extends SynthesisRunnerUtil { +trait CertificationBenchmarks extends SynthesisRunnerUtil { val targets: List[CertificationTarget] val statsFile: File = new File("cert-stats.csv") val defFilename: String = "common" @@ -51,7 +51,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { throw SynthesisException(s"Failed to synthesise:\n$sresult") case procs => synthesizer.trace match { - case trace:ProofTraceCert => + case trace: ProofTraceCert => CertTree.fromTrace(trace) case _ => } @@ -97,7 +97,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"Synthesizing ${tests.length} test cases...") - val synResults = for ((testName, in, params) <- testCases) yield { + val synResults = for ((testName, in, params) <- testCases) yield { println(s"$testName:") val fullInput = List(defs, in).mkString("\n") @@ -153,13 +153,13 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { } } - outputs.find(_.isProof) match { + outputs.find(_.isProof) match { case Some(proof) => print(s" Checking proof size...") val (specSize, proofSize) = checkProofSize(tempDir, proof.filename) println(s"done! (spec: $specSize, proof: $proofSize)") print(s" Compiling proof...") - val (res, proofCheckDuration) = timed (proof.compile(tempDir)) + val (res, proofCheckDuration) = timed(proof.compile(tempDir)) if (res == 0) { println(s"done! (${fmtTime(proofCheckDuration)} s)") testName -> (synDuration, Some(proofGenDuration), Some(specSize, proofSize), Some(proofCheckDuration)) @@ -171,7 +171,8 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { println(s"Certificate was generated, but no proof output was found; skipping verification...") testName -> (synDuration, Some(proofGenDuration), None, None) } - }}.toMap + } + }.toMap println(s"\n****************************************\n") target -> testToStats @@ -186,9 +187,11 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { } } - private def serialize(dir: File, filename: String, body: String): Unit = { + def serialize(dir: File, filename: String, body: String): Unit = { val file = Paths.get(dir.getCanonicalPath, filename).toFile - new PrintWriter(file) { write(body); close() } + new PrintWriter(file) { + write(body); close() + } } private def checkProofSize(dir: File, filename: String): (Int, Int) = { @@ -219,6 +222,7 @@ abstract class CertificationBenchmarks extends SynthesisRunnerUtil { val proofCheckStr = proofCheckDuration.map(fmtTime).getOrElse("-") s"${fmtTime(synDuration)}, $proofGenStr, $proofCheckStr, $proofStatsStr" } + val data = s"$name, ${targetResults.map(ppRes).mkString(", ")}\n" SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) } @@ -238,4 +242,6 @@ object CertificationBenchmarks { benchmarks.runAllTestsFromDir("certification-benchmarks/dll") benchmarks.runAllTestsFromDir("certification-benchmarks/tree") } -} \ No newline at end of file +} + + diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala new file mode 100644 index 000000000..2c52802fe --- /dev/null +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala @@ -0,0 +1,92 @@ +package org.tygus.suslik.synthesis + +import java.io.File + +import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.certification.targets.vst.VST +import org.tygus.suslik.parsing.SSLParser +import org.tygus.suslik.report.StopWatch.timed + +case class CertificationExports(target: CertificationTarget, export_folder: String, common_name: String = "common") extends CertificationBenchmarks { + override val targets: List[CertificationTarget] = List(target) + + def dump_all_tests(base_dir: String, test_folder: String): Unit = { + println(s"=========================================\n") + println(s" Benchmark Group: $test_folder\n") + println(s"=========================================\n") + val path = List(rootDir, base_dir, test_folder).mkString(File.separator) + val testDir = new File(path) + val export_path = List(export_folder, test_folder).mkString(File.separator) + val export_dir = new File(export_path) + export_dir.mkdirs() + + if (testDir.exists() && testDir.isDirectory) { + print(s"Retrieving definitions and specs from ${testDir.getName}...") + val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) + val tests = testDir.listFiles.filter(f => f.isFile && (f.getName.endsWith(s".$testExtension") || f.getName.endsWith(s".$sketchExtension"))).toList + println("done!") + + val parser = new SSLParser + + val testCases = for (f <- tests) yield { + val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) + (testName, in, params) + } + + println(s"\n****************************************\n") + println(s"Running benchmarks for certification target: ${target.name}") + println(s"Initialized output directory: ${export_dir.getCanonicalPath}\n") + println(s"Synthesizing ${tests.length} test cases...") + val synResults = for ((testName, in, params) <- testCases) yield { + println(s"$testName:") + val fullInput = List(defs, in).mkString("\n") + print(s" synthesizing in certification mode...") + val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) + print(s" generating certificate...") + try { + val (cert, proofGenDuration) = timed(target.certify(res.head, env)) + println("done!") + (testName, synDuration, Some(cert, proofGenDuration)) + } catch { + case e => + println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") + (testName, synDuration, None) + } + } + println(s"Successfully synthesized ${tests.length} tests.") + + val validCerts = synResults.map(_._3).filter(_.isDefined).map(_.get).map(_._1) + val predicates = validCerts.flatMap(_.predicates).groupBy(_.name).map(_._2.head).toList + val defFiles = target.generate_common_definitions_of(defFilename, predicates) + defFiles.foreach { + case output => + print(s"\nWriting common definitions to file $export_path/${output.filename}...") + serialize(export_dir, output.filename, output.body) + } + println("done!") + + + synResults.foreach { case (testName, synDuration, certOpt) => certOpt match { + case None => () + case Some((cert, proofGenDuration)) => + val outputs = cert.outputs_with_common_predicates(defFilename, predicates) + for (o <- outputs) yield { + print(s" Writing certificate output to file ${o.filename}...") + serialize(export_dir, o.filename, o.body) + } + } + } + } + } +} +object VSTCertificationExports { + def main(args: Array[String]): Unit = { + val benchmarks = CertificationExports(VST(), "/tmp/benchmarks") + benchmarks.initLog() + benchmarks.dump_all_tests("certification-benchmarks", "ints") + benchmarks.dump_all_tests("certification-benchmarks", "sll-bounds") + benchmarks.dump_all_tests("certification-benchmarks", "sll") + benchmarks.dump_all_tests("certification-benchmarks", "dll") + benchmarks.dump_all_tests("certification-benchmarks", "tree") + } +} \ No newline at end of file From c5590c8efb5cdc3d8c103a6edea53a89c2030bd2 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 2 Mar 2021 18:32:14 +0800 Subject: [PATCH 177/211] fix vst checkpost --- .../targets/vst/translation/VSTProofTranslator.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 868091ea0..31dc3151c 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -457,7 +457,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl Result(valid_pointer_rules, List(with_no_deferreds(clientContext))) /** Ignored rules */ - case SuslikProofStep.CheckPost(_, _) + case SuslikProofStep.CheckPost(_, _, _) | SuslikProofStep.WeakenPre(_) | SuslikProofStep.HeapUnify(_) | SuslikProofStep.HeapUnifyUnfold(_, _, _) From 617baed3621860f9a73b65555e2662f3479fb08f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20P=C3=AErlea?= Date: Tue, 2 Mar 2021 18:43:28 +0800 Subject: [PATCH 178/211] Iris: certification export --- .../vst/translation/VSTProgramTranslator.scala | 2 +- .../suslik/synthesis/CertificationExports.scala | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala index 8289f4d30..6ee5577ea 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala @@ -87,7 +87,7 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS implicit val ctx: VSTProgramTranslator.VSTProgramContext = clientContext value match { case SuslikProofStep.NilNotLval(_) - | SuslikProofStep.CheckPost(_, _) + | SuslikProofStep.CheckPost(_, _, _) | SuslikProofStep.Pick(_, _) | SuslikProofStep.Close(_, _, _, _) | SuslikProofStep.StarPartial(_, _) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala index 2c52802fe..240eeb76d 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala @@ -1,8 +1,8 @@ package org.tygus.suslik.synthesis import java.io.File - import org.tygus.suslik.certification.CertificationTarget +import org.tygus.suslik.certification.targets.iris.Iris import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.StopWatch.timed @@ -89,4 +89,16 @@ object VSTCertificationExports { benchmarks.dump_all_tests("certification-benchmarks", "dll") benchmarks.dump_all_tests("certification-benchmarks", "tree") } +} + +object IrisCertificationExports { + def main(args: Array[String]): Unit = { + val benchmarks = CertificationExports(Iris(), "/tmp/benchmarks") + benchmarks.initLog() + benchmarks.dump_all_tests("certification-benchmarks", "ints") + benchmarks.dump_all_tests("certification-benchmarks", "sll-bounds") + benchmarks.dump_all_tests("certification-benchmarks", "sll") + benchmarks.dump_all_tests("certification-benchmarks", "dll") + benchmarks.dump_all_tests("certification-benchmarks", "tree") + } } \ No newline at end of file From f5381814bd9a967a7c76e8169945f8680601a690 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 13 May 2021 15:42:02 +0900 Subject: [PATCH 179/211] Add advanced cert benchmarks --- .../bst/bst-find-smallest.syn | 30 ++++++++++++++ .../bst/bst-insert.syn | 36 ++++++++++++++++ .../bst/bst-left-rotate.syn | 19 +++++++++ .../bst/bst-remove-root-no-left.syn | 36 ++++++++++++++++ .../bst/bst-remove-root-no-right.syn | 36 ++++++++++++++++ .../bst/bst-right-rotate.syn | 19 +++++++++ .../bst/common.def | 8 ++++ .../dll/common.def | 10 +++++ .../dll/dll-copy.syn | 41 +++++++++++++++++++ .../dll/dll-dupleton.syn | 22 ++++++++++ .../dll/sll-to-dll.syn | 41 +++++++++++++++++++ .../srtl/common.def | 11 +++++ .../srtl/insertion-sort-free.syn | 26 ++++++++++++ .../srtl/insertion-sort.syn | 27 ++++++++++++ .../srtl/srtl-insert.syn | 34 +++++++++++++++ .../srtl/srtl-prepend.syn | 16 ++++++++ 16 files changed, 412 insertions(+) create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-find-smallest.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-insert.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-left-rotate.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-left.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-right.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-right-rotate.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/bst/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/dll/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-copy.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-dupleton.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/dll/sll-to-dll.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/srtl/common.def create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort-free.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-insert.syn create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-prepend.syn diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-find-smallest.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-find-smallest.syn new file mode 100644 index 000000000..0e0af61bc --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-find-smallest.syn @@ -0,0 +1,30 @@ +# -x true -c 2 -p true + +binary search tree: find smallest element + +##### + +{ 0 <= sz /\ 0 <= lo /\ hi <= 7; + retv :-> unused ** + bst(x, sz, lo, hi) } + +void bst_find_smallest (loc x, loc retv) + +{ retv :-> lo ** + bst(x, sz, lo, hi) } + +##### + +{0 <= lo && 0 <= sz && hi <= 7 ; retv :-> unused ** bst(x, sz, lo, hi)} +{retv :-> lo ** bst(x, sz, lo, hi)} +void bst_find_smallest (loc x, loc retv) { + if (x == 0) { + *retv = 7; + } else { + let v = *x; + let lx = *(x + 1); + bst_find_smallest(lx, retv); + let l = *retv; + *retv = v <= l ? v : l; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-insert.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-insert.syn new file mode 100644 index 000000000..107c540c4 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-insert.syn @@ -0,0 +1,36 @@ +# -b true -c 2 +binary search tree: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; retv :-> k ** bst(x, n, lo, hi) } +void bst_insert (loc x, loc retv) +{b == a + 3 /\ n1 == n + 1 /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; retv :-> y ** bst(y, n1, lo1, hi1) } + +##### + +void bst_insert (loc x, loc retv) { + let k = *retv; + if (x == 0) { + let y = malloc(3); + *retv = y; + *(y + 1) = 0; + *(y + 2) = 0; + *y = k; + } else { + let v = *x; + if (v <= k) { + let r = *(x + 2); + bst_insert(r, retv); + let y = *retv; + *retv = x; + *(x + 2) = y; + } else { + let l = *(x + 1); + bst_insert(l, retv); + let y = *retv; + *(x + 1) = y; + *retv = x; + } + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-left-rotate.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-left-rotate.syn new file mode 100644 index 000000000..c235f50e3 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-left-rotate.syn @@ -0,0 +1,19 @@ +binary search tree: rotate left + +##### + +{ not (r == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 /\ 0 <= a /\ 0 <= b ; + retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +void bst_left_rotate (loc x, loc retv) +{ sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; + retv :-> y ** [y, 3] ** y :-> v3 ** (y + 1) :-> x ** (y + 2) :-> r3 ** bst(x, sz3, lo3, hi3) ** bst(r3, sz4, lo4, hi4) } + +##### + +void bst_left_rotate (loc x, loc retv) { + let r = *(x + 2); + let l = *(r + 1); + *(r + 1) = x; + *retv = r; + *(x + 2) = l; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-left.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-left.syn new file mode 100644 index 000000000..ec47e37f1 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-left.syn @@ -0,0 +1,36 @@ +# -x true -c 2 -p true + +binary search tree: delete root + +Simple case: no left subtree + +##### + +{ l == null /\ + 0 <= sz1 /\ 0 <= sz2 /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + retv :-> unused ** + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** + bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } + +void bst_remove_root_no_left (loc x, loc retv) + +{ n1 == sz1 + sz2 /\ + lo == (l == null ? (r == null ? 7 : lo2) : lo1) /\ + hi == (r == null ? (l == null ? 0 : hi1) : hi2) ; + retv :-> y ** bst(y, n1, lo, hi) } + +##### + +{0 <= sz1 && 0 <= sz2 && 0 <= v && hi1 <= v && l == 0 && v <= 7 && v <= lo2 ; (x + 1) :-> l ** (x + 2) :-> r ** retv :-> unused ** x :-> v ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) ** [x, 3]} +{hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) && lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) && n1 == sz1 + sz2 ; retv :-> y ** bst(y, n1, lo, hi)} +void bst_remove_root_no_left (loc x, loc retv) { + let r = *(x + 2); + if (r == 0) { + free(x); + *retv = 0; + } else { + free(x); + *retv = r; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-right.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-right.syn new file mode 100644 index 000000000..196b3361c --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-remove-root-no-right.syn @@ -0,0 +1,36 @@ +# -x true -c 2 -p true + +binary search tree: delete root + +Simple case: no right subtree + +##### + +{ r == null /\ + 0 <= sz1 /\ 0 <= sz2 /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + retv :-> unused ** + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** + bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } + +void bst_remove_root_no_right (loc x, loc retv) + +{ n1 == sz1 + sz2 /\ + lo == (l == null ? (r == null ? 7 : lo2) : lo1) /\ + hi == (r == null ? (l == null ? 0 : hi1) : hi2) ; + retv :-> y ** bst(y, n1, lo, hi) } + +##### + +{0 <= sz1 && 0 <= sz2 && 0 <= v && hi1 <= v && r == 0 && v <= 7 && v <= lo2 ; (x + 1) :-> l ** (x + 2) :-> r ** retv :-> unused ** x :-> v ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) ** [x, 3]} +{hi == (r == 0 ? (l == 0 ? 0 : hi1) : hi2) && lo == (l == 0 ? (r == 0 ? 7 : lo2) : lo1) && n1 == sz1 + sz2 ; retv :-> y ** bst(y, n1, lo, hi)} +void bst_remove_root_no_right (loc x, loc retv) { + let l = *(x + 1); + if (l == 0) { + free(x); + *retv = 0; + } else { + free(x); + *retv = l; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-right-rotate.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-right-rotate.syn new file mode 100644 index 000000000..657219333 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/bst-right-rotate.syn @@ -0,0 +1,19 @@ +binary search tree: rotate right + +##### + +{ not (l == null) /\ 0 <= sz1 /\ 0 <= sz2 /\ 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + retv :-> unused ** [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +void bst_right_rotate (loc x, loc retv) +{ sz3 + sz4 == sz1 + sz2 /\ 0 <= sz3 /\ 0 <= sz4 /\ 0 <= v3 /\ v3 <= 7 /\ hi3 <= v3 /\ v3 <= lo4 ; + retv :-> y ** [y, 3] ** y :-> v3 ** (y + 1) :-> l3 ** (y + 2) :-> x ** bst(l3, sz3, lo3, hi3) ** bst(x, sz4, lo4, hi4) } + +##### + +void bst_right_rotate (loc x, loc retv) { + let l = *(x + 1); + let r = *(l + 2); + *(l + 2) = x; + *retv = l; + *(x + 1) = r; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/bst/common.def b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/common.def new file mode 100644 index 000000000..e6f72099e --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/bst/common.def @@ -0,0 +1,8 @@ +predicate bst(loc x, int sz, int lo, int hi) { +| x == null => { sz == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { sz == 1 + sz1 + sz2 /\ 0 <= sz1 /\ 0 <= sz2 /\ + lo == (v <= lo1 ? v : lo1) /\ + hi == (hi2 <= v ? v : hi2) /\ + 0 <= v /\ v <= 7 /\ hi1 <= v /\ v <= lo2 ; + [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** bst(l, sz1, lo1, hi1) ** bst(r, sz2, lo2, hi2) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/dll/common.def b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/common.def new file mode 100644 index 000000000..4091d0b28 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/common.def @@ -0,0 +1,10 @@ +predicate dll(loc x, loc z, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => + { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> w ** (x + 2) :-> z ** dll(w, x, s1) } +} + +predicate sll(loc x, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-copy.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-copy.syn new file mode 100644 index 000000000..a6c137c3f --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-copy.syn @@ -0,0 +1,41 @@ +# -c 2 -f 1 + +should be able to copy a list + +##### + +{true ; r :-> x ** dll(x, a, s)} +void dll_copy(loc r) +{true ; r :-> y ** dll(x, a, s) ** dll(y, b, s) } + +##### + +void dll_copy (loc r) { + let x = *r; + if (x == null) { + *r = null; + } else { + let vx = *x; + let w = *(x + 1); + *r = w; + dll_copy(r); + let y1 = *r; + if (y1 == null) { + let y = malloc(3); + *r = y; + *(y + 1) = null; + *y = vx; + *(y + 2) = null; + } else { + let v = *y1; + let y = malloc(3); + *(y1 + 2) = y; + *r = y; + *(y + 1) = y1; + *y1 = vx; + *y = v; + *(y + 2) = null; + } + } +} + diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-dupleton.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-dupleton.syn new file mode 100644 index 000000000..c5c51a20e --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/dll-dupleton.syn @@ -0,0 +1,22 @@ +# -c 3 +doubly-linked list: construct a list with two elements + +##### + +{ r :-> a } +void dll_dupleton (int x, int y, loc r) +{ elems =i {x, y} ; r :-> z ** dll(z, null, elems) } + +##### + +void dll_dupleton (int x, int y, loc r) { + let z = malloc(3); + let w = malloc(3); + *r = z; + *(z + 1) = w; + *(z + 2) = 0; + *(w + 1) = 0; + *(w + 2) = z; + *z = y; + *w = x; +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/dll/sll-to-dll.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/sll-to-dll.syn new file mode 100644 index 000000000..4a41ba863 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/dll/sll-to-dll.syn @@ -0,0 +1,41 @@ +# -c 2 + +should be able to convert a singly-linked list to a double-linked list + +####### + +{ f :-> x ** sll(x, s)} +void sll_to_dll(loc f) +{ f :-> i ** dll(i, null, s)} + +####### + +void sll_to_dll (loc f) { + let x = *f; + if (x == 0) { + *f = 0; + } else { + let v = *x; + let n = *(x + 1); + *f = n; + sll_to_dll(f); + let i1 = *f; + if (i1 == 0) { + let i = malloc(3); + free(x); + *f = i; + *(i + 1) = 0; + *i = v; + *(i + 2) = 0; + } else { + let i = malloc(3); + free(x); + *(i1 + 2) = i; + *f = i; + *(i + 1) = i1; + *i = v; + *(i + 2) = 0; + } + } +} + diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/common.def b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/common.def new file mode 100644 index 000000000..bbd264f9d --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/common.def @@ -0,0 +1,11 @@ +predicate sll(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } +} + +predicate srtl(loc x, int len, int lo, int hi) { +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ v <= lo1 /\ 0 <= v /\ v <= 7 ; + [x, 2] ** x :-> v ** (x + 1) :-> nxt ** srtl(nxt, len1, lo1, hi1) } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort-free.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort-free.syn new file mode 100644 index 000000000..e889d6a8d --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort-free.syn @@ -0,0 +1,26 @@ +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == 1 + n /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +{ 0 <= n ; r :-> null ** sll(x, n, lo, hi) } +void insertion_sort_free (loc x, loc r) +{ true ; r :-> y ** srtl(y, n, lo, hi) } + +##### + +void insertion_sort_free (loc x, loc r) { + if (x == null) { + } else { + let n = *(x + 1); + insertion_sort_free(n, r); + let yn = *r; + srtl_insert(yn, x); + let y = *x; + free(x); + *r = y; + } +} diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort.syn new file mode 100644 index 000000000..7dfa81d31 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/insertion-sort.syn @@ -0,0 +1,27 @@ +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == 1 + n /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +{ 0 <= n ; r :-> null ** sll(x, n, lo, hi) } +void insertion_sort (loc x, loc r) +{ true ; r :-> y ** sll(x, n, lo, hi) ** srtl(y, n, lo, hi) } + +##### + +void insertion_sort (loc x, loc r) { + if (x == null) { + } else { + let v = *x; + let n = *(x + 1); + insertion_sort(n, r); + let yn = *r; + srtl_insert(yn, x); + let y = *x; + *r = y; + *x = v; + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-insert.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-insert.syn new file mode 100644 index 000000000..1e2d36b49 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-insert.syn @@ -0,0 +1,34 @@ +#. -b true -c 2 +sorted list: insert an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 ; r :-> k ** srtl(x, n, lo, hi) } +void srtl_insert (loc x, loc r) +{n1 == n + 1 /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } + +##### + +void srtl_insert (loc x, loc r) { + let k = *r; + if (x == null) { + let y = malloc(2); + *r = y; + *(y + 1) = null; + *y = k; + } else { + let v = *x; + if (v <= k) { + let n = *(x + 1); + srtl_insert(n, r); + let y = *r; + *r = x; + *(x + 1) = y; + } else { + let y = malloc(2); + *r = y; + *(y + 1) = x; + *y = k; + } + } +} \ No newline at end of file diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-prepend.syn b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-prepend.syn new file mode 100644 index 000000000..ce20fc4b9 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/srtl/srtl-prepend.syn @@ -0,0 +1,16 @@ +sorted list: prepend an element + +##### + +{0 <= n /\ 0 <= k /\ k <= 7 /\ k <= lo ; r :-> a ** srtl(x, n, lo, hi) } +void srtl_prepend (loc x, int k, loc r) +{n1 == n + 1 ; r :-> y ** srtl(y, n1, k, hi1) } + +##### + +void srtl_prepend (loc x, int k, loc r) { + let y = malloc(2); + *r = y; + *(y + 1) = x; + *y = k; +} \ No newline at end of file From 47ad09148f039f67cd5f80a8b4a3b865abeb69d4 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 14 May 2021 18:10:53 +0900 Subject: [PATCH 180/211] Remove old flags --- README.md | 2 -- certification.md | 15 --------------- .../org/tygus/suslik/synthesis/SynConfig.scala | 2 -- .../tygus/suslik/synthesis/SynthesisRunner.scala | 10 ---------- .../suslik/synthesis/SynthesisRunnerUtil.scala | 11 ----------- 5 files changed, 40 deletions(-) diff --git a/README.md b/README.md index bb265765e..a433fbf89 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,6 @@ where the necessary arguments and options are dump entire proof search trace to a json file; default: none --memo enable memoization; default: true --lexi use lexicographic termination metric (as opposed to total size); default: false - --printTree print tree of successful derivations to path; default: false - --treeDest write tree of successful derivations to path; default: none --certTarget set certification target; default: none --certDest write certificate to path; default: none diff --git a/certification.md b/certification.md index 087fe5118..006e28e86 100644 --- a/certification.md +++ b/certification.md @@ -46,18 +46,3 @@ By providing the `--certDest` flag, SuSLik writes out this certificate as a file ```bash ./suslik examples/listfree.syn --assert false --certTarget htt --certDest . ``` - -### Viewing The Trace - -This package extracts the successful derivations from the synthesis process into a tree structure, for post-hoc access to information needed for certification. - -A standalone representation of this tree can be printed via the the following flags. - -- `--printTree `: Set to `true` to enable printing. -- `--treeDest ` (optional): Specifies the path to output the tree as a file. If not provided, logs output to console instead. - -For example, the following command logs the tree to a file called `trace.out` in the project root directory. - -```bash -./suslik examples/listfree.syn --assert false --printTree true --treeDest trace.out -``` diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala index 82fedc6d7..5285c335c 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala @@ -48,8 +48,6 @@ case class SynConfig( traceToJsonFile: Option[File] = None, timeOut: Long = 120000, startTime: Deadline = Deadline.now, - printTree: Boolean = false, - treeDest: File = null, // Certification certTarget: CertificationTarget = NoCert, certDest: File = null, diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index e6116bfd0..6f5bf32dc 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -45,8 +45,6 @@ object SynthesisRunner extends SynthesisRunnerUtil { * -j, --traceToJsonFile dump entire proof search trace to a json file; default: none * --memo enable memoization; default: true * --lexi use lexicographic termination metric (as opposed to total size); default: false - * --printTree print tree of successful derivations to path; default: false - * --treeDest write tree of successful derivations to path; default: none * --certTarget set certification target; default: none * --certDest write certificate to path; default: none * @@ -215,14 +213,6 @@ object SynthesisRunner extends SynthesisRunnerUtil { rc.copy(synConfig = rc.synConfig.copy(termination = if (b) lexicographic else totalSize)) }.text("use lexicographic termination metric (as opposed to total size); default: false") - opt[Boolean](name="printTree").action { (b, rc) => - rc.copy(synConfig = rc.synConfig.copy(printTree = b)) - }.text("print tree of successful derivations to path; default: false") - - opt[File](name="treeDest").action { (f, rc) => - rc.copy(synConfig = rc.synConfig.copy(treeDest = f)) - }.text("write tree of successful derivations to path; default: none") - opt[CertificationTarget](name="certTarget").action { (t, rc) => rc.copy(synConfig = rc.synConfig.copy(certTarget = t)) }.text("set certification target; default: none") diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index c12b8a3f6..78cdcbf0f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -181,16 +181,6 @@ trait SynthesisRunnerUtil { case _ => } - def printCertTree(): Unit = if (params.printTree) { - val tree = CertTree.pp() - println() - if (params.treeDest == null) println(tree) else { - new PrintWriter(params.treeDest) { write(tree); close() } - val msg = s"Successful derivations saved to ${params.treeDest.getCanonicalPath}" - testPrintln(msg, Console.MAGENTA) - } - } - sresult._1 match { case Nil => printStats(sresult._2) @@ -207,7 +197,6 @@ trait SynthesisRunnerUtil { // [Certify] initialize and print cert tree initCertTree(synthesizer.trace) - printCertTree() if (params.printStats) { testPrintln(s"\n[$testName]:", Console.MAGENTA) From 888292f554bf8277870046f146b216387ea64a83 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 14 May 2021 22:52:05 +0900 Subject: [PATCH 181/211] Make env accessible to translator --- .../htt/translation/HTTTranslator.scala | 190 +++++++++++------- .../htt/translation/ProgramContext.scala | 3 +- .../htt/translation/ProgramTranslator.scala | 1 + .../htt/translation/ProofContext.scala | 5 +- .../htt/translation/ProofTranslator.scala | 1 + .../htt/translation/TranslatableOps.scala | 4 +- .../targets/htt/translation/Translation.scala | 6 +- 7 files changed, 129 insertions(+), 81 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala index f51734135..8efe48942 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -12,109 +12,149 @@ import org.tygus.suslik.logic.Specifications.{Assertion, Goal} import org.tygus.suslik.logic._ trait HTTTranslator[A,B] { - def translate(value: A): B + def translate(value: A)(implicit env: Environment): B } object HTTTranslator { case class TranslatorException(msg: String) extends Exception(msg) - implicit val binOpTranslator: HTTTranslator[BinOp, CBinOp] = { - case OpImplication => COpImplication - case OpPlus => COpPlus - case OpMinus => COpMinus - case OpMultiply => COpMultiply - case OpEq => COpEq - case OpBoolEq => COpBoolEq - case OpLeq => COpLeq - case OpLt => COpLt - case OpAnd => COpAnd - case OpOr => COpOr - case OpUnion => COpUnion - case OpDiff => COpDiff - case OpIn => COpIn - case OpSetEq => COpSetEq - case OpSubset => COpSubset - case OpIntersect => COpIntersect - } - implicit val unopTranslator: HTTTranslator[UnOp, CUnOp] = { - case OpNot => COpNot - case OpUnaryMinus => COpUnaryMinus - } - - implicit val exprTranslator: HTTTranslator[Expr, CExpr] = (value: Expr) => { - def visit(value: Expr): CExpr = value match { - case Var(name) => CVar(name) - case BoolConst(value) => CBoolConst(value) - case LocConst(value) => CPtrConst(value) - case IntConst(value) => CNatConst(value) - case UnaryExpr(op, arg) => CUnaryExpr(op.translate, visit(arg)) - case BinaryExpr(op, left, right) => CBinaryExpr(op.translate, visit(left), visit(right)) - case SetLiteral(elems) => CSetLiteral(elems.map(visit)) - case IfThenElse(c, t, e) => CIfThenElse(visit(c), visit(t), visit(e)) - case _ => throw TranslatorException("Operation not supported") + implicit val binOpTranslator: HTTTranslator[BinOp, CBinOp] = new HTTTranslator[BinOp, CBinOp] { + override def translate(value: BinOp)(implicit env: Environment): CBinOp = value match { + case OpImplication => COpImplication + case OpPlus => COpPlus + case OpMinus => COpMinus + case OpMultiply => COpMultiply + case OpEq => COpEq + case OpBoolEq => COpBoolEq + case OpLeq => COpLeq + case OpLt => COpLt + case OpAnd => COpAnd + case OpOr => COpOr + case OpUnion => COpUnion + case OpDiff => COpDiff + case OpIn => COpIn + case OpSetEq => COpSetEq + case OpSubset => COpSubset + case OpIntersect => COpIntersect } - visit(value) } - implicit val varTranslator: HTTTranslator[Var, CVar] = (value: Var) => CVar(value.name) + implicit val unopTranslator: HTTTranslator[UnOp, CUnOp] = new HTTTranslator[UnOp, CUnOp] { + override def translate(value: UnOp)(implicit env: Environment): CUnOp = value match { + case OpNot => COpNot + case OpUnaryMinus => COpUnaryMinus + } + } - implicit val mallocTranslator: HTTTranslator[Statements.Malloc, CMalloc] = stmt => CMalloc(stmt.to.translate, stmt.tpe.translate, stmt.sz) - implicit val loadTranslator: HTTTranslator[Statements.Load, CLoad] = stmt => CLoad(stmt.to.translate, stmt.tpe.translate, stmt.from.translate, stmt.offset) - implicit val storeTranslator: HTTTranslator[Statements.Store, CStore] = stmt => CStore(stmt.to.translate, stmt.offset, stmt.e.translate) - implicit val callTranslator: HTTTranslator[Statements.Call, CCall] = stmt => CCall(stmt.fun.translate, stmt.args.map(_.translate)) + implicit val exprTranslator: HTTTranslator[Expr, CExpr] = new HTTTranslator[Expr, CExpr] { + override def translate(value: Expr)(implicit env: Environment): CExpr = { + def visit(value: Expr): CExpr = value match { + case Var(name) => CVar(name) + case BoolConst(value) => CBoolConst(value) + case LocConst(value) => CPtrConst(value) + case IntConst(value) => CNatConst(value) + case UnaryExpr(op, arg) => CUnaryExpr(op.translate, visit(arg)) + case BinaryExpr(op, left, right) => CBinaryExpr(op.translate, visit(left), visit(right)) + case SetLiteral(elems) => CSetLiteral(elems.map(visit)) + case IfThenElse(c, t, e) => CIfThenElse(visit(c), visit(t), visit(e)) + case _ => throw TranslatorException("Operation not supported") + } + visit(value) + } + } - implicit val typeTranslator: HTTTranslator[SSLType, HTTType] = { - case BoolType => CBoolType - case IntType => CNatType - case LocType => CPtrType - case IntSetType => CNatSeqType - case VoidType => CUnitType - case CardType => CCardType + implicit val varTranslator: HTTTranslator[Var, CVar] = new HTTTranslator[Var, CVar] { + override def translate(value: Var)(implicit env: Environment): CVar = CVar(value.name) } - implicit val paramTranslator: HTTTranslator[(Var, SSLType), (CVar, HTTType)] = - el => (el._1.translate, el._2.translate) + implicit val mallocTranslator: HTTTranslator[Statements.Malloc, CMalloc] = new HTTTranslator[Statements.Malloc, CMalloc] { + override def translate(stmt: Statements.Malloc)(implicit env: Environment): CMalloc = + CMalloc(stmt.to.translate, stmt.tpe.translate, stmt.sz) + } + implicit val loadTranslator: HTTTranslator[Statements.Load, CLoad] = new HTTTranslator[Statements.Load, CLoad] { + override def translate(stmt: Statements.Load)(implicit env: Environment): CLoad = + CLoad(stmt.to.translate, stmt.tpe.translate, stmt.from.translate, stmt.offset) + } + implicit val storeTranslator: HTTTranslator[Statements.Store, CStore] = new HTTTranslator[Statements.Store, CStore] { + override def translate(stmt: Statements.Store)(implicit env: Environment): CStore = + CStore(stmt.to.translate, stmt.offset, stmt.e.translate) + } + implicit val callTranslator: HTTTranslator[Statements.Call, CCall] = new HTTTranslator[Statements.Call, CCall] { + override def translate(stmt: Statements.Call)(implicit env: Environment): CCall = + CCall(stmt.fun.translate, stmt.args.map(_.translate)) + } - implicit val sappTranslator: HTTTranslator[SApp, CSApp] = el => CSApp(el.pred, el.args.map(_.translate), el.card.translate) + implicit val typeTranslator: HTTTranslator[SSLType, HTTType] = new HTTTranslator[SSLType, HTTType] { + override def translate(value: SSLType)(implicit env: Environment): HTTType = value match { + case BoolType => CBoolType + case IntType => CNatType + case LocType => CPtrType + case IntSetType => CNatSeqType + case VoidType => CUnitType + case CardType => CCardType + } + } - implicit val pointsToTranslator: HTTTranslator[PointsTo, CPointsTo] = el => CPointsTo(el.loc.translate, el.offset, el.value.translate) + implicit val paramTranslator: HTTTranslator[(Var, SSLType), (CVar, HTTType)] = new HTTTranslator[(Var, SSLType), (CVar, HTTType)] { + override def translate(el: (Var, SSLType))(implicit env: Environment): (CVar, HTTType) = (el._1.translate, el._2.translate) + } - implicit val asnTranslator: HTTTranslator[Assertion, CAssertion] = el => { - CAssertion(el.phi.translate, el.sigma.translate).removeCardConstraints + implicit val sappTranslator: HTTTranslator[SApp, CSApp] = new HTTTranslator[SApp, CSApp] { + override def translate(el: SApp)(implicit env: Environment): CSApp = CSApp(el.pred, el.args.map(_.translate), el.card.translate) } - implicit val pFormulaTranslator: HTTTranslator[PFormula, CPFormula] = el => { - CPFormula(el.conjuncts.toSeq.map(c => c.translate.simplify).filterNot(_.isCard)) + implicit val pointsToTranslator: HTTTranslator[PointsTo, CPointsTo] = new HTTTranslator[PointsTo, CPointsTo] { + override def translate(el: PointsTo)(implicit env: Environment): CPointsTo = CPointsTo(el.loc.translate, el.offset, el.value.translate) } - implicit val sFormulaTranslator: HTTTranslator[SFormula, CSFormula] = el => { - val ptss = el.ptss.map(_.translate) - val apps = el.apps.map(_.translate) - CSFormula("h", apps, ptss) + implicit val asnTranslator: HTTTranslator[Assertion, CAssertion] = new HTTTranslator[Assertion, CAssertion] { + override def translate(el: Assertion)(implicit env: Environment): CAssertion = CAssertion(el.phi.translate, el.sigma.translate).removeCardConstraints } - implicit val goalTranslator: HTTTranslator[Goal, Proof.Goal] = goal => { - val pre = goal.pre.translate - val post = goal.post.translate - val gamma = goal.gamma.map(_.translate) - val programVars = goal.programVars.map(_.translate) - val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) - Proof.Goal(pre, post, gamma, programVars, universalGhosts, goal.fname) + implicit val pFormulaTranslator: HTTTranslator[PFormula, CPFormula] = new HTTTranslator[PFormula, CPFormula] { + override def translate(el: PFormula)(implicit env: Environment): CPFormula = CPFormula(el.conjuncts.toSeq.map(c => c.translate.simplify).filterNot(_.isCard)) } - implicit val substTranslator: HTTTranslator[Map[Var, Expr], Map[CVar, CExpr]] = _.map { - case (k, v) => k.translate -> v.translate + implicit val sFormulaTranslator: HTTTranslator[SFormula, CSFormula] = new HTTTranslator[SFormula, CSFormula] { + override def translate(el: SFormula)(implicit env: Environment): CSFormula = { + val ptss = el.ptss.map(_.translate) + val apps = el.apps.map(_.translate) + CSFormula("h", apps, ptss) + } + } + + implicit val goalTranslator: HTTTranslator[Goal, Proof.Goal] = new HTTTranslator[Goal, Proof.Goal] { + override def translate(goal: Goal)(implicit env: Environment): Proof.Goal = { + val pre = goal.pre.translate + val post = goal.post.translate + val gamma = goal.gamma.map(_.translate) + val programVars = goal.programVars.map(_.translate) + val universalGhosts = (pre.valueVars ++ post.valueVars).distinct.filter(v => goal.universalGhosts.contains(Var(v.name))) + Proof.Goal(pre, post, gamma, programVars, universalGhosts, goal.fname) + } + } + + implicit val substTranslator: HTTTranslator[Map[Var, Expr], Map[CVar, CExpr]] = new HTTTranslator[Map[Var, Expr], Map[CVar, CExpr]] { + override def translate(el: Map[Var, Expr])(implicit env: Environment): Map[CVar, CExpr] = el.map { + case (k, v) => k.translate -> v.translate + } } - implicit val substVarTranslator: HTTTranslator[Map[Var, Var], Map[CVar, CVar]] = _.map { - case (k, v) => k.translate -> v.translate + implicit val substVarTranslator: HTTTranslator[Map[Var, Var], Map[CVar, CVar]] = new HTTTranslator[Map[Var, Var], Map[CVar, CVar]] { + override def translate(el: Map[Var, Var])(implicit env: Environment): Map[CVar, CVar] = el.map { + case (k, v) => k.translate -> v.translate + } } - implicit val substExprTranslator: HTTTranslator[Map[Expr, Expr], Map[CExpr, CExpr]] = _.map { - case (k, v) => k.translate -> v.translate + implicit val substExprTranslator: HTTTranslator[Map[Expr, Expr], Map[CExpr, CExpr]] = new HTTTranslator[Map[Expr, Expr], Map[CExpr, CExpr]] { + override def translate(el: Map[Expr, Expr])(implicit env: Environment): Map[CExpr, CExpr] = el.map { + case (k, v) => k.translate -> v.translate + } } - implicit val gammaTranslator: HTTTranslator[Map[Var, SSLType], Map[CVar, HTTType]] = _.map { - case (k, v) => k.translate -> v.translate + implicit val gammaTranslator: HTTTranslator[Map[Var, SSLType], Map[CVar, HTTType]] = new HTTTranslator[Map[Var, SSLType], Map[CVar, HTTType]] { + override def translate(el: Map[Var, SSLType])(implicit env: Environment): Map[CVar, HTTType] = el.map { + case (k, v) => k.translate -> v.translate + } } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala index cc0c868a1..c53d31977 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramContext.scala @@ -2,5 +2,6 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement import org.tygus.suslik.certification.traversal.Evaluator.ClientContext +import org.tygus.suslik.logic.Environment -case class ProgramContext() extends ClientContext[CStatement] +case class ProgramContext(env: Environment) extends ClientContext[CStatement] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala index b42505582..bfae921ca 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala @@ -9,6 +9,7 @@ import org.tygus.suslik.certification.traversal.Translator.Result object ProgramTranslator extends Translator[SuslikProofStep, CStatement, ProgramContext] { override def translate(value: SuslikProofStep, ctx: ProgramContext): Translator.Result[CStatement, ProgramContext] = { val withNoDeferred = (Nil, None, ctx) + implicit val env = ctx.env value match { case SuslikProofStep.Open(_, _, _, selectors) => val stmt = CIf(selectors.map(_.translate)) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala index 0b120bd83..ec0317d01 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofContext.scala @@ -6,6 +6,7 @@ import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CAssertion, CInductivePredicate} import org.tygus.suslik.certification.targets.htt.translation.ProofContext.{AppliedConstructor, PredicateEnv} import org.tygus.suslik.certification.traversal.Evaluator.{ClientContext, EvaluatorException} +import org.tygus.suslik.logic.Environment import scala.collection.immutable.ListMap import scala.collection.mutable.ListBuffer @@ -27,7 +28,9 @@ case class ProofContext(// Map of predicates referenced by the spec // Current number of subgoals; used to calculate how many times to shelve/unshelve numSubgoals: Int = 0, // Used to track which post-condition predicate assertions remain to be solved - appsToSolve: Seq[CSApp] = Seq.empty) + appsToSolve: Seq[CSApp] = Seq.empty, + // Synthesis environment + env: Environment) extends ClientContext[Proof.Step] { /** diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index c8f4c9cfd..b31220153 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -70,6 +70,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont } override def translate(value: SuslikProofStep, ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { + implicit val env = ctx.env value match { /** Initialization */ case SuslikProofStep.Init(goal) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala index a1ac3c367..c26df2a07 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/TranslatableOps.scala @@ -1,8 +1,10 @@ package org.tygus.suslik.certification.targets.htt.translation +import org.tygus.suslik.logic.Environment + object TranslatableOps { implicit class Translatable[S](value: S) { - def translate[D](implicit translator: HTTTranslator[S,D]): D = { + def translate[D](implicit translator: HTTTranslator[S,D], env: Environment): D = { translator.translate(value) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index f03c9c819..23fbb773d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -40,17 +40,17 @@ object Translation { val spec = node.goal.translate.toFunspec val suslikTree = SuslikProofStep.of_certtree(node) - val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint]) + val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint], env = env) val proofBody = ProofEvaluator.run(suslikTree, ctx) val proof = Proof(proofBody) val hints = ctx.hints.filter(_.numHypotheses > 0) - val progBody = ProgramEvaluator.run(suslikTree, ProgramContext()) + val progBody = ProgramEvaluator.run(suslikTree, ProgramContext(env)) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) HTTCertificate(cproc.name, cpreds.values.toList, spec, auxSpecs, proof, cproc, hints) } - private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma): CInductivePredicate = { + private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma)(implicit env: Environment): CInductivePredicate = { val cParams = el.params.map(_.translate) :+ (CVar("h"), CHeapType) val cGamma = gamma.translate From dc6677ff9ac0dec926feda8e99dffc6ca3b74965 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 14 May 2021 23:01:54 +0900 Subject: [PATCH 182/211] HTT: Make set representation toggleable --- .../certification/targets/htt/language/Expressions.scala | 8 ++------ .../targets/htt/translation/HTTTranslator.scala | 2 +- .../targets/htt/translation/ProofTranslator.scala | 4 +++- src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala | 4 +++- .../org/tygus/suslik/synthesis/SynthesisRunner.scala | 5 +++++ 5 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index d3ffceff4..cc4f10e8e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -136,13 +136,12 @@ object Expressions { case class CBinaryExpr(op: CBinOp, left: CExpr, right: CExpr) extends CExpr { override def pp: String = op match { case COpSubset => s"@sub_mem nat_eqType (mem (${left.pp})) (mem (${right.pp}))" -// case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" + case COpSetEq => s"@perm_eq nat_eqType (${left.pp}) (${right.pp})" case _ => s"(${left.pp}) ${op.pp} (${right.pp})" } override def ppProp: String = op match { case COpEq => s"(${left.pp}) ${op.ppProp} (${right.pp})" - case COpSetEq => s"(${left.pp}) ${op.ppProp} (${right.pp})" case _ => pp } } @@ -264,10 +263,7 @@ object Expressions { object COpIn extends CBinOp - object COpSetEq extends CBinOp { - override def ppProp: String = "=" - override def pp: String = "==" - } + object COpSetEq extends CBinOp object COpSubset extends CBinOp { override def pp: String = "<=" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala index 8efe48942..d0cbc7b76 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/HTTTranslator.scala @@ -33,7 +33,7 @@ object HTTTranslator { case OpUnion => COpUnion case OpDiff => COpDiff case OpIn => COpIn - case OpSetEq => COpSetEq + case OpSetEq => if (env.config.certSetRepr) COpSetEq else COpEq case OpSubset => COpSubset case OpIntersect => COpIntersect } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index b31220153..4449ae278 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -110,7 +110,9 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // initialize context with post-condition info and predicate hints, if any val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap -// ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) + if (env.config.certSetRepr) { + ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) + } val postEx = ListMap(cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)): _*) val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala index 5285c335c..9f74dd31f 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala @@ -51,6 +51,7 @@ case class SynConfig( // Certification certTarget: CertificationTarget = NoCert, certDest: File = null, + certSetRepr: Boolean = false, // Internal (not directly settable through CLI) inputFormat: InputFormat = dotSyn, script: List[Int] = List() @@ -65,7 +66,8 @@ case class SynConfig( (if (depthFirst == defaultConfig.depthFirst) Nil else List(s"depthFirst = $depthFirst")) ++ (if (memoization == defaultConfig.memoization) Nil else List(s"memoization = $memoization")) ++ (if (certTarget == defaultConfig.certTarget) Nil else List(s"certTarget = ${certTarget.name}")) ++ - (if (certDest == defaultConfig.certDest) Nil else List(s"certDest = ${certDest.getCanonicalPath}")) + (if (certDest == defaultConfig.certDest) Nil else List(s"certDest = ${certDest.getCanonicalPath}")) ++ + (if (certSetRepr == defaultConfig.certSetRepr) Nil else List(s"certSetRepr = $certSetRepr")) ).mkString(", ") } diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 6f5bf32dc..b2c5ac899 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -47,6 +47,7 @@ object SynthesisRunner extends SynthesisRunnerUtil { * --lexi use lexicographic termination metric (as opposed to total size); default: false * --certTarget set certification target; default: none * --certDest write certificate to path; default: none + * --certSetRepr use list permutations to represent sets (HTT only); default: false * * --help prints the help reference * @@ -221,6 +222,10 @@ object SynthesisRunner extends SynthesisRunnerUtil { _.copy(certDest = f) }).text("write certificate to path; default: none") + opt[Boolean](name = "certSetRepr").action(cfg { b => + _.copy(certSetRepr = b) + }).text("use list permutations to represent sets (HTT only); default: false") + help("help").text("prints this usage text") note("\nOnce the synthesis is done execution, statistics will be available in stats.csv (rewritten every time).\n") From 22b98d720d4680260165bc1743ad6fc7372f4638 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Fri, 14 May 2021 23:11:40 +0900 Subject: [PATCH 183/211] HTT: Make hammer toggleable --- .../suslik/certification/targets/htt/logic/Hint.scala | 10 ++++++---- .../targets/htt/translation/ProofTranslator.scala | 6 ++++-- .../scala/org/tygus/suslik/synthesis/SynConfig.scala | 2 ++ .../org/tygus/suslik/synthesis/SynthesisRunner.scala | 5 +++++ 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index ea8466da9..614653740 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -18,7 +18,7 @@ object Hint { private var _hintId: Int = 0 private def freshHintId: Int = {_hintId += 1; _hintId} - case class PredicateSetTransitive(pred: CInductivePredicate) extends Hint { + case class PredicateSetTransitive(pred: CInductivePredicate, hammer: Boolean) extends Hint { val dbName: String = "ssl_pred" case class Hypothesis(params: Seq[CVar], idx: Int) { @@ -32,7 +32,8 @@ object Hint { def pp: String = { val hyp = s"Lemma $name ${args.map(_.pp).mkString(" ")} : perm_eq ${s1.pp} ${s2.pp} -> ${pred.name} ${params1.map(_.pp).mkString(" ")} -> ${pred.name} ${params2.map(_.pp).mkString(" ")}" - s"$hyp. intros; hammer. Qed.\n${ppResolve(name)}" + val prf = if (hammer) "intros; hammer. Qed." else "Admitted." + s"$hyp. $prf\n${ppResolve(name)}" } } @@ -46,7 +47,7 @@ object Hint { def pp: String = hypotheses.map(_.pp).mkString(".\n") + "." } - case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr], gamma: Map[CVar, HTTType]) extends Hint { + case class PureEntailment(prePhi: Set[CExpr], postPhi: Set[CExpr], gamma: Map[CVar, HTTType], hammer: Boolean) extends Hint { val dbName: String = "ssl_pure" case class Hypothesis(args: Set[CVar], ctx: Set[CExpr], goal: CExpr) { val name = s"pure$freshHintId" @@ -61,7 +62,8 @@ object Hint { }) s"${s.mkString(" ")} " } - s"Lemma $name $argsStr: $hypStr. intros; hammer. Qed.\n${ppResolve(name)}" + val prf = if (hammer) "intros; hammer. Qed." else "Admitted." + s"Lemma $name $argsStr: $hypStr. $prf\n${ppResolve(name)}" } } private type ReachableMap = Map[CExpr, Set[CExpr]] diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index 4449ae278..d00703c2b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -111,7 +111,8 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont // initialize context with post-condition info and predicate hints, if any val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap if (env.config.certSetRepr) { - ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p)) + val hammer = env.config.certHammerPure + ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p, hammer)) } val postEx = ListMap(cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)): _*) val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) @@ -292,7 +293,8 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont /** Pure entailments */ case SuslikProofStep.CheckPost(prePhi, postPhi, gamma) => - ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate), gamma.translate) + val hammer = env.config.certHammerPure + ctx.hints += Hint.PureEntailment(prePhi.conjuncts.map(_.translate), postPhi.conjuncts.map(_.translate), gamma.translate, hammer) Result(List(), List(withNoDeferred(ctx))) /** Ignored */ diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala index 9f74dd31f..ad95d85cb 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynConfig.scala @@ -51,6 +51,7 @@ case class SynConfig( // Certification certTarget: CertificationTarget = NoCert, certDest: File = null, + certHammerPure: Boolean = false, certSetRepr: Boolean = false, // Internal (not directly settable through CLI) inputFormat: InputFormat = dotSyn, @@ -67,6 +68,7 @@ case class SynConfig( (if (memoization == defaultConfig.memoization) Nil else List(s"memoization = $memoization")) ++ (if (certTarget == defaultConfig.certTarget) Nil else List(s"certTarget = ${certTarget.name}")) ++ (if (certDest == defaultConfig.certDest) Nil else List(s"certDest = ${certDest.getCanonicalPath}")) ++ + (if (certHammerPure == defaultConfig.certHammerPure) Nil else List(s"certHammerPure = $certHammerPure")) ++ (if (certSetRepr == defaultConfig.certSetRepr) Nil else List(s"certSetRepr = $certSetRepr")) ).mkString(", ") } diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index b2c5ac899..11bc50305 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -47,6 +47,7 @@ object SynthesisRunner extends SynthesisRunnerUtil { * --lexi use lexicographic termination metric (as opposed to total size); default: false * --certTarget set certification target; default: none * --certDest write certificate to path; default: none + * --certHammerPure use hammer to solve pure lemmas (HTT only); default: false * --certSetRepr use list permutations to represent sets (HTT only); default: false * * --help prints the help reference @@ -222,6 +223,10 @@ object SynthesisRunner extends SynthesisRunnerUtil { _.copy(certDest = f) }).text("write certificate to path; default: none") + opt[Boolean](name = "certHammerPure").action(cfg { b => + _.copy(certHammerPure = b) + }).text("use hammer to solve pure lemmas (HTT only); default: false") + opt[Boolean](name = "certSetRepr").action(cfg { b => _.copy(certSetRepr = b) }).text("use list permutations to represent sets (HTT only); default: false") From 67805fff1b65352d72873db08d56e79dd4528a95 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 15 May 2021 16:51:44 +0900 Subject: [PATCH 184/211] Make source ProofTree available to certify() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Doesn’t make sense for each target framework to generate it on its own. --- .../org/tygus/suslik/certification/Certificate.scala | 1 + .../suslik/certification/CertificationTarget.scala | 7 +++++-- .../tygus/suslik/certification/targets/htt/HTT.scala | 8 +++++--- .../certification/targets/htt/HTTCertificate.scala | 2 +- .../targets/htt/translation/Translation.scala | 11 ++++++----- .../suslik/certification/targets/iris/Iris.scala | 12 ++++++------ .../certification/targets/iris/IrisCertificate.scala | 2 +- .../targets/iris/translation/Translation.scala | 12 ++++++------ .../tygus/suslik/certification/targets/vst/VST.scala | 12 ++++++------ .../certification/targets/vst/VSTCertificate.scala | 2 +- .../targets/vst/translation/Translation.scala | 5 ++--- .../suslik/synthesis/CertificationBenchmarks.scala | 5 ++++- .../suslik/synthesis/CertificationExports.scala | 8 ++++++-- .../tygus/suslik/synthesis/SynthesisRunnerUtil.scala | 7 +++++-- 14 files changed, 55 insertions(+), 39 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/Certificate.scala b/src/main/scala/org/tygus/suslik/certification/Certificate.scala index 35620d204..0d390c0e5 100644 --- a/src/main/scala/org/tygus/suslik/certification/Certificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/Certificate.scala @@ -56,6 +56,7 @@ case class CoqOutput(filename: String, name: String, body: String) extends Certi * certification backends */ trait Certificate[T <: CertificationTarget, P <: Predicate] { + val testName: String val predicates: List[P] /** return a list of outputs */ diff --git a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala index 388b60035..7ecc1cde6 100644 --- a/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationTarget.scala @@ -1,10 +1,13 @@ package org.tygus.suslik.certification +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.HTT import org.tygus.suslik.certification.targets.iris.Iris import org.tygus.suslik.certification.targets.vst.VST +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import org.tygus.suslik.logic.Specifications.Goal /** @@ -19,7 +22,7 @@ trait CertificationTarget { type P <: Predicate val name: String val suffix: String - def certify(proc: Procedure, env: Environment): Certificate[T,P] + def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): Certificate[T,P] /** * Generate a list of outputs for the following predicates. @@ -44,7 +47,7 @@ object CertificationTarget { val name: String = "" override val suffix: String = "" - override def certify(proc: Procedure, env: Environment): Certificate[T,P] = ??? + override def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): Certificate[T,P] = ??? override def generate_common_definitions_of(base_filename: String, predicates: List[P]): List[CertificateOutput] = ??? } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 7cea8f204..4e3350488 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -1,11 +1,14 @@ package org.tygus.suslik.certification.targets.htt import org.tygus.suslik.certification._ +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate import org.tygus.suslik.certification.targets.htt.translation.Translation import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import org.tygus.suslik.logic.Specifications.Goal case class HTT() extends CertificationTarget { type T = HTT @@ -13,9 +16,8 @@ case class HTT() extends CertificationTarget { val name: String = "HTT" val suffix: String = ".v" - def certify(proc: Procedure, env: Environment): HTTCertificate = { - val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - Translation.translate(root, proc)(env) + def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): HTTCertificate = { + Translation.translate(testName, tree, goal, proc)(env) } def generate_common_definitions_of(defFileName: String, predicates: List[CInductivePredicate]): List[CertificateOutput] = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index a9340071b..c8d8495a0 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.htt.logic.Sentences.{CFunSpec, CIn import org.tygus.suslik.certification.targets.htt.program.Program import org.tygus.suslik.certification.{Certificate, CertificateOutput, CoqOutput} -case class HTTCertificate(name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate[HTT, CInductivePredicate] { +case class HTTCertificate(testName: String, name: String, predicates: List[CInductivePredicate], spec: CFunSpec, auxSpecs: Seq[CFunSpec], proof: Proof, proc: Program, hints: Seq[Hint] = Seq.empty) extends Certificate[HTT, CInductivePredicate] { // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 23fbb773d..6f734f367 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -9,7 +9,9 @@ import org.tygus.suslik.certification.targets.htt.logic.{Hint, Proof} import org.tygus.suslik.certification.targets.htt.logic.Sentences._ import org.tygus.suslik.certification.targets.htt.program.Program import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Statements._ +import org.tygus.suslik.logic.Specifications.Goal import org.tygus.suslik.logic.{Environment, FunSpec, Gamma, InductivePredicate, Specifications} import scala.collection.mutable.ListBuffer @@ -19,12 +21,12 @@ object Translation { /** * Produces the components of a HTT certificate, from the tree of successful derivations and a synthesized procedure - * @param node the root of the derivation tree + * @param suslikTree the root of the derivation tree * @param proc the synthesized procedure * @param env the synthesis environment * @return the inductive predicates, fun spec, proof, and program translated to HTT */ - def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): HTTCertificate = { + def translate(testName: String, suslikTree: ProofTree[SuslikProofStep], goal: Goal, proc: Procedure)(implicit env: Environment): HTTCertificate = { val cpreds = env.predicates.mapValues(p => { val gamma = p.resolve(p.params.toMap, env).get val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) @@ -37,8 +39,7 @@ object Translation { goal.translate.toFunspec } - val spec = node.goal.translate.toFunspec - val suslikTree = SuslikProofStep.of_certtree(node) + val spec = goal.translate.toFunspec val ctx: ProofContext = ProofContext(predicates = cpreds, hints = ListBuffer.empty[Hint], env = env) val proofBody = ProofEvaluator.run(suslikTree, ctx) @@ -47,7 +48,7 @@ object Translation { val progBody = ProgramEvaluator.run(suslikTree, ProgramContext(env)) val cproc = Program(proc.name, proc.tp.translate, proc.formals.map(_.translate), progBody) - HTTCertificate(cproc.name, cpreds.values.toList, spec, auxSpecs, proof, cproc, hints) + HTTCertificate(testName, cproc.name, cpreds.values.toList, spec, auxSpecs, proof, cproc, hints) } private def translateInductivePredicate(el: InductivePredicate, gamma: Gamma)(implicit env: Environment): CInductivePredicate = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index 10dc732c8..c37099ee3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -4,22 +4,22 @@ import org.tygus.suslik.certification.source.{SuslikPrinter, SuslikProofStep} import org.tygus.suslik.certification.targets.iris.logic.Assertions.IPredicate import org.tygus.suslik.certification.targets.iris.translation.Translation import org.tygus.suslik.certification.targets.iris.translation.Translation.TranslationException +import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget, CoqOutput} import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment +import org.tygus.suslik.logic.Specifications.Goal case class Iris() extends CertificationTarget { type T = Iris type P = IPredicate - val name: String = "HTT" + val name: String = "Iris" val suffix: String = ".v" - def certify(proc: Procedure, env: Environment): IrisCertificate = { - val root = CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - val cert = Translation.translate(root, proc)(env) + def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): IrisCertificate = { + val cert = Translation.translate(testName, tree, goal, proc)(env) - val simplified = SuslikProofStep.of_certtree(root) - println(s"Suslik Proof:\n ${SuslikPrinter.pp(simplified)}") + println(s"Suslik Proof:\n ${SuslikPrinter.pp(tree)}") cert } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala index 8c40b0e6d..70c3735cd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificate.scala @@ -20,7 +20,7 @@ case object IrisCertificate { } } -case class IrisCertificate(name: String, predicates: List[IPredicate], funDef: HFunDef, auxSpecs: List[IFunSpec], funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { +case class IrisCertificate(testName: String, name: String, predicates: List[IPredicate], funDef: HFunDef, auxSpecs: List[IFunSpec], funSpec: IFunSpec, proofStr: String) extends Certificate[Iris, IPredicate] { val target: CertificationTarget = Iris() def pp : String = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 70f368056..5bd7bded6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -11,7 +11,8 @@ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.T import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator._ -import org.tygus.suslik.certification.traversal.{StackEvaluator, Translator} +import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator, Translator} +import org.tygus.suslik.logic.Specifications.Goal object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramTranslationContext] { val translator: Translator[SuslikProofStep, HExpr, ProgramTranslationContext] = ProgramTranslator @@ -25,14 +26,13 @@ object Translation { case class TranslationException(msg: String) extends Exception(msg) - def translate(node: CertTree.Node, proc: Procedure)(implicit env: Environment): IrisCertificate = { + def translate(testName: String, suslikTree: ProofTree[SuslikProofStep], goal: Goal, proc: Procedure)(implicit env: Environment): IrisCertificate = { - val suslikTree = SuslikProofStep.of_certtree(node) val params = proc.formals.map(_.translate) println(SuslikPrinter.pp(suslikTree)) // We have this "dummy" value to generate progToSpec for the actual context, ctx - val pre_ctx = ProgramTranslationContext(env, proc, node.goal.gamma, Map.empty, node.goal.gamma.translate) + val pre_ctx = ProgramTranslationContext(env, proc, goal.gamma, Map.empty, goal.gamma.translate) val progToSpec = params.map(p => (p, p.translate(progVarToSpecVar, Some(pre_ctx)))) val ctx = pre_ctx.copy(pts = progToSpec.toMap) @@ -42,7 +42,7 @@ object Translation { val progTree = ProgramEvaluator.run(suslikTree, ctx) val funDef = HFunDef(proc.name, params, progTree) - val funSpec = node.goal.translate(goalToFunSpecTranslator, Some(ctx)) + val funSpec = goal.translate(goalToFunSpecTranslator, Some(ctx)) val predMap = predicates.map(p => (p.name, p)).toMap val helperSpecs = env.functions.map { case (fname, spec) => @@ -61,6 +61,6 @@ object Translation { // throw e // s"(* Error in proof generation:$e\n${e.getStackTrace.mkString("\n")} *)\n" } - IrisCertificate(proc.name, predicates, funDef, helperSpecs.values.toList, funSpec, proofStr) + IrisCertificate(testName, proc.name, predicates, funDef, helperSpecs.values.toList, funSpec, proofStr) } } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala index e5620cd49..1da152d11 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VST.scala @@ -1,5 +1,6 @@ package org.tygus.suslik.certification.targets.vst +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.{CertTree, CertificateOutput, CertificationTarget} import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment @@ -8,6 +9,9 @@ import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDef import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.translation.Translation +import org.tygus.suslik.certification.traversal.ProofTree +import org.tygus.suslik.language.Statements.Procedure +import org.tygus.suslik.logic.Specifications.Goal case class VST() extends CertificationTarget { type T = VST @@ -17,12 +21,8 @@ case class VST() extends CertificationTarget { val common_coq_lib_name = "common" - override def certify(proc: Statements.Procedure, env: Environment): VSTCertificate = { - // retrieve the search tree - val root = - CertTree.root.getOrElse(throw TranslationException("Search tree is uninitialized")) - - Translation.translate(root, proc, env) + override def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): VSTCertificate = { + Translation.translate(testName, tree, proc, env) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala index 855cef458..6896edb97 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificate.scala @@ -5,7 +5,7 @@ import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDef import org.tygus.suslik.certification.targets.vst.logic.Proof import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate -case class VSTCertificate(name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate[VST, VSTPredicate] { +case class VSTCertificate(testName: String, name:String, CProcedureDefinition: CProcedureDefinition, Proof: Proof) extends Certificate[VST, VSTPredicate] { override val predicates: List[VSTPredicate] = Proof.predicates override def outputs: List[CertificateOutput] = { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index c1e91adc5..4c43919f6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -29,8 +29,7 @@ object Translation { evaluator.run(proof, initial_context) } - def translate(root: CertTree.Node, proc: Procedure, env: Environment): VSTCertificate = { - val base_proof = SuslikProofStep.of_certtree(root) + def translate(testName: String, base_proof: ProofTree[SuslikProofStep], proc: Procedure, env: Environment): VSTCertificate = { val predicates = env.predicates.map({ case (_, predicate) => ProofSpecTranslation.translate_predicate(env)(predicate)}).toList val pred_map = predicates.map(v => (v.name,v)).toMap val pred_type_map = predicates.map(v => (v.name, v.params.map(_._2))).toMap @@ -64,6 +63,6 @@ object Translation { uses_malloc = proof_translator.contains_malloc ) - VSTCertificate(proc.f.name, procedure, proof) + VSTCertificate(testName, proc.f.name, procedure, proof) } } diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index be6e2f5d1..c94f1cf31 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.{Files, Paths} +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.HTT import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.certification.targets.iris.Iris @@ -107,7 +108,9 @@ trait CertificationBenchmarks extends SynthesisRunnerUtil { print(s" generating certificate...") try { - val (cert, proofGenDuration) = timed(target.certify(res.head, env)) + val root = CertTree.root.getOrElse(throw new Exception("Search tree is uninitialized")) + val tree = SuslikProofStep.of_certtree(root) + val (cert, proofGenDuration) = timed(target.certify(testName, res.head, tree, root.goal, env)) println("done!") (testName, synDuration, Some(cert, proofGenDuration)) } catch { diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala index 240eeb76d..34ea35f90 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala @@ -1,7 +1,9 @@ package org.tygus.suslik.synthesis import java.io.File -import org.tygus.suslik.certification.CertificationTarget + +import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.iris.Iris import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.parsing.SSLParser @@ -44,7 +46,9 @@ case class CertificationExports(target: CertificationTarget, export_folder: Stri val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) print(s" generating certificate...") try { - val (cert, proofGenDuration) = timed(target.certify(res.head, env)) + val root = CertTree.root.getOrElse(throw new Exception("Search tree is uninitialized")) + val tree = SuslikProofStep.of_certtree(root) + val (cert, proofGenDuration) = timed(target.certify(testName, res.head, tree, root.goal, env)) println("done!") (testName, synDuration, Some(cert, proofGenDuration)) } catch { diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala index 78cdcbf0f..0b56dbf4e 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunnerUtil.scala @@ -5,7 +5,8 @@ import java.nio.file.Paths import org.tygus.suslik.LanguageUtils import org.tygus.suslik.certification.CertificationTarget.NoCert -import org.tygus.suslik.certification.{CertTree, CertificateOutput} +import org.tygus.suslik.certification.source.SuslikProofStep +import org.tygus.suslik.certification.CertTree import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor._ import org.tygus.suslik.logic.smt.SMTSolving @@ -223,7 +224,9 @@ trait SynthesisRunnerUtil { if (params.certTarget != NoCert) { val certTarget = params.certTarget val targetName = certTarget.name - val certificate = certTarget.certify(procs.head, env) + val root = CertTree.root.getOrElse(throw SynthesisException("Search tree is uninitialized")) + val tree = SuslikProofStep.of_certtree(root) + val certificate = certTarget.certify(testName, procs.head, tree, root.goal, env) if (params.certDest == null) { testPrintln(s"\n$targetName certificate:", Console.MAGENTA) certificate.outputs.foreach(o => { From b5f7038be69ea3abc1d83bcd8e4a8f939c4b8b76 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 15 May 2021 17:02:21 +0900 Subject: [PATCH 185/211] Remove debug print --- .../org/tygus/suslik/certification/targets/iris/Iris.scala | 2 -- .../certification/targets/iris/translation/Translation.scala | 2 -- .../certification/targets/vst/translation/Translation.scala | 5 ----- .../targets/vst/translation/VSTProofTranslator.scala | 1 - 4 files changed, 10 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala index c37099ee3..8e2f2f518 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/Iris.scala @@ -19,8 +19,6 @@ case class Iris() extends CertificationTarget { def certify(testName: String, proc: Procedure, tree: ProofTree[SuslikProofStep], goal: Goal, env: Environment): IrisCertificate = { val cert = Translation.translate(testName, tree, goal, proc)(env) - println(s"Suslik Proof:\n ${SuslikPrinter.pp(tree)}") - cert } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 5bd7bded6..85d458cab 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -29,7 +29,6 @@ object Translation { def translate(testName: String, suslikTree: ProofTree[SuslikProofStep], goal: Goal, proc: Procedure)(implicit env: Environment): IrisCertificate = { val params = proc.formals.map(_.translate) - println(SuslikPrinter.pp(suslikTree)) // We have this "dummy" value to generate progToSpec for the actual context, ctx val pre_ctx = ProgramTranslationContext(env, proc, goal.gamma, Map.empty, goal.gamma.translate) @@ -37,7 +36,6 @@ object Translation { val ctx = pre_ctx.copy(pts = progToSpec.toMap) val predicates = env.predicates.map({ case (_, pred) => pred.translate(predicateTranslator, Some(ctx))}).toList - predicates.foreach(p => println(p.pp)) val progTree = ProgramEvaluator.run(suslikTree, ctx) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 4c43919f6..4748c6ef6 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -34,11 +34,9 @@ object Translation { val pred_map = predicates.map(v => (v.name,v)).toMap val pred_type_map = predicates.map(v => (v.name, v.params.map(_._2))).toMap var f_gamma = proc.f.gamma(env) - println(f_gamma) env.functions.foreach { case (_, spec) => val gamma = spec.gamma(env) - println(gamma) } val params = proc.formals.map({case (Var(name), ty) => ty match { case LocType => (name, CoqPtrValType) @@ -46,11 +44,8 @@ object Translation { }}) val spec = ProofSpecTranslation.translate_conditions(env)(pred_type_map)(proc.f) val helper_specs = env.functions.map {case (fname, spec) => (fname, ProofSpecTranslation.translate_conditions(env)(pred_type_map)(spec))} - println(spec.pp) val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body, helper_specs.values.toList) - println(procedure.pp) - println(spec.pp) val spec_map = Map(proc.name -> spec) ++ helper_specs val proof_translator = VSTProofTranslator(spec) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala index 31dc3151c..5aced9071 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala @@ -200,7 +200,6 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl ctx = ctx with_variables_of program_vars ctx = ctx with_variables_of ghost_vars ctx = ctx with_existential_variables_of existentials - println(goal.pp) val deferreds: Deferred = (ctx: VSTClientContext) => { val steps: List[VSTProofStep] = existentials.map(v => Exists(ctx resolve_existential v._1)) val new_ctx = ctx.without_existentials_of (existentials.map(_._1)) From f4ecec364287b18f3a03f846f4cbdf1eb042e1e6 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sat, 15 May 2021 17:19:52 +0900 Subject: [PATCH 186/211] Update benchmarks --- .../htt/HTTCertificationBenchmarks.scala | 19 - .../iris/IrisCertificationBenchmarks.scala | 19 - .../vst/VSTCertificationBenchmarks.scala | 20 - .../synthesis/CertificationBenchmarks.scala | 350 +++++++++++------- .../synthesis/CertificationExports.scala | 108 ------ 5 files changed, 220 insertions(+), 296 deletions(-) delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala delete mode 100644 src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala delete mode 100644 src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala deleted file mode 100644 index 9e8d0e618..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationBenchmarks.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.tygus.suslik.certification.targets.htt - -import org.tygus.suslik.certification.CertificationTarget -import org.tygus.suslik.synthesis.CertificationBenchmarks - -object HTTCertificationBenchmarks extends CertificationBenchmarks { - val targets: List[CertificationTarget] = List(HTT()) - - def main(args: Array[String]): Unit = { - initLog() - runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/sll-bounds") - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/srtl") - runAllTestsFromDir("certification/tree") - runAllTestsFromDir("certification/bst") - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala deleted file mode 100644 index c54e43f39..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/IrisCertificationBenchmarks.scala +++ /dev/null @@ -1,19 +0,0 @@ -package org.tygus.suslik.certification.targets.iris - -import org.tygus.suslik.certification.CertificationTarget -import org.tygus.suslik.synthesis.CertificationBenchmarks - -object IrisCertificationBenchmarks extends CertificationBenchmarks { - val targets: List[CertificationTarget] = List(Iris()) - - def main(args: Array[String]): Unit = { - initLog() - runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/tree") - runAllTestsFromDir("certification/sll-bounds") - runAllTestsFromDir("certification/srtl") - runAllTestsFromDir("certification/bst") - } -} diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala deleted file mode 100644 index ca85c8188..000000000 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationBenchmarks.scala +++ /dev/null @@ -1,20 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import org.tygus.suslik.certification.CertificationTarget -import org.tygus.suslik.synthesis.CertificationBenchmarks - -object VSTCertificationBenchmarks extends CertificationBenchmarks { - - val targets: List[CertificationTarget] = List(VST()) - - def main(args: Array[String]): Unit = { - initLog() - runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/tree") - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/sll-bounds") - runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/bst") - runAllTestsFromDir("certification/srtl") - } -} diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index c94f1cf31..9cda78efa 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -1,7 +1,7 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} -import java.nio.file.{Files, Paths} +import java.nio.file.Paths import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.HTT @@ -17,12 +17,31 @@ import org.tygus.suslik.report.StopWatch.timed import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} -import scala.sys.process._ - -trait CertificationBenchmarks extends SynthesisRunnerUtil { - val targets: List[CertificationTarget] - val statsFile: File = new File("cert-stats.csv") +import scala.collection.mutable +import scala.io.StdIn +import scala.sys.process.Process + +class CertificationBenchmarks( + targets: List[CertificationTarget], + params: SynConfig, + outputDirName: String, + statsFilePrefix: String + ) extends SynthesisRunnerUtil { + val outputDir: File = new File(outputDirName) + val synStatsFile = new File(List(outputDirName, s"$statsFilePrefix-syn.csv").mkString(File.separator)) + val certStatsFiles: Map[CertificationTarget, File] = + targets.map(t => t -> new File(List(outputDirName, s"$statsFilePrefix-${t.name}.csv").mkString(File.separator))).toMap val defFilename: String = "common" + val exclusions: List[(String, String)] = List( + ("VST", "sll-bounds/sll-len"), + ("VST", "tree/tree-size"), + ("Iris", "sll-bounds/sll-max"), + ("Iris", "sll-bounds/sll-min") + ) + + outputDir.mkdirs() + initSynLog() + targets.foreach(initCertLog) def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { val res = params.inputFormat match { @@ -73,120 +92,120 @@ trait CertificationBenchmarks extends SynthesisRunnerUtil { println(s"=========================================\n") val path = List(rootDir, dir).mkString(File.separator) val testDir = new File(path) - val tempDirNames = targets.map(t => t -> Files.createTempDirectory("suslik-").toFile).toMap + val outputDirs = targets.map { target => + val targetDir = Paths.get(outputDir.getPath, target.name, dir).toFile + targetDir.mkdirs() + target -> targetDir + }.toMap if (testDir.exists() && testDir.isDirectory) { print(s"Retrieving definitions and specs from ${testDir.getName}...") val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) val tests = testDir.listFiles.filter(f => f.isFile && (f.getName.endsWith(s".$testExtension") || - f.getName.endsWith(s".$sketchExtension"))).toList + f.getName.endsWith(s".$sketchExtension"))) + .map(f => getDescInputOutput(f.getAbsolutePath, params)).toList println("done!") val parser = new SSLParser - val testCases = for (f <- tests) yield { - val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) - (testName, in, params) - } + println("Synthesizing specifications...") + val synResults = for ((testName, _, in, _, params) <- tests) yield { + val fullInput = List(defs, in).mkString("\n") + print(s" $testName...") + val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) + println(s"done! (${fmtTime(synDuration)} s)") + + val root = CertTree.root.getOrElse(throw new Exception("Search tree is uninitialized")) + val tree = SuslikProofStep.of_certtree(root) + + logSynStat(List(testName, fmtTime(synDuration))) - val results = targets.map(target => { - val tempDir = tempDirNames(target) - println(s"\n****************************************\n") - println(s"Running benchmarks for certification target: ${target.name}") - println(s"Initialized output directory: ${tempDir.getCanonicalPath}\n") - - println(s"Synthesizing ${tests.length} test cases...") - - val synResults = for ((testName, in, params) <- testCases) yield { - println(s"$testName:") - val fullInput = List(defs, in).mkString("\n") - - print(s" synthesizing in certification mode...") - val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) - println(s"done! (${fmtTime(synDuration)} s)") - - print(s" generating certificate...") - try { - val root = CertTree.root.getOrElse(throw new Exception("Search tree is uninitialized")) - val tree = SuslikProofStep.of_certtree(root) - val (cert, proofGenDuration) = timed(target.certify(testName, res.head, tree, root.goal, env)) - println("done!") - (testName, synDuration, Some(cert, proofGenDuration)) - } catch { - case e => - println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") - (testName, synDuration, None) + (testName, res.head, tree, root.goal, env) + } + println("Finished synthesizing specifications!") + + for (target <- targets) { + val outputDir = outputDirs(target) + println(s"Generating ${target.name} certificates...") + val certs = for ((testName, proc, tree, goal, env) <- synResults) yield { + if (exclusions.contains((target.name, testName))) { + print(s" $testName...skipping unsupported test case for ${target.name}") + None + } else { + try { + print(s" $testName...") + val cert = target.certify(testName, proc, tree, goal, env) + println("done!") + Some(cert) + } catch { + case e: Throwable => + println(s"- ERR\n failed to generate certificate for $testName (${e.getLocalizedMessage})") + None + } } } + println(s"Finished generating ${target.name} certificates!") - println(s"Successfully synthesized ${tests.length} tests.") - - val validCerts = synResults.map(_._3).filter(_.isDefined).map(_.get).map(_._1) + val validCerts = certs.filter(_.isDefined).map(_.get) val predicates = validCerts.flatMap(_.predicates).groupBy(_.name).map(_._2.head).toList + println(s"Compiling ${target.name} common definitions to ${outputDir.getPath}...") val defFiles = target.generate_common_definitions_of(defFilename, predicates) - defFiles.foreach { - case output => - print(s"\nWriting common definitions to file $tempDir/${output.filename}...") - serialize(tempDir, output.filename, output.body) + defFiles.foreach { output => + print(s" File ${output.filename}...") + serialize(outputDir, output.filename, output.body) + print("compiling...") + output.compile(outputDir) + println("done!") } - println("done!") - print(s"Compiling definitions...") - defFiles.foreach(output => output.compile(tempDir)) - println("done!") - - println(s"\nGenerating statistics...") - val testToStats = synResults.map { case (testName, synDuration, certOpt) => certOpt match { - case None => - println(s"$testName:") - println(" No certificate was generated; skipping proof check...") - testName -> (synDuration, None, None, None) - case Some((cert, proofGenDuration)) => - println(s"$testName:") - - val outputs = cert.outputs_with_common_predicates(defFilename, predicates) - for (o <- outputs) yield { - print(s" Writing certificate output to file ${o.filename}...") - serialize(tempDir, o.filename, o.body) - println("done!") - if (!o.isProof) { - print(s" Compiling output...") - o.compile(tempDir) - println("done!") + println(s"Compiled ${target.name} common definitions to ${outputDir.getPath}!") + + println(s"Writing ${target.name} certificate outputs to ${outputDir.getPath}...") + for (cert <- validCerts) { + val outputs = cert.outputs_with_common_predicates(defFilename, predicates) + for (o <- outputs) { + print(s" File ${o.filename}...") + serialize(outputDir, o.filename, o.body) + if (!o.isProof) { + print("compiling...") + o.compile(outputDir) match { + case 0 => println("done!") + case _ => println("ERR\n Failed to compile common definition file!") } - } - - outputs.find(_.isProof) match { - case Some(proof) => - print(s" Checking proof size...") - val (specSize, proofSize) = checkProofSize(tempDir, proof.filename) - println(s"done! (spec: $specSize, proof: $proofSize)") - print(s" Compiling proof...") - val (res, proofCheckDuration) = timed(proof.compile(tempDir)) - if (res == 0) { - println(s"done! (${fmtTime(proofCheckDuration)} s)") - testName -> (synDuration, Some(proofGenDuration), Some(specSize, proofSize), Some(proofCheckDuration)) - } else { - println(s"ERR\n Failed to verify ${proof.filename}!") - testName -> (synDuration, Some(proofGenDuration), Some(specSize, proofSize), None) - } - case None => - println(s"Certificate was generated, but no proof output was found; skipping verification...") - testName -> (synDuration, Some(proofGenDuration), None, None) - } + } else println("done!") + } + outputs.find(_.isProof) match { + case None => + println(s" Warning: No ${target.name} proof output found in certificate for ${cert.testName}! Skipping compilation.") + logCertStat(target, List(cert.testName, "-", "-", "-", "-")) + case Some(proof) => + print(s" Checking size of main proof ${proof.filename}...") + val (specSize, proofSize) = checkProofSize(outputDir, proof.filename) + println(s"done! (spec: $specSize, proof: $proofSize)") + print(s" Compiling main proof ${proof.filename}...") + val (res, compileTime) = timed(proof.compile(outputDir)) + val compileTimeOpt = if (res == 0) { + println(s"done! (${fmtTime(compileTime)} s)") + Some(compileTime) + } else { + println(s"ERR\n Failed to compile ${proof.filename}!") + None + } + logCertStat(target, List( + cert.testName, + proof.filename, + specSize.toString, + proofSize.toString, + compileTimeOpt.map(fmtTime).getOrElse("-") + )) + } } - }.toMap - println(s"\n****************************************\n") - - target -> testToStats - }).toMap - - for ((testName, _, _) <- testCases) { - val resultsByTarget = targets.map(target => results(target)(testName)) - logStat(testName, resultsByTarget) + println(s"Wrote ${target.name} certificate outputs to ${outputDir.getPath}!") } - println(s"\nStatistics written to: ${statsFile.getCanonicalPath}\n\n") + println("\n\n") + } else { + println(s"- ERR\n no such test directory $testDir!") } } @@ -204,47 +223,118 @@ trait CertificationBenchmarks extends SynthesisRunnerUtil { (specSize.toInt, proofSize.toInt) } - def initLog(): Unit = { - if (statsFile.exists()) statsFile.delete() - statsFile.createNewFile() - val topLevelHeader = List("") ++ targets.flatMap(t => List(t.name, "", "", "", "")) - val targetHeader = List("Synthesis Time (sec)", "Proof Generation Time (sec)", "Proof Checking Time (sec)", "Spec Size", "Proof Size") - val targetHeaders = targets.flatMap(_ => targetHeader) - val statsHeader = topLevelHeader.mkString(", ") + "\n" + (List("Benchmark Name") ++ targetHeaders).mkString(", ") + "\n" - SynStatUtil.using(new FileWriter(statsFile, true))(_.write(statsHeader)) + private def initLog(file: File, header: List[String]): Unit = { + if (file.exists()) file.delete() + file.getParentFile.mkdirs() + file.createNewFile() + val data = header.mkString(", ") + "\n" + SynStatUtil.using(new FileWriter(file, true))(_.write(data)) } - private def logStat(name: String, targetResults: List[(Long, Option[Long], Option[(Int, Int)], Option[Long])]): Unit = { - def ppRes(res: (Long, Option[Long], Option[(Int, Int)], Option[Long])): String = { - val (synDuration, proofGenDuration, proofStats, proofCheckDuration) = res - val proofGenStr = proofGenDuration.map(fmtTime).getOrElse("-") - val proofStatsStr = proofStats match { - case Some((specSize, proofSize)) => List(specSize, proofSize).mkString(", ") - case None => "-, -, -" - } - val proofCheckStr = proofCheckDuration.map(fmtTime).getOrElse("-") - s"${fmtTime(synDuration)}, $proofGenStr, $proofCheckStr, $proofStatsStr" - } + private def initSynLog(): Unit = { + val header = List("Benchmark Name", "Synthesis Time (sec)") + initLog(synStatsFile, header) + } + + private def initCertLog(target: CertificationTarget): Unit = { + val header = List("Benchmark Name", "File Name", "Spec Size", "Proof Size", "Proof Checking Time (sec)") + initLog(certStatsFiles(target), header) + } - val data = s"$name, ${targetResults.map(ppRes).mkString(", ")}\n" - SynStatUtil.using(new FileWriter(statsFile, true))(_.write(data)) + private def logStat(file: File, row: List[String]): Unit = { + val data = s"${row.mkString(", ")}\n" + SynStatUtil.using(new FileWriter(file, true))(_.write(data)) } + private def logSynStat(row: List[String]): Unit = logStat(synStatsFile, row) + private def logCertStat(target: CertificationTarget, row: List[String]): Unit = logStat(certStatsFiles(target), row) private def fmtTime(ms: Long): String = "%.1f".format(ms.toDouble / 1000) } object CertificationBenchmarks { - def main(args: Array[String]): Unit = { - val benchmarks = new CertificationBenchmarks { - override val targets: List[CertificationTarget] = List(HTT(), VST(), Iris()) + val allTargets = List(HTT(), VST(), Iris()) + val allStandard = List( + "certification-benchmarks/ints", + "certification-benchmarks/sll-bounds", + "certification-benchmarks/sll", + "certification-benchmarks/dll", + "certification-benchmarks/tree" + ) + val allAdvanced = List( + "certification-benchmarks-advanced/bst", + "certification-benchmarks-advanced/dll", + "certification-benchmarks-advanced/srtl", + ) + val defaultStandardConfig = BenchmarkConfig(allTargets, allStandard) + val defaultAdvancedConfig = BenchmarkConfig(List(HTT()), allAdvanced) + + case class BenchmarkConfig(targets: List[CertificationTarget], groups: List[String]) { + def updateTargets: BenchmarkConfig = { + println(s"\nBenchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") + val s = StdIn.readLine("Manually select targets instead? y/N: ") + if (s.toLowerCase() == "y") { + val newTargets = mutable.ListBuffer[CertificationTarget]() + for (t <- targets) { + val s = StdIn.readLine(s"Include target '${t.name}'? Y/n: ") + if (s.toLowerCase() != "n") newTargets.append(t) + } + println(s"Benchmarks will be evaluated on target(s): ${newTargets.map(_.name).mkString(", ")}") + this.copy(targets = newTargets.toList) + } else { + println("Evaluation targets unchanged.") + this + } + } + + def updateGroups: BenchmarkConfig = { + println("\nBenchmarks will be run on the following group(s):") + for (g <- groups) println(s"- $g") + val s = StdIn.readLine("Manually select groups instead? y/N: ") + if (s.toLowerCase() == "y") { + val newGroups = mutable.ListBuffer[String]() + for (g <- groups) { + val s = StdIn.readLine(s"Include group '$g'? Y/n: ") + if (s.toLowerCase() != "n") newGroups.append(g) + } + println("Benchmarks will be run on the following group(s):") + for (g <- newGroups) println(s"- $g") + this.copy(groups = newGroups.toList) + } else { + println("Evaluation groups unchanged.") + this + } } - benchmarks.initLog() - benchmarks.runAllTestsFromDir("certification-benchmarks/ints") - benchmarks.runAllTestsFromDir("certification-benchmarks/sll-bounds") - benchmarks.runAllTestsFromDir("certification-benchmarks/sll") - benchmarks.runAllTestsFromDir("certification-benchmarks/dll") - benchmarks.runAllTestsFromDir("certification-benchmarks/tree") } -} + def main(args: Array[String]): Unit = { + println("==========STANDARD BENCHMARK CONFIGURATION==========") + val standardConfig = defaultStandardConfig.updateTargets.updateGroups + println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") + val advancedConfig = defaultAdvancedConfig.updateGroups + println("\n\nResults will be produced in the following directory:") + println(s" ${new File("certify").getCanonicalPath}") + val s = StdIn.readLine("Existing files will be overwritten. Continue? Y/n: ") + if (s.toLowerCase == "n") { + println("Canceled job.") + sys.exit(0) + } else { + println("Starting benchmarks...\n\n") + } + val standard = new CertificationBenchmarks ( + standardConfig.targets, + SynConfig(certHammerPure = true), + List("certify").mkString(File.separator), + "standard" + ) + standardConfig.groups.map(standard.runAllTestsFromDir) + + val advanced = new CertificationBenchmarks( + advancedConfig.targets, + SynConfig(certSetRepr = true), + List("certify").mkString(File.separator), + "advanced" + ) + advancedConfig.groups.map(advanced.runAllTestsFromDir) + } +} diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala deleted file mode 100644 index 34ea35f90..000000000 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationExports.scala +++ /dev/null @@ -1,108 +0,0 @@ -package org.tygus.suslik.synthesis - -import java.io.File - -import org.tygus.suslik.certification.{CertTree, CertificationTarget} -import org.tygus.suslik.certification.source.SuslikProofStep -import org.tygus.suslik.certification.targets.iris.Iris -import org.tygus.suslik.certification.targets.vst.VST -import org.tygus.suslik.parsing.SSLParser -import org.tygus.suslik.report.StopWatch.timed - -case class CertificationExports(target: CertificationTarget, export_folder: String, common_name: String = "common") extends CertificationBenchmarks { - override val targets: List[CertificationTarget] = List(target) - - def dump_all_tests(base_dir: String, test_folder: String): Unit = { - println(s"=========================================\n") - println(s" Benchmark Group: $test_folder\n") - println(s"=========================================\n") - val path = List(rootDir, base_dir, test_folder).mkString(File.separator) - val testDir = new File(path) - val export_path = List(export_folder, test_folder).mkString(File.separator) - val export_dir = new File(export_path) - export_dir.mkdirs() - - if (testDir.exists() && testDir.isDirectory) { - print(s"Retrieving definitions and specs from ${testDir.getName}...") - val defs = getDefs(testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(s".$defExtension")).toList) - val tests = testDir.listFiles.filter(f => f.isFile && (f.getName.endsWith(s".$testExtension") || f.getName.endsWith(s".$sketchExtension"))).toList - println("done!") - - val parser = new SSLParser - - val testCases = for (f <- tests) yield { - val (testName, desc, in, out, params) = getDescInputOutput(f.getAbsolutePath) - (testName, in, params) - } - - println(s"\n****************************************\n") - println(s"Running benchmarks for certification target: ${target.name}") - println(s"Initialized output directory: ${export_dir.getCanonicalPath}\n") - println(s"Synthesizing ${tests.length} test cases...") - val synResults = for ((testName, in, params) <- testCases) yield { - println(s"$testName:") - val fullInput = List(defs, in).mkString("\n") - print(s" synthesizing in certification mode...") - val (res, env, synDuration) = synthesizeOne(fullInput, parser, params.copy(assertSuccess = false)) - print(s" generating certificate...") - try { - val root = CertTree.root.getOrElse(throw new Exception("Search tree is uninitialized")) - val tree = SuslikProofStep.of_certtree(root) - val (cert, proofGenDuration) = timed(target.certify(testName, res.head, tree, root.goal, env)) - println("done!") - (testName, synDuration, Some(cert, proofGenDuration)) - } catch { - case e => - println(s"- ERR\n failed to generate certificate for ${testName} (${e.getLocalizedMessage})") - (testName, synDuration, None) - } - } - println(s"Successfully synthesized ${tests.length} tests.") - - val validCerts = synResults.map(_._3).filter(_.isDefined).map(_.get).map(_._1) - val predicates = validCerts.flatMap(_.predicates).groupBy(_.name).map(_._2.head).toList - val defFiles = target.generate_common_definitions_of(defFilename, predicates) - defFiles.foreach { - case output => - print(s"\nWriting common definitions to file $export_path/${output.filename}...") - serialize(export_dir, output.filename, output.body) - } - println("done!") - - - synResults.foreach { case (testName, synDuration, certOpt) => certOpt match { - case None => () - case Some((cert, proofGenDuration)) => - val outputs = cert.outputs_with_common_predicates(defFilename, predicates) - for (o <- outputs) yield { - print(s" Writing certificate output to file ${o.filename}...") - serialize(export_dir, o.filename, o.body) - } - } - } - } - } -} -object VSTCertificationExports { - def main(args: Array[String]): Unit = { - val benchmarks = CertificationExports(VST(), "/tmp/benchmarks") - benchmarks.initLog() - benchmarks.dump_all_tests("certification-benchmarks", "ints") - benchmarks.dump_all_tests("certification-benchmarks", "sll-bounds") - benchmarks.dump_all_tests("certification-benchmarks", "sll") - benchmarks.dump_all_tests("certification-benchmarks", "dll") - benchmarks.dump_all_tests("certification-benchmarks", "tree") - } -} - -object IrisCertificationExports { - def main(args: Array[String]): Unit = { - val benchmarks = CertificationExports(Iris(), "/tmp/benchmarks") - benchmarks.initLog() - benchmarks.dump_all_tests("certification-benchmarks", "ints") - benchmarks.dump_all_tests("certification-benchmarks", "sll-bounds") - benchmarks.dump_all_tests("certification-benchmarks", "sll") - benchmarks.dump_all_tests("certification-benchmarks", "dll") - benchmarks.dump_all_tests("certification-benchmarks", "tree") - } -} \ No newline at end of file From d24b15730d03d3599a4b1351e0f20a9c8e6749e2 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 16 May 2021 21:31:13 +0900 Subject: [PATCH 187/211] Add benchmarking utility shortcut --- .gitignore | 3 +++ certify-benchmarks | 2 ++ 2 files changed, 5 insertions(+) create mode 100755 certify-benchmarks diff --git a/.gitignore b/.gitignore index 33b69d78c..dc3ddad56 100644 --- a/.gitignore +++ b/.gitignore @@ -147,3 +147,6 @@ node_modules # traces *.out + +# certification results +certify/ diff --git a/certify-benchmarks b/certify-benchmarks new file mode 100755 index 000000000..474476ea2 --- /dev/null +++ b/certify-benchmarks @@ -0,0 +1,2 @@ +scala -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks + From c71b27708692c9527ec566036b62cf6987520277 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 18 May 2021 17:09:37 +0900 Subject: [PATCH 188/211] HTT: Per-certificate hint ids --- .../scala/org/tygus/suslik/certification/targets/htt/HTT.scala | 1 - .../org/tygus/suslik/certification/targets/htt/logic/Hint.scala | 1 + .../certification/targets/htt/translation/Translation.scala | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 4e3350488..6c51f9317 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -4,7 +4,6 @@ import org.tygus.suslik.certification._ import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.logic.Sentences.CInductivePredicate import org.tygus.suslik.certification.targets.htt.translation.Translation -import org.tygus.suslik.certification.targets.htt.translation.Translation.TranslationException import org.tygus.suslik.certification.traversal.ProofTree import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 614653740..7e7e395aa 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -17,6 +17,7 @@ abstract class Hint { object Hint { private var _hintId: Int = 0 private def freshHintId: Int = {_hintId += 1; _hintId} + def initHintId: Unit = _hintId = 0 case class PredicateSetTransitive(pred: CInductivePredicate, hammer: Boolean) extends Hint { val dbName: String = "ssl_pred" diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala index 6f734f367..628bc3a14 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/Translation.scala @@ -27,6 +27,7 @@ object Translation { * @return the inductive predicates, fun spec, proof, and program translated to HTT */ def translate(testName: String, suslikTree: ProofTree[SuslikProofStep], goal: Goal, proc: Procedure)(implicit env: Environment): HTTCertificate = { + Hint.initHintId val cpreds = env.predicates.mapValues(p => { val gamma = p.resolve(p.params.toMap, env).get val p1 = p.copy(clauses = p.clauses.map(_.resolveOverloading(gamma))) From 2bda6d0009e99dcd8c0ad40869acf7c91cc50296 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 18 May 2021 18:12:53 +0900 Subject: [PATCH 189/211] Allow synthesis/cert output only --- .../synthesis/CertificationBenchmarks.scala | 180 ++++++++++++------ 1 file changed, 127 insertions(+), 53 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 9cda78efa..4c0b2cf8d 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -14,6 +14,7 @@ import org.tygus.suslik.logic.Preprocessor.preprocessProgram import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.report.StopWatch.timed +import org.tygus.suslik.synthesis.CertificationBenchmarks.{BenchmarkConfig, BenchmarkMode} import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} @@ -22,15 +23,13 @@ import scala.io.StdIn import scala.sys.process.Process class CertificationBenchmarks( - targets: List[CertificationTarget], params: SynConfig, - outputDirName: String, - statsFilePrefix: String + cfg: BenchmarkConfig ) extends SynthesisRunnerUtil { - val outputDir: File = new File(outputDirName) - val synStatsFile = new File(List(outputDirName, s"$statsFilePrefix-syn.csv").mkString(File.separator)) + val outputDir: File = new File(cfg.outputDirName) + val synStatsFile = new File(List(cfg.outputDirName, s"${cfg.statsFilePrefix}-syn.csv").mkString(File.separator)) val certStatsFiles: Map[CertificationTarget, File] = - targets.map(t => t -> new File(List(outputDirName, s"$statsFilePrefix-${t.name}.csv").mkString(File.separator))).toMap + cfg.targets.map(t => t -> new File(List(cfg.outputDirName, s"${cfg.statsFilePrefix}-${t.name}.csv").mkString(File.separator))).toMap val defFilename: String = "common" val exclusions: List[(String, String)] = List( ("VST", "sll-bounds/sll-len"), @@ -39,10 +38,6 @@ class CertificationBenchmarks( ("Iris", "sll-bounds/sll-min") ) - outputDir.mkdirs() - initSynLog() - targets.foreach(initCertLog) - def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { val res = params.inputFormat match { case `dotSyn` => parser.parseGoalSYN(text) @@ -87,12 +82,12 @@ class CertificationBenchmarks( } override def runAllTestsFromDir(dir: String): Unit = { - println(s"=========================================\n") + println(s"\n\n=========================================\n") println(s" Benchmark Group: $dir\n") println(s"=========================================\n") val path = List(rootDir, dir).mkString(File.separator) val testDir = new File(path) - val outputDirs = targets.map { target => + val outputDirs = cfg.targets.map { target => val targetDir = Paths.get(outputDir.getPath, target.name, dir).toFile targetDir.mkdirs() target -> targetDir @@ -125,7 +120,11 @@ class CertificationBenchmarks( } println("Finished synthesizing specifications!") - for (target <- targets) { + if (cfg.mode == BenchmarkMode.SynOnly) { + return + } + + for (target <- cfg.targets) { val outputDir = outputDirs(target) println(s"Generating ${target.name} certificates...") val certs = for ((testName, proc, tree, goal, env) <- synResults) yield { @@ -149,24 +148,26 @@ class CertificationBenchmarks( val validCerts = certs.filter(_.isDefined).map(_.get) val predicates = validCerts.flatMap(_.predicates).groupBy(_.name).map(_._2.head).toList - println(s"Compiling ${target.name} common definitions to ${outputDir.getPath}...") + println(s"Writing ${target.name} common definitions to ${outputDir.getPath}...") val defFiles = target.generate_common_definitions_of(defFilename, predicates) defFiles.foreach { output => - print(s" File ${output.filename}...") + print(s" File ${output.filename}: serializing...") serialize(outputDir, output.filename, output.body) - print("compiling...") - output.compile(outputDir) + if (cfg.mode == BenchmarkMode.SynGenCompile) { + print("compiling...") + output.compile(outputDir) + } println("done!") } - println(s"Compiled ${target.name} common definitions to ${outputDir.getPath}!") + println(s"Wrote ${target.name} common definitions to ${outputDir.getPath}!") println(s"Writing ${target.name} certificate outputs to ${outputDir.getPath}...") for (cert <- validCerts) { val outputs = cert.outputs_with_common_predicates(defFilename, predicates) for (o <- outputs) { - print(s" File ${o.filename}...") + print(s" File ${o.filename}: serializing...") serialize(outputDir, o.filename, o.body) - if (!o.isProof) { + if (!o.isProof && cfg.mode == BenchmarkMode.SynGenCompile) { print("compiling...") o.compile(outputDir) match { case 0 => println("done!") @@ -182,13 +183,17 @@ class CertificationBenchmarks( print(s" Checking size of main proof ${proof.filename}...") val (specSize, proofSize) = checkProofSize(outputDir, proof.filename) println(s"done! (spec: $specSize, proof: $proofSize)") - print(s" Compiling main proof ${proof.filename}...") - val (res, compileTime) = timed(proof.compile(outputDir)) - val compileTimeOpt = if (res == 0) { - println(s"done! (${fmtTime(compileTime)} s)") - Some(compileTime) + val compileTimeOpt = if (cfg.mode == BenchmarkMode.SynGenCompile) { + print(s" Compiling main proof ${proof.filename}...") + val (res, compileTime) = timed(proof.compile(outputDir)) + if (res == 0) { + println(s"done! (${fmtTime(compileTime)} s)") + Some(compileTime) + } else { + println(s"ERR\n Failed to compile ${proof.filename}!") + None + } } else { - println(s"ERR\n Failed to compile ${proof.filename}!") None } logCertStat(target, List( @@ -202,14 +207,23 @@ class CertificationBenchmarks( } println(s"Wrote ${target.name} certificate outputs to ${outputDir.getPath}!") } - - println("\n\n") } else { println(s"- ERR\n no such test directory $testDir!") } } - def serialize(dir: File, filename: String, body: String): Unit = { + def runBenchmarks(): Unit = { + outputDir.mkdirs() + initSynLog() + if (cfg.groups.nonEmpty) { + if (cfg.mode != BenchmarkMode.SynOnly) { + cfg.targets.foreach(initCertLog) + } + cfg.groups.foreach(runAllTestsFromDir) + } + } + + private def serialize(dir: File, filename: String, body: String): Unit = { val file = Paths.get(dir.getCanonicalPath, filename).toFile new PrintWriter(file) { write(body); close() @@ -265,20 +279,48 @@ object CertificationBenchmarks { "certification-benchmarks-advanced/dll", "certification-benchmarks-advanced/srtl", ) - val defaultStandardConfig = BenchmarkConfig(allTargets, allStandard) - val defaultAdvancedConfig = BenchmarkConfig(List(HTT()), allAdvanced) + val defaultStandardConfig: BenchmarkConfig = BenchmarkConfig(allTargets, allStandard, BenchmarkMode.SynGenCompile, "standard") + val defaultAdvancedConfig: BenchmarkConfig = BenchmarkConfig(List(HTT()), allAdvanced, BenchmarkMode.SynGen, "advanced") - case class BenchmarkConfig(targets: List[CertificationTarget], groups: List[String]) { - def updateTargets: BenchmarkConfig = { + trait BenchmarkMode { + def pp: String + } + object BenchmarkMode { + case object SynOnly extends BenchmarkMode { + def pp: String = "synthesize only" + } + case object SynGen extends BenchmarkMode { + def pp: String = "synthesize; generate proofs" + } + case object SynGenCompile extends BenchmarkMode { + def pp: String = "synthesize; generate and compile proofs" + } + } + + case class BenchmarkConfig( + targets: List[CertificationTarget], + groups: List[String], + mode: BenchmarkMode, + statsFilePrefix: String, + outputDirName: String = "certify", + ) { + def updateTargets(): BenchmarkConfig = { + if (mode == BenchmarkMode.SynOnly) { + return this + } println(s"\nBenchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") - val s = StdIn.readLine("Manually select targets instead? y/N: ") + val s = StdIn.readLine("Manually select targets instead? [y/N] ") if (s.toLowerCase() == "y") { val newTargets = mutable.ListBuffer[CertificationTarget]() for (t <- targets) { - val s = StdIn.readLine(s"Include target '${t.name}'? Y/n: ") + val s = StdIn.readLine(s"Include target '${t.name}'? [Y/n] ") if (s.toLowerCase() != "n") newTargets.append(t) } - println(s"Benchmarks will be evaluated on target(s): ${newTargets.map(_.name).mkString(", ")}") + if (newTargets.isEmpty) { + println(s"Benchmarks will not be evaluated on any targets.") + } else { + println(s"Benchmarks will be evaluated on target(s): ${newTargets.map(_.name).mkString(", ")}") + } this.copy(targets = newTargets.toList) } else { println("Evaluation targets unchanged.") @@ -286,34 +328,70 @@ object CertificationBenchmarks { } } - def updateGroups: BenchmarkConfig = { + def updateGroups(): BenchmarkConfig = { + if (targets.isEmpty) { + return this + } println("\nBenchmarks will be run on the following group(s):") for (g <- groups) println(s"- $g") - val s = StdIn.readLine("Manually select groups instead? y/N: ") + val s = StdIn.readLine("Manually select groups instead? [y/N] ") if (s.toLowerCase() == "y") { val newGroups = mutable.ListBuffer[String]() for (g <- groups) { - val s = StdIn.readLine(s"Include group '$g'? Y/n: ") + val s = StdIn.readLine(s"Include group '$g'? [Y/n] ") if (s.toLowerCase() != "n") newGroups.append(g) } - println("Benchmarks will be run on the following group(s):") - for (g <- newGroups) println(s"- $g") + if (newGroups.isEmpty) { + println("Benchmarks will not be evaluated on any groups.") + } else { + println("Benchmarks will be run on the following group(s):") + for (g <- newGroups) println(s"- $g") + } this.copy(groups = newGroups.toList) } else { println("Evaluation groups unchanged.") this } } + + def updateMode(): BenchmarkConfig = { + println(s"\nBenchmarks will be run in mode: ${mode.pp}") + val s = StdIn.readLine("Manually select mode instead? [y/N] ") + if (s.toLowerCase() == "y") { + var generate = true + var compile = true + var s = StdIn.readLine("Generate proof certificates for synthesized results? [Y/n] ") + if (s.toLowerCase() == "n") { + generate = false + compile = false + } else { + s = StdIn.readLine("Compile generated certificates? [Y/n] ") + if (s.toLowerCase() == "n") { + compile = false + } + } + val newMode = (generate, compile) match { + case (true, true) => BenchmarkMode.SynGenCompile + case (true, false) => BenchmarkMode.SynGen + case _ => BenchmarkMode.SynOnly + } + println(s"\nBenchmarks updated to run in mode: ${newMode.pp}") + this.copy(mode = newMode) + } else { + println("Evaluation mode unchanged.") + this + } + } } def main(args: Array[String]): Unit = { println("==========STANDARD BENCHMARK CONFIGURATION==========") - val standardConfig = defaultStandardConfig.updateTargets.updateGroups + val standardConfig = defaultStandardConfig.updateMode().updateTargets().updateGroups() println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") - val advancedConfig = defaultAdvancedConfig.updateGroups + val advancedConfig = defaultAdvancedConfig.updateMode().updateGroups() println("\n\nResults will be produced in the following directory:") println(s" ${new File("certify").getCanonicalPath}") - val s = StdIn.readLine("Existing files will be overwritten. Continue? Y/n: ") + val s = StdIn.readLine("Existing files will be overwritten. Continue? [Y/n] ") if (s.toLowerCase == "n") { println("Canceled job.") sys.exit(0) @@ -322,19 +400,15 @@ object CertificationBenchmarks { } val standard = new CertificationBenchmarks ( - standardConfig.targets, SynConfig(certHammerPure = true), - List("certify").mkString(File.separator), - "standard" + standardConfig ) - standardConfig.groups.map(standard.runAllTestsFromDir) + standard.runBenchmarks() val advanced = new CertificationBenchmarks( - advancedConfig.targets, - SynConfig(certSetRepr = true), - List("certify").mkString(File.separator), - "advanced" + SynConfig(certSetRepr = true, certHammerPure = true), + advancedConfig ) - advancedConfig.groups.map(advanced.runAllTestsFromDir) + advanced.runBenchmarks() } } From 1f959438ba017bd55c2be9a148765d277ff63222 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 18 May 2021 21:30:16 +0900 Subject: [PATCH 190/211] Minor --- certify-benchmarks | 2 +- .../tygus/suslik/certification/targets/htt/logic/Hint.scala | 4 ++-- .../targets/htt/translation/ProofTranslator.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/certify-benchmarks b/certify-benchmarks index 474476ea2..89e523681 100755 --- a/certify-benchmarks +++ b/certify-benchmarks @@ -1,2 +1,2 @@ -scala -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks +scala -Dfile.encoding=UTF-8 -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala index 7e7e395aa..c87cf8f49 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/logic/Hint.scala @@ -19,11 +19,11 @@ object Hint { private def freshHintId: Int = {_hintId += 1; _hintId} def initHintId: Unit = _hintId = 0 - case class PredicateSetTransitive(pred: CInductivePredicate, hammer: Boolean) extends Hint { + case class PredicateSetCongruence(pred: CInductivePredicate, hammer: Boolean) extends Hint { val dbName: String = "ssl_pred" case class Hypothesis(params: Seq[CVar], idx: Int) { - val name = s"${pred.name}_perm_eq_trans$freshHintId" + val name = s"${pred.name}_perm_eq_congr$freshHintId" val (before, after) = pred.params.map(_._1).splitAt(idx) val s1: CVar = CVar("s_1") val s2: CVar = CVar("s_2") diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala index d00703c2b..56d24b5a5 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala @@ -112,7 +112,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont val appAliases = (cgoal.pre.sigma.apps ++ cgoal.post.sigma.apps).map(a => a -> a).toMap if (env.config.certSetRepr) { val hammer = env.config.certHammerPure - ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetTransitive(p, hammer)) + ctx.hints ++= ctx.predicates.values.map(p => Hint.PredicateSetCongruence(p, hammer)) } val postEx = ListMap(cgoal.existentials.map(v => v -> (goal.getType(Var(v.name)).translate, v)): _*) val ctx1 = ctx.copy(postEx = postEx, appAliases = appAliases) From 55fa0e87cee56ce202a9e42d255e60efcb5d55bf Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Tue, 18 May 2021 23:07:45 +0900 Subject: [PATCH 191/211] =?UTF-8?q?Don=E2=80=99t=20generate=20cert=20stat?= =?UTF-8?q?=20CSV=20if=20not=20compiling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../synthesis/CertificationBenchmarks.scala | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 4c0b2cf8d..889c35e32 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -175,7 +175,7 @@ class CertificationBenchmarks( } } else println("done!") } - outputs.find(_.isProof) match { + if (cfg.mode == BenchmarkMode.SynGenCompile) outputs.find(_.isProof) match { case None => println(s" Warning: No ${target.name} proof output found in certificate for ${cert.testName}! Skipping compilation.") logCertStat(target, List(cert.testName, "-", "-", "-", "-")) @@ -183,17 +183,13 @@ class CertificationBenchmarks( print(s" Checking size of main proof ${proof.filename}...") val (specSize, proofSize) = checkProofSize(outputDir, proof.filename) println(s"done! (spec: $specSize, proof: $proofSize)") - val compileTimeOpt = if (cfg.mode == BenchmarkMode.SynGenCompile) { - print(s" Compiling main proof ${proof.filename}...") - val (res, compileTime) = timed(proof.compile(outputDir)) - if (res == 0) { - println(s"done! (${fmtTime(compileTime)} s)") - Some(compileTime) - } else { - println(s"ERR\n Failed to compile ${proof.filename}!") - None - } + print(s" Compiling main proof ${proof.filename}...") + val (res, compileTime) = timed(proof.compile(outputDir)) + val compileTimeOpt = if (res == 0) { + println(s"done! (${fmtTime(compileTime)} s)") + Some(compileTime) } else { + println(s"ERR\n Failed to compile ${proof.filename}!") None } logCertStat(target, List( @@ -216,7 +212,7 @@ class CertificationBenchmarks( outputDir.mkdirs() initSynLog() if (cfg.groups.nonEmpty) { - if (cfg.mode != BenchmarkMode.SynOnly) { + if (cfg.mode == BenchmarkMode.SynGenCompile) { cfg.targets.foreach(initCertLog) } cfg.groups.foreach(runAllTestsFromDir) @@ -241,7 +237,7 @@ class CertificationBenchmarks( if (file.exists()) file.delete() file.getParentFile.mkdirs() file.createNewFile() - val data = header.mkString(", ") + "\n" + val data = header.mkString(",") + "\n" SynStatUtil.using(new FileWriter(file, true))(_.write(data)) } @@ -256,7 +252,7 @@ class CertificationBenchmarks( } private def logStat(file: File, row: List[String]): Unit = { - val data = s"${row.mkString(", ")}\n" + val data = s"${row.mkString(",")}\n" SynStatUtil.using(new FileWriter(file, true))(_.write(data)) } private def logSynStat(row: List[String]): Unit = logStat(synStatsFile, row) @@ -308,9 +304,9 @@ object CertificationBenchmarks { if (mode == BenchmarkMode.SynOnly) { return this } - println(s"\nBenchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") - val s = StdIn.readLine("Manually select targets instead? [y/N] ") - if (s.toLowerCase() == "y") { + println(s"\nBy default, benchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") + val s = StdIn.readLine("Proceed with default targets? [Y/n] ") + if (s.toLowerCase() == "n") { val newTargets = mutable.ListBuffer[CertificationTarget]() for (t <- targets) { val s = StdIn.readLine(s"Include target '${t.name}'? [Y/n] ") @@ -332,10 +328,10 @@ object CertificationBenchmarks { if (targets.isEmpty) { return this } - println("\nBenchmarks will be run on the following group(s):") + println("\nBy default, benchmarks will be run on the following group(s):") for (g <- groups) println(s"- $g") - val s = StdIn.readLine("Manually select groups instead? [y/N] ") - if (s.toLowerCase() == "y") { + val s = StdIn.readLine("Proceed with default groups? [Y/n] ") + if (s.toLowerCase() == "n") { val newGroups = mutable.ListBuffer[String]() for (g <- groups) { val s = StdIn.readLine(s"Include group '$g'? [Y/n] ") @@ -355,9 +351,9 @@ object CertificationBenchmarks { } def updateMode(): BenchmarkConfig = { - println(s"\nBenchmarks will be run in mode: ${mode.pp}") - val s = StdIn.readLine("Manually select mode instead? [y/N] ") - if (s.toLowerCase() == "y") { + println(s"\nBy default, benchmarks will be run in mode: ${mode.pp}") + val s = StdIn.readLine("Proceed with default mode? [Y/n] ") + if (s.toLowerCase() == "n") { var generate = true var compile = true var s = StdIn.readLine("Generate proof certificates for synthesized results? [Y/n] ") From c9485b8b32228d6047436b2aedbc0d97ed6fca64 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 00:11:28 +0900 Subject: [PATCH 192/211] Update certification docs --- certification.md | 49 +++++++++---------- .../suslik/synthesis/SynthesisRunner.scala | 16 +++--- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/certification.md b/certification.md index 006e28e86..1f02afed4 100644 --- a/certification.md +++ b/certification.md @@ -1,38 +1,23 @@ # Certified Synthesis -Generation of correctness certificates for synthesized programs. Currently, we support HTT as the certification backend. +Generation of correctness certificates for synthesized programs. +Currently, we support three target verification frameworks in Coq: HTT, VST, and Iris. -## HTT +## Requirements -### Examples +Follow the instructions in the README of each repository below to install the necessary +Coq libraries. Each repository also has a `benchmarks/` directory where examples of generated +certificates are viewable. -Visit the [`ssl-htt`](https://github.com/yasunariw/ssl-htt) repository to see examples of generated certificates. +- HTT ([`TyGuS/ssl-htt`](https://github.com/TyGuS/ssl-htt)) +- VST ([`TyGuS/ssl-vst`](https://github.com/TyGuS/ssl-vst)) +- Iris ([`TyGuS/ssl-iris`](https://github.com/TyGuS/ssl-iris)) -### Requirements - -- [Coq](https://coq.inria.fr/) (>= "8.10.0" & < "8.12~") -- [Mathematical Components](http://math-comp.github.io/math-comp/) `ssreflect` (>= "1.10.0" & < "1.11~") -- [FCSL PCM library](https://github.com/imdea-software/fcsl-pcm) (>= "1.0.0" & < "1.3~") -- [HTT](https://github.com/TyGuS/htt) -- [CoqHammer](https://coqhammer.github.io) -- [SSL-HTT](https://github.com/TyGuS/ssl-htt) - -### Building Definitions and Proofs - -We recommend installing with [OPAM](https://opam.ocaml.org/doc/Install.html): - -``` -opam repo add coq-released https://coq.inria.fr/opam/released -opam pin add coq-htt git+https://github.com/TyGuS/htt\#master --no-action --yes -opam pin add coq-ssl-htt git+https://github.com/TyGuS/ssl-htt\#master --no-action --yes -opam install coq coq-mathcomp-ssreflect coq-fcsl-pcm coq-htt coq-hammer coq-ssl-htt -``` - -### Running Synthesis with Certification +## Synthesis with Certification Add the following flags to run synthesis with certification. -- `--certTarget `: Currently supports value `htt`. +- `--certTarget `: Currently supports values `htt`, `vst`, `iris`. - `--certDest ` (optional): Specifies the directory in which to generate a certificate file. Logs output to console if not provided. For example, the following command produces a HTT certificate of the specification `listfree.syn`, and logs its contents to the console. @@ -46,3 +31,15 @@ By providing the `--certDest` flag, SuSLik writes out this certificate as a file ```bash ./suslik examples/listfree.syn --assert false --certTarget htt --certDest . ``` + +### Optional Flags + +If HTT is chosen as the certification target, you can control additional certification parameters. +Note that this has no effect if Iris or VST are chosen. + +- `--certHammerPure ` (boolean; default is `false`): +Controls whether to use CoqHammer to solve pure lemmas. +If `false`, the generated certificate will have all pure lemmas `Admitted` instead. +- `--certSetRepr ` (boolean; default is `false`): +Controls whether to use SSReflect's `perm_eq` to express multi-set equality. +If `false`, the generated certificate will use regular equality (`=`) instead. diff --git a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala index 11bc50305..aaaa6eab1 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/SynthesisRunner.scala @@ -45,10 +45,10 @@ object SynthesisRunner extends SynthesisRunnerUtil { * -j, --traceToJsonFile dump entire proof search trace to a json file; default: none * --memo enable memoization; default: true * --lexi use lexicographic termination metric (as opposed to total size); default: false - * --certTarget set certification target; default: none - * --certDest write certificate to path; default: none - * --certHammerPure use hammer to solve pure lemmas (HTT only); default: false - * --certSetRepr use list permutations to represent sets (HTT only); default: false + * --certTarget set certification target; default: none (options: htt | vst | iris) + * --certDest specify the directory in which to store the certificate file; default: none + * --certHammerPure use hammer to solve pure lemmas instead of admitting them (HTT only); default: false + * --certSetRepr use SSReflect's perm_eq to represent set equality (HTT only); default: false * * --help prints the help reference * @@ -217,19 +217,19 @@ object SynthesisRunner extends SynthesisRunnerUtil { opt[CertificationTarget](name="certTarget").action { (t, rc) => rc.copy(synConfig = rc.synConfig.copy(certTarget = t)) - }.text("set certification target; default: none") + }.text("set certification target; default: none (options: htt | vst | iris)") opt[File](name="certDest").action(cfg { f => _.copy(certDest = f) - }).text("write certificate to path; default: none") + }).text("specify the directory in which to store the certificate file; default: none") opt[Boolean](name = "certHammerPure").action(cfg { b => _.copy(certHammerPure = b) - }).text("use hammer to solve pure lemmas (HTT only); default: false") + }).text("use hammer to solve pure lemmas instead of admitting them (HTT only); default: false") opt[Boolean](name = "certSetRepr").action(cfg { b => _.copy(certSetRepr = b) - }).text("use list permutations to represent sets (HTT only); default: false") + }).text("use SSReflect's perm_eq to represent set equality (HTT only); default: false") help("help").text("prints this usage text") From b1c62a9ba7eb6c29cc16baeafebefcc0a8522d1c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 00:47:31 +0900 Subject: [PATCH 193/211] Update examples --- examples/123.sus | 6 +++--- examples/insertion-sort.syn | 2 +- examples/listcopy.syn | 4 ++-- examples/listfree.syn | 2 +- examples/listmorph.syn | 4 ++-- examples/max.syn | 2 +- examples/predicates.def | 20 +++++++++---------- .../targets/htt/HTTCertificate.scala | 13 ++++++------ .../targets/htt/language/Expressions.scala | 2 +- 9 files changed, 27 insertions(+), 28 deletions(-) diff --git a/examples/123.sus b/examples/123.sus index 904f95d8a..35643123a 100644 --- a/examples/123.sus +++ b/examples/123.sus @@ -1,7 +1,7 @@ predicate dllseg(loc x, loc z, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> w ** (x + 2) :-> z ** dllseg(w, x, s1) } } @@ -15,7 +15,7 @@ void sll_to_dll(loc f) { true ; f :-> i ** dllseg(i, z, s)} { let x2 = *f; - if (x2 == 0) { + if (x2 == null) { } else { /*strange bug: if you remove this comment, then synthesis will break if printDerivations=false*/ let nxt2 = *(x2 + 1); diff --git a/examples/insertion-sort.syn b/examples/insertion-sort.syn index 1b45d11ad..ec19214ed 100644 --- a/examples/insertion-sort.syn +++ b/examples/insertion-sort.syn @@ -6,7 +6,7 @@ sorted list: insert an element void srtl_insert (loc x, loc r) {n1 == n + 1 /\ lo1 == (k <= lo ? k : lo) /\ hi1 == (hi <= k ? k : hi) ; r :-> y ** srtl(y, n1, lo1, hi1) } -{ 0 <= n ; r :-> 0 ** sll(x, n, lo, hi) } +{ 0 <= n ; r :-> null ** sll(x, n, lo, hi) } void insertion_sort_free (loc x, loc r) { true ; r :-> y ** srtl(y, n, lo, hi) } diff --git a/examples/listcopy.syn b/examples/listcopy.syn index c22a33947..ecdf2532d 100644 --- a/examples/listcopy.syn +++ b/examples/listcopy.syn @@ -2,10 +2,10 @@ Copy a linked list ### -{ r :-> x ** lseg(x, S) } +{ r :-> x ** lseg(x, s) } void listcopy(loc r) -{ true ; r :-> y ** lseg(x, S) ** lseg(y, S) } +{ true ; r :-> y ** lseg(x, s) ** lseg(y, s) } ##### \ No newline at end of file diff --git a/examples/listfree.syn b/examples/listfree.syn index 042d02142..5551dc671 100644 --- a/examples/listfree.syn +++ b/examples/listfree.syn @@ -2,7 +2,7 @@ Listfree example ### -{true; lseg(x, S)} +{true; lseg(x, s)} void listfree(loc x) {true ; emp } diff --git a/examples/listmorph.syn b/examples/listmorph.syn index d569e9906..dee8e49af 100644 --- a/examples/listmorph.syn +++ b/examples/listmorph.syn @@ -2,10 +2,10 @@ Morph lseg to lseg 2 ### -{r :-> 0 ** lseg(x, S)} +{r :-> 0 ** lseg(x, s)} void listmorph(loc x, loc r) -{r :-> y ** lseg2(y, S) } +{r :-> y ** lseg2(y, s) } ##### \ No newline at end of file diff --git a/examples/max.syn b/examples/max.syn index 39cbd2d27..ab68d1591 100644 --- a/examples/max.syn +++ b/examples/max.syn @@ -3,7 +3,7 @@ maximum of two integers ### -{ r :-> 0 } +{ r :-> null } void max (loc r, int x, int y) diff --git a/examples/predicates.def b/examples/predicates.def index 6d8b13469..41a3db5b6 100644 --- a/examples/predicates.def +++ b/examples/predicates.def @@ -1,27 +1,27 @@ predicate lseg(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** lseg(nxt, s1) } } predicate lseg2(loc x, set s) { -| x == 0 => { s =i {} ; emp } -| not (x == 0) => { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> v + 1 ** (x + 2) :-> nxt ** lseg2(nxt, s1) } +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 3] ** x :-> v ** (x + 1) :-> (v + 1) ** (x + 2) :-> nxt ** lseg2(nxt, s1) } } predicate tree(loc x, set s) { -| x == 0 => {s =i {}; emp} -| not (x == 0) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} +| x == null => {s =i {}; emp} +| not (x == null) => {s =i {v} ++ s1 ++ s2 ; [x, 3] ** x :-> v ** (x + 1) :-> l ** (x + 2) :-> r ** tree(l, s1) ** tree(r, s2)} } predicate srtl(loc x, int len, int lo, int hi) { -| x == 0 => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } -| not (x == 0) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ v <= lo1 /\ 0 <= v /\ v <= 7 ; +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ v <= lo1 /\ 0 <= v /\ v <= 7 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** srtl(nxt, len1, lo1, hi1) } } predicate sll(loc x, int len, int lo, int hi) { -| x == 0 => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } -| not (x == 0) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; +| x == null => { len == 0 /\ lo == 7 /\ hi == 0 ; emp } +| not (x == null) => { len == 1 + len1 /\ 0 <= len1 /\ lo == (v <= lo1 ? v : lo1) /\ hi == (hi1 <= v ? v : hi1) /\ 0 <= v /\ v <= 7; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, len1, lo1, hi1) } } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala index c8d8495a0..9be442f61 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTTCertificate.scala @@ -9,13 +9,9 @@ case class HTTCertificate(testName: String, name: String, predicates: List[CIndu // Replace hyphens with underscores def sanitize(txt: String): String = txt.replace('-', '_') - def pp: String = - s"""${HTT.prelude} - |${predicates.map(_.pp).mkString("\n\n")} - |$ppMain - |""".stripMargin + def pp: String = s"${HTT.prelude}\n${predicates.map(_.pp).mkString("\n\n")}\n$ppMain" - def ppExternalDefs: String = s"${HTT.prelude}\nRequire Import common.\n\n$ppMain" + def ppExternalDefs(baseFileName: String): String = s"${HTT.prelude}\nRequire Import $baseFileName.\n\n$ppMain" private def ppMain: String = { val builder = new StringBuilder @@ -39,6 +35,9 @@ case class HTTCertificate(testName: String, name: String, predicates: List[CIndu builder.toString } - override def outputs: List[CertificateOutput] = List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), ppExternalDefs)) + override def outputs: List[CertificateOutput] = List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), pp)) + + override def outputs_with_common_predicates(base_filename: String, common_predicates: List[CInductivePredicate]): List[CertificateOutput] = + List(CoqOutput(s"${sanitize(name)}.v", sanitize(name), ppExternalDefs(base_filename))) } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala index cc4f10e8e..9aa2464bd 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/language/Expressions.scala @@ -152,7 +152,7 @@ object Expressions { case class CPointsTo(loc: CExpr, offset: Int = 0, value: CExpr) extends CExpr { def locPP: String = if (offset == 0) loc.pp else s"${loc.pp} .+ $offset" - override def pp: String = s"$locPP :-> ${value.pp}" + override def pp: String = s"$locPP :-> (${value.pp})" override def subst(sigma: CSubst): CPointsTo = CPointsTo(loc.subst(sigma), offset, value.subst(sigma)) } From cca3bc00e151439cd6d75e8b0001db2acdd0ddec Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 02:37:04 +0900 Subject: [PATCH 194/211] UTF-8 for Iris certs in main suslik executable --- suslik | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/suslik b/suslik index 7ed7a7be5..4937ea4bd 100755 --- a/suslik +++ b/suslik @@ -1,3 +1,3 @@ #!/bin/bash -java -jar ./target/scala-2.12/suslik.jar $* +java -Dfile.encoding=UTF-8 -jar ./target/scala-2.12/suslik.jar $* From 1592c1ed0ca4105bb8b8bb663daba0f16cfd141d Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 21:25:20 +0900 Subject: [PATCH 195/211] CLI options --- .../synthesis/CertificationBenchmarks.scala | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 889c35e32..8590a4e97 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -17,6 +17,7 @@ import org.tygus.suslik.report.StopWatch.timed import org.tygus.suslik.synthesis.CertificationBenchmarks.{BenchmarkConfig, BenchmarkMode} import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} +import scopt.OptionParser import scala.collection.mutable import scala.io.StdIn @@ -380,30 +381,50 @@ object CertificationBenchmarks { } } + private case class Config( + configure: Boolean = false, + outputDirName: String = "certify" + ) + + private val parser = new OptionParser[Config]("certify-benchmarks") { + head("Certified Synthesis Benchmarks") + help("help") text "prints this usage text" + + opt[Unit]("configure") action { (_, c) => + c.copy(configure = true) } text "Evaluate benchmarks with custom parameters (invoke config wizard)" + opt[String]("outputDir") action { (x, c) => + c.copy(outputDirName = x) } text "Specify a custom directory for output files" + } + def main(args: Array[String]): Unit = { - println("==========STANDARD BENCHMARK CONFIGURATION==========") - val standardConfig = defaultStandardConfig.updateMode().updateTargets().updateGroups() - println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") - val advancedConfig = defaultAdvancedConfig.updateMode().updateGroups() - println("\n\nResults will be produced in the following directory:") - println(s" ${new File("certify").getCanonicalPath}") - val s = StdIn.readLine("Existing files will be overwritten. Continue? [Y/n] ") - if (s.toLowerCase == "n") { - println("Canceled job.") - sys.exit(0) - } else { - println("Starting benchmarks...\n\n") + val runConfig = parser.parse(args, Config()) match { + case Some(config) => config + case None => + System.err.println("Bad argument format.") + sys.exit(1) } + val (standardConfig, advancedConfig) = if (runConfig.configure) { + println("==========STANDARD BENCHMARK CONFIGURATION==========") + val standardConfig = defaultStandardConfig.updateMode().updateTargets().updateGroups() + println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") + val advancedConfig = defaultAdvancedConfig.updateMode().updateGroups() + (standardConfig, advancedConfig) + } else (defaultStandardConfig, defaultAdvancedConfig) + + println("\n\nResults will be produced in the following directory:") + println(s" ${new File(runConfig.outputDirName).getCanonicalPath}") + println("\n\nStarting benchmarks...\n\n") + val standard = new CertificationBenchmarks ( SynConfig(certHammerPure = true), - standardConfig + standardConfig.copy(outputDirName = runConfig.outputDirName) ) standard.runBenchmarks() val advanced = new CertificationBenchmarks( SynConfig(certSetRepr = true, certHammerPure = true), - advancedConfig + advancedConfig.copy(outputDirName = runConfig.outputDirName) ) advanced.runBenchmarks() } From 31a4ba7379ee84238b9c5011d2c859a4b59b9d7c Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 21:32:20 +0900 Subject: [PATCH 196/211] Allow args --- certify-benchmarks | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/certify-benchmarks b/certify-benchmarks index 89e523681..929248e58 100755 --- a/certify-benchmarks +++ b/certify-benchmarks @@ -1,2 +1,3 @@ -scala -Dfile.encoding=UTF-8 -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks +#!/bin/bash +scala -Dfile.encoding=UTF-8 -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks $* From f4cdeeacc58f1ff7493b841a142a933bb8390f25 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 22:16:47 +0900 Subject: [PATCH 197/211] Restrict benchmark mode to compile on/off --- .../synthesis/CertificationBenchmarks.scala | 74 ++++--------------- 1 file changed, 15 insertions(+), 59 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 8590a4e97..95e13ee97 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -14,7 +14,7 @@ import org.tygus.suslik.logic.Preprocessor.preprocessProgram import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.report.StopWatch.timed -import org.tygus.suslik.synthesis.CertificationBenchmarks.{BenchmarkConfig, BenchmarkMode} +import org.tygus.suslik.synthesis.CertificationBenchmarks.BenchmarkConfig import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} import scopt.OptionParser @@ -121,10 +121,6 @@ class CertificationBenchmarks( } println("Finished synthesizing specifications!") - if (cfg.mode == BenchmarkMode.SynOnly) { - return - } - for (target <- cfg.targets) { val outputDir = outputDirs(target) println(s"Generating ${target.name} certificates...") @@ -154,7 +150,7 @@ class CertificationBenchmarks( defFiles.foreach { output => print(s" File ${output.filename}: serializing...") serialize(outputDir, output.filename, output.body) - if (cfg.mode == BenchmarkMode.SynGenCompile) { + if (cfg.compile) { print("compiling...") output.compile(outputDir) } @@ -168,7 +164,7 @@ class CertificationBenchmarks( for (o <- outputs) { print(s" File ${o.filename}: serializing...") serialize(outputDir, o.filename, o.body) - if (!o.isProof && cfg.mode == BenchmarkMode.SynGenCompile) { + if (!o.isProof && cfg.compile) { print("compiling...") o.compile(outputDir) match { case 0 => println("done!") @@ -176,7 +172,7 @@ class CertificationBenchmarks( } } else println("done!") } - if (cfg.mode == BenchmarkMode.SynGenCompile) outputs.find(_.isProof) match { + if (cfg.compile) outputs.find(_.isProof) match { case None => println(s" Warning: No ${target.name} proof output found in certificate for ${cert.testName}! Skipping compilation.") logCertStat(target, List(cert.testName, "-", "-", "-", "-")) @@ -213,7 +209,7 @@ class CertificationBenchmarks( outputDir.mkdirs() initSynLog() if (cfg.groups.nonEmpty) { - if (cfg.mode == BenchmarkMode.SynGenCompile) { + if (cfg.compile) { cfg.targets.foreach(initCertLog) } cfg.groups.foreach(runAllTestsFromDir) @@ -276,35 +272,17 @@ object CertificationBenchmarks { "certification-benchmarks-advanced/dll", "certification-benchmarks-advanced/srtl", ) - val defaultStandardConfig: BenchmarkConfig = BenchmarkConfig(allTargets, allStandard, BenchmarkMode.SynGenCompile, "standard") - val defaultAdvancedConfig: BenchmarkConfig = BenchmarkConfig(List(HTT()), allAdvanced, BenchmarkMode.SynGen, "advanced") - - trait BenchmarkMode { - def pp: String - } - object BenchmarkMode { - case object SynOnly extends BenchmarkMode { - def pp: String = "synthesize only" - } - case object SynGen extends BenchmarkMode { - def pp: String = "synthesize; generate proofs" - } - case object SynGenCompile extends BenchmarkMode { - def pp: String = "synthesize; generate and compile proofs" - } - } + val defaultStandardConfig: BenchmarkConfig = BenchmarkConfig(allTargets, allStandard, compile = true, "standard") + val defaultAdvancedConfig: BenchmarkConfig = BenchmarkConfig(List(HTT()), allAdvanced, compile = false, "advanced") case class BenchmarkConfig( targets: List[CertificationTarget], groups: List[String], - mode: BenchmarkMode, + compile: Boolean, statsFilePrefix: String, outputDirName: String = "certify", ) { def updateTargets(): BenchmarkConfig = { - if (mode == BenchmarkMode.SynOnly) { - return this - } println(s"\nBy default, benchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") val s = StdIn.readLine("Proceed with default targets? [Y/n] ") if (s.toLowerCase() == "n") { @@ -351,33 +329,11 @@ object CertificationBenchmarks { } } - def updateMode(): BenchmarkConfig = { - println(s"\nBy default, benchmarks will be run in mode: ${mode.pp}") - val s = StdIn.readLine("Proceed with default mode? [Y/n] ") - if (s.toLowerCase() == "n") { - var generate = true - var compile = true - var s = StdIn.readLine("Generate proof certificates for synthesized results? [Y/n] ") - if (s.toLowerCase() == "n") { - generate = false - compile = false - } else { - s = StdIn.readLine("Compile generated certificates? [Y/n] ") - if (s.toLowerCase() == "n") { - compile = false - } - } - val newMode = (generate, compile) match { - case (true, true) => BenchmarkMode.SynGenCompile - case (true, false) => BenchmarkMode.SynGen - case _ => BenchmarkMode.SynOnly - } - println(s"\nBenchmarks updated to run in mode: ${newMode.pp}") - this.copy(mode = newMode) - } else { - println("Evaluation mode unchanged.") - this - } + def updateCompile(): BenchmarkConfig = { + val s = StdIn.readLine("Do you wish to compile generated certificates? [Y/n] ") + val newCompile = s.toLowerCase() != "n" + println(s"\nBenchmarks will be evaluated with certificate compilation ${if (newCompile) "ENABLED" else "DISABLED"}") + this.copy(compile = newCompile) } } @@ -406,9 +362,9 @@ object CertificationBenchmarks { val (standardConfig, advancedConfig) = if (runConfig.configure) { println("==========STANDARD BENCHMARK CONFIGURATION==========") - val standardConfig = defaultStandardConfig.updateMode().updateTargets().updateGroups() + val standardConfig = defaultStandardConfig.updateCompile().updateTargets().updateGroups() println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") - val advancedConfig = defaultAdvancedConfig.updateMode().updateGroups() + val advancedConfig = defaultAdvancedConfig.updateCompile().updateGroups() (standardConfig, advancedConfig) } else (defaultStandardConfig, defaultAdvancedConfig) From e1b9ec65009130cee970a9f964a5fd946bf561f7 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 22:18:34 +0900 Subject: [PATCH 198/211] Minor --- .../org/tygus/suslik/synthesis/CertificationBenchmarks.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 95e13ee97..d81227098 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -206,9 +206,9 @@ class CertificationBenchmarks( } def runBenchmarks(): Unit = { - outputDir.mkdirs() - initSynLog() if (cfg.groups.nonEmpty) { + outputDir.mkdirs() + initSynLog() if (cfg.compile) { cfg.targets.foreach(initCertLog) } From 7562e106ad5beadbd5e7f8b651e9a661be4777e0 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Wed, 19 May 2021 22:49:22 +0900 Subject: [PATCH 199/211] Printing --- .../org/tygus/suslik/synthesis/CertificationBenchmarks.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index d81227098..78b8e75c7 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -126,7 +126,7 @@ class CertificationBenchmarks( println(s"Generating ${target.name} certificates...") val certs = for ((testName, proc, tree, goal, env) <- synResults) yield { if (exclusions.contains((target.name, testName))) { - print(s" $testName...skipping unsupported test case for ${target.name}") + println(s" $testName...skipping unsupported test case for ${target.name}") None } else { try { From 99d1fb3905c17c19b409ac9cc33a63399d56f1d8 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 20 May 2021 12:34:58 +0900 Subject: [PATCH 200/211] Increase ATP limit for slow machines --- .../scala/org/tygus/suslik/certification/targets/htt/HTT.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala index 6c51f9317..75f81637b 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/HTT.scala @@ -36,6 +36,7 @@ object HTT { |Require Import core. |From Hammer Require Import Hammer. |(* Configure Hammer *) + |Set Hammer ATPLimit 60. |Unset Hammer Eprover. |Unset Hammer Vampire. |Add Search Blacklist "fcsl.". From 6b653b26f8848e2cf2a5b07716e3394a67202174 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 20 May 2021 17:37:41 +0900 Subject: [PATCH 201/211] Fresh names per spec and update readme --- README.md | 6 ++++-- .../tygus/suslik/synthesis/CertificationBenchmarks.scala | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a433fbf89..22e7ea0a6 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,10 @@ where the necessary arguments and options are dump entire proof search trace to a json file; default: none --memo enable memoization; default: true --lexi use lexicographic termination metric (as opposed to total size); default: false - --certTarget set certification target; default: none - --certDest write certificate to path; default: none + --certTarget set certification target; default: none (options: htt | vst | iris) + --certDest specify the directory in which to store the certificate file; default: none + --certHammerPure use hammer to solve pure lemmas instead of admitting them (HTT only); default: false + --certSetRepr use SSReflect's perm_eq to represent set equality (HTT only); default: false --help prints the help reference diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 78b8e75c7..a4187d59b 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -3,6 +3,7 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.Paths +import org.tygus.suslik.LanguageUtils import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.HTT import org.tygus.suslik.certification.targets.vst.VST @@ -40,6 +41,7 @@ class CertificationBenchmarks( ) def synthesizeOne(text: String, parser: SSLParser, params: SynConfig): (List[Statements.Procedure], Environment, Long) = { + LanguageUtils.resetFreshNameGenerator() val res = params.inputFormat match { case `dotSyn` => parser.parseGoalSYN(text) case `dotSus` => parser.parseGoalSUS(text) From b0d54d4be3a7ef1e0dcb0cccf6e423f40a776dc9 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Thu, 20 May 2021 17:41:55 +0900 Subject: [PATCH 202/211] Translator -> Interpreter --- .../htt/translation/ProgramEvaluator.scala | 2 +- ...anslator.scala => ProgramInterpreter.scala} | 8 ++++---- .../htt/translation/ProofEvaluator.scala | 2 +- ...Translator.scala => ProofInterpreter.scala} | 10 +++++----- ...anslator.scala => ProgramInterpreter.scala} | 8 ++++---- ...Translator.scala => ProofInterpreter.scala} | 12 ++++++------ .../targets/iris/translation/Translation.scala | 6 +++--- .../targets/vst/translation/Translation.scala | 14 +++++++------- ...lator.scala => VSTProgramInterpreter.scala} | 18 +++++++++--------- ...nslator.scala => VSTProofInterpreter.scala} | 14 +++++++------- .../certification/traversal/Evaluator.scala | 2 +- .../{Translator.scala => Interpreter.scala} | 6 +++--- .../traversal/TranslatableOps.scala | 4 ++-- 13 files changed, 53 insertions(+), 53 deletions(-) rename src/main/scala/org/tygus/suslik/certification/targets/htt/translation/{ProgramTranslator.scala => ProgramInterpreter.scala} (81%) rename src/main/scala/org/tygus/suslik/certification/targets/htt/translation/{ProofTranslator.scala => ProofInterpreter.scala} (97%) rename src/main/scala/org/tygus/suslik/certification/targets/iris/translation/{ProgramTranslator.scala => ProgramInterpreter.scala} (86%) rename src/main/scala/org/tygus/suslik/certification/targets/iris/translation/{ProofTranslator.scala => ProofInterpreter.scala} (97%) rename src/main/scala/org/tygus/suslik/certification/targets/vst/translation/{VSTProgramTranslator.scala => VSTProgramInterpreter.scala} (90%) rename src/main/scala/org/tygus/suslik/certification/targets/vst/translation/{VSTProofTranslator.scala => VSTProofInterpreter.scala} (97%) rename src/main/scala/org/tygus/suslik/certification/traversal/{Translator.scala => Interpreter.scala} (64%) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala index d3595573a..39752f418 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramEvaluator.scala @@ -5,5 +5,5 @@ import org.tygus.suslik.certification.targets.htt.program.Statements.CStatement import org.tygus.suslik.certification.traversal.StackEvaluator object ProgramEvaluator extends StackEvaluator[SuslikProofStep, CStatement, ProgramContext] { - val translator = ProgramTranslator + val interpreter = ProgramInterpreter } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramInterpreter.scala similarity index 81% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramInterpreter.scala index bfae921ca..693083f63 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProgramInterpreter.scala @@ -3,11 +3,11 @@ package org.tygus.suslik.certification.targets.htt.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.program.Statements.{CFree, CGuarded, CIf, CSkip, CStatement, Noop} import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.traversal.Translator -import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.Interpreter +import org.tygus.suslik.certification.traversal.Interpreter.Result -object ProgramTranslator extends Translator[SuslikProofStep, CStatement, ProgramContext] { - override def translate(value: SuslikProofStep, ctx: ProgramContext): Translator.Result[CStatement, ProgramContext] = { +object ProgramInterpreter extends Interpreter[SuslikProofStep, CStatement, ProgramContext] { + override def interpret(value: SuslikProofStep, ctx: ProgramContext): Interpreter.Result[CStatement, ProgramContext] = { val withNoDeferred = (Nil, None, ctx) implicit val env = ctx.env value match { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala index 9062ccb7b..3f6d14683 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofEvaluator.scala @@ -5,5 +5,5 @@ import org.tygus.suslik.certification.targets.htt.logic.Proof import org.tygus.suslik.certification.traversal.StackEvaluator object ProofEvaluator extends StackEvaluator[SuslikProofStep, Proof.Step, ProofContext] { - val translator = ProofTranslator + val interpreter = ProofInterpreter } diff --git a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofInterpreter.scala similarity index 97% rename from src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofInterpreter.scala index 56d24b5a5..c0c18bc1d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/htt/translation/ProofInterpreter.scala @@ -7,15 +7,15 @@ import org.tygus.suslik.certification.targets.htt.logic.Sentences.CAssertion import org.tygus.suslik.certification.targets.htt.translation.ProofContext.AppliedConstructor import org.tygus.suslik.certification.targets.htt.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.{Deferred, EvaluatorException} -import org.tygus.suslik.certification.traversal.Translator -import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.Interpreter +import org.tygus.suslik.certification.traversal.Interpreter.Result import org.tygus.suslik.language.Expressions.Var import org.tygus.suslik.language.Statements.{Free, Load, Malloc, Store} import org.tygus.suslik.logic.SApp import scala.collection.immutable.ListMap -object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofContext] { +object ProofInterpreter extends Interpreter[SuslikProofStep, Proof.Step, ProofContext] { private def withNoDeferred(ctx: ProofContext): (List[Proof.Step], Option[Deferred[Proof.Step, ProofContext]], ProofContext) = (Nil, None, ctx) private def initPre(asn: CAssertion, uniqueName: String): List[Proof.Step] = { @@ -50,7 +50,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont } yield Proof.Rename(from, to) } - def handleSubstitution(m: Map[CVar, CExpr], ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { + def handleSubstitution(m: Map[CVar, CExpr], ctx: ProofContext): Interpreter.Result[Proof.Step, ProofContext] = { val affectedApps = ctx.appsAffectedBySubst(m) val ctx1 = ctx.withSubst(m, affectedApps) val steps = renameAppsStep(affectedApps) @@ -69,7 +69,7 @@ object ProofTranslator extends Translator[SuslikProofStep, Proof.Step, ProofCont } } - override def translate(value: SuslikProofStep, ctx: ProofContext): Translator.Result[Proof.Step, ProofContext] = { + override def interpret(value: SuslikProofStep, ctx: ProofContext): Interpreter.Result[Proof.Step, ProofContext] = { implicit val env = ctx.env value match { /** Initialization */ diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramInterpreter.scala similarity index 86% rename from src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramInterpreter.scala index 5c94a6137..39c6549df 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProgramInterpreter.scala @@ -6,8 +6,8 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Types.HType import org.tygus.suslik.certification.targets.iris.logic.Assertions.IQuantifiedVar import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.certification.traversal.Translator -import org.tygus.suslik.certification.traversal.Translator.Result +import org.tygus.suslik.certification.traversal.Interpreter +import org.tygus.suslik.certification.traversal.Interpreter.Result import org.tygus.suslik.language.Ident import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.{Environment, Gamma} @@ -17,9 +17,9 @@ case class ProgramTranslationContext(env: Environment, proc: Procedure, gamma: G /** * Extract a HeapLang program directly from the SSL proof. */ -object ProgramTranslator extends Translator[SuslikProofStep, HExpr, ProgramTranslationContext] { +object ProgramInterpreter$ extends Interpreter[SuslikProofStep, HExpr, ProgramTranslationContext] { - override def translate(step: SuslikProofStep, ctx: ProgramTranslationContext): Translator.Result[HExpr, ProgramTranslationContext] = { + override def interpret(step: SuslikProofStep, ctx: ProgramTranslationContext): Interpreter.Result[HExpr, ProgramTranslationContext] = { val withNoDeferred = (Nil, None, ctx) step match { case SuslikProofStep.Open(_, _, _, selectors) => diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofInterpreter.scala similarity index 97% rename from src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofInterpreter.scala index dab3f5c62..340a4405d 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/ProofInterpreter.scala @@ -5,10 +5,10 @@ import org.tygus.suslik.certification.targets.iris.heaplang.Types.{HCardType, HL import org.tygus.suslik.certification.targets.iris.logic.Assertions.{ICardConstructor, IFunSpec, IPredicate, IPureAssertion, ISpecVar} import org.tygus.suslik.certification.targets.iris.logic._ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.Translatable -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.normalize_renaming +import org.tygus.suslik.certification.targets.vst.translation.VSTProofInterpreter.normalize_renaming import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.certification.traversal.Translator.Result -import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.certification.traversal.Interpreter.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Interpreter} import org.tygus.suslik.language.Expressions.{Expr, Var} import org.tygus.suslik.language.Statements.Load import org.tygus.suslik.language.{Expressions, Ident, Statements} @@ -88,9 +88,9 @@ case class IProofContext( } } -case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, IProofStep, IProofContext] { +case class ProofInterpreter(spec: IFunSpec) extends Interpreter[SuslikProofStep, IProofStep, IProofContext] { type Deferred = Evaluator.Deferred[IProofStep, IProofContext] - type Result = Translator.Result[IProofStep, IProofContext] + type Result = Interpreter.Result[IProofStep, IProofContext] private val noDeferreds: Option[Deferred] = None @@ -99,7 +99,7 @@ case class ProofTranslator(spec: IFunSpec) extends Translator[SuslikProofStep, I private val irisRet = "Ret" private val irisSelf: String = spec.fname - override def translate(value: SuslikProofStep, clientCtx: IProofContext): Result = + override def interpret(value: SuslikProofStep, clientCtx: IProofContext): Result = value match { case SuslikProofStep.Init(_) => var ctx = clientCtx diff --git a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala index 85d458cab..d2fa7ea83 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/iris/translation/Translation.scala @@ -11,15 +11,15 @@ import org.tygus.suslik.certification.targets.iris.translation.TranslatableOps.T import org.tygus.suslik.language.Statements.Procedure import org.tygus.suslik.logic.Environment import org.tygus.suslik.certification.targets.iris.translation.IrisTranslator._ -import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator, Translator} +import org.tygus.suslik.certification.traversal.{ProofTree, StackEvaluator, Interpreter} import org.tygus.suslik.logic.Specifications.Goal object ProgramEvaluator extends StackEvaluator[SuslikProofStep, HExpr, ProgramTranslationContext] { - val translator: Translator[SuslikProofStep, HExpr, ProgramTranslationContext] = ProgramTranslator + val interpreter: Interpreter[SuslikProofStep, HExpr, ProgramTranslationContext] = ProgramInterpreter$ } case class ProofEvaluator(spec: IFunSpec) extends StackEvaluator[SuslikProofStep, IProofStep, IProofContext] { - val translator: Translator[SuslikProofStep, IProofStep, IProofContext] = ProofTranslator(spec) + val interpreter: Interpreter[SuslikProofStep, IProofStep, IProofContext] = ProofInterpreter(spec) } object Translation { diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala index 4748c6ef6..4b12c83ca 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/Translation.scala @@ -4,10 +4,10 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.targets.vst.Types.{CoqIntValType, CoqPtrValType} import org.tygus.suslik.certification.targets.vst.VSTCertificate import org.tygus.suslik.certification.targets.vst.logic.{Proof, VSTProofStep} -import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.certification.targets.vst.translation.VSTProgramInterpreter.VSTProgramContext +import org.tygus.suslik.certification.targets.vst.translation.VSTProofInterpreter.VSTClientContext import org.tygus.suslik.certification.traversal.Step.DestStep -import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Translator} +import org.tygus.suslik.certification.traversal.{Evaluator, ProofTree, StackEvaluator, Interpreter} import org.tygus.suslik.certification.CertTree import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.clang.Statements.CProcedureDefinition @@ -22,9 +22,9 @@ object Translation { def fail_with(msg: String) = throw TranslationException(msg) - def translate_proof[S <: DestStep,C <: Evaluator.ClientContext[S]](proof: ProofTree[SuslikProofStep])(implicit t: Translator[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { + def translate_proof[S <: DestStep,C <: Evaluator.ClientContext[S]](proof: ProofTree[SuslikProofStep])(implicit t: Interpreter[SuslikProofStep, S, C], initial_context:C): ProofTree[S] = { val evaluator = new StackEvaluator[SuslikProofStep, S, C] { - val translator = t + val interpreter = t } evaluator.run(proof, initial_context) } @@ -44,11 +44,11 @@ object Translation { }}) val spec = ProofSpecTranslation.translate_conditions(env)(pred_type_map)(proc.f) val helper_specs = env.functions.map {case (fname, spec) => (fname, ProofSpecTranslation.translate_conditions(env)(pred_type_map)(spec))} - val program_body = translate_proof(base_proof)(new VSTProgramTranslator, VSTProgramTranslator.empty_context) + val program_body = translate_proof(base_proof)(new VSTProgramInterpreter, VSTProgramInterpreter.empty_context) val procedure = CProcedureDefinition(proc.name, params, program_body, helper_specs.values.toList) val spec_map = Map(proc.name -> spec) ++ helper_specs - val proof_translator = VSTProofTranslator(spec) + val proof_translator = VSTProofInterpreter(spec) val steps = translate_proof(base_proof)(proof_translator, VSTClientContext.make_context(pred_map, spec_map)) val proof = Proof( diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramInterpreter.scala similarity index 90% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramInterpreter.scala index 6ee5577ea..0b75049b3 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProgramInterpreter.scala @@ -8,19 +8,19 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.VSTPredicate import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{Forward, ForwardIf, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.certification.traversal.Translator.Result -import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.certification.traversal.Interpreter.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Interpreter} import org.tygus.suslik.language.Expressions.{Expr, Subst, SubstVar, Var} import org.tygus.suslik.language.{IntType, LocType, SSLType} import org.tygus.suslik.certification.targets.vst.clang.Statements.{CCall, CElif, CFree, CIf, CLoadInt, CLoadLoc, CMalloc, CSkip, CWriteInt, CWriteLoc, StatementStep} -import org.tygus.suslik.certification.targets.vst.translation.VSTProgramTranslator.VSTProgramContext -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.VSTClientContext +import org.tygus.suslik.certification.targets.vst.translation.VSTProgramInterpreter.VSTProgramContext +import org.tygus.suslik.certification.targets.vst.translation.VSTProofInterpreter.VSTClientContext import org.tygus.suslik.language.Statements.{Call, Free, Load, Malloc, Store} import org.tygus.suslik.logic.{Block, Gamma, Heaplet, PointsTo, SApp} import scala.collection.immutable.Queue -object VSTProgramTranslator { +object VSTProgramInterpreter { case class VSTProgramContext ( @@ -34,7 +34,7 @@ object VSTProgramTranslator { } -class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VSTProgramContext] { +class VSTProgramInterpreter extends Interpreter[SuslikProofStep, StatementStep, VSTProgramContext] { type Deferred = Evaluator.Deferred[StatementStep, VSTProgramContext] private val no_deferreds: Option[Deferred] = None private val no_ops : List[StatementStep] = List() @@ -45,7 +45,7 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS - def with_no_op(implicit context: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = + def with_no_op(implicit context: VSTProgramInterpreter.VSTProgramContext): Result[StatementStep, VSTProgramInterpreter.VSTProgramContext] = Result(List(), List((Nil, None, context))) @@ -83,8 +83,8 @@ class VSTProgramTranslator extends Translator[SuslikProofStep, StatementStep, VS case LocType => CoqPtrValType } - override def translate(value: SuslikProofStep, clientContext: VSTProgramTranslator.VSTProgramContext): Result[StatementStep, VSTProgramTranslator.VSTProgramContext] = { - implicit val ctx: VSTProgramTranslator.VSTProgramContext = clientContext + override def interpret(value: SuslikProofStep, clientContext: VSTProgramInterpreter.VSTProgramContext): Result[StatementStep, VSTProgramInterpreter.VSTProgramContext] = { + implicit val ctx: VSTProgramInterpreter.VSTProgramContext = clientContext value match { case SuslikProofStep.NilNotLval(_) | SuslikProofStep.CheckPost(_, _, _) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala similarity index 97% rename from src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala rename to src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala index 5aced9071..3aac8d62e 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofTranslator.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala @@ -8,14 +8,14 @@ import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecif import org.tygus.suslik.certification.targets.vst.logic.{Formulae, VSTProofStep} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, ForwardTernary, Free, Intros, IntrosTuple, Malloc, Rename, TentativeEntailer, UnfoldRewrite, ValidPointer} import org.tygus.suslik.certification.traversal.Evaluator.ClientContext -import org.tygus.suslik.certification.traversal.Translator.Result -import org.tygus.suslik.certification.traversal.{Evaluator, Translator} +import org.tygus.suslik.certification.traversal.Interpreter.Result +import org.tygus.suslik.certification.traversal.{Evaluator, Interpreter} import org.tygus.suslik.language.Expressions.{Expr, SubstVar, Var} import org.tygus.suslik.language.{Expressions, Ident, SSLType, Statements} -import org.tygus.suslik.certification.targets.vst.translation.VSTProofTranslator.{PendingCall, VSTClientContext, normalize_renaming} +import org.tygus.suslik.certification.targets.vst.translation.VSTProofInterpreter.{PendingCall, VSTClientContext, normalize_renaming} import org.tygus.suslik.language.Statements.Load -object VSTProofTranslator { +object VSTProofInterpreter { /** * Represents a pending function call. @@ -170,9 +170,9 @@ object VSTProofTranslator { } -case class VSTProofTranslator(spec: FormalSpecification) extends Translator[SuslikProofStep, VSTProofStep, VSTProofTranslator.VSTClientContext] { +case class VSTProofInterpreter(spec: FormalSpecification) extends Interpreter[SuslikProofStep, VSTProofStep, VSTProofInterpreter.VSTClientContext] { type Deferred = Evaluator.Deferred[VSTProofStep, VSTClientContext] - type Result = Translator.Result[VSTProofStep, VSTClientContext] + type Result = Interpreter.Result[VSTProofStep, VSTClientContext] var contains_free: Boolean = false var contains_malloc: Boolean = false @@ -189,7 +189,7 @@ case class VSTProofTranslator(spec: FormalSpecification) extends Translator[Susl case v => v } - override def translate(value: SuslikProofStep, clientContext: VSTClientContext): Result = { + override def interpret(value: SuslikProofStep, clientContext: VSTClientContext): Result = { value match { /** Initialization */ case SuslikProofStep.Init(goal) => diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala index 1bce36716..bcaba338c 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Evaluator.scala @@ -7,7 +7,7 @@ import org.tygus.suslik.logic.Specifications.GoalLabel import scala.collection.immutable.Queue trait Evaluator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { - implicit val translator: Translator[S,D,C] + implicit val interpreter: Interpreter[S,D,C] def run(node: ProofTree[S], initialClientContext: C): ProofTree[D] diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala b/src/main/scala/org/tygus/suslik/certification/traversal/Interpreter.scala similarity index 64% rename from src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala rename to src/main/scala/org/tygus/suslik/certification/traversal/Interpreter.scala index 4f56bc48e..2da78167d 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/Translator.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/Interpreter.scala @@ -3,10 +3,10 @@ package org.tygus.suslik.certification.traversal import org.tygus.suslik.certification.traversal.Evaluator._ import org.tygus.suslik.certification.traversal.Step._ -trait Translator[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { - def translate(value: S, clientContext: C): Translator.Result[D,C] +trait Interpreter[S <: SourceStep, D <: DestStep, C <: ClientContext[D]] { + def interpret(value: S, clientContext: C): Interpreter.Result[D,C] } -object Translator { +object Interpreter { case class Result[D <: DestStep, C <: ClientContext[D]](steps: List[D], childParams: List[(List[D], Option[Deferred[D,C]], C)]) } \ No newline at end of file diff --git a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala index 363865a0f..029f38c28 100644 --- a/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala +++ b/src/main/scala/org/tygus/suslik/certification/traversal/TranslatableOps.scala @@ -5,8 +5,8 @@ import org.tygus.suslik.certification.traversal.Step._ object TranslatableOps { implicit class Translatable[S <: SourceStep](step: S) { - def translate[D <: DestStep, C <: ClientContext[D]](clientContext: C)(implicit translator: Translator[S, D, C]): Translator.Result[D,C] = { - translator.translate(step, clientContext) + def translate[D <: DestStep, C <: ClientContext[D]](clientContext: C)(implicit translator: Interpreter[S, D, C]): Interpreter.Result[D,C] = { + translator.interpret(step, clientContext) } } } From ce3c8c8579cbc908ce03bf469fd984492a59ff2b Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Mon, 28 Jun 2021 13:20:42 +0800 Subject: [PATCH 203/211] Fixed implementation of VST translation --- .../targets/vst/translation/VSTProofInterpreter.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala index c49dd2bac..ebeee16cb 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala @@ -3,7 +3,7 @@ package org.tygus.suslik.certification.targets.vst.translation import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.vst.Types import org.tygus.suslik.certification.targets.vst.Types.{CoqCardType, CoqPtrValType, VSTType} -import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCVar} +import org.tygus.suslik.certification.targets.vst.logic.Expressions.{ProofCBinaryExpr, ProofCCardinalityConstructor, ProofCExpr, ProofCIfThenElse, ProofCVar} import org.tygus.suslik.certification.targets.vst.logic.ProofTerms.{FormalSpecification, PureFormula, VSTPredicate} import org.tygus.suslik.certification.targets.vst.logic.{Formulae, VSTProofStep} import org.tygus.suslik.certification.targets.vst.logic.VSTProofStep.{AssertProp, AssertPropSubst, Exists, Forward, ForwardCall, ForwardEntailer, ForwardIf, ForwardIfConstructor, ForwardTernary, Free, Intros, IntrosTuple, Malloc, Rename, TentativeEntailer, UnfoldRewrite, ValidPointer} @@ -353,8 +353,8 @@ case class VSTProofInterpreter(spec: FormalSpecification) extends Interpreter[Su // SubstL translated into `assert (from = to) as H; rewrite H in *` // No changes to context val ctx = clientContext with_mapping_between(from, to) - val from_type = clientContext.typing_context(from) - val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = Some(from_type)) + val from_type = clientContext.typing_context.get(from) + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = from_type) val step = AssertPropSubst(from, to_expr) Result(List(step), List((with_no_deferreds(ctx)))) From ba9774bb9e5c52c806d2e2cfb73bf21107697448 Mon Sep 17 00:00:00 2001 From: Kiran Gopinathan Date: Wed, 30 Jun 2021 14:16:53 +0800 Subject: [PATCH 204/211] Handles weird SUBSTL rules that didn't seem to exist in previous iteration --- .../vst/translation/VSTProofInterpreter.scala | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala index ebeee16cb..336775719 100644 --- a/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala +++ b/src/main/scala/org/tygus/suslik/certification/targets/vst/translation/VSTProofInterpreter.scala @@ -353,10 +353,15 @@ case class VSTProofInterpreter(spec: FormalSpecification) extends Interpreter[Su // SubstL translated into `assert (from = to) as H; rewrite H in *` // No changes to context val ctx = clientContext with_mapping_between(from, to) - val from_type = clientContext.typing_context.get(from) - val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = from_type) - val step = AssertPropSubst(from, to_expr) - Result(List(step), List((with_no_deferreds(ctx)))) + val step = + if (ctx.coq_context.contains(from)) { + val from_type = clientContext.typing_context.get(from) + val to_expr = ProofSpecTranslation.translate_expression(clientContext.typing_context)(to, target = from_type) + List(AssertPropSubst(from, to_expr)) + } else { + List() + } + Result(step, List((with_no_deferreds(ctx)))) case SuslikProofStep.SubstR(Var(from), to) => val from_type = clientContext.typing_context.get(from) From d5205815bf687b9055a8fe4665e3472fe62ca247 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 4 Jul 2021 19:41:05 +0900 Subject: [PATCH 205/211] Fix regressions and remove old tests --- .../synthesis/rules/UnificationRules.scala | 5 ++- .../targets/htt/HTTCertificationTests.scala | 45 ------------------- .../targets/vst/VSTCertificationTests.scala | 42 ----------------- 3 files changed, 4 insertions(+), 88 deletions(-) delete mode 100644 src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala delete mode 100644 src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 5f2c2f0ab..509afc065 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -96,7 +96,10 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils val subExpr = goal.substToFormula(subst) val newPost = Assertion(post.phi && subExpr, post.sigma) val newGoal = goal.spawnChild(post = newPost) - val kont = SubstVarProducer(y.asInstanceOf[Var], x.asInstanceOf[Var]) >> IdProducer >> ExtractHelper(goal) + val kont = (x, y) match { + case (x:Var, y:Var) => SubstVarProducer(y, x) >> IdProducer >> ExtractHelper(goal) + case _ => IdProducer >> ExtractHelper(goal) + } List(RuleResult(List(newGoal), kont, this, goal)) } } diff --git a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala deleted file mode 100644 index 79578c283..000000000 --- a/src/test/scala/org/tygus/suslik/certification/targets/htt/HTTCertificationTests.scala +++ /dev/null @@ -1,45 +0,0 @@ -package org.tygus.suslik.certification.targets.htt - -import java.io.File -import java.nio.file.{Files, Paths} - -import org.scalatest.{FunSpec, Matchers} -import org.tygus.suslik.synthesis.{SynConfig, SynthesisRunnerUtil, defaultConfig} - -import scala.sys.process._ - -/** - * @author Yasunari Watanabe - */ - -class HTTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { - // Create a temporary directory to store the generated certificates - // (The directory and its contents remain accessible after the test ends, but are eventually deleted) - val certRoot: File = Files.createTempDirectory("suslik-").toFile - - override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = - it(s"certifies that it $desc") { - synthesizeFromSpec(testName, in, out, params.copy(assertSuccess = false, certTarget = HTT(), certDest = certRoot)) - val fname = testName.split('/').last - - // Find the path to the certificated that was written out to the temp directory - val fileName = s"${fname.replace('-', '_')}.v" - val pathToCertificate = Paths.get(certRoot.getCanonicalPath, fileName).toFile.getCanonicalPath - - // Try compiling the ".v" file with coqc - val result = s"coqc -vok -w none $pathToCertificate".! - - // Check that Coq compilation succeeded with exit code 0 - assert(result == 0) - } - - describe("SL-based synthesizer with certification") { - runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/list") - runAllTestsFromDir("certification/tree") - runAllTestsFromDir("certification/sll") - runAllTestsFromDir("certification/dll") - runAllTestsFromDir("certification/bst") - } - -} \ No newline at end of file diff --git a/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala b/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala deleted file mode 100644 index 820ade52a..000000000 --- a/src/test/scala/org/tygus/suslik/certification/targets/vst/VSTCertificationTests.scala +++ /dev/null @@ -1,42 +0,0 @@ -package org.tygus.suslik.certification.targets.vst - -import java.io.File -import java.nio.file.{Files, Paths} - -import org.scalatest.{FunSpec, Matchers} -import org.tygus.suslik.certification.targets.vst.VST -import org.tygus.suslik.synthesis.{SynConfig, SynthesisRunnerUtil, defaultConfig} - -import scala.sys.process._ - - -class VSTCertificationTests extends FunSpec with Matchers with SynthesisRunnerUtil { - val certRoot: File = Files.createTempDirectory("suslik-").toFile - - override def doRun(testName: String, desc: String, in: String, out: String, params: SynConfig = defaultConfig): Unit = - it(s"certifies that it $desc") { - synthesizeFromSpec( - testName, in, out, - params.copy( - assertSuccess = false, certTarget = VST(), - certDest = certRoot - )) - val fname = testName.split('/').last - val pathToCertificate = Paths.get(certRoot.getCanonicalPath, s"${fname.replace('-', '_')}.v").toFile.getCanonicalPath - - // verify - val result = s"coqc -vok $pathToCertificate".! - - // check that Coq compilation succeeded - assert(result == 0) - } - - describe("SL-based synthesizer with certification") { -// runAllTestsFromDir("certification/ints") - runAllTestsFromDir("certification/list") -// runAllTestsFromDir("certification/tree") -// runAllTestsFromDir("certification/sll") - } - - -} From 194a8fd7ba12ee13249ede35f3549e8b015ba3f9 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 4 Jul 2021 23:39:46 +0900 Subject: [PATCH 206/211] Revert addition of block size constraint on HeapUnifyPointer --- .../org/tygus/suslik/synthesis/rules/UnificationRules.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala index 509afc065..099a281a2 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/rules/UnificationRules.scala @@ -84,7 +84,7 @@ object UnificationRules extends PureLogicUtils with SepLogicUtils with RuleUtils PointsTo(y, oy, _) <- postPtss if y.vars.exists(goal.isExistential) t@PointsTo(x, ox, _) <- prePtss - if post.sigma.block_size(y) == pre.sigma.block_size(x) +// if post.sigma.block_size(y) == pre.sigma.block_size(x) if ox == oy if !postPtss.exists(sameLhs(t)) } yield (y -> x) From 8f64cf1d34c9053eba21af278223a209f6c4766e Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 4 Jul 2021 23:54:38 +0900 Subject: [PATCH 207/211] Rename test cases for consistency --- .../resources/synthesis/certification-benchmarks/ints/min.syn | 4 ++-- .../synthesis/certification-benchmarks/ints/swap2.syn | 4 ++-- .../synthesis/certification-benchmarks/ints/swap4.syn | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/min.syn b/src/test/resources/synthesis/certification-benchmarks/ints/min.syn index 5b72a2cd5..5d85a0ae0 100644 --- a/src/test/resources/synthesis/certification-benchmarks/ints/min.syn +++ b/src/test/resources/synthesis/certification-benchmarks/ints/min.syn @@ -5,13 +5,13 @@ minimum of two integers { true ; r :-> null } -void min2(loc r, int x, int y) +void min(loc r, int x, int y) { m <= x /\ m <= y; r :-> m } ### -void min2 (loc r, int x, int y) { +void min (loc r, int x, int y) { if (x <= y) { *r = x; } else { diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn b/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn index d8aab2c3c..da32457e8 100644 --- a/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn +++ b/src/test/resources/synthesis/certification-benchmarks/ints/swap2.syn @@ -3,13 +3,13 @@ Swapping example { x :-> a ** y :-> b } -void swap(loc x, loc y) +void swap2(loc x, loc y) { x :-> b ** y :-> a } ### -void swap (loc x, loc y) { +void swap2 (loc x, loc y) { let a2 = *x; let b2 = *y; *x = b2; diff --git a/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn b/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn index f188b4785..2cc9f0ee7 100644 --- a/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn +++ b/src/test/resources/synthesis/certification-benchmarks/ints/swap4.syn @@ -3,12 +3,12 @@ should be able to synthesize a more complex swap program ### {true; x :-> a ** y :-> c ** z :-> b ** t :-> q } -void swap2 (loc x, loc z, loc y, loc t) +void swap4 (loc x, loc z, loc y, loc t) { true; x :-> q ** z :-> c ** t :-> a ** y :-> b } ### -void swap2 (loc x, loc z, loc y, loc t) { +void swap4 (loc x, loc z, loc y, loc t) { let a = *x; let c = *y; let b = *z; From ac78f0657e741da72dbb177ada0e5b51870ddd75 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Sun, 4 Jul 2021 23:56:28 +0900 Subject: [PATCH 208/211] Update benchmark script to latest --- .../synthesis/CertificationBenchmarks.scala | 96 +++++++++++++++---- 1 file changed, 76 insertions(+), 20 deletions(-) diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index 346b5c10d..f252c4239 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -2,6 +2,8 @@ package org.tygus.suslik.synthesis import java.io.{File, FileWriter, PrintWriter} import java.nio.file.Paths +import java.text.SimpleDateFormat +import java.util.Date import org.tygus.suslik.LanguageUtils import org.tygus.suslik.certification.source.SuslikProofStep @@ -15,7 +17,7 @@ import org.tygus.suslik.logic.Preprocessor.preprocessProgram import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.report.StopWatch.timed -import org.tygus.suslik.synthesis.CertificationBenchmarks.BenchmarkConfig +import org.tygus.suslik.synthesis.CertificationBenchmarks.{BenchmarkConfig, serialize} import org.tygus.suslik.synthesis.tactics.PhasedSynthesis import org.tygus.suslik.util.{SynStatUtil, SynStats} import scopt.OptionParser @@ -27,7 +29,7 @@ import scala.sys.process.Process class CertificationBenchmarks( params: SynConfig, cfg: BenchmarkConfig - ) extends SynthesisRunnerUtil { + ) extends SynthesisRunnerUtil { val outputDir: File = new File(cfg.outputDirName) val synStatsFile = new File(List(cfg.outputDirName, s"${cfg.statsFilePrefix}-syn.csv").mkString(File.separator)) val certStatsFiles: Map[CertificationTarget, File] = @@ -102,7 +104,8 @@ class CertificationBenchmarks( val tests = testDir.listFiles.filter(f => f.isFile && (f.getName.endsWith(s".$testExtension") || f.getName.endsWith(s".$sketchExtension"))) - .map(f => getDescInputOutput(f.getAbsolutePath, params)).toList + .map(f => getDescInputOutput(f.getAbsolutePath, params)) + .sortWith { case (a,b) => a._1 < b._1 }.toList // alphabetize by test name println("done!") val parser = new SSLParser @@ -218,13 +221,6 @@ class CertificationBenchmarks( } } - private def serialize(dir: File, filename: String, body: String): Unit = { - val file = Paths.get(dir.getCanonicalPath, filename).toFile - new PrintWriter(file) { - write(body); close() - } - } - private def checkProofSize(dir: File, filename: String): (Int, Int) = { val cmd = Seq("coqwc", filename) val proofSizes = Process(cmd, dir).!! @@ -270,21 +266,51 @@ object CertificationBenchmarks { "certification-benchmarks/tree" ) val allAdvanced = List( - "certification-benchmarks-advanced/bst", - "certification-benchmarks-advanced/sll", "certification-benchmarks-advanced/dll", + "certification-benchmarks-advanced/bst", "certification-benchmarks-advanced/srtl", ) - val defaultStandardConfig: BenchmarkConfig = BenchmarkConfig(allTargets, allStandard, compile = true, "standard") - val defaultAdvancedConfig: BenchmarkConfig = BenchmarkConfig(List(HTT()), allAdvanced, compile = false, "advanced") + val defaultStandardConfig: BenchmarkConfig = BenchmarkConfig(allTargets, allStandard, allStandard, compile = true, "standard") + val defaultAdvancedConfig: BenchmarkConfig = BenchmarkConfig(List(HTT()), allAdvanced, allAdvanced, compile = false, "advanced") case class BenchmarkConfig( targets: List[CertificationTarget], + allGroups: List[String], groups: List[String], compile: Boolean, statsFilePrefix: String, - outputDirName: String = "certify", + outputDirName: String = "certify" ) { + def pp: String = { + val builder = new StringBuilder() + if (groups.nonEmpty) { + builder.append(s"${groups.length} benchmark group(s) will be evaluated with ${targets.length} certification target(s).\n") + builder.append("Results will be written to:\n") + builder.append(s"- $outputDirName${File.separator}$statsFilePrefix-syn.csv (synthesis times)\n") + for (t <- targets) { + builder.append(s"- $outputDirName${File.separator}$statsFilePrefix-${t.name}.csv (${t.name} statistics)\n") + } + builder.append("\nThis run will perform actions in the following order.\n\n") + for (g <- allGroups) { + if (groups.contains(g)) { + builder.append(s"Group '$g': synthesize/generate certificates with compilation ${if (compile) "ENABLED" else "DISABLED"} for ${if (targets.nonEmpty) targets.map(_.name).mkString(", ") else "no targets"}\n") + val rootDir = "./src/test/resources/synthesis".replace("/", File.separator) + val path = List(rootDir, g).mkString(File.separator) + val testDir = new File(path) + val tests = testDir.listFiles.filter(f => f.isFile && f.getName.endsWith(".syn")).map(_.getName).sorted.toList + for (test <- tests) { + builder.append(s"- ${test.stripSuffix(".syn")}\n") + } + } else { + builder.append(s"Group '$g': don't synthesize, don't compile, don't certify\n") + } + } + } else { + builder.append("No benchmark groups will be evaluated.") + } + builder.toString + } + def updateTargets(): BenchmarkConfig = { println(s"\nBy default, benchmarks will be evaluated on target(s): ${targets.map(_.name).mkString(", ")}") val s = StdIn.readLine("Proceed with default targets? [Y/n] ") @@ -340,7 +366,7 @@ object CertificationBenchmarks { } } - private case class Config( + case class Config( configure: Boolean = false, outputDirName: String = "certify" ) @@ -355,6 +381,13 @@ object CertificationBenchmarks { c.copy(outputDirName = x) } text "Specify a custom directory for output files" } + def serialize(dir: File, filename: String, body: String): Unit = { + val file = Paths.get(dir.getCanonicalPath, filename).toFile + new PrintWriter(file) { + write(body); close() + } + } + def main(args: Array[String]): Unit = { val runConfig = parser.parse(args, Config()) match { case Some(config) => config @@ -363,28 +396,51 @@ object CertificationBenchmarks { sys.exit(1) } + // Initialize output dir + val outputDir = new File(runConfig.outputDirName) + if (!outputDir.exists()) { + outputDir.mkdirs() + } + + // Build standard + advanced config from user input val (standardConfig, advancedConfig) = if (runConfig.configure) { println("==========STANDARD BENCHMARK CONFIGURATION==========") - val standardConfig = defaultStandardConfig.updateCompile().updateTargets().updateGroups() + val standardConfig = defaultStandardConfig.copy(outputDirName = runConfig.outputDirName).updateCompile().updateTargets().updateGroups() println("\n\n==========ADVANCED BENCHMARK CONFIGURATION==========") - val advancedConfig = defaultAdvancedConfig.updateCompile().updateGroups() + val advancedConfig = defaultAdvancedConfig.copy(outputDirName = runConfig.outputDirName).updateCompile().updateGroups() (standardConfig, advancedConfig) - } else (defaultStandardConfig, defaultAdvancedConfig) - + } else (defaultStandardConfig.copy(outputDirName = runConfig.outputDirName), defaultAdvancedConfig.copy(outputDirName = runConfig.outputDirName)) + + // Finalize configuration settings + val standardConfigStr = standardConfig.pp + val advancedConfigStr = advancedConfig.pp + val timestamp = new SimpleDateFormat("YYYY-MM-dd_HHmmss").format(new Date) + val configStr = s"Evaluation start time: $timestamp\n\nSTANDARD BENCHMARK CONFIGURATION:\n$standardConfigStr\n\nADVANCED BENCHMARK CONFIGURATION:\n$advancedConfigStr" + val logFilename = s"certify-benchmarks-$timestamp.log" + + // Write configuration settings for this run to a log file and also print to stdout + serialize(new File(runConfig.outputDirName), logFilename, configStr) + println(s"\n$configStr") println("\n\nResults will be produced in the following directory:") println(s" ${new File(runConfig.outputDirName).getCanonicalPath}") println("\n\nStarting benchmarks...\n\n") + // Run standard benchmarks val standard = new CertificationBenchmarks ( SynConfig(certHammerPure = true), standardConfig.copy(outputDirName = runConfig.outputDirName) ) standard.runBenchmarks() + println("\n\nFinished evaluating all standard benchmarks!\n\n") + // Run advanced benchmarks val advanced = new CertificationBenchmarks( SynConfig(certSetRepr = true, certHammerPure = true), advancedConfig.copy(outputDirName = runConfig.outputDirName) ) advanced.runBenchmarks() + println("\n\nFinished evaluating all advanced benchmarks!\n\n") + + println(s"\n\nEnd of benchmark evaluation! Please check CSV files in directory ${runConfig.outputDirName} for results.") } } From d4b3ee4d01b675094cd5ed2bfc878fbb2eefe297 Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 5 Jul 2021 00:47:12 +0900 Subject: [PATCH 209/211] Add certification to README --- README.md | 9 ++- certification.md | 188 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 193 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8263ddf0c..df487e236 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,9 @@ The details of Synthetic Separation Logic and its extensions can be found in the * **[Cyclic Program Synthesis](https://doi.org/10.1145/3453483.3454087)** Shachar Itzhaky, Hila Peleg, Nadia Polikarpova, Reuben Rowe, and Ilya Sergey. PLDI'21 - [Artifact, 11 Apr 2021](https://doi.org/10.5281/zenodo.4679743) +* **[Certifying the Synthesis of Heap-Manipulating Programs](https://doi.org/10.1145/3473589)** + Yasunari Watanabe, Kiran Gopinathan, George Pîrlea, Nadia Polikarpova, and Ilya Sergey. ICFP'21 + - [Artifact, 21 May 2021](http://doi.org/10.5281/zenodo.5005829) ## Benchmark Statistics @@ -217,4 +220,8 @@ void listcopy (loc r) { For running advanced examples from the accompanying test suite, execute, e.g., ``` ./suslik src/test/resources/synthesis/all-benchmarks/sll/append.syn -``` \ No newline at end of file +``` + +## Certification + +Please refer to `certification.md` for information on certifying the synthesis results. diff --git a/certification.md b/certification.md index 1f02afed4..0f77bc421 100644 --- a/certification.md +++ b/certification.md @@ -1,7 +1,14 @@ -# Certified Synthesis +# Certifying Synthesis Results -Generation of correctness certificates for synthesized programs. -Currently, we support three target verification frameworks in Coq: HTT, VST, and Iris. +This page contains instructions for running and evaluating the certification component of SuSLik, corresponding to the +following paper. + +**[Certifying the Synthesis of Heap-Manipulating Programs](https://doi.org/10.1145/3473589)** + Yasunari Watanabe, Kiran Gopinathan, George Pîrlea, Nadia Polikarpova, and Ilya Sergey. ICFP'21 + - [Artifact, 21 May 2021](http://doi.org/10.5281/zenodo.5005829) + +Currently, we support three target verification frameworks in Coq—HTT, VST, and Iris—to generate correctness +certificates for synthesized programs. ## Requirements @@ -43,3 +50,178 @@ If `false`, the generated certificate will have all pure lemmas `Admitted` inste - `--certSetRepr ` (boolean; default is `false`): Controls whether to use SSReflect's `perm_eq` to express multi-set equality. If `false`, the generated certificate will use regular equality (`=`) instead. + +## Evaluation + +### Overview + +The paper discusses two classes of benchmarks. + +1. **Standard benchmarks** are supported by HTT, VST, and Iris. This + corresponds to Table 1 of the paper. These are available in the folder + `$SUSLIK_ROOT/src/test/resources/synthesis/certification-benchmarks`. +2. **Advanced benchmarks** are supported by HTT only. These include test + cases that use an alternative representation of multi-set equality and + requires manual editing of pure lemma proofs. This corresponds to + Table 2 of the paper. These are available in the folder + `$SUSLIK_ROOT/src/test/resources/synthesis/certification-benchmarks-advanced` + +The tool `certify-benchmarks` synthesizes proof certificates for + both standard and advanced benchmarks, and compiles the certificates for + standard benchmarks only. + +The [`ssl-htt`](https://github.com/TyGuS/ssl-htt) repository contains a benchmarking script to compile the HTT +certificates for advanced benchmarks. + +### Generating All Certificates and Compiling Standard Benchmarks + +To synthesize standard and advanced benchmark certificates, and compile +the standard benchmark certificates, execute: + +``` +cd $SUSLIK_ROOT +./certify-benchmarks +``` + +This will run SuSLik with certificate generation flags enabled, for all +specification (`.syn`) files in the standard and advanced benchmarks, +for all three target frameworks. +Then, for the standard benchmarks, it will also compile the generated +certificates. +These are the default evaluation configuration, and the exact actions that will +be performed under this configuration are written to timestamped `.log` files in +the `certify` directory before each run. This configuration can be modified +by setting the `--configure` flag. See section "Customizing Benchmark Options" +for details. + +As this script produces verbose output, you may consider teeing the script's +output to a log file for viewing/debugging later, instead of running the script +directly. + +``` +./certify-benchmarks> >(tee certify.log) +cat certify.log +``` + +When running the tool, please keep in mind the following. + +- **It should take _2-3 hours_ to run this evaluation on all benchmark + groups and all target frameworks.** If you need to interrupt the evaluation, + or if the benchmarking encounters a timeout error on a slow machine, please + refer to section "Customizing Benchmark Options" on how to resume the task + with selected benchmark groups/target frameworks only. +- **Files in the `certify` directory will be overwritten on each subsequent + run.** +- Unsupported test cases for certain targets (such as `sll_max` for Iris, + as indicated in Table 1) are ignored. +- Warnings displayed during the proofs do not affect the functionality of the + certificates. + +After the script terminates, it will create five output files in +`$SUSLIK_ROOT/certify`. + +- `standard-syn.csv` contains synthesis times reported in Table 1. +- `standard-{HTT,VST,Iris}.csv` contain proof/spec size and compilation + times for each of the three target frameworks in Table 1. +- `advanced-syn.csv` contains synthesis times reported in Table 2. + +#### Customizing Benchmark Options + +You may wish to run the benchmarks with alternative settings. + +To produce certificates and stats CSV files in a directory other than +`$SUSLIK_ROOT/certify`, run the tool with the `--outputDir` flag. + +``` +cd $SUSLIK_ROOT +./certify-benchmarks --outputDir +``` + +As the benchmark tool takes several hours to run, you may need to interrupt +its execution, and then resume later on a subset of the benchmarks/frameworks. +If so, you can run the tool with the `--configure` flag. + +``` +cd $SUSLIK_ROOT +./certify-benchmarks --configure +``` + +When this flag is set, a configuration wizard is run before the benchmarks, +where you can selectively enable/disable three benchmarking parameters. + +- **Compilation:** Choose whether to measure compilation times of the + generated certificates or not. +- **Benchmark groups:** Select which benchmark groups to evaluate. +- **Target frameworks:** Select which target framework to generate/compile + certificates for. + +At each prompt, press ENTER to select the default option; alternatively, choose +the desired option (`y` or `n`). The default settings are: + +- _For standard benchmarks_: Synthesize programs in all benchmark groups. Then + generate and compile certificates for all three targets (HTT/VST/Iris). +- _For advanced benchmarks_: Synthesize programs in all benchmark groups. Then + generate (but _don't_ compile) certificates for HTT only. + +Each execution of the script generates a timestamped `.log` file listing +exactly what actions will be performed given the user-specified configuration, +which may be useful for debugging. + +### Compiling Advanced Benchmarks (Manually Edited Proofs) + +The steps from the above section do not produce target-specific statistics for +advanced benchmarks. This is because the test cases in Table 2 require manual +editing of the pure lemma proofs (as discussed in Section 8.2 of the paper), +whereas that tool only checks SuSLik-generated certificates. + +To compile the advanced benchmark certificates with manually edited pure lemma +proofs and execute the following (`$SSL_HTT_ROOT` refers to the local `ssl-htt` repository). + +``` +cd $SSL_HTT_ROOT/benchmarks/advanced +python3 benchmark.py --diffSource $SUSLIK_ROOT/certify/HTT/certification-benchmarks-advanced +``` + +**It should take roughly _10 minutes_ to run this evaluation**. + +After the script terminates, it will create two output files in +`$SSL_HTT_ROOT/benchmarks/advanced`. + +- `advanced-HTT.csv` contains proof/spec size and compilation times for HTT + in Table 2. +- `advanced-HTT.diff` compares the original SuSLik-generated certificates + (stored in + `$SUSLIK_ROOT/certify/HTT/certification-benchmarks-advanced`) with the + manually edited ones (stored in `$SSL_HTT_ROOT/benchmarks/advanced`), + showing which lines have been modified. The diff file will only be created if + the original SuSLik-generated certificates exist. + +The number of pure lemmas and those with manual proofs can be verified by +inspecting the files at `$SSL_HTT_ROOT/benchmarks/advanced`. +You can also view the diff file to verify which parts of the proofs have been +edited. + +``` +less advanced-HTT.diff +``` + +You can expect to see the following differences between the two proof versions. + +- _Proofs for the pure lemmas:_ In the generated scripts, pure lemmas have + their `hammer` proofs replaced with `Admitted` statements. In the manually + edited scripts, these are replaced with either a call to `hammer` when that is + sufficient, or with a proof manually constructed by the authors. Note that in + the diff file, pure lemmas may be ordered differently between the two + versions, due to non-determinism in the way Scala accumulates hints during + execution. +- _Administrative renaming statements in the main proof:_ These are + identifiable by the `try rename ...` statements; they may occur when + two variable rename statements resulting from the same proof step + are applied in different orders between the compared proofs, again + dependent on Scala's execution. + +You should _not_ see edits that modify the main proof structure (aside from +renaming variables). + +To use this tool with custom settings, execute the script with the `--help` flag to see all +available options. From 05d4841bdc6f3259a7fb21bd3beaca1bfa7f40be Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 5 Jul 2021 13:18:58 +0900 Subject: [PATCH 210/211] Add moved sll case to benchmark script --- .../org/tygus/suslik/synthesis/CertificationBenchmarks.scala | 1 + .../certification-benchmarks-advanced/sll/common.def | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 src/test/resources/synthesis/certification-benchmarks-advanced/sll/common.def diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala index f252c4239..f5f4bd9a5 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala @@ -266,6 +266,7 @@ object CertificationBenchmarks { "certification-benchmarks/tree" ) val allAdvanced = List( + "certification-benchmarks-advanced/sll", "certification-benchmarks-advanced/dll", "certification-benchmarks-advanced/bst", "certification-benchmarks-advanced/srtl", diff --git a/src/test/resources/synthesis/certification-benchmarks-advanced/sll/common.def b/src/test/resources/synthesis/certification-benchmarks-advanced/sll/common.def new file mode 100644 index 000000000..da2578bf1 --- /dev/null +++ b/src/test/resources/synthesis/certification-benchmarks-advanced/sll/common.def @@ -0,0 +1,4 @@ +predicate sll(loc x, set s) { +| x == null => { s =i {} ; emp } +| not (x == null) => { s =i {v} ++ s1 ; [x, 2] ** x :-> v ** (x + 1) :-> nxt ** sll(nxt, s1) } +} From 19a0617e4a36a5d7971ff3fd8d18c2d70af105eb Mon Sep 17 00:00:00 2001 From: Yasunari Watanabe Date: Mon, 5 Jul 2021 13:57:38 +0900 Subject: [PATCH 211/211] Move benchmark script to appropriate package and check for Coq on startup --- certify-benchmarks | 2 +- .../CertificationBenchmarks.scala | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) rename src/main/scala/org/tygus/suslik/{synthesis => certification}/CertificationBenchmarks.scala (97%) diff --git a/certify-benchmarks b/certify-benchmarks index 929248e58..b7397edc3 100755 --- a/certify-benchmarks +++ b/certify-benchmarks @@ -1,3 +1,3 @@ #!/bin/bash -scala -Dfile.encoding=UTF-8 -classpath target/scala-2.12/suslik.jar org.tygus.suslik.synthesis.CertificationBenchmarks $* +scala -Dfile.encoding=UTF-8 -classpath target/scala-2.12/suslik.jar org.tygus.suslik.certification.CertificationBenchmarks $* diff --git a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala b/src/main/scala/org/tygus/suslik/certification/CertificationBenchmarks.scala similarity index 97% rename from src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala rename to src/main/scala/org/tygus/suslik/certification/CertificationBenchmarks.scala index f5f4bd9a5..8e4ac0a32 100644 --- a/src/main/scala/org/tygus/suslik/synthesis/CertificationBenchmarks.scala +++ b/src/main/scala/org/tygus/suslik/certification/CertificationBenchmarks.scala @@ -1,4 +1,4 @@ -package org.tygus.suslik.synthesis +package org.tygus.suslik.certification import java.io.{File, FileWriter, PrintWriter} import java.nio.file.Paths @@ -6,25 +6,25 @@ import java.text.SimpleDateFormat import java.util.Date import org.tygus.suslik.LanguageUtils +import org.tygus.suslik.certification.CertificationBenchmarks.{BenchmarkConfig, serialize} import org.tygus.suslik.certification.source.SuslikProofStep import org.tygus.suslik.certification.targets.htt.HTT -import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.certification.targets.iris.Iris -import org.tygus.suslik.certification.{CertTree, CertificationTarget} +import org.tygus.suslik.certification.targets.vst.VST import org.tygus.suslik.language.Statements import org.tygus.suslik.logic.Environment import org.tygus.suslik.logic.Preprocessor.preprocessProgram import org.tygus.suslik.parsing.SSLParser import org.tygus.suslik.report.ProofTraceCert import org.tygus.suslik.report.StopWatch.timed -import org.tygus.suslik.synthesis.CertificationBenchmarks.{BenchmarkConfig, serialize} import org.tygus.suslik.synthesis.tactics.PhasedSynthesis +import org.tygus.suslik.synthesis.{SynConfig, Synthesis, SynthesisException, SynthesisRunnerUtil, dotSus, dotSyn} import org.tygus.suslik.util.{SynStatUtil, SynStats} import scopt.OptionParser import scala.collection.mutable import scala.io.StdIn -import scala.sys.process.Process +import scala.sys.process._ class CertificationBenchmarks( params: SynConfig, @@ -397,6 +397,12 @@ object CertificationBenchmarks { sys.exit(1) } + // Check that Coq exists + if (Seq("command", "-v", "coqc").! != 0) { + println("Coq is not installed. Aborting!") + scala.sys.exit(0) + } + // Initialize output dir val outputDir = new File(runConfig.outputDirName) if (!outputDir.exists()) {