├── bors.toml ├── .gitmodules ├── .github ├── dependabot.yml └── workflows │ ├── update-submodules.yml │ └── ci.yml ├── .gitignore ├── src ├── test │ ├── resources │ │ ├── local │ │ │ └── placeholder.vpr │ │ ├── regression │ │ │ ├── wands │ │ │ │ ├── transfer_naive_issue_1.vpr │ │ │ │ ├── inconsistency_scenario_2.vpr │ │ │ │ ├── unfolding_ambiguity.vpr │ │ │ │ ├── apply_potential_incompleteness.vpr │ │ │ │ ├── unfolding_trivial_wand.vpr │ │ │ │ ├── let_test1.vpr │ │ │ │ ├── well_formedness_wand_1.vpr │ │ │ │ ├── packaging_shield.vpr │ │ │ │ ├── resultstate1.vpr │ │ │ │ ├── package_inc1.vpr │ │ │ │ ├── snapshot_report.vpr │ │ │ │ ├── apply1.vpr │ │ │ │ ├── exec_unfolding.vpr │ │ │ │ ├── folding_fun_frame_2.vpr │ │ │ │ ├── wand_shapes_simple_exhale.vpr │ │ │ │ ├── known_folded_1.vpr │ │ │ │ ├── unfolding_ambiguity2.vpr │ │ │ │ ├── packaging_apply.vpr │ │ │ │ ├── package_simple_no_perm.vpr │ │ │ │ ├── package_inc3.vpr │ │ │ │ ├── wand_conjunction.vpr │ │ │ │ ├── packaging_nested.vpr │ │ │ │ ├── folding_inc1.vpr │ │ │ │ ├── package_inc5.vpr │ │ │ │ ├── wand_shapes_1.vpr │ │ │ │ ├── folding_fun_frame.vpr │ │ │ │ ├── folding_2.vpr │ │ │ │ ├── folding_unfolding_combo.vpr │ │ │ │ ├── potential_unsoundness.vpr │ │ │ │ ├── package_inc2.vpr │ │ │ │ ├── package_hyp.vpr │ │ │ │ ├── packaging_cond_perm.vpr │ │ │ │ └── packaging_1.vpr │ │ │ └── fraction_translation_bug.vpr │ │ └── LICENSE.txt │ └── scala │ │ └── viper │ │ └── carbon │ │ ├── ViperTutorialExamples.scala │ │ ├── CarbonSIFTests.scala │ │ ├── CarbonBackendTypeTest.scala │ │ ├── CarbonTestsOldPermissionSemantics.scala │ │ ├── GraphTests.scala │ │ ├── SmokeDetectionPluginTests.scala │ │ ├── AdtPluginTests.scala │ │ ├── AllTests.scala │ │ ├── CarbonCounterexampleVariablesTests.scala │ │ ├── CarbonQuantifierWeightTests.scala │ │ └── PortableCarbonTests.scala └── main │ ├── scala │ └── viper │ │ └── carbon │ │ ├── utility │ │ ├── LoopGenKind.scala │ │ ├── PartialSort.scala │ │ └── PolyMapDesugarHelper.scala │ │ ├── modules │ │ ├── components │ │ │ ├── Component.scala │ │ │ ├── InhaleComponent.scala │ │ │ ├── TransferComponent.scala │ │ │ ├── SimpleStmtComponent.scala │ │ │ ├── ExhaleComponent.scala │ │ │ ├── StmtComponent.scala │ │ │ ├── ComponentRegistry.scala │ │ │ ├── CarbonStateComponent.scala │ │ │ └── DefinednessComponent.scala │ │ ├── TypeModule.scala │ │ ├── MapModule.scala │ │ ├── SeqModule.scala │ │ ├── SetModule.scala │ │ ├── DomainModule.scala │ │ ├── LoopModule.scala │ │ ├── impls │ │ │ ├── LabelHelper.scala │ │ │ ├── DefaultTypeModule.scala │ │ │ ├── sequence_axioms │ │ │ │ └── LICENSE.txt │ │ │ ├── DefaultDomainModule.scala │ │ │ ├── DefaultMapModule.scala │ │ │ ├── DefaultSeqModule.scala │ │ │ ├── map_axioms │ │ │ │ └── MapAxiomatization.scala │ │ │ ├── DefaultSetModule.scala │ │ │ └── DefaultInhaleModule.scala │ │ ├── StmtModule.scala │ │ ├── MainModule.scala │ │ ├── TransferEntity.scala │ │ ├── Module.scala │ │ ├── ExpModule.scala │ │ ├── InhaleModule.scala │ │ ├── FuncPredModule.scala │ │ ├── ExhaleModule.scala │ │ ├── PermModule.scala │ │ ├── HeapModule.scala │ │ └── StateModule.scala │ │ ├── boogie │ │ ├── UnicodeString.scala │ │ ├── Implicits.scala │ │ ├── Visitor.scala │ │ ├── BoogieModelTransformer.scala │ │ ├── BoogieNameGenerator.scala │ │ ├── Optimizer.scala │ │ └── utility.scala │ │ ├── verifier │ │ ├── Verifier.scala │ │ └── Environment.scala │ │ └── Carbon.scala │ └── resources │ └── logback.xml ├── project ├── build.properties └── plugins.sbt ├── carbon.sh ├── README.md └── carbon.bat /bors.toml: -------------------------------------------------------------------------------- 1 | status = ["build"] 2 | timeout_sec = 7200 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "silver"] 2 | path = silver 3 | url = https://github.com/viperproject/silver.git 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: "/" 6 | schedule: 7 | interval: monthly 8 | day: monday 9 | groups: 10 | all: 11 | patterns: 12 | - "*" 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .idea_modules 3 | project/project 4 | project/target 5 | target 6 | bin/** 7 | carbon_classpath.txt 8 | lib/ 9 | viper_tutorial_examples 10 | tmp/ 11 | .bsp/ 12 | .bloop/ 13 | .metals/ 14 | *.smt2 15 | *.smt2.* 16 | results.txt 17 | *.bpl 18 | *.bpl.* 19 | -------------------------------------------------------------------------------- /src/test/resources/local/placeholder.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | 5 | // DO NOT DELETE 6 | 7 | // this file is an empty test-case to ensure that the local directory does not get deleted. 8 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/transfer_naive_issue_1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f:Ref 5 | 6 | method t01(x:Ref) { 7 | 8 | package acc(x.f)&&acc(x.f.f) --* acc(x.f)&&acc(x.f.f) 9 | 10 | } -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Copyright (c) 2011-2019 ETH Zurich. 6 | 7 | sbt.version=1.6.2 8 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/utility/LoopGenKind.scala: -------------------------------------------------------------------------------- 1 | package viper.carbon.utility 2 | 3 | sealed trait LoopGenKind { 4 | 5 | } 6 | 7 | case class BeforeEnterLoop(loopId: Int) extends LoopGenKind 8 | 9 | case class ExitLoops(loopIds: Seq[Int]) extends LoopGenKind 10 | 11 | case class Backedge(loopId: Int) extends LoopGenKind 12 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | 2 | // This Source Code Form is subject to the terms of the Mozilla Public 3 | // License, v. 2.0. If a copy of the MPL was not distributed with this 4 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 5 | // 6 | // Copyright (c) 2011-2019 ETH Zurich. 7 | 8 | addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "1.0.0") 9 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/inconsistency_scenario_2.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f:Int 5 | 6 | method t01(x:Ref) { 7 | 8 | inhale acc(x.f) 9 | 10 | package false --* acc(x.f) 11 | 12 | exhale acc(x.f) 13 | 14 | //:: ExpectedOutput(assert.failed:assertion.false) 15 | assert false 16 | 17 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/unfolding_ambiguity.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t(x:Ref,y:Ref) { 7 | 8 | package acc(y.f)&&acc(P(x)) --* acc(y.f)&&(unfolding P(x) in true) 9 | 10 | //:: ExpectedOutput(assert.failed:assertion.false) 11 | assert false 12 | 13 | } 14 | 15 | predicate P(x:Ref) { 16 | acc(x.f) 17 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/apply_potential_incompleteness.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t(x:Ref) { 7 | package acc(x.f) --* acc(x.f) 8 | inhale acc(x.f)&&x.f==2 9 | apply acc(x.f) --* acc(x.f) 10 | 11 | assert x.f==2 12 | 13 | //:: ExpectedOutput(assert.failed:assertion.false) 14 | assert false 15 | } 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/Component.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | /** 10 | * Common trait for components. 11 | 12 | */ 13 | trait Component { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/unfolding_trivial_wand.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t(x:Ref) { 7 | 8 | package acc(P(x),2/1) --* (unfolding P(x) in (unfolding P(x) in false)) 9 | 10 | // assert acc(x.f) --* false 11 | //:: ExpectedOutput(assert.failed:assertion.false) 12 | assert false 13 | } 14 | 15 | predicate P(x:Ref) { 16 | acc(x.f) 17 | } -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/ViperTutorialExamples.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import org.scalatest.DoNotDiscover 10 | 11 | @DoNotDiscover 12 | class ViperTutorialExamples extends AllTests { 13 | override val testDirectories = Seq("viper_tutorial_examples") 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/let_test1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | 6 | predicate P(x:Ref) { 7 | acc(x.f)&&acc(x.f.f) 8 | } 9 | 10 | method t1(x:Ref) { 11 | package acc(P(x)) --* unfolding P(x) in (let y==(x.f) in acc(y.f)) 12 | 13 | /*this wand is ill-defined (if we interpret the let as a ghost operation) 14 | in Carbon an error is thrown due to this, but the error message needs 15 | to be improved */ 16 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/well_formedness_wand_1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | 6 | 7 | method test01(x: Ref,y:Ref) { 8 | package true --* acc(x.f.f) 9 | } 10 | 11 | method test02(x: Ref,y:Ref) { 12 | inhale acc(x.f) 13 | inhale acc(x.f.f) 14 | package true --* acc(x.f.f) 15 | } 16 | 17 | method test03(x: Ref,y:Ref) { 18 | inhale acc(x.f) 19 | inhale acc(x.f.f) 20 | package true --* acc(x.f)&&acc(x.f.f) 21 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/packaging_shield.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field next: Ref 6 | 7 | 8 | 9 | method t01(ys: Ref) 10 | { 11 | //should fail since facts learnt about the heap values in nested packages should not be available in the outer packages 12 | 13 | //:: ExpectedOutput(package.failed:assertion.false) 14 | package acc(ys.f) --* packaging (acc(ys.f,1/2)&&ys.f==2 --* acc(ys.f)) in acc(ys.f,1/2)&&ys.f==2 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/resultstate1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Int 6 | field h: Ref 7 | 8 | predicate P(x: Ref) { acc(x.h) && x.h != null && acc(x.h.h) && x.h.h != null } 9 | predicate Q(x: Ref) { acc(x.h) && x.h != null } 10 | 11 | 12 | method test05(l: Ref) { 13 | package acc(P(l)) --* unfolding P(l) in folding Q(l) in acc(Q(l)) 14 | 15 | //:: ExpectedOutput(assert.failed:assertion.false) 16 | assert false 17 | } 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/TypeModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.Type 11 | 12 | /** 13 | * A module for translating types. 14 | 15 | */ 16 | trait TypeModule extends Module { 17 | def translateType(typ: sil.Type): Type 18 | } 19 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/CarbonSIFTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2025 ETH Zurich. 6 | 7 | package viper.carbon 8 | class CarbonSIFTests extends AllTests { 9 | override def testDirectories: Seq[String] = Vector( 10 | "sif" 11 | ) 12 | 13 | override val commandLineArguments: Seq[String] = 14 | Seq("--plugin=viper.silver.plugin.sif.SIFPlugin") 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_inc1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test01(x: Ref) { 9 | package true --* packaging (false --* true) in true 10 | 11 | //:: ExpectedOutput(assert.failed:assertion.false) 12 | assert false 13 | } 14 | 15 | method test02(x:Ref) { 16 | 17 | package false --* applying (false --* true) in true 18 | 19 | //:: ExpectedOutput(assert.failed:assertion.false) 20 | assert false 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/CarbonBackendTypeTest.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2025 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.reporter.StdIOReporter 10 | import viper.silver.testing.BackendTypeTest 11 | import viper.silver.verifier.Verifier 12 | 13 | class CarbonBackendTypeTest extends BackendTypeTest { 14 | override val verifier: Verifier = CarbonVerifier(StdIOReporter()) 15 | } 16 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/snapshot_report.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Int 6 | 7 | //this test is very similar to "snapshots.sil" in the Silicon test suite 8 | method test01(x: Ref) 9 | { 10 | inhale acc(x.g)&&acc(x.f) 11 | x.f := 1 12 | x.g := 1 13 | package acc(x.f,1/2)&&x.f==1 --* acc(x.f,1/2)&&acc(x.g) 14 | apply acc(x.f,1/2)&&x.f==1 --* acc(x.f,1/2)&&acc(x.g) 15 | assert x.f==1 16 | 17 | //:: UnexpectedOutput(assert.failed:assertion.false, /silicon-wands/issue/1/) 18 | assert x.g==1 19 | } 20 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/MapModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{Type, Exp} 11 | 12 | /** 13 | * A module for translating maps. 14 | */ 15 | trait MapModule extends Module { 16 | def isUsed() : Boolean 17 | def translateMapExp(exp : sil.Exp) : Exp 18 | def translateMapType(mapType : sil.MapType) : Type 19 | } 20 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/apply1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t01(x: Ref) { 7 | inhale acc(x.f) 8 | package acc(x.f) --* acc(x.f) 9 | apply acc(x.f) --* acc(x.f) 10 | 11 | 12 | //:: ExpectedOutput(assert.failed:assertion.false) 13 | assert false 14 | } 15 | 16 | method t02(x:Ref) { 17 | inhale acc(x.f,1/4)&&x.f==2 18 | package acc(x.f,1/4)&&x.f==2 --* acc(x.f,1/4) 19 | 20 | apply acc(x.f,1/8+1/8)&&x.f==2 --* acc(x.f,1/4) 21 | 22 | //:: ExpectedOutput(assert.failed:assertion.false) 23 | assert false 24 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/exec_unfolding.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f:Ref 5 | 6 | predicate P(x:Ref) { 7 | acc(x.f,1/4) 8 | } 9 | 10 | method t01(x:Ref) { 11 | package acc(x.f,1/4)&&acc(P(x.f)) --* (folding P(x) in (unfolding P(x) in (unfolding P(x.f) in acc(x.f,1/4)&&acc(x.f.f,1/4)))) 12 | 13 | //:: ExpectedOutput(assert.failed:assertion.false) 14 | assert false 15 | } 16 | 17 | method t02(x:Ref) { 18 | package acc(x.f,1/4) --* (folding P(x) in (unfolding P(x) in acc(x.f,1/4))) 19 | 20 | //:: ExpectedOutput(assert.failed:assertion.false) 21 | assert false 22 | } -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/SeqModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{Exp, Type} 11 | 12 | /** 13 | * A module for translating sequences. 14 | 15 | */ 16 | trait SeqModule extends Module { 17 | def translateSeqExp(exp: sil.Exp): Exp 18 | def translateSeqType(seqType: sil.SeqType): Type 19 | def rewriteToTermsInTriggers(e: Exp) : Exp 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/folding_fun_frame_2.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Int 6 | 7 | predicate Single(this: Ref) { 8 | acc(this.f) 9 | } 10 | 11 | function sum(p: Ref): Int 12 | requires acc(Single(p)) 13 | { 14 | unfolding acc(Single(p)) in p.f 15 | } 16 | 17 | method t01(xs:Ref) 18 | requires acc(Single(xs)) 19 | { 20 | var i:Int 21 | i := sum(xs) 22 | 23 | unfold Single(xs) 24 | package true --* (folding Single(xs) in acc(Single(xs)) && sum(xs)==i) 25 | 26 | //:: ExpectedOutput(assert.failed:assertion.false) 27 | assert false 28 | } 29 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/SetModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{Type, Exp} 11 | 12 | /** 13 | * A module for translating sets and multisets. 14 | */ 15 | trait SetModule extends Module { 16 | def translateSetExp(exp: sil.Exp): Exp 17 | def translateSetType(setType: sil.SetType): Type 18 | def translateMultisetType(setType: sil.MultisetType): Type 19 | } 20 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/DomainModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{Type, Decl, Exp} 11 | 12 | /** 13 | * A module for translating Viper domains. 14 | 15 | */ 16 | trait DomainModule extends Module { 17 | def translateDomain(exp: sil.Domain): Seq[Decl] 18 | def translateDomainFuncApp(fa: sil.DomainFuncApp): Exp 19 | def translateDomainTyp(typ: sil.DomainType): Type 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 11 | 12 | %msg%n //%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/wand_shapes_simple_exhale.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | field g: Int 6 | 7 | 8 | method t01(p: Ref, r:Ref) { 9 | package acc(p.f) --* true 10 | exhale acc(p.f) --* true 11 | 12 | //:: ExpectedOutput(exhale.failed:wand.not.found) 13 | exhale acc(r.f) --* true 14 | } 15 | 16 | 17 | method t02(x: Ref) { 18 | inhale acc(P(x)) 19 | unfold P(x) 20 | exhale (acc(x.f) --* acc(x.f)) 21 | 22 | //:: ExpectedOutput(exhale.failed:wand.not.found) 23 | exhale (acc(x.f) --* acc(x.f)) 24 | } 25 | 26 | predicate P(x:Ref) { 27 | acc(x.f) --* acc(x.f) 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/LoopModule.scala: -------------------------------------------------------------------------------- 1 | package viper.carbon.modules 2 | 3 | import viper.silver.{ast => sil} 4 | 5 | trait LoopModule extends Module { 6 | 7 | /* Returns method annotated with loop information and initializes loop module accordingly. 8 | * This must be called before starting the method translation and the returned method should be used for the 9 | * translation. 10 | */ 11 | def initializeMethod(m: sil.Method): sil.Method 12 | 13 | // Return true iff the statement is solely used for the loop module itself and is irrelevant otherwise. 14 | def isLoopDummyStmt(s: sil.Stmt) : Boolean 15 | 16 | // Return true iff the axioms to sum states is required. 17 | def sumOfStatesAxiomRequired : Boolean 18 | } 19 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/InhaleComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.carbon.boogie.Stmt 10 | import viper.silver.verifier.PartialVerificationError 11 | import viper.silver.{ast => sil} 12 | 13 | /** 14 | * Takes care of inhaling one or several kinds of expressions. 15 | 16 | */ 17 | trait InhaleComponent extends Component { 18 | 19 | /** 20 | * Inhale a single expression. 21 | */ 22 | def inhaleExp(exp: sil.Exp, error: PartialVerificationError): Stmt 23 | } 24 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/known_folded_1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t01(x:Ref) { 7 | 8 | package acc(x.f)&&x.f==2 --* (folding P(x) in (unfolding P(x) in acc(x.f)&&x.f==2)) 9 | 10 | //:: ExpectedOutput(assert.failed:assertion.false) 11 | assert false 12 | 13 | } 14 | 15 | method t02(x:Ref) { 16 | 17 | package acc(x.f,1/2) --* 18 | (packaging (acc(x.f,1/2) --* (folding Q(x) in (folding Q(x) in acc(Q(x),2/1)))) in true) 19 | 20 | //:: ExpectedOutput(assert.failed:assertion.false) 21 | assert false 22 | 23 | } 24 | 25 | 26 | predicate Q(x:Ref) { 27 | acc(x.f,1/2) 28 | } 29 | 30 | 31 | predicate P(x:Ref) { 32 | acc(x.f) 33 | } 34 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/unfolding_ambiguity2.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t01(x:Ref) { 7 | package acc(P(x)) --* unfolding P(x) in acc(x.f) 8 | 9 | exhale (acc(P(x)) --* acc(x.f)) 10 | } 11 | 12 | method t02(x:Ref) { 13 | /*there's an ambiguity here, it may make more sense to interpret the unfolding as a ghost operation since one may want the pure part in the body of the unfolding ghost expression to be the right hand side of the wand 14 | 15 | if the unfolding is interpreted just as an unfolding expression then the wand is not well-formed */ 16 | 17 | package acc(P(x)) --* unfolding P(x) in true 18 | 19 | exhale (acc(P(x)) --* true) 20 | } 21 | 22 | predicate P(x:Ref) { 23 | acc(x.f) 24 | } -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/UnicodeString.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | import language.implicitConversions 10 | 11 | /** 12 | * A class to extend strings with an `or` method that allows one 13 | * to use a Unicode string with a backup option if the file encoding used 14 | * is not UTF-8. 15 | 16 | */ 17 | class UnicodeString(val s: String) { 18 | def or(safe: String) = { 19 | //if (System.getProperty("file.encoding") == "UTF-8") s else safe 20 | safe 21 | } 22 | } 23 | object UnicodeString { 24 | implicit def string2unicodestring(s: String) = new UnicodeString(s) 25 | } 26 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/packaging_apply.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t04(x:Ref) { 7 | inhale acc(x.f)&&x.f==2 8 | 9 | package acc(x.f,1/4) --* acc(x.f)&&x.f==2 10 | 11 | //this package should work, but after the package the current state shouldn't have any permission for x.f anymore 12 | package acc(x.f,1/4)&&x.f==2 --* 13 | (packaging (acc(x.f,1/4)&&x.f==3 --* acc(x.f)&&false) in 14 | acc(x.f,1/4)&&(acc(x.f,1/4)&&x.f==3 --* acc(x.f)&&false)) 15 | 16 | //this apply should fail since current state shouldn't have any permission for x.f anymore 17 | 18 | //:: ExpectedOutput(apply.failed:insufficient.permission) 19 | apply acc(x.f,1/4)&&x.f==2 --* acc(x.f,1/4)&&(acc(x.f,1/4)&&x.f==3 --* acc(x.f)&&false) 20 | 21 | 22 | } -------------------------------------------------------------------------------- /src/test/resources/regression/fraction_translation_bug.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int; 5 | 6 | /* This test case concerns a previous bug in Carbon, due to 1/3 being translated to 0.333333333, hence the assertion in test02 was proven correct (even though isn't) 7 | and assertion in test01 wasn't proven to be correct (even though it is) 8 | 9 | The origin of the bug was the translation of fractions in Silver to a decimal represented by a finite number of digits in Boogie 10 | */ 11 | 12 | 13 | method test01(x: Ref) { 14 | var p: Perm 15 | p := 1/3 16 | assert p+p+p == 1/1 //failed to verify in previous version 17 | } 18 | 19 | method test02(x: Ref) 20 | requires acc(x.f,1/3) 21 | { 22 | var p: Perm 23 | p := 1/3 24 | assert p+p+p == 999999999/1000000000 //wrongly verified in previous version 25 | } 26 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/CarbonTestsOldPermissionSemantics.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.frontend.Frontend 10 | import viper.silver.logger.SilentLogger 11 | import viper.silver.reporter.{NoopReporter, StdIOReporter} 12 | import viper.silver.testing.SilSuite 13 | import viper.silver.verifier.Verifier 14 | 15 | import java.nio.file.Path 16 | 17 | /** All tests for carbon. 18 | 19 | */ 20 | class CarbonTestsOldPermissionSemantics extends AllTests { 21 | override def testDirectories: Seq[String] = Vector( 22 | "oldpermsemantics" 23 | ) 24 | 25 | override val commandLineArguments: Seq[String] = 26 | Seq("--respectFunctionPrePermAmounts") 27 | } 28 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_simple_no_perm.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | /* This example encodes and specifies an iterative computation of the 5 | * sum of a recursively defined linked-list. Magic wands are used to 6 | * book-keep permissions in the loop. 7 | */ 8 | 9 | field val: Int 10 | field next: Ref 11 | 12 | 13 | method t01(ys: Ref) 14 | requires ys != null 15 | { 16 | //:: ExpectedOutput(package.failed:assertion.false) 17 | package true --* false 18 | } 19 | 20 | method t02(x:Int) 21 | { 22 | package x >= 2 --* x >=2 && x >=1 23 | 24 | //:: ExpectedOutput(assert.failed:assertion.false) 25 | assert false 26 | } 27 | 28 | method t03(x: Int) 29 | requires x == 0 30 | { 31 | package x==1 --* false 32 | 33 | //:: ExpectedOutput(assert.failed:assertion.false) 34 | assert false 35 | 36 | } 37 | 38 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_inc3.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test01(x: Ref) { 9 | package acc(x.f) --* packaging (acc(x.f) --* acc(x.f)) in acc(x.f) --* acc(x.f) 10 | inhale(acc(x.f)) 11 | apply acc(x.f) --* (acc(x.f) --* acc(x.f)) 12 | 13 | //:: ExpectedOutput(assert.failed:assertion.false) 14 | assert false 15 | } 16 | 17 | method test02(x: Ref) { 18 | package false --* packaging (acc(x.f) --* acc(x.f)) in acc(x.f) --* acc(x.g) 19 | 20 | //:: ExpectedOutput(assert.failed:assertion.false) 21 | assert false 22 | 23 | } 24 | 25 | method test03(x: Ref) 26 | requires acc(x.f) 27 | { 28 | package acc(x.f,1/2)&&x.f == 2&&x.f ==3 --* acc(x.f)&&false 29 | 30 | exhale acc(x.f) 31 | 32 | //:: ExpectedOutput(assert.failed:assertion.false) 33 | assert false 34 | } 35 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/wand_conjunction.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | 6 | method t01(x:Ref) { 7 | 8 | package acc(x.f) --* acc(x.f) 9 | package acc(x.f) --* acc(x.f) 10 | 11 | exhale (acc(x.f) --* acc(x.f)) && (acc(x.f) --* acc(x.f)) //failed previously 12 | 13 | //:: ExpectedOutput(assert.failed:assertion.false) 14 | assert false 15 | 16 | } 17 | 18 | method t02(x:Ref) 19 | requires acc(x.f) 20 | { 21 | package acc(x.f) --* acc(x.f) 22 | exhale acc(x.f)&&(acc(x.f) --* acc(x.f)) //failed previously 23 | 24 | //:: ExpectedOutput(assert.failed:assertion.false) 25 | assert false 26 | } 27 | 28 | method t03(x:Ref) 29 | requires acc(x.f) 30 | { 31 | package acc(x.f) --* acc(x.f) 32 | exhale (acc(x.f) --* acc(x.f))&&acc(x.f) //failed previously 33 | 34 | //:: ExpectedOutput(assert.failed:assertion.false) 35 | assert false 36 | } -------------------------------------------------------------------------------- /carbon.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # To verify a SIL file 'test.sil', run './carbon.sh test.sil'. 4 | # 5 | # The script expects the environment variable $BOOGIE_HOME to be set, e.g. 6 | # export BOOGIE_HOME=/home/severinh/Software/boogie 7 | # Consider adding such a line to your ~/.profile. 8 | # 9 | # Ensure that the file 'Boogie.exe' in the Boogie folder is executable. 10 | # Note that the 'z3.exe' file must be located inside the Boogie folder. 11 | # 12 | # Alternatively you could also just set the BOOGIE_EXE and Z3_EXE to point o 13 | # the executable files 14 | 15 | set -e 16 | 17 | BOOGIE_EXE="${BOOGIE_EXE:-$BOOGIE_HOME/Boogie.exe}" 18 | Z3_EXE="${Z3_EXE:-$BOOGIE_HOME/z3.exe}" 19 | 20 | export Z3_EXE 21 | export BOOGIE_EXE 22 | 23 | BASEDIR="$(realpath "$(dirname "$0")")" 24 | 25 | CP_FILE="$BASEDIR/carbon_classpath.txt" 26 | 27 | if [ ! -f "$CP_FILE" ]; then 28 | (cd "$BASEDIR"; sbt "export runtime:dependencyClasspath" | tail -n1 > "$CP_FILE") 29 | fi 30 | 31 | exec java -Xss30M -cp "$(cat "$CP_FILE")" viper.carbon.Carbon "$@" 32 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/packaging_nested.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field next: Ref 6 | 7 | method t01(x:Ref) { 8 | 9 | 10 | //package true --* (packaging (false --* (packaging ((true --* (packaging ((true --* false) in (true --* false)))) in true --* (true --* false)))) in (false --* (true --* (true --* false)))) 11 | 12 | package true --* (packaging (false --* (packaging (true --* false) in (true --* false))) in (false --* true --* false)) 13 | 14 | exhale true --* false --* true --* false 15 | 16 | //:: ExpectedOutput(assert.failed:assertion.false) 17 | assert false 18 | 19 | } 20 | 21 | 22 | method t03(x:Ref) { 23 | var y:Ref 24 | 25 | package acc(x.f,1/2)&&x.f==2 --* (packaging (acc(x.f,1/4)--* acc(x.f,3/4)) in ((acc(x.f,1/4)--* acc(x.f,3/4)))) 26 | 27 | y := x 28 | 29 | exhale acc(y.f,1/2)&&y.f==2 --* (acc(y.f,1/4)--* acc(y.f,3/4)) 30 | 31 | //:: ExpectedOutput(assert.failed:assertion.false) 32 | assert false 33 | 34 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/folding_inc1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test02(x: Ref) { 9 | package true --* (packaging (false --* folding P(x) in true) in true) 10 | 11 | //:: ExpectedOutput(assert.failed:assertion.false) 12 | assert false 13 | } 14 | 15 | method test03(x:Ref) 16 | requires acc(P(x),1/2) 17 | { 18 | package acc(P(x),1/2) --* acc(P(x)) 19 | } 20 | 21 | method test04(x:Ref) 22 | { 23 | package acc(x.f) --* packaging (acc(x.f) --* folding Q(x) in false) in true 24 | 25 | //:: ExpectedOutput(assert.failed:assertion.false) 26 | assert false 27 | } 28 | 29 | 30 | method test06(x:Ref) 31 | { 32 | //:: ExpectedOutput(package.failed:insufficient.permission) 33 | package acc(x.f) --* folding P(x) in acc(x.f) //should fail 34 | } 35 | 36 | 37 | predicate P(x: Ref) { 38 | acc(x.f) 39 | } 40 | 41 | predicate Q(x: Ref) { 42 | acc(x.f)&&acc(x.f) 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_inc5.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | 6 | method t01(x:Ref,y:Ref) 7 | requires acc(x.f) 8 | requires acc(y.f) 9 | { 10 | package acc(x.f) --* acc(x.f)&&acc(x.f)&&acc(y.f) 11 | 12 | //since inconsistency is noticed before transferring permission to y.f from the state before the package, 13 | //the verifier can notice that no permission has to be removed from that state to y.f 14 | 15 | exhale acc(y.f) 16 | 17 | //:: ExpectedOutput(assert.failed:assertion.false) 18 | assert false 19 | } 20 | 21 | method t02(x:Ref,y:Ref) 22 | requires acc(x.f,1/3) 23 | { 24 | package acc(x.f,1/3)&&x.f==2 --* packaging (acc(x.f,1/3)&&x.f==3 --* acc(x.f)&&false) in true 25 | 26 | //since inconsistency is noticed before transferring permission from the state before the package, 27 | //the verifier can notice that no permission has to be removed from that state 28 | exhale acc(x.f,1/3) 29 | 30 | //:: ExpectedOutput(assert.failed:assertion.false) 31 | assert false 32 | } 33 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/wand_shapes_1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Ref 5 | field g: Int 6 | 7 | method test07(p: Ref) { 8 | var r:Ref 9 | inhale acc(p.f) 10 | package (acc(p.f)&&acc(p.f.g) --* true) 11 | exhale acc(p.f) 12 | r := p; 13 | exhale (acc(r.f)&&acc(r.f.g) --* true) 14 | 15 | //:: ExpectedOutput(assert.failed:assertion.false) 16 | assert false 17 | 18 | } 19 | 20 | method test08() { 21 | var x:Int 22 | var y:Int 23 | var t:Int 24 | package x == 2 && y ==2 --* true 25 | exhale y == 2 && x ==2 --* true 26 | 27 | //:: ExpectedOutput(assert.failed:assertion.false) 28 | assert false 29 | } 30 | 31 | method test09() { 32 | var x:Int 33 | var y:Int 34 | var t:Int 35 | package x == 2 && y ==2 --* true 36 | x := t 37 | 38 | //:: ExpectedOutput(exhale.failed:wand.not.found) 39 | exhale y == 2 && x ==2 --* true 40 | } 41 | 42 | method test10(p:Ref, q:Ref) { 43 | package acc(p.f)&&acc(q.f) --* true 44 | 45 | //:: ExpectedOutput(exhale.failed:wand.not.found) 46 | exhale acc(q.f)&&acc(p.f) --* true 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/folding_fun_frame.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | /* This example encodes and specifies an iterative computation of the 5 | * sum of a recursively defined linked-list. Magic wands are used to 6 | * book-keep permissions in the loop. 7 | */ 8 | 9 | field val: Int 10 | field next: Ref 11 | 12 | /* Linked-list abstract predicate. Contains access to the fields of the current 13 | * node ys, and transitively, to the fields of all nodes in the tail. 14 | */ 15 | predicate List(ys: Ref) { 16 | acc(ys.val) && acc(ys.next) && (ys.next != null ==> acc(List(ys.next))) 17 | } 18 | 19 | /* Pure function that computes the sum in a straight-forward, recursive way. */ 20 | function sum_rec(ys: Ref): Int 21 | requires acc(List(ys)) 22 | { unfolding List(ys) in ys.val + (ys.next == null ? 0 : sum_rec(ys.next)) } 23 | 24 | 25 | method t01(xs:Ref) 26 | requires acc(List(xs)) 27 | { 28 | var i:Int 29 | i := sum_rec(xs) 30 | unfold List(xs) 31 | 32 | package true --* (folding List(xs) in acc(List(xs)) && sum_rec(xs)==i) 33 | 34 | //:: ExpectedOutput(assert.failed:assertion.false) 35 | assert false 36 | } 37 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/folding_2.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test01(x:Ref) 9 | requires acc(x.f,1/2) 10 | { 11 | package acc(x.f,1/2) --* (folding P(x) in (folding Q(x) in acc(P(x))&&acc(Q(x)))) 12 | 13 | //:: ExpectedOutput(exhale.failed:insufficient.permission) 14 | exhale(acc(x.f,1/2)) //should fail 15 | 16 | } 17 | 18 | method test02(x:Ref) 19 | requires acc(x.f) 20 | { 21 | package acc(x.f,1/2) --* (folding P(x) in (folding Q(x) in acc(P(x))&&acc(Q(x)))) 22 | exhale(acc(x.f,1/2)) //should succeed 23 | 24 | //:: ExpectedOutput(assert.failed:assertion.false) 25 | assert false 26 | } 27 | 28 | method test03(x:Ref,y:Ref) 29 | requires acc(x.f,1/2) && acc(y.f,1/2) 30 | { 31 | package acc(x.f,1/2) --* (folding P(x) in (folding Q(x) in (folding P(y) in acc(P(x))&&acc(Q(x))&&acc(P(y))))) 32 | 33 | //:: ExpectedOutput(assert.failed:assertion.false) 34 | assert false 35 | } 36 | 37 | predicate P(x: Ref) { 38 | acc(x.f,1/2) 39 | } 40 | 41 | predicate Q(x: Ref) { 42 | acc(x.f,1/2) 43 | } 44 | 45 | 46 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/LabelHelper.scala: -------------------------------------------------------------------------------- 1 | package viper.carbon.modules.impls 2 | 3 | import viper.carbon.modules.StateModule 4 | 5 | object LabelHelper { 6 | 7 | /** 8 | * Would prefer to use a method that takes a state module as input, but since it would have a dependent return type 9 | * (stateModule.StateSnapshot) IntelliJ can report false errors for callers. 10 | * @param labelName 11 | * @param freshTempStateNoSideEffect provide a fresh state without any side effects 12 | * @param getSnapshot obtain a stored snapshot 13 | * @param storeSnapshot store a snapshot 14 | * @tparam T: state snapshot representation 15 | * @return state snapshot associated with input label 16 | */ 17 | def getLabelState[T](labelName: String, 18 | freshTempStateNoSideEffect: String => T, 19 | getSnapshot: String => Option[T], 20 | storeSnapshot: (String, T) => Unit): T = { 21 | getSnapshot(labelName) match { 22 | case Some(labelState) => labelState 23 | case None => { 24 | val s = freshTempStateNoSideEffect("Label"+labelName) 25 | storeSnapshot(labelName,s) 26 | s 27 | } 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/folding_unfolding_combo.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Int 6 | 7 | predicate Single(this: Ref) { 8 | acc(this.f) 9 | } 10 | 11 | function sum(p: Ref): Int 12 | requires acc(Single(p)) 13 | { 14 | unfolding acc(Single(p)) in p.f 15 | } 16 | 17 | 18 | method t01(xs:Ref) 19 | requires acc(Single(xs)) 20 | { 21 | var i:Int 22 | unfold Single(xs) 23 | xs.f := 3 24 | fold Single(xs) 25 | 26 | package true --* (unfolding acc(Single(xs)) in acc(xs.f)&&xs.f==3) 27 | 28 | //:: ExpectedOutput(assert.failed:assertion.false) 29 | assert false 30 | } 31 | 32 | 33 | 34 | method t02(xs:Ref) 35 | requires acc(Single(xs)) 36 | { 37 | assume sum(xs) == 3 38 | package true --* (unfolding acc(Single(xs)) in folding acc(Single(xs)) in acc(Single(xs))&&sum(xs) == 3) 39 | 40 | //:: ExpectedOutput(assert.failed:assertion.false) 41 | assert false 42 | 43 | } 44 | 45 | 46 | method t03(x:Ref) 47 | { 48 | inhale acc(Single(x)) 49 | inhale acc(x.g) 50 | 51 | package true --* acc(Single(x))&&acc(x.g) 52 | 53 | //:: ExpectedOutput(assert.failed:assertion.false) 54 | assert false 55 | 56 | } -------------------------------------------------------------------------------- /src/test/resources/regression/wands/potential_unsoundness.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field next: Ref 6 | 7 | 8 | method t01(x:Ref, y: Ref) 9 | { 10 | inhale acc(x.f) 11 | inhale acc(y.f) 12 | 13 | package acc(x.f,1/2)&&x.f==2 --* acc(x.f,3/4)&&acc(y.f,1/2) 14 | 15 | inhale acc(y.f,1/2) 16 | 17 | assert x.f == 2 18 | /*see Gaurav's report on the discussion on why it is sound with a certain view to let this assertion go through, but also see why it may also not be consistent/"unsound" behaviour*/ 19 | 20 | assert false 21 | //if the assertion before should go through then it makes sense for this to go through too (see the discussion referred to above) 22 | } 23 | 24 | method t02(x:Ref, y: Ref) 25 | { 26 | inhale acc(x.f) 27 | inhale acc(y.f) 28 | 29 | package acc(x.f,1/2)&&x.f==2 --* acc(x.f,3/4)&&acc(y.f,1/2) 30 | 31 | exhale acc(x.f,1/2)&&x.f==2 --* acc(x.f,3/4)&&acc(y.f,1/2) 32 | 33 | inhale acc(y.f,1/2) 34 | 35 | assert x.f == 2 36 | /*see Gaurav's report on the discussion on why it is sound with a certain view to let this assertion go through, but also see why it may also not be consistent/"unsound" behaviour*/ 37 | 38 | assert false 39 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Carbon 2 | === 3 | 4 | Carbon is a verification-condition-generation-based verifier for [the Viper intermediate verification language](https://github.com/viperproject/silver). 5 | 6 | [The Viper project](http://www.pm.inf.ethz.ch/research/viper.html) is developed by the [Programming Methodology](http://www.pm.inf.ethz.ch/) group at ETH Zurich. 7 | 8 | See [the documentation wiki](https://github.com/viperproject/documentation/wiki) for instructions on how to try out or install the Viper tools. 9 | 10 | Installation Instructions: 11 | --- 12 | 13 | We recommend using carbon through the [VS Code plugin](https://marketplace.visualstudio.com/items?itemName=viper-admin.viper). Alternatively, one can compile carbon from source with the following steps: 14 | 15 | * Install Z3 and Boogie and set the environment variables `Z3_EXE` and `BOOGIE_EXE` correspondingly (see wiki above) 16 | * Clone this repository *recursively* by running: 17 | `git clone --recursive https://github.com/viperproject/carbon` 18 | 19 | And then 20 | * Compile and run with: 21 | `sbt "run [options] "` 22 | * Alternatively, for a faster startup without compilation each time, build a `.jar` file: 23 | `sbt assembly` 24 | And then run with: 25 | `java -jar ./target/scala-*/carbon.jar [options] ` 26 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/utility/PartialSort.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.utility 8 | 9 | import org.jgrapht.graph.{DefaultDirectedGraph, DefaultEdge} 10 | import org.jgrapht.traverse.TopologicalOrderIterator 11 | 12 | import scala.collection.mutable.ListBuffer 13 | 14 | /** 15 | * A utility object for sorting based on a partial order. 16 | 17 | */ 18 | object PartialSort { 19 | 20 | /** 21 | * Returns a sequence with the same elements as `s`, but sorted according to a given partial order. 22 | */ 23 | def sort[T](s: Seq[T], ordering: PartialOrdering[T]): Seq[T] = { 24 | val g = new DefaultDirectedGraph[T, DefaultEdge](classOf[DefaultEdge]) 25 | for (e <- s) { 26 | g.addVertex(e) 27 | } 28 | for (e1 <- s; e2 <- s if e1 != e2) { 29 | if (ordering.lteq(e1, e2)) { 30 | g.addEdge(e1, e2) 31 | } 32 | } 33 | val topo = new TopologicalOrderIterator[T, DefaultEdge](g) 34 | val res = ListBuffer[T]() 35 | while (topo.hasNext) { 36 | res += topo.next() 37 | } 38 | res.toSeq 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/GraphTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import java.nio.file.Path 10 | 11 | import org.scalatest.DoNotDiscover 12 | import viper.silver.frontend.Frontend 13 | import viper.silver.logger.SilentLogger 14 | import viper.silver.reporter.{NoopReporter, StdIOReporter} 15 | import viper.silver.testing.SilSuite 16 | import viper.silver.verifier.Verifier 17 | 18 | /** 19 | * Currently, we do not run the graph tests by default. This could be easily changed later by removing the 20 | * [[DoNotDiscover]] annotation. 21 | */ 22 | @DoNotDiscover 23 | class GraphTests extends SilSuite { 24 | override def testDirectories: Seq[String] = Vector("graphs") 25 | 26 | override def frontend(verifier: Verifier, files: Seq[Path]): Frontend = { 27 | require(files.length == 1, "tests should consist of exactly one file") 28 | val fe = new CarbonFrontend(NoopReporter, SilentLogger().get) 29 | fe.init(verifier) 30 | fe.reset(files.head) 31 | fe 32 | } 33 | 34 | lazy val verifiers = List(CarbonVerifier(StdIOReporter())) 35 | } 36 | -------------------------------------------------------------------------------- /carbon.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set CURR_DIR=%cd% 4 | set BASE_DIR=%~dp0 5 | 6 | :: switch to repository root to check for classpath file and possibly call sbt. 7 | cd /D %BASE_DIR% 8 | 9 | :: Only call sbt if the classpath file is missing. 10 | if not exist carbon_classpath.txt ( 11 | rem Read all lines of the sbt runtime classpath output and parse it against the regex supplied to findstr. 12 | rem The regex looks for lines not starting with '[' and ending in '.jar' as the classpath usually does 13 | rem (and log lines don't, since they are prefixed with [LOGLEVEL]). 14 | echo Generating missing carbon_classpath.txt file from sbt classpath 15 | for /f "tokens=*" %%i in ('sbt "export runtime:dependencyClasspath" ^| findstr "[^\[].*\.jar$"') do ( 16 | echo |set /p=%%i > carbon_classpath.txt 17 | ) 18 | ) 19 | 20 | :: Read classpath file in rather cumbersome way to avoid the 1024 character buffer limit. 21 | :: Note: this solutions breaks, once the classpath is longer than 8192 characters! 22 | for /f "delims=" %%x in (carbon_classpath.txt) do set CP=%%x 23 | 24 | :: switch back to original directory 25 | cd /D %CURR_DIR% 26 | 27 | set JAVA_EXE=java 28 | set JVM_OPTS=-Xss30m -Dfile.encoding=UTF-8 29 | set CARBON_MAIN=viper.carbon.Carbon 30 | set CARBON_OPTS= %* 31 | set CMD=%JAVA_EXE% %JVM_OPTS% -cp "%CP%" %CARBON_MAIN% %CARBON_OPTS% 32 | 33 | call %CMD% 34 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_inc2.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test01(x: Ref) 9 | requires acc(x.f) && x.f == 3 10 | { 11 | //package acc(x.f,1/2) && x.f == 2 --* acc(x.f) 12 | 13 | 14 | //x.f in rhs will be 2 and 3 --> inconsistent state 15 | package acc(x.f,1/2) && x.f == 2 --* acc(x.f) && false 16 | 17 | //:: ExpectedOutput(apply.failed:assertion.false) 18 | apply acc(x.f,1/2) && x.f == 2 --* acc(x.f) && false 19 | } 20 | 21 | method test02(x:Ref) 22 | requires acc(x.f) 23 | { 24 | package acc(x.f,1/2) --* acc(x.f,1/2)&&acc(x.f,1/2) 25 | apply acc(x.f,1/2) --* acc(x.f,1/2)&&acc(x.f,1/2) 26 | exhale acc(x.f) 27 | 28 | //:: ExpectedOutput(assert.failed:assertion.false) 29 | assert false 30 | } 31 | 32 | 33 | method test03(x: Ref) 34 | requires acc(x.f,1/3) && x.f == 2 35 | { 36 | 37 | //:: ExpectedOutput(package.failed:insufficient.permission) 38 | package acc(x.f,1/3) && x.f == 2 --* acc(x.f) 39 | } 40 | 41 | method test04(x: Ref) 42 | requires acc(x.f,1/3) && x.f == 3 43 | { 44 | //here inconsistency on rhs 45 | package acc(x.f,1/3) && x.f == 2 --* acc(x.f) 46 | 47 | //:: ExpectedOutput(assert.failed:assertion.false) 48 | assert false 49 | } 50 | 51 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/SmokeDetectionPluginTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2022 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.frontend.Frontend 10 | import viper.silver.logger.SilentLogger 11 | import viper.silver.reporter.{NoopReporter, StdIOReporter} 12 | import viper.silver.testing.SilSuite 13 | import viper.silver.verifier.Verifier 14 | 15 | import java.nio.file.Path 16 | 17 | class SmokeDetectionPluginTests extends SilSuite { 18 | override val testDirectories: Seq[String] = Seq("smoke") 19 | 20 | override def frontend(verifier: Verifier, files: Seq[Path]): Frontend = { 21 | require(files.length == 1, "tests should consist of exactly one file") 22 | val fe = new CarbonFrontend(NoopReporter, SilentLogger().get) 23 | fe.init(verifier) 24 | fe.reset(files.head) 25 | fe 26 | } 27 | 28 | val carbon: CarbonVerifier = CarbonVerifier(StdIOReporter()) 29 | 30 | lazy val verifiers = List(carbon) 31 | 32 | override def configureVerifiersFromConfigMap(configMap: Map[String, Any]): Unit = { 33 | carbon.parseCommandLine(Seq("--enableSmokeDetection") :+ "dummy-file-to-prevent-cli-parser-from-complaining-about-missing-file-name.silver.vpr") 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/AdtPluginTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.frontend.Frontend 10 | import viper.silver.logger.SilentLogger 11 | import viper.silver.reporter.{NoopReporter, StdIOReporter} 12 | import viper.silver.testing.SilSuite 13 | import viper.silver.verifier.Verifier 14 | 15 | import java.nio.file.Path 16 | 17 | /** All tests for the Adt Plugin. */ 18 | class AdtPluginTests extends SilSuite { 19 | override def testDirectories: Seq[String] = Vector("adt") 20 | 21 | override def frontend(verifier: Verifier, files: Seq[Path]): Frontend = { 22 | require(files.length == 1, "tests should consist of exactly one file") 23 | val fe = new CarbonFrontend(NoopReporter, SilentLogger().get) 24 | fe.init(verifier) 25 | fe.reset(files.head) 26 | fe 27 | } 28 | 29 | val carbon: CarbonVerifier = CarbonVerifier(StdIOReporter()) 30 | 31 | lazy val verifiers = List(carbon) 32 | 33 | override def configureVerifiersFromConfigMap(configMap: Map[String, Any]): Unit = { 34 | carbon.parseCommandLine(Seq("--plugin", "viper.silver.plugin.standard.adt.AdtPlugin") :+ "dummy-file-to-prevent-cli-parser-from-complaining-about-missing-file-name.silver.vpr") 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/TransferComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | /** 10 | * Created by Gaurav on 08.03.2015. 11 | */ 12 | 13 | import viper.carbon.boogie.{Exp, Stmt} 14 | import viper.carbon.modules.TransferableEntity 15 | 16 | /** 17 | *used to handle the transfer operation across different modules 18 | */ 19 | trait TransferComponent extends Component { 20 | /** 21 | * 22 | * @param e 23 | * @return the statements 24 | */ 25 | def transferValid(e: TransferableEntity):Seq[(Stmt,Exp)] 26 | 27 | /** 28 | * 29 | * @param e 30 | * @param cond all assumptions and assertions should be wrapped inside an if statement with condition cond 31 | * @return statement which adds the expression e to the current state (for example permissions, wand) 32 | */ 33 | def transferAdd(e:TransferableEntity, cond: Exp): Stmt 34 | 35 | /** 36 | * 37 | * @param e 38 | * @param cond all assumptions and assertions should be wrapped inside an if statement with condition cond 39 | * @return statement which removes the expression e to the current state (for example permissions, wand) 40 | */ 41 | def transferRemove(e:TransferableEntity, cond:Exp): Stmt 42 | } 43 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/package_hyp.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field g: Ref 6 | field h: Ref 7 | 8 | method test01(x: Ref) 9 | requires acc(x.f) 10 | { 11 | package acc(x.f)&&acc(x.f) --* false //theoretically should be packaged since LHS is inconsistent 12 | package acc(x.f)&&acc(x.f) --* acc(x.f)&&acc(x.f)&&false //here inconsistency is also seen in right hand side 13 | 14 | //:: ExpectedOutput(assert.failed:assertion.false) 15 | assert false 16 | } 17 | 18 | method test02(x: Ref) 19 | requires acc(x.f) 20 | { 21 | package acc(x.f)&&x.f==2&&x.f==3 --* false 22 | 23 | //:: ExpectedOutput(assert.failed:assertion.false) 24 | assert false 25 | } 26 | 27 | method test03(x: Ref) 28 | requires acc(x.f) 29 | { 30 | package (acc(x.f)&&x.f==2&&x.f==3) --* packaging (true --* false) in false 31 | 32 | //:: ExpectedOutput(assert.failed:assertion.false) 33 | assert false 34 | } 35 | 36 | method test05(x:Ref) 37 | { 38 | //:: ExpectedOutput(package.failed:assertion.false) 39 | package true --* packaging (true --* false) in true 40 | //fails since inner package fails even though true --* true always holds in any footprint 41 | } 42 | 43 | method test04(x:Ref) 44 | { 45 | package (acc(x.f)&&acc(x.f)) --* packaging (true --* false) in false 46 | //:: ExpectedOutput(assert.failed:assertion.false) 47 | assert false 48 | } -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/AllTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.testing.SilSuite 10 | import viper.silver.verifier.Verifier 11 | import viper.silver.frontend.Frontend 12 | import java.nio.file.Path 13 | 14 | import viper.silver.logger.SilentLogger 15 | import viper.silver.reporter.{NoopReporter, StdIOReporter} 16 | 17 | /** All tests for carbon. 18 | 19 | */ 20 | class AllTests extends SilSuite { 21 | override def testDirectories: Seq[String] = Vector( 22 | "local", "all", "quantifiedpermissions", "quantifiedpredicates", "quantifiedcombinations", 23 | "wands", "examples", "termination", "refute", "quasihavoc" 24 | ) 25 | 26 | override def frontend(verifier: Verifier, files: Seq[Path]): Frontend = { 27 | require(files.length == 1, "tests should consist of exactly one file") 28 | val fe = new CarbonFrontend(NoopReporter, SilentLogger().get) 29 | fe.init(verifier) 30 | fe.reset(files.head) 31 | fe 32 | } 33 | 34 | lazy val verifier: CarbonVerifier = { 35 | val carbon = CarbonVerifier(StdIOReporter()) 36 | carbon.parseCommandLine(commandLineArguments ++ Seq("dummy.vpr")) 37 | carbon 38 | } 39 | 40 | lazy val verifiers = List(verifier) 41 | 42 | val commandLineArguments: Seq[String] = 43 | Seq() 44 | } 45 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/packaging_cond_perm.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | field f: Int 5 | field next: Ref 6 | 7 | 8 | method t01(x: Ref) 9 | { 10 | //should fail since not enough permission to evaluate x.f > 0 ? 1/4:1/2 11 | //:: ExpectedOutput(package.failed:insufficient.permission) 12 | package acc(x.f,1/2) && x.f > -1 --* (acc(x.f, (x.f > 0 ? 1/4: 1/2)) ) 13 | } 14 | 15 | 16 | method t02(x: Ref) 17 | { 18 | inhale acc(x.f,1/2) 19 | package acc(x.f,1/2) && x.f > 0 --* (acc(x.f, 1/4) && acc(x.f, (x.f > 0 ? 1/2: 1/4)) ) 20 | 21 | exhale acc(x.f,1/4) 22 | 23 | //:: ExpectedError(exhale.failed:insufficient.permission) 24 | exhale acc(x.f,1/4) 25 | } 26 | 27 | method t03(x: Ref) 28 | requires x != null 29 | requires acc(x.f,3/4) 30 | { 31 | package true --* (acc(x.f, 1/4) && acc(x.f, (x.f > 0 ? 1/4: 1/2)) ) 32 | 33 | //:: ExpectedError(exhale.failed:insufficient.permission) 34 | exhale(acc(x.f,1/4)) //shouldn't work, since can't prove that x.f > 0 35 | } 36 | 37 | method t04(x: Ref) 38 | requires x != null 39 | requires acc(x.f,3/4) 40 | requires x.f > 0 41 | { 42 | package true --* (acc(x.f, 1/4) && acc(x.f, (x.f > 0 ? 1/4: 1/2)) ) 43 | exhale(acc(x.f,1/4)) //should work 44 | 45 | //:: ExpectedOutput(assert.failed:assertion.false) 46 | assert false; 47 | } 48 | 49 | method t05(x: Ref) 50 | requires x != null 51 | requires acc(x.f,3/4) 52 | requires x.f > 0 53 | { 54 | package true --* (acc(x.f, 1/4) && acc(x.f, (x.f > 0 ? 1/4: 1/2)) ) 55 | 56 | //:: ExpectedError(exhale.failed:insufficient.permission) 57 | exhale(acc(x.f,1/2)) 58 | } 59 | 60 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/StmtModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.carbon.modules.components.{ComponentRegistry, StmtComponent} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie.{Exp, Namespace, Stmt, TrueLit} 12 | 13 | /** 14 | * A module for translating Viper statements. 15 | */ 16 | 17 | /** 18 | * statesStackForPackageStmt: stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 19 | * insidePackageStmt: Boolean that represents whether this method is being called during packaging a wand or not. 20 | * allStateAssms: is a boolean expression that carries the conjunction for all the assumptions made about all states on 'statesStackForPackageStmt' 21 | * 22 | * These wand-related parameters (mentioned above) are used when translating statements during packaging a wand. 23 | * For more details refer to the general note in 'wandModule'. 24 | */ 25 | trait StmtModule extends Module with ComponentRegistry[StmtComponent] { 26 | 27 | /** 28 | * Return initial statement to be used at the beginning of the method body encoding (for example, code for labels). 29 | * This method also sets up the internal state of the module and should thus be invoked before any statements are 30 | * translated 31 | */ 32 | def initStmt(methodBody: sil.Stmt) : Stmt 33 | 34 | def translateStmt(stmt: sil.Stmt, statesStackForPackageStmt: List[Any] = null, allStateAssms: Exp = TrueLit(), insidePackageStmt: Boolean = false): Stmt 35 | 36 | def labelNamespace: Namespace 37 | } 38 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/SimpleStmtComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie._ 11 | 12 | /** 13 | * A statement component that only contributes at the end. 14 | 15 | */ 16 | trait SimpleStmtComponent extends StmtComponent { 17 | 18 | /** 19 | * Potentially contributes to the translation of a statement. If no contribution 20 | * is desired, then [[viper.carbon.boogie.Statements.EmptyStmt]] can be used as a 21 | * return value. 22 | */ 23 | def simpleHandleStmt(s: sil.Stmt, statesStackForPackageStmt: List[Any] = null, allStateAssms: Exp = TrueLit(), insidePackageStmt: Boolean = false): Stmt 24 | 25 | /** 26 | * statesStackForPackageStmt: stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 27 | * insidePackageStmt: Boolean that represents whether this method is being called during packaging a wand or not. 28 | * allStateAssms: is a boolean expression that carries the conjunction for all the assumptions made about all states on 'statesStackForPackageStmt' 29 | * 30 | * These wand-related parameters (mentioned above) are used when translating statements during packaging a wand. 31 | * For more details refer to the general note in 'wandModule'. 32 | */ 33 | override def handleStmt(s: sil.Stmt, statesStackForPackageStmt: List[Any] = null, allStateAssms: Exp = TrueLit(), insidePackageStmt: Boolean = false) : (Seqn => Seqn) 34 | // = (simpleHandleStmt(s),Statements.EmptyStmt ) 35 | } 36 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/ExhaleComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.carbon.boogie.{Statements, Stmt} 10 | import viper.silver.{ast => sil} 11 | import viper.silver.verifier.PartialVerificationError 12 | 13 | /** 14 | * Takes care of exhaling one or several kinds of expressions. 15 | 16 | */ 17 | trait ExhaleComponent extends Component { 18 | 19 | /** 20 | * Exhale a single expression. 21 | */ 22 | def exhaleExp(e: sil.Exp, error: PartialVerificationError, definednessStateOpt: Option[DefinednessState]): Stmt = Statements.EmptyStmt 23 | 24 | /** 25 | */ 26 | 27 | /*** 28 | * Method that allows components to contribute to exhaling an expression. 29 | * The first part of the result is used before exhaling the expression, and finally after exhaling the expression 30 | * the second part of the result is used. 31 | * @param e expression to be exhaled. 32 | * @param error exhale error 33 | * @param definednessStateOpt If defined, then this expresses the current state in which definedness checks are performed 34 | * ("definedness state"). If defined and if the implementing component is responsible for the 35 | * top-level operator in @{code e}, then the component must ensure that that the definedness 36 | * state is updated (and restored) correctly. 37 | * @return 38 | */ 39 | def exhaleExpBeforeAfter(e: sil.Exp, error: PartialVerificationError, definednessStateOpt: Option[DefinednessState]): (() => Stmt, () => Stmt) = 40 | (() => exhaleExp(e, error, definednessStateOpt), () => Statements.EmptyStmt) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/Implicits.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | import language.implicitConversions 10 | 11 | /** 12 | * A collection of implicits for working with the Boogie AST. 13 | 14 | */ 15 | object Implicits { 16 | implicit def lift[T](t: T): Seq[T] = Seq(t) 17 | implicit def liftStmt(ss: Seq[Stmt]) = Seqn(ss) 18 | implicit def liftSeq(ss: Seq[Exp]) = new BoolSeq(ss) 19 | 20 | /** 21 | * Adds methods to turn a sequence of expressions into their conjunction or disjunction. 22 | */ 23 | class BoolSeq(xs: Seq[Exp]) { 24 | /** Returns the conjunction of all expressions, or 'true' if the sequence is empty'. */ 25 | def all: Exp = all(xs).getOrElse(TrueLit()) 26 | /** Returns the conjunction of all expressions, or 'None' if the sequence is empty'. */ 27 | def allOption: Option[Exp] = all(xs) 28 | 29 | /** Returns the disjunction of all expressions, or 'false' if the sequence is empty'. */ 30 | def any: Exp = any(xs).getOrElse(FalseLit()) 31 | /** Returns the disjunction of all expressions, or 'None' if the sequence is empty'. */ 32 | def anyOption: Option[Exp] = any(xs) 33 | 34 | private def any(xss: Seq[Exp]): Option[Exp] = { 35 | xss match { 36 | case Nil => None 37 | case Seq(x) => Some(x) 38 | case Seq(x, y) => Some(BinExp(x, Or, y)) 39 | case x +: xs => Some(BinExp(x, Or, all(xs).get)) 40 | } 41 | } 42 | 43 | private def all(xss: Seq[Exp]): Option[Exp] = { 44 | xss match { 45 | case Nil => None 46 | case Seq(x) => Some(x) 47 | case Seq(x, y) => Some(BinExp(x, And, y)) 48 | case x +: xs => Some(BinExp(x, And, all(xs).get)) 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/resources/regression/wands/packaging_1.vpr: -------------------------------------------------------------------------------- 1 | // Any copyright is dedicated to the Public Domain. 2 | // http://creativecommons.org/publicdomain/zero/1.0/ 3 | 4 | /* This example encodes and specifies an iterative computation of the 5 | * sum of a recursively defined linked-list. Magic wands are used to 6 | * book-keep permissions in the loop. 7 | */ 8 | 9 | field f: Int 10 | field next: Ref 11 | 12 | 13 | method t01(ys: Ref) 14 | requires ys != null 15 | requires acc(ys.f,1/3) 16 | { 17 | package acc(ys.f,1/3) --* packaging (acc(ys.f,1/3) --* acc(ys.f)) in true 18 | 19 | //:: ExpectedOutput(assert.failed:assertion.false) 20 | assert false 21 | } 22 | 23 | method t02(x: Ref) 24 | { 25 | 26 | inhale acc(x.f,1/2) 27 | 28 | //this package should succeed, but note on the right hand side for acc(x.f,1/4) the permission is transferred from the current state and in the current state itself the value of x.f is not known. In the transfer of acc(x.f,1/2) in the packaging ghost operation we learn that x.f is 2 in the current state but that information can generally only be used until the packaging ghost operation is finished executing. So in the current version of Carbon (23.08) that fact won't be available when transferring acc(x.f,1/4), hence it doesn't verify currently in Carbon. 29 | package acc(x.f,1/4)&&x.f==2 --* packaging (true --* acc(x.f,1/2)&&x.f == 2) in 30 | acc(x.f,1/4)&&x.f==2 31 | 32 | 33 | //:: ExpectedOutput(assert.failed:assertion.false) 34 | assert false 35 | 36 | } 37 | 38 | method t03(x: Ref) 39 | { 40 | //:: ExpectedOutput(package.failed:assertion.false) 41 | package acc(x.f,1/2)&&x.f==2 --* packaging (acc(x.f,1/2)&&x.f==3 --* false) in true 42 | 43 | } 44 | 45 | method t04(x: Ref) 46 | { 47 | package acc(x.f,1/2)&&x.f==2 --* packaging (acc(x.f,1/2)&&x.f==3 --* acc(x.f)&&false) in true 48 | 49 | //:: ExpectedOutput(assert.failed:assertion.false) 50 | assert false 51 | } 52 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/Visitor.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | /** 10 | * An implementation for visitors of the Boogie AST. 11 | 12 | */ 13 | object Visitor { 14 | 15 | /** 16 | * See Node.reduce. 17 | */ 18 | def reduce[T](n: Node)(f: (Node, Seq[T]) => T): T = { 19 | val subResults = n.subnodes.map(reduce[T](_)(f)) 20 | f(n, subResults) 21 | } 22 | 23 | /** 24 | * See Node.reduce. 25 | */ 26 | def reduce[C, R](n: Node)(context: C, enter: (Node, C) => C, combine: (Node, C, Seq[R]) => R): R = { 27 | val newContext = enter(n, context) 28 | val subResults = n.subnodes.map(reduce[C, R](_)(newContext, enter, combine)) 29 | combine(n, context, subResults) 30 | } 31 | 32 | /** 33 | * See Node.visit. 34 | */ 35 | def visit(n: Node)(f: PartialFunction[Node, Unit]): Unit = { 36 | if (f.isDefinedAt(n)) f(n) 37 | for (sub <- n.subnodes) { 38 | visit(sub)(f) 39 | } 40 | } 41 | 42 | /** 43 | * See Node.visit. 44 | */ 45 | def visit(n: Node, f1: PartialFunction[Node, Unit], f2: PartialFunction[Node, Unit]): Unit = { 46 | if (f1.isDefinedAt(n)) f1(n) 47 | for (sub <- n.subnodes) { 48 | visit(sub, f1, f2) 49 | } 50 | if (f2.isDefinedAt(n)) f2(n) 51 | } 52 | 53 | /** 54 | * See Node.visitOpt. 55 | */ 56 | def visitOpt(n: Node)(f: Node => Boolean): Unit = { 57 | if (f(n)) { 58 | for (sub <- n.subnodes) { 59 | visitOpt(sub)(f) 60 | } 61 | } 62 | } 63 | 64 | /** 65 | * See Node.visitOpt. 66 | */ 67 | def visitOpt(n: Node, f1: Node => Boolean, f2: Node => Unit): Unit = { 68 | if (f1(n)) { 69 | for (sub <- n.subnodes) { 70 | visitOpt(sub, f1, f2) 71 | } 72 | } 73 | f2(n) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultTypeModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.modules.{StatelessComponent, TypeModule} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie._ 12 | import viper.carbon.verifier.Verifier 13 | 14 | /** 15 | * The default implementation of a [[viper.carbon.modules.TypeModule]]. 16 | */ 17 | class DefaultTypeModule(val verifier: Verifier) extends TypeModule with StatelessComponent { 18 | 19 | import verifier._ 20 | import heapModule._ 21 | import domainModule._ 22 | import permModule._ 23 | import seqModule._ 24 | import setModule._ 25 | import mapModule._ 26 | 27 | def name = "Type module" 28 | override def translateType(t: sil.Type): Type = { 29 | t match { 30 | case sil.Bool => 31 | Bool 32 | case sil.Int => 33 | Int 34 | case sil.Ref => 35 | refType 36 | case sil.Perm => 37 | permType 38 | case t: sil.SeqType => 39 | translateSeqType(t) 40 | case t: sil.SetType => 41 | translateSetType(t) 42 | case t: sil.MultisetType => 43 | translateMultisetType(t) 44 | case t: sil.MapType => 45 | translateMapType(t) 46 | case sil.InternalType => 47 | sys.error("This is an internal type, not expected here") 48 | case sil.TypeVar(name) => 49 | TypeVar(name) 50 | case t@sil.DomainType(_, _) => 51 | translateDomainTyp(t) 52 | case sil.BackendType(_, interpretations) if interpretations.contains("Boogie") => NamedType(interpretations("Boogie")) 53 | case sil.BackendType(_, _) => sys.error("Found non-Boogie-compatible backend type.") 54 | case _ => sys.error("Viper type didn't match any existing case.") 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/StmtComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie._ 11 | import viper.silver.ast.LocalVar 12 | 13 | /** 14 | * Contributes to the translation of one or several statements. 15 | */ 16 | trait StmtComponent extends Component { 17 | 18 | /** 19 | * Potentially contributes to the translation of a statement. If no contribution 20 | * is desired, then [[viper.carbon.boogie.Statements.EmptyStmt]] can be used as a 21 | * return value. 22 | * The return is a function that shows how the returned statemenst should be merged with the translation for other components 23 | * e.g., Some statements is added to the beginning of the translation and others at the end 24 | * 25 | * 26 | * statesStackForPackageStmt: stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 27 | * insidePackageStmt: Boolean that represents whether this method is being called during packaging a wand or not. 28 | * allStateAssms: is a boolean expression that carries the conjunction for all the assumptions made about all states on 'statesStackForPackageStmt' 29 | * 30 | * These wand-related parameters (mentioned above) are used when translating statements during packaging a wand. 31 | * For more details refer to the general note in 'wandModule'. 32 | */ 33 | def handleStmt(s: sil.Stmt, statesStackOfPackageStmt: List[Any] = null, allStateAssms: Exp = TrueLit(), insidePackageStmt: Boolean = false): (Seqn => Seqn) 34 | 35 | /** 36 | * This method is called when translating a "fresh" statement, and by default does nothing 37 | */ 38 | def freshReads(fb: Seq[LocalVar]): Stmt = Statements.EmptyStmt 39 | } 40 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/MainModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie._ 11 | import viper.carbon.verifier.Environment 12 | import viper.silver.reporter.Reporter 13 | 14 | /** 15 | * A module for translating Viper programs by invoking the right modules and 16 | * gathering all the preambles, etc. 17 | */ 18 | trait MainModule extends Module { 19 | /** 20 | * Translate a Viper program into a Boogie program. 21 | * Returns a Boogie program along with a map that maps Viper names to their respective Boogie names, 22 | * i.e. Viper member name -> (Viper variable name -> Boogie variable name) 23 | */ 24 | def translate(p: sil.Program, reporter: Reporter): (Program, Map[String, Map[String, String]]) 25 | 26 | /** 27 | * Translate a local variable along with its type (into a boogie declaration). Assumes that the variable is already 28 | * defined in the current environment. 29 | */ 30 | def translateLocalVarSig(typ:sil.Type, v:sil.LocalVar): LocalVarDecl 31 | def translateLocalVarDecl(l: sil.LocalVarDecl): LocalVarDecl = { 32 | translateLocalVarSig(l.typ,l.localVar) 33 | } 34 | 35 | /** The current environment. */ 36 | var env: Environment = null 37 | 38 | /** The namespace for Viper local variables. */ 39 | def silVarNamespace: Namespace 40 | 41 | /** Used to encode assumptions made about valid values of a given type */ 42 | /** the "isParameter" flag can be used to select assumptions which only apply to parameters */ 43 | def allAssumptionsAboutValue(typ:sil.Type, arg: LocalVarDecl, isParameter: Boolean): Stmt 44 | def allAssumptionsAboutValue(arg:sil.LocalVarDecl, isParameter: Boolean) : Stmt = { 45 | allAssumptionsAboutValue(arg.typ,translateLocalVarDecl(arg),isParameter) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/TransferEntity.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.carbon.boogie.Exp 10 | import viper.silver.ast.LocationAccess 11 | import viper.silver.verifier.{PartialVerificationError, VerificationError, reasons} 12 | 13 | import viper.silver.{ast => sil} 14 | 15 | sealed trait TransferableEntity { 16 | def rcv: Exp 17 | def loc: Exp 18 | def transferAmount: Exp 19 | def originalSILExp: sil.Exp 20 | 21 | def transferError(error: PartialVerificationError):VerificationError 22 | } 23 | 24 | object TransferableEntity { 25 | def unapply(te: TransferableEntity) = Some((te.rcv,te.loc,te.transferAmount)) 26 | } 27 | 28 | sealed trait TransferableAccessPred extends TransferableEntity { 29 | override def transferError(error: PartialVerificationError):VerificationError = 30 | originalSILExp.loc match { 31 | case loc: LocationAccess => error.dueTo(reasons.InsufficientPermission(loc)) 32 | case other => sys.error(s"Unexpectedly found resource access node $other") 33 | } 34 | override def originalSILExp: sil.AccessPredicate 35 | } 36 | 37 | object TransferableAccessPred { 38 | def unapply(tap: TransferableAccessPred) = Some((tap.rcv,tap.loc,tap.transferAmount, tap.originalSILExp)) 39 | } 40 | 41 | case class TransferableFieldAccessPred(rcv: Exp, loc:Exp, transferAmount: Exp,originalSILExp: sil.AccessPredicate) extends TransferableAccessPred 42 | 43 | case class TransferablePredAccessPred(rcv: Exp, loc:Exp, transferAmount: Exp,originalSILExp: sil.AccessPredicate) extends TransferableAccessPred 44 | 45 | case class TransferableWand(rcv:Exp, loc: Exp, transferAmount: Exp,originalSILExp: sil.MagicWand) extends TransferableEntity { 46 | override def transferError(error: PartialVerificationError): VerificationError = { 47 | error.dueTo(reasons.MagicWandChunkNotFound(originalSILExp)) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/Module.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.components.{StatefulComponent, LifetimeComponent} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie.{LocalVar, Exp, Decl} 12 | import viper.carbon.verifier.Verifier 13 | 14 | /** 15 | * Common trait for modules. 16 | 17 | */ 18 | trait Module extends LifetimeComponent with StatefulComponent with viper.carbon.modules.components.Component { 19 | /** The verifier to interact with other modules. */ 20 | def verifier: Verifier 21 | 22 | /** 23 | * The name of this module, e.g., "Heap module" 24 | */ 25 | def name: String 26 | 27 | /** 28 | * Initialize this module and register any potential components with other modules. At the point 29 | * where this method is called, all modules in Verifier have been set, but the other modules 30 | * might not have been fully initialized (by calling this method). 31 | */ 32 | def start(): Unit = {} 33 | 34 | /** 35 | * Currently not used. 36 | */ 37 | def stop(): Unit = {} 38 | 39 | /** 40 | * The Boogie code that this module will insert into the preamble (optional). 41 | * 42 | * The convention is that the verifier will first translate the full Viper program, and only after the 43 | * translation ask all the modules to give preamble definitions. This allows modules to first see 44 | * if certain features are needed, and only output parts of the preamble that are used. 45 | */ 46 | def preamble: Seq[Decl] = Nil 47 | 48 | /** 49 | * The assumptions that can be made about a value of a given type (e.g. "isGoodMultiset" in the DefaultSetModule). 50 | * the "isParameter" flag can be used to select assumptions which only apply to parameters 51 | */ 52 | def validValue(typ: sil.Type, variable: LocalVar, isParameter: Boolean): Option[Exp] = None 53 | 54 | override def toString = name 55 | } 56 | 57 | 58 | /** 59 | * Trait to extend in modules which promise *not* to require a reset method 60 | */ 61 | trait StatelessComponent { 62 | final def reset = { } 63 | } -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/CarbonCounterexampleVariablesTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2025 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import viper.silver.reporter.StdIOReporter 10 | import viper.silver.testing.{CounterexampleComparison, CounterexampleVariablesTests, ExpectedCounterexample, ExpectedValuesCounterexampleAnnotation, OutputAnnotationId, TestCustomError, TestError} 11 | import viper.silver.verifier.{FailureContext, SimpleCounterexample} 12 | 13 | import java.nio.file.Path 14 | 15 | class CarbonCounterexampleVariablesTests extends AllTests with CounterexampleVariablesTests { 16 | override lazy val verifier: CarbonVerifier = { 17 | val carbon = CarbonVerifier(StdIOReporter()) 18 | carbon.parseCommandLine(commandLineArguments ++ Seq("--counterexample=variables", "dummy.vpr")) 19 | carbon 20 | } 21 | 22 | override def createExpectedValuesCounterexampleAnnotation(id: OutputAnnotationId, file: Path, forLineNr: Int, expectedCounterexample: ExpectedCounterexample): ExpectedValuesCounterexampleAnnotation = 23 | CarbonExpectedValuesCounterexampleAnnotation(id, file, forLineNr, expectedCounterexample) 24 | } 25 | 26 | /** represents an expected output (identified by `id`) with an associated (possibly partial) counterexample model */ 27 | case class CarbonExpectedValuesCounterexampleAnnotation(id: OutputAnnotationId, file: Path, forLineNr: Int, expectedCounterexample: ExpectedCounterexample) 28 | extends ExpectedValuesCounterexampleAnnotation(id, file, forLineNr, expectedCounterexample) { 29 | 30 | def containsExpectedCounterexample(failureContext: FailureContext): Boolean = 31 | failureContext.counterExample match { 32 | case Some(silCounterexample: SimpleCounterexample) => CounterexampleComparison.meetsExpectations(expectedCounterexample, silCounterexample.model) 33 | case _ => false 34 | } 35 | 36 | override def notFoundError: TestError = TestCustomError(s"Expected the following counterexample on line $forLineNr: $expectedCounterexample") 37 | 38 | override def withForLineNr(line: Int = forLineNr): ExpectedValuesCounterexampleAnnotation = CarbonExpectedValuesCounterexampleAnnotation(id, file, line, expectedCounterexample) 39 | } 40 | -------------------------------------------------------------------------------- /.github/workflows/update-submodules.yml: -------------------------------------------------------------------------------- 1 | # This Source Code Form is subject to the terms of the Mozilla Public 2 | # License, v. 2.0. If a copy of the MPL was not distributed with this 3 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | # 5 | # Copyright (c) 2011-2022 ETH Zurich. 6 | 7 | name: Update Submodules 8 | 9 | on: 10 | workflow_dispatch: # allow to manually trigger this workflow 11 | schedule: 12 | - cron: '0 6 * * *' # run every day at 06:00 UTC 13 | 14 | jobs: 15 | # Update silver and create a PR if there are any changes 16 | update: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Check out the repo 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | 24 | - name: Get current commits 25 | run: echo "PREV_SILVER_REF=$(git -C silver rev-parse HEAD)" >> $GITHUB_ENV 26 | 27 | - name: Update Silver submodule 28 | run: git checkout master && git pull 29 | working-directory: silver 30 | 31 | - name: Get new commits 32 | run: echo "CUR_SILVER_REF=$(git -C silver rev-parse HEAD)" >> $GITHUB_ENV 33 | 34 | - name: Create PR body 35 | run: | 36 | if [[ "${{ env.PREV_SILVER_REF }}" != "${{ env.CUR_SILVER_REF }}" ]]; then 37 | echo 'PR_BODY_LINE=* Updates Silver from `${{ env.PREV_SILVER_REF }}` to `${{ env.CUR_SILVER_REF }}`.' >> $GITHUB_ENV 38 | else 39 | echo 'PR_BODY_LINE=' >> $GITHUB_ENV 40 | fi 41 | 42 | - name: Open a pull request 43 | id: pr 44 | uses: peter-evans/create-pull-request@v7 45 | if: (env.PREV_SILVER_REF != env.CUR_SILVER_REF) 46 | with: 47 | # Use viper-admin's token to workaround a restriction of GitHub. 48 | # See: https://github.com/peter-evans/create-pull-request/issues/48 49 | token: ${{ secrets.UPDATE_SILVER }} 50 | commit-message: Updates submodules 51 | title: Update Submodules 52 | branch: auto-update-submodules 53 | delete-branch: true 54 | labels: | 55 | automated pr 56 | body: | 57 | ${{ env.PR_BODY_LINE1 }} 58 | 59 | 60 | - name: Enable auto-merge of PR 61 | if: (env.PREV_SILVER_REF != env.CUR_SILVER_REF) 62 | run: gh pr merge --merge --auto "${{ steps.pr.outputs.pull-request-number }}" 63 | env: 64 | GH_TOKEN: ${{ secrets.UPDATE_SILVER }} 65 | 66 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/verifier/Verifier.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.verifier 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.modules._ 11 | import viper.carbon.boogie.Namespace 12 | 13 | /** 14 | * A verifier for Viper in Carbon (defines what modules need to be available). 15 | */ 16 | trait Verifier { 17 | 18 | // All modules 19 | // Note: we use vals to make it possible to import methods from other modules for 20 | // convenience. 21 | val mainModule: MainModule 22 | val heapModule: HeapModule 23 | val stateModule: StateModule 24 | val stmtModule: StmtModule 25 | val expModule: ExpModule 26 | val typeModule: TypeModule 27 | val exhaleModule: ExhaleModule 28 | val inhaleModule: InhaleModule 29 | val funcPredModule: FuncPredModule 30 | val permModule: PermModule 31 | val domainModule: DomainModule 32 | val seqModule: SeqModule 33 | val setModule: SetModule 34 | val mapModule: MapModule 35 | val wandModule: WandModule 36 | val loopModule: LoopModule 37 | 38 | /** 39 | * A list of all modules. 40 | */ 41 | lazy val allModules: Seq[Module] = { 42 | Seq(mainModule, stateModule, heapModule, permModule, stmtModule, expModule, typeModule, 43 | exhaleModule, inhaleModule, funcPredModule, domainModule, seqModule, setModule, 44 | loopModule, mapModule, wandModule) 45 | } ensuring { 46 | mods => true 47 | } 48 | 49 | /** 50 | * Debug information (e.g., the full command used to invoke this verification). 51 | */ 52 | def getDebugInfo: Seq[(String, Any)] 53 | 54 | /** 55 | * The tool (including version) used for this translation. 56 | */ 57 | def toolDesc: String 58 | 59 | /** 60 | * A descriptive string for every dependency 61 | */ 62 | def dependencyDescs: Seq[String] 63 | 64 | /** 65 | * Create a new namespace with a unique ID. 66 | */ 67 | def freshNamespace(name: String): Namespace 68 | 69 | /** 70 | * The program currently being verified. 71 | */ 72 | def program: sil.Program 73 | 74 | /** 75 | * Replace the program with the provided one (for instance, to achieve whole-program transformations, including updating lookups of method definitions etc.) 76 | */ 77 | def replaceProgram(prog : sil.Program): Unit 78 | 79 | def assumeInjectivityOnInhale: Boolean 80 | 81 | def usePolyMapsInEncoding: Boolean 82 | 83 | def respectFunctionPrecPermAmounts: Boolean 84 | 85 | } 86 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/ComponentRegistry.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.carbon.utility.PartialSort 10 | import scala.collection.mutable 11 | 12 | /** 13 | * A trait to allow registering of components. 14 | 15 | */ 16 | trait ComponentRegistry[C <: Component] { 17 | 18 | var _components: Seq[C] = Nil 19 | val beforePairs: mutable.HashSet[(Component,Component)] = mutable.HashSet.empty[(Component,Component)] 20 | 21 | /** All currently registered components. */ 22 | /** NOTE: this should only be called once all components have been added! */ 23 | lazy val components: Seq[C] = { 24 | // complete transitive closure of beforePairs (Warshall's algorithm) 25 | for(c <- _components) { 26 | for (x <- _components; y <- _components if x != c && y != c && beforePairs.contains(x,c) && beforePairs.contains(c,y)) { 27 | beforePairs.add((x,y)) 28 | } 29 | } 30 | PartialSort.sort(_components, ordering) 31 | } 32 | 33 | /** Register a new component. */ 34 | /* If C should be before c then c goes in the "before" sequence. If C should be after c, then c goes in the "after" sequence. Note that this used to be the other way around. */ 35 | def register(component: C, before: Seq[Component] = Nil, after: Seq[Component] = Nil): Unit = { 36 | _components = _components :+ component 37 | beforePairs.add(component,component) 38 | for(c <- before) beforePairs.add((component,c)) 39 | for(c <- after) beforePairs.add((c,component)) 40 | } 41 | 42 | /** The partial ordering of components based on before and after. */ 43 | def ordering: PartialOrdering[C] = new PartialOrdering[C] { 44 | def tryCompare(x: C, y: C): Option[Int] = { 45 | if (x == y) Some(0) 46 | else if (lteq(x, y)) (if (lteq(y,x)) sys.error("Internal Error: Carbon modules are incorrectly configured - module " + x.toString + " is in cyclic ordering with module " + y.toString + "\n" + "The complete ordering was: " + beforePairs.toString) else Some(-1)) 47 | else if (lteq(y, x)) Some(1) 48 | else None 49 | } 50 | def lteq(x: C, y: C): Boolean = { 51 | if (x == y) return true 52 | if (beforePairs.contains((x,y))) (if(beforePairs.contains((y,x))) sys.error("Internal Error: Carbon modules are incorrectly configured - module " + x.toString + " is in cyclic ordering with module " + y.toString + "\n" + "The complete ordering was: " + beforePairs.toString) else return true) 53 | false 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/CarbonQuantifierWeightTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2022 ETH Zurich. 6 | 7 | import org.scalatest.BeforeAndAfterAll 8 | import org.scalatest.funsuite.AnyFunSuite 9 | import viper.carbon.boogie.PrettyPrinter 10 | import viper.carbon.verifier.Environment 11 | import viper.carbon.CarbonVerifier 12 | import viper.silver.ast.{Add, AnonymousDomainAxiom, Domain, DomainFunc, DomainFuncApp, EqCmp, Exists, Forall, Int, IntLit, LocalVar, LocalVarDecl, Method, Program, Seqn, Trigger, TrueLit, WeightedQuantifier} 13 | import viper.silver.reporter.NoopReporter 14 | import viper.silver.verifier.{Failure, Success} 15 | 16 | class CarbonQuantifierWeightTests extends AnyFunSuite with BeforeAndAfterAll { 17 | val carbon: CarbonVerifier = CarbonVerifier(NoopReporter) 18 | 19 | override def beforeAll() { 20 | carbon.parseCommandLine(Seq("dummy.vpr")) 21 | carbon.start() 22 | } 23 | 24 | override def afterAll() { 25 | carbon.stop() 26 | } 27 | 28 | test("The quantifier weight inhibits instantiations") { 29 | def verifyUsingWeight(weight: Int) = { 30 | val domainName = "MyDomain" 31 | val xDecl = LocalVarDecl("x", Int)() 32 | val x = LocalVar("x", Int)() 33 | val f = DomainFunc("f", Seq(xDecl), Int)(domainName = domainName) 34 | val axiom = AnonymousDomainAxiom(Forall( 35 | Seq(xDecl), 36 | Seq(Trigger(Seq(DomainFuncApp(f, Seq(x), Map())()))()), 37 | EqCmp(DomainFuncApp(f, Seq(Add(x, IntLit(1)())()), Map())(), IntLit(42)())(), 38 | )( 39 | // Set the weight of the quantifier 40 | info = WeightedQuantifier(weight) 41 | ))(domainName = domainName) 42 | val domain = Domain(domainName, Seq(f), Seq(axiom))() 43 | val pre = EqCmp(DomainFuncApp(f, Seq(IntLit(0)()), Map())(), IntLit(42)())() 44 | val post = EqCmp(DomainFuncApp(f, Seq(IntLit(50)()), Map())(), IntLit(42)())() 45 | val method = Method("test", Seq(LocalVarDecl("x", Int)()), Seq(), Seq(pre), Seq(post), Some(Seqn(Seq(), Seq())()))() 46 | val program = Program(Seq(domain), Seq(), Seq(), Seq(), Seq(method), Seq())() 47 | carbon.verify(program) 48 | } 49 | 50 | // A small weight should allow the axiom to be instantiated 51 | verifyUsingWeight(1) match { 52 | case Success => // Ok 53 | case Failure(errors) => assert(false) 54 | } 55 | 56 | // A big weight should prevent the axiom from being instantiated 57 | verifyUsingWeight(999) match { 58 | case Success => assert(false) 59 | case Failure(errors) => // Ok 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Carbon CI 2 | 3 | on: 4 | push: 5 | branches: [ master, staging, trying ] 6 | pull_request: 7 | branches: [ master ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | container: gobraverifier/gobra-base:v5_z3_4.8.7 # Thank you, Gobra team 15 | env: 16 | BOOGIE_EXE: "boogie/Boogie" 17 | LINUX_BOOGIE_URL: "https://github.com/viperproject/boogie-builder/releases/download/7449a7a899c02c95/boogie-linux.zip" 18 | steps: 19 | - name: Checkout Carbon 20 | uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | - name: Java Version 24 | run: java --version 25 | - name: Z3 Version 26 | run: z3 -version 27 | - name: Create version file 28 | run: | 29 | echo "Carbon: commit $(git -C . rev-parse HEAD)" >> versions.txt 30 | echo "Silver: commit $(git -C silver rev-parse HEAD)" >> versions.txt 31 | # first line overwrites versions.txt in case it already exists, all other append to the file 32 | - name: Upload version file 33 | uses: actions/upload-artifact@v4 34 | with: 35 | name: versions-${{ matrix.name }}.txt 36 | path: versions.txt 37 | 38 | - name: Set sbt cache variables 39 | run: echo "SBT_OPTS=-Dsbt.global.base=sbt-cache/.sbtboot -Dsbt.boot.directory=sbt-cache/.boot -Dsbt.ivy.home=sbt-cache/.ivy" >> $GITHUB_ENV 40 | # note that the cache path is relative to the directory in which sbt is invoked. 41 | 42 | - name: Cache SBT 43 | uses: actions/cache@v4 44 | with: 45 | path: | 46 | sbt-cache/.sbtboot 47 | sbt-cache/.boot 48 | sbt-cache/.ivy/cache 49 | # project/target and target, are intentionally not included as several occurrences 50 | # of NoSuchMethodError exceptions have been observed during CI runs. It seems 51 | # like sbt is unable to correctly compute source files that require a recompilation. Therefore, we have 52 | # disabled caching of compiled source files altogether 53 | key: ${{ runner.os }}-sbt-no-precompiled-sources-${{ hashFiles('**/build.sbt') }} 54 | - name: Download Boogie 55 | run: | 56 | curl --fail --silent --show-error -L ${{ env.LINUX_BOOGIE_URL }} --output boogie-linux.zip 57 | unzip boogie-linux.zip 58 | rm -rf boogie-linux.zip 59 | mv binaries-linux boogie 60 | boogie/Boogie /version 61 | pwd 62 | 63 | - name: Test Carbon 64 | run: sbt test 65 | 66 | - name: Assemble Carbon fat JAR 67 | run: sbt "set test in assembly := {}" clean assembly 68 | 69 | - name: Upload Carbon fat JAR 70 | uses: actions/upload-artifact@v4 71 | with: 72 | name: carbon-jar 73 | path: target/scala-2.13/carbon.jar 74 | retention-days: 14 75 | if-no-files-found: error 76 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/sequence_axioms/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Microsoft Public License (Ms-PL) 2 | 3 | This license governs use of the accompanying software. If you use the 4 | software, you accept this license. If you do not accept the license, do 5 | not use the software. 6 | 7 | 1. Definitions 8 | 9 | The terms "reproduce," "reproduction," "derivative works," and 10 | "distribution" have the same meaning here as under U.S. copyright law. 11 | 12 | A "contribution" is the original software, or any additions or changes 13 | to the software. 14 | 15 | A "contributor" is any person that distributes its contribution under 16 | this license. 17 | 18 | "Licensed patents" are a contributor's patent claims that read directly 19 | on its contribution. 20 | 21 | 2. Grant of Rights 22 | 23 | (A) Copyright Grant- Subject to the terms of this license, including the 24 | license conditions and limitations in section 3, each contributor grants 25 | you a non-exclusive, worldwide, royalty-free copyright license to 26 | reproduce its contribution, prepare derivative works of its 27 | contribution, and distribute its contribution or any derivative works 28 | that you create. 29 | 30 | (B) Patent Grant- Subject to the terms of this license, including the 31 | license conditions and limitations in section 3, each contributor grants 32 | you a non-exclusive, worldwide, royalty-free license under its licensed 33 | patents to make, have made, use, sell, offer for sale, import, and/or 34 | otherwise dispose of its contribution in the software or derivative 35 | works of the contribution in the software. 36 | 37 | 3. Conditions and Limitations 38 | 39 | (A) No Trademark License- This license does not grant you rights to use 40 | any contributors' name, logo, or trademarks. 41 | 42 | (B) If you bring a patent claim against any contributor over patents 43 | that you claim are infringed by the software, your patent license from 44 | such contributor to the software ends automatically. 45 | 46 | (C) If you distribute any portion of the software, you must retain all 47 | copyright, patent, trademark, and attribution notices that are present 48 | in the software. 49 | 50 | (D) If you distribute any portion of the software in source code form, 51 | you may do so only under this license by including a complete copy of 52 | this license with your distribution. If you distribute any portion of 53 | the software in compiled or object code form, you may only do so under a 54 | license that complies with this license. 55 | 56 | (E) The software is licensed "as-is." You bear the risk of using it. The 57 | contributors give no express warranties, guarantees or conditions. You 58 | may have additional consumer rights under your local laws which this 59 | license cannot change. To the extent permitted under your local laws, 60 | the contributors exclude the implied warranties of merchantability, 61 | fitness for a particular purpose and non-infringement. 62 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/ExpModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{Exp, LocalVar, Stmt} 11 | import viper.carbon.modules.components.{ComponentRegistry, DefinednessComponent, DefinednessState} 12 | import viper.silver.verifier.PartialVerificationError 13 | 14 | /** 15 | * A module for translating Viper expressions. 16 | */ 17 | trait ExpModule extends Module with ComponentRegistry[DefinednessComponent] { 18 | def translateExp(exp: sil.Exp): Exp 19 | 20 | /** 21 | * Used to translate expressions during packaging a wand. 22 | * This method Prepares the correct state in which translating an expression takes place then calls translateExp 23 | */ 24 | def translateExpInWand(exp: sil.Exp): Exp 25 | 26 | def translateLocalVar(l: sil.LocalVar): LocalVar 27 | 28 | /** 29 | * Free assumptions about an expression. 30 | */ 31 | def allFreeAssumptions(e: sil.Exp): Stmt 32 | 33 | /*** 34 | * Check well-definedness of Viper expressions. This method should only be invoked on pure Viper expressions or 35 | * impure *atomic* Viper assertions (e.g., accessibility predicates, quantified permissions). 36 | * For other kinds of impure assertions such as separating conjunctions where one conjunct is impure, well-definedness 37 | * is always tied to an inhale or exhale (or assert) operation. In those cases, the corresponding inhale and exhale 38 | * methods should be invoked, which permit switching on well-definedness checks. 39 | * 40 | * @param e 41 | * @param error 42 | * @param makeChecks provides the possibility of switching off most checks, to get only the side-effects (unfoldings) 43 | * of unravelling the expression. Note that the parameter should be passed down through recursive 44 | * calls (default true) 45 | * @param definednessStateOpt If defined, then represents the state in which permission checks that are part of the definedness 46 | * check should be made, otherwise these checks should be made in the currently active state. 47 | * Expressions should be evaluated in the currently active state. 48 | * @param insidePackageStmt true if call is made during a package statement 49 | * @param ignoreIfInWand gives the option to ignore the definedness check if it is called during a package statement 50 | * @return 51 | */ 52 | def checkDefinedness(e: sil.Exp, error: PartialVerificationError, makeChecks: Boolean = true, 53 | definednessStateOpt: Option[DefinednessState] = None, 54 | insidePackageStmt: Boolean = false, ignoreIfInWand: Boolean = false): Stmt 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/CarbonStateComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.carbon.boogie._ 10 | 11 | /** 12 | * The [[viper.carbon.modules.StateModule]] allows to register state components that 13 | * contribute to the state of the execution of the program. In the rest of this class, 14 | * we use 'state' to refer to the program state during execution. 15 | 16 | */ 17 | trait CarbonStateComponent extends Component { 18 | 19 | /** 20 | * The statements necessary to initialize the part of the state belonging to this module. 21 | */ 22 | def initBoogieState: Stmt 23 | 24 | /** 25 | * The statements necessary to reset the part of the state belonging to this module. 26 | */ 27 | def resetBoogieState: Stmt 28 | 29 | /** 30 | * The name and type of the static contribution of this component to the state. The returned value should remain the 31 | * same even if the state is changed. 32 | */ 33 | def staticStateContributions(withHeap: Boolean, withPermissions: Boolean): Seq[LocalVarDecl] 34 | 35 | 36 | /** 37 | * The name and type of the current contribution of this component to the state. The numbers of elements in the list and 38 | * the types must correspond to the ones given in `staticStateContributions`. 39 | */ 40 | def currentStateContributions: Seq[LocalVarDecl] 41 | 42 | 43 | /** 44 | * The current values for this components state contributions. The number of elements 45 | * in the list and the types must correspond to the ones given in `staticStateContributions`. 46 | * 47 | * NOTE: these variables may need wrapping in Old(.) when used, as according to usingOldState etc. To do this wrapping internally, call instead currentStateExps below 48 | */ 49 | def currentStateVars: Seq[Var] 50 | 51 | /** 52 | * The current values for this components state contributions, adjusted for "old". The number of elements 53 | * in the list and the types must correspond to the ones given in `stateContributions`. 54 | */ 55 | def currentStateExps: Seq[Exp] 56 | 57 | /** 58 | * Set up a fresh temporary state and returns that new state. 59 | */ 60 | def freshTempState(name: String): Seq[Var] 61 | 62 | /** 63 | * Throw away the current state and go back to a snapshot. 64 | */ 65 | def restoreState(previousState: Seq[Var]): Unit 66 | 67 | /** 68 | * Are we currently using an "old" state? Note: this is mainly as documentation that the states passed to other methods above will need wrapping in "olD2 when *used*, if we are currently using an old state. This method would typically be implemented by querying the corresponding StateModule 69 | * 70 | * Note in particular that variable passed in via "replaceState" above will typically need wrapping in old(.) if this is set to true 71 | */ 72 | def usingOldState: Boolean 73 | 74 | /** 75 | * Are we currently using a pure state without a heap (i.e., translating a domain)? 76 | */ 77 | def usingPureState: Boolean 78 | } 79 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/InhaleModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import components.{ComponentRegistry, InhaleComponent} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie.Stmt 12 | import viper.silver.ast.utility.Expressions.{whenExhaling, whenInhaling} 13 | import viper.silver.verifier.PartialVerificationError 14 | 15 | /** 16 | * A module for translating inhale statements. The module takes care of the basic 17 | * structure of inhaling as well as inhaling boolean connectives 18 | * such as logical and or logical implication. Other modules can register themselves 19 | * as [[viper.carbon.modules.components.InhaleComponent]]s to perform the inhale 20 | * operation of certain expressions. 21 | * 22 | * The module also implements [[viper.carbon.modules.components.InhaleComponent]] 23 | * and performs some default behavior for most expressions. 24 | 25 | */ 26 | trait InhaleModule extends Module with InhaleComponent with ComponentRegistry[InhaleComponent] { 27 | 28 | /** 29 | * Inhale assertions and pure expressions with potential definedness checks made along the way. 30 | * Special case: If definedness checks are added and if the input Viper state has no permissions, then the returned 31 | * Boogie statement checks whether the input assertions are self-framing. 32 | * @param exps 33 | * @param addDefinednessChecks enables definedness checks iff set to true 34 | * @param statesStackForPackageStmt stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 35 | * @param insidePackageStmt indicates whether this method is being called during a package of a wand (true) or not (false) 36 | * @return 37 | */ 38 | def inhale(exps: Seq[(sil.Exp, PartialVerificationError)], addDefinednessChecks: Boolean, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt 39 | 40 | def inhaleWithDefinednessCheck(exp: sil.Exp, error: PartialVerificationError, statesStack: List[Any] = null, inWand: Boolean = false): Stmt = 41 | inhale(Seq((exp, error)), addDefinednessChecks = true, statesStack, inWand) 42 | 43 | /** 44 | * Convert all InhaleExhale expressions to their exhale part and inhale with definedness checks. 45 | */ 46 | def inhaleExhaleSpecWithDefinednessCheck(expressions: Seq[sil.Exp], 47 | errorConstructor: sil.Exp => PartialVerificationError): Seq[Stmt] = { 48 | expressions map (e => { 49 | inhaleWithDefinednessCheck(whenExhaling(e), errorConstructor(e)) 50 | }) 51 | } 52 | 53 | /** 54 | * Convert all InhaleExhale expressions to their inhale part and inhale with definedness checks. 55 | */ 56 | def inhaleInhaleSpecWithDefinednessCheck(expressions: Seq[sil.Exp], 57 | errorConstructor: sil.Exp => PartialVerificationError): Seq[Stmt] = { 58 | expressions map (e => { 59 | inhaleWithDefinednessCheck(whenInhaling(e), errorConstructor(e)) 60 | }) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/FuncPredModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie._ 11 | 12 | import scala.collection.mutable 13 | 14 | /** 15 | * A module for translating functions and predicates. 16 | 17 | */ 18 | trait FuncPredModule extends Module { 19 | /* Translate a function. If the second parameter is defined, also record the Boogie names of all translated Viper 20 | * variables to the given map. 21 | */ 22 | def translateFunction(f: sil.Function, names: Option[mutable.Map[String, String]]): Seq[Decl] 23 | 24 | def translateFuncApp(fa: sil.FuncApp): Exp 25 | 26 | // wrap an expression in a dummy function with "true" value (sometimes useful for triggering) 27 | def dummyFuncApp(e: Exp): Exp 28 | 29 | /* Translate a predicate. If the second parameter is defined, also record the Boogie names of all translated Viper 30 | * variables to the given map. 31 | */ 32 | def translatePredicate(p: sil.Predicate, names: Option[mutable.Map[String, String]]): Seq[Decl] 33 | 34 | def predicateVersionType : Type 35 | 36 | def assumeAllFunctionDefinitions: Stmt 37 | 38 | def translateResult(r: sil.Result): Exp 39 | 40 | // code to go first, and code to go last (other modules may contribute in between) 41 | /** 42 | * statesStackForPackageStmt: stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 43 | * insidePackageStmt: Boolean that represents whether this method is being called during packaging a wand or not. 44 | * The 'statesStackForPackageStmt' and 'insidePackageStmt' are used when translating statements during packaging a wand. 45 | * For more details refer to the general note in 'wandModule'. 46 | */ 47 | def translateFold(fold: sil.Fold, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): (Stmt,Stmt) 48 | 49 | /** 50 | * statesStackForPackageStmt: stack of states used in translating statements during packaging a wand (carries currentState and LHS of wands) 51 | * insidePackageStmt: Boolean that represents whether this method is being called during packaging a wand or not. 52 | * The 'statesStackForPackageStmt' and 'insidePackageStmt' are used when translating statements during packaging a wand. 53 | * For more details refer to the general note in 'wandModule'. 54 | */ 55 | def translateUnfold(unfold: sil.Unfold, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt 56 | 57 | def toExpressionsUsedInTriggers(e: Exp): Seq[Exp] 58 | def toExpressionsUsedInTriggers(e: Seq[Exp]): Seq[Seq[Exp]] 59 | def rewriteTriggersToExpressionsUsedInTriggers(e: Seq[Trigger]) : Seq[Trigger] = 60 | e flatMap (t => (toExpressionsUsedInTriggers(t.exps)) 61 | map (Trigger(_)) // build a trigger for each sequence element returned (in general, one original trigger can yield multiple alternative new triggers) 62 | ) 63 | 64 | 65 | def translateBackendFuncApp(fa: sil.BackendFuncApp): Exp 66 | def translateBackendFunc(f: sil.DomainFunc): Seq[Decl] 67 | } 68 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/BoogieModelTransformer.scala: -------------------------------------------------------------------------------- 1 | package viper.carbon.boogie 2 | 3 | import viper.carbon.verifier.FailureContextImpl 4 | import viper.silver.verifier.{AbstractError, Counterexample, FailureContext, Model, ModelEntry, SimpleCounterexample, VerificationError} 5 | 6 | import scala.collection.mutable 7 | 8 | /** 9 | * Transforms a counterexample returned by Boogie back to a Viper counterexample. 10 | */ 11 | object BoogieModelTransformer { 12 | 13 | /** 14 | * Adds a counterexample to the given error if one is available. 15 | */ 16 | def transformCounterexample(e: AbstractError, names: Map[String, Map[String, String]]) : Unit = { 17 | if (e.isInstanceOf[VerificationError] && ErrorMemberMapping.mapping.contains(e.asInstanceOf[VerificationError])){ 18 | val ve = e.asInstanceOf[VerificationError] 19 | val methodName = ErrorMemberMapping.mapping.get(ve).get.name 20 | val namesInMember = names.get(methodName).get.map(e => e._2 -> e._1) 21 | val originalEntries = ve.failureContexts(0).counterExample.get.model.entries 22 | 23 | val model = transformModelEntries(originalEntries, namesInMember) 24 | ve.failureContexts = Seq(FailureContextImpl(Some(SimpleCounterexample(model)))) 25 | } 26 | } 27 | 28 | /** 29 | * 30 | * @param originalEntries Entries of the counterexample produced by Boogie 31 | * @param namesInMember Mapping from Boogie names to Viper names for the Viper member belonging to this error. 32 | * @return 33 | */ 34 | def transformModelEntries(originalEntries: Map[String, ModelEntry], namesInMember: Map[String, String]) : Model = { 35 | val newEntries = mutable.HashMap[String, ModelEntry]() 36 | val currentEntryForName = mutable.HashMap[String, String]() 37 | for ((vname, e) <- originalEntries) { 38 | var originalName = vname 39 | if (originalName.startsWith("q@")){ 40 | originalName = originalName.substring(2) 41 | } else if (originalName.indexOf("@@") != -1){ 42 | originalName = originalName.substring(0, originalName.indexOf("@@")) 43 | } else if (originalName.indexOf("@") != -1){ 44 | originalName = originalName.substring(0, originalName.indexOf("@")) 45 | } 46 | if (PrettyPrinter.backMap.contains(originalName)){ 47 | val originalViperName = PrettyPrinter.backMap.get(originalName).get 48 | if (namesInMember.contains(originalViperName)){ 49 | val viperName = namesInMember.get(originalViperName).get 50 | if (!currentEntryForName.contains(viperName) || 51 | isLaterVersion(vname, originalName, currentEntryForName.get(viperName).get)){ 52 | newEntries.update(viperName, e) 53 | currentEntryForName.update(viperName, vname) 54 | } 55 | } 56 | } 57 | } 58 | Model(newEntries.toMap) 59 | } 60 | 61 | /** 62 | * Heuristically (based on Boogie's naming practices) checks if, of the two Boogie versions of the given 63 | * original Viper variable name, the first denotes a "later" version (as in, occurring later in the program, 64 | * therefore containing a more recent value). 65 | */ 66 | def isLaterVersion(firstName: String, originalName: String, secondName: String) : Boolean = { 67 | if (secondName == originalName || secondName == "q@" + originalName || secondName.indexOf("@@") != -1){ 68 | true 69 | } else if (secondName.indexOf("@") != -1 && firstName.indexOf("@@") == -1 && firstName.indexOf("@") != -1) { 70 | val firstIndex = Integer.parseInt(firstName.substring(firstName.indexOf("@") + 1)) 71 | val secondIndex = Integer.parseInt(secondName.substring(secondName.indexOf("@") + 1)) 72 | firstIndex > secondIndex 73 | } else { 74 | false 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/components/DefinednessComponent.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.components 8 | 9 | import viper.carbon.boogie.{Statements, Stmt} 10 | import viper.silver.{ast => sil} 11 | import viper.silver.verifier.PartialVerificationError 12 | 13 | /** 14 | * Takes care of determining whether expressions are well-formed. 15 | */ 16 | trait DefinednessComponent extends Component { 17 | 18 | /** 19 | * Free assumptions about an expression. 20 | */ 21 | def freeAssumptions(e: sil.Exp): Stmt = Statements.EmptyStmt 22 | 23 | /** 24 | * Well-definedness check for `e` itself (not its subnodes). This check is invoked *before* invoking the 25 | * well-definedness checks for `e`'s subnodes. The resulting Boogie encoding is also emitted *before* the encoding for 26 | * the well-definedness checks for `e`'s subnodes. 27 | * If `makeChecks` is set to false, then no definedness checks should be emitted. See [[partialCheckDefinedness]] 28 | * for the purpose of `makeChecks`. 29 | * If `definednessStateOpt` is defined, then it represents the state in which permission checks that are part of the 30 | * definedness check should be made, otherwise these checks should be done in the currently active state. 31 | * Expressions should be evaluated in the currently active state. 32 | */ 33 | def simplePartialCheckDefinednessBefore(e: sil.Exp, error: PartialVerificationError, makeChecks: Boolean, 34 | definednessStateOpt: Option[DefinednessState]): Stmt = Statements.EmptyStmt 35 | 36 | /** 37 | * Same as [[simplePartialCheckDefinednessBefore]], except that this well-definedness check is invoked and emitted 38 | * *after* the well-definedness checks of `e`'s subnodes are invoked and emitted. 39 | */ 40 | def simplePartialCheckDefinednessAfter(e: sil.Exp, error: PartialVerificationError, makeChecks: Boolean, 41 | definednessStateOpt: Option[DefinednessState]): Stmt = Statements.EmptyStmt 42 | 43 | /** 44 | * Proof obligations for a given expression. The first part of the result is used before 45 | * visiting all subexpressions, then all subexpressions are checked for definedness, and finally 46 | * the second part of the result is used. 47 | * 48 | * The makeChecks argument can be set to false to cause the expression to be explored (and 49 | * corresponding unfoldings to be executed), but no other checks to actually be made. Note that this method 50 | * must be overridden for this parameter to be used. 51 | * If `definednessStateOpt` is defined, then it represents the state in which permission checks that are part of the 52 | * definedness check should be made, otherwise these checks should be done in the currently active state. 53 | * Expressions should be evaluated in the currently active state. 54 | */ 55 | def partialCheckDefinedness(e: sil.Exp, error: PartialVerificationError, makeChecks: Boolean, 56 | definednessStateOpt: Option[DefinednessState]): (() => Stmt, () => Stmt) = 57 | ( 58 | () => simplePartialCheckDefinednessBefore(e, error, makeChecks, definednessStateOpt), 59 | () => simplePartialCheckDefinednessAfter(e, error, makeChecks, definednessStateOpt) 60 | ) 61 | 62 | } 63 | 64 | /** 65 | * Wrapper for representing the definedness state. 66 | * @param setDefState Running this should set the currently active state to the definedness state. 67 | */ 68 | case class DefinednessState(var setDefState: () => Unit) 69 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultDomainModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.modules.{DomainModule, StatelessComponent} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie._ 12 | import viper.carbon.verifier.{Environment, Verifier} 13 | import viper.carbon.boogie.Implicits._ 14 | import viper.silver.ast.NamedDomainAxiom 15 | 16 | /** 17 | * The default implementation of [[viper.carbon.modules.DomainModule]]. 18 | */ 19 | class DefaultDomainModule(val verifier: Verifier) extends DomainModule with StatelessComponent { 20 | 21 | import verifier._ 22 | import typeModule._ 23 | import expModule._ 24 | import mainModule._ 25 | 26 | def name = "Domain module" 27 | 28 | implicit val namespace = verifier.freshNamespace("domain") 29 | 30 | // name for output identifier (to try to avoid clashes - should be improved for robustness (see issue #19) 31 | def outputName(domain: sil.Domain) : String = domain.name + "DomainType" 32 | 33 | override def translateDomain(domain: sil.Domain): Seq[Decl] = { 34 | val prevState = stateModule.state 35 | stateModule.replaceState(stateModule.pureState) 36 | val fs = domain.functions.filter(f => f.interpretation.isEmpty) flatMap translateDomainFunction 37 | val as = domain.axioms flatMap translateDomainAxiom 38 | val ts = CommentedDecl(s"The type for domain ${domain.name}", 39 | TypeDecl(NamedType(this.outputName(domain) , domain.typVars map (tv => TypeVar(tv.name)))), size = 1) 40 | stateModule.replaceState(prevState) 41 | CommentedDecl(s"Translation of domain ${domain.name}", ts ++ fs ++ as, nLines = 2) 42 | } 43 | 44 | private def translateDomainFunction(f: sil.DomainFunc): Seq[Decl] = { 45 | env = Environment(verifier, f) 46 | val t = translateType(f.typ) 47 | val res = if (f.unique) { 48 | val func = ConstDecl(Identifier(f.name), t, unique = true) 49 | MaybeCommentedDecl(s"Translation of domain unique function ${f.name}", func, size = 1) 50 | } else { 51 | val args = f.formalArgs map (x => LocalVarDecl(Identifier(if (x.isInstanceOf[sil.LocalVarDecl]) x.asInstanceOf[sil.LocalVarDecl].name else env.uniqueName(f.name + "_param")), translateType(x.typ))) 52 | val func = Func(Identifier(f.name), args, t) 53 | MaybeCommentedDecl(s"Translation of domain function ${f.name}", func, size = 1) 54 | } 55 | env = null 56 | res 57 | } 58 | 59 | private def translateDomainAxiom(axiom: sil.DomainAxiom): Seq[Decl] = { 60 | env = Environment(verifier, axiom) 61 | //(AS): I believe this is not needed, as locals are introduced in the translation 62 | //mainModule.defineLocalVars(axiom) 63 | val res = MaybeCommentedDecl(if (axiom.isInstanceOf[NamedDomainAxiom]) 64 | (s"Translation of domain axiom ${axiom.asInstanceOf[NamedDomainAxiom].name}") 65 | else 66 | (s"Translation of anonymous domain axiom") 67 | , Axiom(translateExp(axiom.exp)), size = 1) 68 | //mainModule.undefineLocalVars(axiom) 69 | env = null 70 | res 71 | } 72 | 73 | override def translateDomainFuncApp(fa: sil.DomainFuncApp): Exp = { 74 | val funct = verifier.program.findDomainFunction(fa.funcname) 75 | if (funct.unique) { 76 | Const(Identifier(funct.name)) 77 | } else { 78 | val res = FuncApp(Identifier(funct.name), fa.args map translateExp, translateType(fa.typ)) 79 | res.showReturnType = true 80 | res 81 | } 82 | } 83 | 84 | override def translateDomainTyp(typ: sil.DomainType): Type = { 85 | val domain = verifier.program.findDomain(typ.domainName) 86 | val typArgs = domain.typVars map (tv => typ.typVarsMap.getOrElse(tv, tv)) 87 | NamedType(this.outputName(domain), typArgs map translateType) 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/test/scala/viper/carbon/PortableCarbonTests.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import java.nio.file.Path 10 | 11 | import org.scalatest.DoNotDiscover 12 | import viper.silver.frontend.Frontend 13 | import viper.silver.logger.SilentLogger 14 | import viper.silver.reporter.NoopReporter 15 | import viper.silver.testing.{SilSuite, StatisticalTestSuite} 16 | import viper.silver.verifier.Verifier 17 | 18 | 19 | /** This test mechanism is intended for running non-default test suites, 20 | * in a portable way. Example run command: 21 | * 22 | * ``` 23 | * Z3_EXE=z3.exe 24 | * BOOGIE_EXE=Boogie.exe 25 | * sbt "test:runMain 26 | * -DCARBONTESTS_TARGET=./target 27 | * -DCARBONTESTS_WARMUP=./warmup 28 | * -DCARBONTESTS_REPETITIONS=5 29 | * -DCARBONTESTS_CSV=data.csv 30 | * org.scalatest.tools.Runner 31 | * -o -s 32 | * viper.carbon.PortableCarbonTests" 33 | * ``` 34 | * 35 | * The command above will: 36 | * 1. Warm-up the JVM by verifying all .vpr files in ./warmup 37 | * 2. Measure time of 5 runs of each .vpr file in ./target 38 | * 3. Discard ("trim") the slowest and the fastest runs and compute 39 | * - the mean 40 | * - absolute and relative standard deviation 41 | * - the best 42 | * - the median 43 | * - the worst 44 | * run times of all these tests, and 45 | * 4. Print the timing info (per phase) into STDOUT, and write mean and standard deviation 46 | * to file data.csv 47 | * 5. Create JAR files (e.g., target/scala-2.12/silicon_2.12-1.1-SNAPSHOT.jar, 48 | * target/scala-2.12/silicon_2.12-1.1-SNAPSHOT-tests.jar) 49 | * that can be used to run tests with SBT without the need to distribute/ recompile 50 | * the Viper sources. To run the test without recompiling the sources, these 51 | * JAR files should be put into a directory test-location/lib/ 52 | * where test-location/ is the directory where you invoke SBT via: 53 | * ``` 54 | * sbt "set trapExit := false" \ 55 | * "test:runMain org.scalatest.tools.Runner -o -s viper.silicon.tests.PortableCarbonTests" 56 | * ``` 57 | * Note that this command takes the same JVM property arguments as used above. 58 | * 59 | * The warmup and the target must be disjoint (not in a sub-directory relation). 60 | * 61 | * The following JVM properties are available: 62 | * - CARBONTESTS_TARGET = path/to/target/files/ // Mandatory 63 | * - CARBONTESTS_WARMUP = path/to/warmup/files/ // Optional. If not specified, skip JVM warmup phase. 64 | * - CARBONTESTS_REPETITIONS = n // Optional, defaults to 1. If less then 3, no "trimming" will happen. 65 | * - CARBONTESTS_CSV = path/to/file.csv // Optional. If provided, mean & stddev are written to CSV file. 66 | */ 67 | @DoNotDiscover 68 | class PortableCarbonTests extends SilSuite with StatisticalTestSuite { 69 | /** Following a hyphenation-based naming scheme is important for handling project-specific annotations. 70 | * See comment for [[viper.silver.testing.TestAnnotations.projectNameMatches()]]. 71 | */ 72 | override def name = "Carbon-Statistics" 73 | 74 | override val repetitionsPropertyName = "CARBONTESTS_REPETITIONS" 75 | override val warmupLocationPropertyName = "CARBONTESTS_WARMUP" 76 | override val targetLocationPropertyName = "CARBONTESTS_TARGET" 77 | override val csvFilePropertyName = "CARBONTESTS_CSV" 78 | 79 | val reporter = NoopReporter 80 | override def verifier: CarbonVerifier = CarbonVerifier(reporter) 81 | 82 | override def frontend(verifier: Verifier, files: Seq[Path]): Frontend = { 83 | require(files.length == 1, "tests should consist of exactly one file") 84 | val fe = new CarbonFrontend(reporter, SilentLogger().get) 85 | fe.init(verifier) 86 | fe.reset(files.head) 87 | fe 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/utility/PolyMapDesugarHelper.scala: -------------------------------------------------------------------------------- 1 | package viper.carbon.utility 2 | 3 | import viper.carbon.boogie.{Axiom, Forall, Func, FuncApp, Identifier, LocalVarDecl, NamedType, Namespace, Trigger, Type, TypeVar} 4 | 5 | /** 6 | * Representation of desugared version of polymorphic type 7 | * @param select Boogie function for map lookups 8 | * @param store Boogie function for map stores 9 | * @param axioms axioms constraining the select and store functions 10 | */ 11 | case class PolyMapRep(select: Func, store: Func, axioms: Seq[Axiom]) 12 | 13 | /*** 14 | * Class that can desugar a specific category of polymorphic Boogie maps, namely Boogie maps of the form 15 | * {@code <...>[ref, Field ...]RangeType} where \code{ref} is type representing references (no type arguments), 16 | * {@code Field} is a type constructor representing fields. 17 | * @param refType type representing references 18 | * @param fieldTypeConstructor [[fieldTypeConstructor._2]] constructs a field type given [[fieldTypeConstructor._1]] type arguments 19 | * @param namespace 20 | */ 21 | case class PolyMapDesugarHelper(refType: Type, fieldTypeConstructor: (Int, Seq[Type] => Type), namespace: Namespace) { 22 | implicit val ns = namespace 23 | 24 | /** 25 | * Creates store and select functions with corresponding axioms to desugar a Boogie map of the form 26 | * {@code <...>[ref, Field ... ...]RangeType} . 27 | * @param mapRepType the type that should be used to represent the map type 28 | * @param selectAndStoreId the identifiers for selection and store functions 29 | * @param mapRangeTypeFromField the range type of the map as a function of the field type 30 | * @return [[PolyMapRep]] representation of desugared type 31 | */ 32 | def desugarPolyMap(mapRepType: NamedType, 33 | selectAndStoreId: (Identifier, Identifier), 34 | mapRangeTypeFromField: Type => Type): PolyMapRep = { 35 | val (selectId, storeId) = selectAndStoreId 36 | val mapTypeId = Identifier(mapRepType.name) 37 | val h = LocalVarDecl(mapTypeId, mapRepType) 38 | val obj = LocalVarDecl(Identifier("obj"), refType) 39 | val obj2 = LocalVarDecl(Identifier("obj2"), refType) 40 | 41 | val field = LocalVarDecl(Identifier("f"), 42 | fieldTypeConstructor._2(Seq.tabulate(fieldTypeConstructor._1){ i => TypeVar("A"+i) })) 43 | val field2 = LocalVarDecl(Identifier("f2"), 44 | fieldTypeConstructor._2(Seq.tabulate(fieldTypeConstructor._1){ i => TypeVar("B"+i) })) 45 | 46 | val selectFun = 47 | Func(selectId, 48 | Seq(h, obj, field), 49 | mapRangeTypeFromField(field.typ)) 50 | 51 | val storeFun = 52 | Func(storeId, 53 | Seq(h, obj, field, LocalVarDecl(Identifier("y"), mapRangeTypeFromField(field.typ))), 54 | mapRepType) 55 | 56 | val declInHeapRange = LocalVarDecl(Identifier("y"), mapRangeTypeFromField(field.typ)) 57 | val readUpdateGeneral = 58 | FuncApp(selectId, 59 | Seq(FuncApp(storeId, Seq(h.l, obj.l, field.l, declInHeapRange.l), mapRepType), obj2.l, field2.l), 60 | mapRangeTypeFromField(field2.typ) 61 | ) 62 | val axioms = 63 | Seq( 64 | Axiom(Forall( 65 | Seq(h,obj,field,declInHeapRange), 66 | Seq(Trigger(Seq(FuncApp(storeId, Seq(h.l, obj.l, field.l, declInHeapRange.l), mapRepType)))), 67 | FuncApp(selectId, 68 | Seq(FuncApp(storeId, Seq(h.l, obj.l, field.l, declInHeapRange.l), mapRepType), obj.l, field.l), 69 | mapRangeTypeFromField(field.typ) 70 | ) === declInHeapRange.l 71 | )), 72 | Axiom(Forall( 73 | Seq(h,obj,obj2, field,field2, declInHeapRange), 74 | Seq(Trigger(Seq(readUpdateGeneral))), 75 | ( (obj.l !== obj2.l) || (field.l !== field2.l) ) ==> 76 | ( readUpdateGeneral === FuncApp(selectId, Seq(h.l, obj2.l, field2.l), 77 | mapRangeTypeFromField(field2.typ) ) ) 78 | ))) 79 | 80 | PolyMapRep(selectFun, storeFun, axioms) 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/verifier/Environment.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.verifier 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie.{BoogieNameGenerator, Identifier, LocalVar} 11 | 12 | /** 13 | * An environment that assigns unique names to Viper variables; in SIL, loops can have 14 | * local variables and thus a method might have two declarations of a local variable 15 | * with the same name (in different loops). In Boogie on the other hand, all variables 16 | * need to be unique. 17 | */ 18 | case class Environment(verifier: Verifier, member: sil.Node) { 19 | 20 | private val names = new BoogieNameGenerator() 21 | 22 | /** The current mapping of variables. */ 23 | private val currentMapping = collection.mutable.HashMap[sil.LocalVar, LocalVar]() 24 | 25 | /** Records the generated Boogie names of all translated Viper variables. */ 26 | private val allUsedNames = collection.mutable.HashMap[String, String]() 27 | 28 | // register types from member 29 | member match { 30 | case sil.Method(_, args, returns, _, _, _) => 31 | for (v <- args ++ returns) { 32 | define(v.localVar) 33 | } 34 | case f@sil.Function(_, args, _, _, _, _) => 35 | for (v <- args) { 36 | define(v.localVar) 37 | } 38 | case sil.Predicate(_, args, _) => 39 | for (v <- args) { 40 | define(v.localVar) 41 | } 42 | case f@sil.DomainFunc(_, args, _, _, _) => 43 | for (v <- args) { 44 | v match { 45 | case n: sil.LocalVarDecl => define(n.localVar) 46 | case u: sil.UnnamedLocalVarDecl => define(sil.LocalVar(uniqueName(f.name + "_param"), u.typ)(u.pos, u.info, u.errT)) 47 | } 48 | //? for (v <- args if (v.isInstanceOf[sil.LocalVarDecl])) { 49 | //? define(v.asInstanceOf[sil.LocalVarDecl].localVar) 50 | } 51 | case _ => 52 | } 53 | 54 | def currentNameMapping : Map[String, String] = allUsedNames.toMap 55 | 56 | /** 57 | * Returns the Boogie variable for a given Viper variable (it has to be defined first, 58 | * otherwise an error is thrown). 59 | */ 60 | def get(variable: sil.LocalVar): LocalVar = { 61 | currentMapping.get(variable) match { 62 | case Some(t) => t 63 | case None => sys.error(s"Internal Error: variable $variable is not defined.") 64 | } 65 | } 66 | 67 | /** 68 | * Defines a local variable in this environment for a given Viper variable, and returns the corresponding 69 | * Boogie variable. 70 | */ 71 | def define(variable: sil.LocalVar): LocalVar = { 72 | currentMapping.get(variable) match { 73 | case Some(t) => 74 | sys.error(s"Internal Error: variable $variable is already defined.") 75 | case None => 76 | val name = uniqueName(variable.name) 77 | val bvar = LocalVar(Identifier(name)(verifier.mainModule.silVarNamespace), verifier.typeModule.translateType(variable.typ)) 78 | currentMapping.put(variable, bvar) 79 | allUsedNames.update(variable.name, name) 80 | bvar 81 | } 82 | } 83 | 84 | def allDefinedVariables() : Set[sil.LocalVar] = currentMapping.keys.toSet 85 | 86 | def allDefinedNames(p : sil.Program) : Set[String] = 87 | allDefinedVariables().map(_.name) union p.scopedDecls.map(_.name).toSet 88 | 89 | def isDefinedAt(variable: sil.LocalVar) : Boolean = currentMapping.isDefinedAt(variable) 90 | 91 | def makeUniquelyNamed(decl: sil.LocalVarDecl) : sil.LocalVarDecl = 92 | if (isDefinedAt(decl.localVar)) new sil.LocalVarDecl(this.uniqueName(decl.localVar.name),decl.typ)(decl.pos, decl.info) else decl 93 | 94 | def undefine(variable: sil.LocalVar): Unit = { 95 | require(currentMapping.contains(variable)) 96 | currentMapping.remove(variable) 97 | } 98 | 99 | /** 100 | * Takes a string and tries to produce a similar string that is not already used. 101 | */ 102 | def uniqueName(s: String): String = { 103 | names.createUniqueIdentifier(s) 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/ExhaleModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import components.{ComponentRegistry, DefinednessState, ExhaleComponent} 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie.Stmt 12 | import viper.silver.verifier.PartialVerificationError 13 | 14 | /** 15 | * A module for translating exhale statements. The module takes care of the basic 16 | * structure of exhaling (like multiple phases) and exhaling boolean connectives 17 | * such as logical and or logical implication. Other modules can register themselves 18 | * as [[viper.carbon.modules.components.ExhaleComponent]]s to perform the exhale 19 | * operation of certain expressions. 20 | * 21 | * The module also implements [[viper.carbon.modules.components.ExhaleComponent]] 22 | * and performs some default behavior for most expressions. 23 | 24 | */ 25 | trait ExhaleModule extends Module with ExhaleComponent with ComponentRegistry[ExhaleComponent] { 26 | 27 | /** 28 | * @param exp A sequence of triples where the conjunction of the corresponding expressions (projection on the 29 | * the first element) is to be exhaled. The triple includes the error to be raised if the exhale fails and 30 | * the error to be raised if a well-definedness check fails during the exhale (if the latter is set 31 | * to [[None]], then no well-definedness checks are performed). 32 | * @param havocHeap A boolean used to allow or prevent havocing the heap after the exhale. 33 | * For example, the heap is not havoced after the exhale when translating a fold statement. 34 | * @param isAssert A boolean that tells whether the exhale method is being called during an exhale statement or an assert statement 35 | * (Assert reuses the code for exhale). 36 | * This is used for optimization purposes (remove extra operations if the statement is an 'assert') 37 | * and also used to translate the 'assert' statements during packaging a wand. 38 | * @param statesStackForPackageStmt stack of states used to translate statements during packaging a wand (carries currentState and LHS of wands) 39 | * @param insidePackageStmt Boolean that represents whether this method is being called during packaging a wand or not. 40 | * 41 | * The 'statesStackForPackageStmt' and 'insidePackageStmt' are used when translating statements during packaging a wand. 42 | * For more details refer to the note in the wand module. 43 | */ 44 | def exhale(exp: Seq[(sil.Exp, PartialVerificationError, Option[PartialVerificationError])], havocHeap: Boolean = true, 45 | isAssert: Boolean = false, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt 46 | 47 | /** convenience methods */ 48 | 49 | def exhaleWithoutDefinedness(exp: Seq[(sil.Exp, PartialVerificationError)], havocHeap: Boolean = true, 50 | isAssert: Boolean = false, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt = { 51 | exhale(exp.map(eError => (eError._1, eError._2, None)), havocHeap = havocHeap, isAssert = isAssert, statesStackForPackageStmt, insidePackageStmt = insidePackageStmt) 52 | } 53 | 54 | def exhaleSingleWithoutDefinedness(exp: sil.Exp, exhaleError: PartialVerificationError, havocHeap: Boolean = true, 55 | isAssert: Boolean = false, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt = { 56 | exhale(Seq((exp, exhaleError, None)), havocHeap = havocHeap, isAssert = isAssert, statesStackForPackageStmt, insidePackageStmt = insidePackageStmt) 57 | } 58 | 59 | def exhaleSingleWithDefinedness(exp: sil.Exp, exhaleError: PartialVerificationError, definednessError: PartialVerificationError, 60 | havocHeap: Boolean = true, isAssert: Boolean = false, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false) = { 61 | exhale(Seq((exp, exhaleError, Some(definednessError))), havocHeap = havocHeap, isAssert = isAssert, 62 | statesStackForPackageStmt, insidePackageStmt = insidePackageStmt) 63 | } 64 | 65 | } -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/BoogieNameGenerator.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | import viper.silver.utility.DefaultNameGenerator 10 | 11 | class BoogieNameGenerator extends DefaultNameGenerator { 12 | // in principle, some others would also be allowed, but this is good enough for now 13 | private val otherChars = "_.$#'`~^\\?a-zA-Z" 14 | def firstCharacter = s"[$otherChars]".r 15 | def otherCharacter = s"[0-9$otherChars]".r 16 | def separator = "_" 17 | def reservedNames = allReservedNames 18 | val boogieReservedNames: Set[String] = Set("div", "mod", "const", "procedure", "type", "function", "lambda", "unique", "complete", "if", "then", "else", "while", "free", 19 | "invariant", "goto", "break", "return", "call", "forall", "assert", "havoc", "assume", "returns", "var", "implementation", 20 | "axiom", "exists", "old", "false", "real", "int", "true", "bool", "finite", "ensures", "requires", "modifies", "where", "par", "lambda", 21 | "uses", "RTP", "RTZ", "yield", "async", "roundNearestTiesToAway", "roundNearestTiesToEven", "extends", "roundTowardPositive", 22 | "roundTowardZero", "RNA", "RTN", "roundTowardNegative", "RNE", "is") ++ 23 | Set("Set", "MultiSet", "Seq") 24 | val SMTreservedNames: Set[String] = Set( 25 | // Basic symbols: 26 | "", "!", "_", "as", "DECIMAL", "exists", "forall", "let", "NUMERAL", "par", "STRING", 27 | // Commands: 28 | "assert", "check-sat", "declare-sort", "declare-fun", "define-sort,", "define-fun", "exit", 29 | "get-assertions", "get-assignment", "get-info", "get-option,", "get-proof", "get-unsat-core", 30 | "get-value", "pop", "push", "set-logic", "set-info", "set-option", 31 | // Core theory: 32 | "and", "or", "not", "iff", "true", "false", "xor", "distinct", "ite", "=", "Bool", 33 | "=>", // implies (sic!) 34 | // Integers and reals 35 | "Int", "Real", "*", "/", "-", "~", "+", "<", "<=", ">", ">=", "div", "mod", "rem", 36 | "^", "sin", "cos", "tan", "asin", "acos", "atan", "sinh", "cosh", "tanh", "asinh", "acosh", "atanh", "pi", "euler", 37 | "to_real", "to_int", "is_int", 38 | // Bitvectors 39 | "extract", "concat", 40 | "bvnot", "bvneg", "bvand", "bvor", "bvadd", "bvmul", "bvudiv", "bvurem", "bvshl", "bvlshr", "bvult", 41 | // arrays 42 | "store", "select", "const", "default", "map", "union", "intersect", "difference", "complement", 43 | "subset", "array-ext", "as-array", "Array", 44 | // Z3 (and not only?) extensions to bitvectors 45 | "bit1", "bit0", "bvsub", "bvsdiv", "bvsrem", "bvsmod", "bvsdiv0", "bvudiv0", "bvsrem0", "bvurem0", 46 | "bvsmod0", "bvsdiv_i", "bvudiv_i", "bvsrem_i", "bvurem_i", "bvumod_i", "bvule", "bvsle", "bvuge", 47 | "bvsge", "bvslt", "bvugt", "bvsgt", "bvxor", "bvnand", "bvnor", "bvxnor", "sign_extend", "zero_extend", 48 | "repeat", "bvredor", "bvredand", "bvcomp", "bvumul_noovfl", "bvsmul_noovfl", "bvsmul_noudfl", "bvashr", 49 | "rotate_left", "rotate_right", "ext_rotate_left", "ext_rotate_right", "int2bv", "bv2int", "mkbv", 50 | // floating point (FIXME: Legacy, remove this) 51 | "plusInfinity", "minusInfinity", 52 | "+", "-", "/", "*", "==", "<", ">", "<=", ">=", 53 | "abs", "remainder", "fusedMA", "squareRoot", "roundToIntegral", 54 | "isZero", "isNZero", "isPZero", "isSignMinus", "min", "max", "asFloat", 55 | // SMT v1 stuff (FIXME: Legacy, remove this) 56 | "flet", "implies", "!=", "if_then_else", 57 | // Z3 extensions 58 | "lblneg", "lblpos", "lbl-lit", 59 | "if", "&&", "||", "equals", "equiv", "bool", "minimize", "maximize", 60 | // Boogie-defined 61 | "real_pow", "UOrdering2", "UOrdering3", 62 | // Floating point (final draft SMTLIB-v2.5) 63 | "NaN", 64 | "fp.abs", "fp.neg", "fp.add", "fp.sub", "fp.mul", "fp.div", "fp.fma", "fp.sqrt", "fp.rem", "fp.roundToIntegral", 65 | "fp.min", "fp.max", "fp.leq", "fp.lt", "fp.geq", "fp.gt", "fp.eq", 66 | "fp.isNormal", "fp.isSubnormal", "fp.isZero", "fp.isInfinite", "fp.isNaN", "fp.isNegative", "fp.isPositive", 67 | "fp", "fp.to_ubv", "fp.to_sbv", "to_fp", 68 | // Rounding mode 69 | "rmode", 70 | "roundNearestTiesToEven", "roundNearestTiesToAway", "roundTowardPositive", "roundTowardNegative", "roundTowardZero", 71 | "RNE", "RNA", "RTP", "RTN", "RTZ", 72 | ) 73 | val allReservedNames = boogieReservedNames.union(SMTreservedNames) 74 | } 75 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultMapModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.boogie._ 10 | import viper.carbon.modules.MapModule 11 | import viper.carbon.modules.components.{DefinednessComponent, DefinednessState} 12 | import viper.carbon.modules.impls.map_axioms.MapAxiomatization 13 | import viper.carbon.verifier.Verifier 14 | import viper.silver.verifier.{PartialVerificationError, reasons} 15 | import viper.silver.{ast => sil} 16 | 17 | class DefaultMapModule(val verifier: Verifier) extends MapModule with DefinednessComponent { 18 | import verifier._ 19 | import typeModule._ 20 | import expModule._ 21 | import DefaultMapModule._ 22 | 23 | /** The name of this module. */ 24 | override def name: String = "Map module" 25 | 26 | implicit val namespace = verifier.freshNamespace("map") 27 | 28 | /** Have maps been used so far (to determine if we need to include the set axiomatisation in the prelude). */ 29 | private var used = false 30 | 31 | override def isUsed() : Boolean = used 32 | 33 | override def preamble : Seq[Decl] = 34 | if (used) Seq(LiteralDecl(MapAxiomatization.value)) else Seq() 35 | 36 | override def start() : Unit = expModule.register(this) 37 | 38 | override def translateMapExp(exp : sil.Exp) : Exp = { 39 | used = true 40 | 41 | def rec(e : sil.Exp) = translateExp(e) // recurse 42 | val typ = translateType(exp.typ) 43 | 44 | exp match { 45 | case _: sil.EmptyMap => { 46 | val fa = FuncApp(Identifier(mapEmptyOpName), Nil, typ) 47 | fa.showReturnType = true // needed (in general) to avoid Boogie complaints about ambiguous type variable 48 | fa 49 | } 50 | 51 | case exp: sil.ExplicitMap => 52 | translateMapExp(exp.desugared) // desugar into a series of map updates starting from an empty map 53 | case sil.MapCardinality(base) => 54 | FuncApp(Identifier(mapCardOpName), Seq(rec(base)), Int) 55 | case sil.MapDomain(base) => 56 | FuncApp(Identifier(mapDomainOpName), Seq(rec(base)), typ) 57 | case sil.MapRange(base) => 58 | FuncApp(Identifier(mapValuesOpName), Seq(rec(base)), typ) 59 | case sil.MapUpdate(base, key, value) => 60 | FuncApp(Identifier(mapBuildOpName), Seq(rec(base), rec(key), rec(value)), typ) 61 | case exp: sil.MapContains => 62 | translateExp(exp.desugared) 63 | case sil.EqCmp(left, right) => 64 | FuncApp(Identifier(mapEqualOpName), List(rec(left), rec(right)), typ) 65 | case sil.NeCmp(left, right) => 66 | UnExp(Not, FuncApp(Identifier(mapEqualOpName), List(rec(left), rec(right)), typ)) 67 | 68 | case sil.MapLookup(base, key) => base.typ match { 69 | case sil.MapType(keyType, valueType) => { 70 | val mTyp = MapType(Seq(translateType(keyType)), translateType(valueType)) 71 | val mExp = FuncApp(Identifier(mapElementsOpName), Seq(rec(base)), mTyp) 72 | MapSelect(mExp, Seq(rec(key))) 73 | } 74 | case t => sys.error(s"expected a map type, but found $t") 75 | } 76 | 77 | case _ => sys.error("not a map expression") 78 | } 79 | } 80 | 81 | override def translateMapType(mapType : sil.MapType) : Type = { 82 | used = true 83 | NamedType("Map", Seq(translateType(mapType.keyType), translateType(mapType.valueType))) 84 | } 85 | 86 | override def simplePartialCheckDefinednessAfter(exp: sil.Exp, error: PartialVerificationError, makeChecks: Boolean, definednessStateOpt: Option[DefinednessState]): Stmt = { 87 | if (makeChecks) exp match { 88 | case sil.MapLookup(base, key) => { 89 | val containsExp = translateMapExp(sil.MapContains(key, base)(exp.pos, exp.info, exp.errT)) 90 | Assert(containsExp, error.dueTo(reasons.MapKeyNotContained(base, key))) 91 | } 92 | case _ => Statements.EmptyStmt 93 | } 94 | else Statements.EmptyStmt 95 | } 96 | 97 | override def reset() : Unit = used = false 98 | } 99 | 100 | object DefaultMapModule { 101 | private def opName(name : String) = s"Map#$name" 102 | 103 | val mapBuildOpName : String = opName("Build") 104 | val mapCardOpName : String = opName("Card") 105 | val mapDomainOpName : String = opName("Domain") 106 | val mapElementsOpName : String = opName("Elements") 107 | val mapEmptyOpName : String = opName("Empty") 108 | val mapEqualOpName : String = opName("Equal") 109 | val mapValuesOpName : String = opName("Values") 110 | } -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/Carbon.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon 8 | 9 | import ch.qos.logback.classic.Logger 10 | import viper.silver.frontend.{MinimalViperFrontendAPI, SilFrontend, SilFrontendConfig, ViperFrontendAPI} 11 | import viper.silver.logger.ViperStdOutLogger 12 | import viper.silver.reporter.{Reporter, StdIOReporter} 13 | import viper.silver.verifier.{Verifier => SilVerifier} 14 | import viper.silver.utility.{FileProgramSubmitter} 15 | 16 | /** 17 | * The main object for Carbon containing the execution start-point. 18 | */ 19 | object Carbon extends CarbonFrontend(StdIOReporter("carbon_reporter"), ViperStdOutLogger("Carbon", "INFO").get) { 20 | def main(args: Array[String]): Unit = { 21 | val submitter = new FileProgramSubmitter(this) 22 | submitter.setArgs(args) 23 | 24 | execute(args) 25 | specifyAppExitCode() 26 | 27 | submitter.submit() 28 | sys.exit(appExitCode) 29 | } 30 | } 31 | 32 | class CarbonFrontend(override val reporter: Reporter, 33 | override val logger: Logger) extends SilFrontend { 34 | 35 | private var carbonInstance: CarbonVerifier = _ 36 | 37 | override def backendTypeFormat: Option[String] = Some("Boogie") 38 | 39 | def createVerifier(fullCmd: String) = { 40 | carbonInstance = CarbonVerifier(reporter, Seq("Arguments: " -> fullCmd)) 41 | 42 | carbonInstance 43 | } 44 | 45 | def configureVerifier(args: Seq[String]) = { 46 | carbonInstance.parseCommandLine(args) 47 | 48 | carbonInstance.config 49 | } 50 | 51 | override def init(verifier: SilVerifier): Unit = { 52 | verifier match { 53 | case carbon: CarbonVerifier => 54 | carbonInstance = carbon 55 | case _ => 56 | sys.error( "Expected verifier to be an instance of CarbonVerifier but got an instance " + 57 | s"of ${verifier.getClass}") 58 | } 59 | 60 | super.init(verifier) 61 | 62 | _config = carbonInstance.config 63 | } 64 | } 65 | 66 | /** 67 | * Carbon "frontend" for use by actual Viper frontends. 68 | * Performs consistency check and verification. 69 | * See [[viper.silver.frontend.ViperFrontendAPI]] for usage information. 70 | */ 71 | class CarbonFrontendAPI(override val reporter: Reporter) 72 | extends CarbonFrontend(reporter, ViperStdOutLogger("CarbonFrontend", "INFO").get) with ViperFrontendAPI 73 | 74 | /** 75 | * Carbon "frontend" for use by actual Viper frontends. 76 | * Performs only verification (no consistency check). 77 | * See [[viper.silver.frontend.ViperFrontendAPI]] for usage information. 78 | */ 79 | class MinimalCarbonFrontendAPI(override val reporter: Reporter) 80 | extends CarbonFrontend(reporter, ViperStdOutLogger("CarbonFrontend", "INFO").get) with MinimalViperFrontendAPI 81 | 82 | class CarbonConfig(args: Seq[String]) extends SilFrontendConfig(args, "Carbon") { 83 | val boogieProverLog = opt[String]("proverLog", 84 | descr = "Prover log file written by Boogie (default: none)", 85 | default = None, 86 | noshort = true 87 | ) 88 | 89 | val boogieOut = opt[String]("print", 90 | descr = "Write the Boogie output file to the provided filename (default: none)", 91 | default = None, 92 | noshort = true 93 | ) 94 | 95 | val boogieOpt = opt[String]("boogieOpt", 96 | descr = "Option(s) to pass-through as options to Boogie (changing the output generated by Boogie is not supported) (default: none)", 97 | default = None, 98 | noshort = true 99 | ) 100 | 101 | val boogieExecutable = opt[String]("boogieExe", 102 | descr = "Manually-specified full path to Boogie.exe executable (default: ${BOOGIE_EXE})", 103 | default = None, 104 | noshort = true 105 | ) 106 | 107 | val Z3executable = opt[String]("z3Exe", 108 | descr = "Manually-specified full path to Z3.exe executable (default: ${Z3_EXE})", 109 | default = None, 110 | noshort = true 111 | ) 112 | 113 | val disableAllocEncoding = opt[Boolean]("disableAllocEncoding", 114 | descr = "Disable Allocation-related assumptions (default: enabled)", 115 | default = None, 116 | noshort = true 117 | ) 118 | 119 | val desugarPolymorphicMaps = opt[Boolean]("desugarPolymorphicMaps", 120 | descr = "Do not use polymorphic maps in the Boogie encoding and instead desugar them (default: false).", 121 | default = Some(false), 122 | noshort = true 123 | ) 124 | 125 | val timeout = opt[Int]("timeout", 126 | descr = ("Time out after approx. n seconds. The timeout is for the whole verification in Boogie, " 127 | + "not per method or proof obligation (default: 0, i.e. no timeout)."), 128 | default = None, 129 | noshort = true 130 | ) 131 | 132 | verify() 133 | } 134 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultSeqModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.modules.SeqModule 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie._ 12 | import viper.carbon.verifier.Verifier 13 | import viper.carbon.boogie.Implicits._ 14 | import viper.carbon.modules.impls.dafny_axioms.SequenceAxiomatization 15 | import viper.carbon.modules.components.{DefinednessComponent, DefinednessState} 16 | import viper.silver.ast.{SeqIndex, SeqLength} 17 | import viper.silver.verifier.{PartialVerificationError, reasons} 18 | 19 | /** 20 | * The default implementation of [[viper.carbon.modules.SeqModule]]. 21 | */ 22 | class DefaultSeqModule(val verifier: Verifier) 23 | extends SeqModule 24 | with DefinednessComponent { 25 | 26 | import verifier._ 27 | import typeModule._ 28 | import expModule._ 29 | 30 | /** 31 | * Have sequences been used so far (to determine if we need to include 32 | * the sequence axiomatization in the prelude. 33 | */ 34 | private var used = false 35 | 36 | def name = "Sequence module" 37 | implicit val namespace = verifier.freshNamespace("seq") 38 | 39 | override def preamble = { 40 | if (used) { 41 | LiteralDecl(SequenceAxiomatization.value) 42 | } else { 43 | Nil 44 | } 45 | } 46 | 47 | override def start(): Unit = { 48 | expModule.register(this) 49 | } 50 | 51 | override def translateSeqExp(e: sil.Exp): Exp = { 52 | def t(e: sil.Exp) = translateExp(e) 53 | used = true 54 | val typ = translateType(e.typ) 55 | e match { 56 | case sil.EmptySeq(elemTyp) => 57 | val fa = FuncApp(Identifier("Seq#Empty"), Nil, typ) 58 | fa.showReturnType = true // needed (in general) to avoid Boogie complaints about ambiguous type variable 59 | fa 60 | case s@sil.ExplicitSeq(elems) => 61 | elems.toList match { 62 | case Nil => sys.error("did not expect empty sequence") 63 | case a :: Nil => 64 | FuncApp(Identifier("Seq#Singleton"), t(a), typ) 65 | case a :: as => 66 | translateSeqExp(s.desugared) // desugar into appends and singletons 67 | } 68 | case sil.RangeSeq(low, high) => 69 | FuncApp(Identifier("Seq#Range"), List(t(low), t(high)), typ) 70 | case sil.SeqAppend(left, right) => 71 | FuncApp(Identifier("Seq#Append"), List(t(left), t(right)), typ) 72 | case sil.SeqIndex(seq, idx) => 73 | FuncApp(Identifier("Seq#Index"), List(t(seq), t(idx)), typ) 74 | case sil.SeqTake(seq, n) => 75 | FuncApp(Identifier("Seq#Take"), List(t(seq), t(n)), typ) 76 | case sil.SeqDrop(seq, n) => 77 | FuncApp(Identifier("Seq#Drop"), List(t(seq), t(n)), typ) 78 | case sil.SeqContains(elem, seq) => 79 | FuncApp(Identifier("Seq#Contains"), List(t(seq), t(elem)), typ) 80 | case sexp@sil.SeqUpdate(seq, idx, elem) => 81 | { 82 | // translate as (s[..i] ++ ([i] ++ s[i+1..])) (NOTE: this assumes i is in the range, which is not yet checked) 83 | t(sexp.desugaredAssumingIndexInRange) 84 | // val s = t(seq) 85 | // val i = t(idx) 86 | // val v = t(elem) 87 | // FuncApp(Identifier("Seq#Append"),List(FuncApp(Identifier("Seq#Take"), List(s,i),typ),FuncApp(Identifier("Seq#Append"),List(FuncApp(Identifier("Seq#Singleton"), v, typ),FuncApp(Identifier("Seq#Drop"), List(s,BinExp(i,Add,IntLit(1))),typ)),typ)),typ) 88 | } 89 | case sil.SeqLength(seq) => 90 | FuncApp(Identifier("Seq#Length"), t(seq), typ) 91 | case sil.EqCmp(left, right) => 92 | FuncApp(Identifier("Seq#Equal"), List(t(left), t(right)), typ) 93 | case sil.NeCmp(left, right) => 94 | UnExp(Not, FuncApp(Identifier("Seq#Equal"), List(t(left), t(right)), typ)) 95 | case _ => sys.error("not a sequence expression") 96 | } 97 | } 98 | 99 | override def translateSeqType(seqType: sil.SeqType): Type = { 100 | used = true 101 | NamedType("Seq", translateType(seqType.elementType)) 102 | } 103 | 104 | def rewriteToTermsInTriggers(e: Exp) : Exp = 105 | { 106 | Transformer.transform(e, { 107 | case FuncApp(Identifier("Seq#Contains",_),args,typ) => 108 | FuncApp(Identifier("Seq#ContainsTrigger"),args,typ) 109 | })(_ => true) // always recurse 110 | } 111 | 112 | 113 | override def simplePartialCheckDefinednessAfter(e: sil.Exp, error: PartialVerificationError, makeChecks: Boolean, definednessStateOpt: Option[DefinednessState]): Stmt = { 114 | 115 | if(makeChecks) 116 | e match { 117 | case si@SeqIndex(s,idx) => { 118 | val index = translateExp(idx) 119 | val length = translateSeqExp(SeqLength(s)(s.pos,s.info)) 120 | Assert(BinExp(index,GeCmp,IntLit(0)),error.dueTo(reasons.SeqIndexNegative(s,si))) ++ Assert(BinExp(index,LtCmp,length),error.dueTo(reasons.SeqIndexExceedsLength(s,si))) 121 | } 122 | case _ => Nil 123 | } 124 | else Nil 125 | } 126 | 127 | /** 128 | * Reset the state of this module so that it can be used for new program. This method is called 129 | * after verifier gets a new program. 130 | */ 131 | override def reset(): Unit = { 132 | used = false 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/PermModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.carbon.boogie._ 10 | import viper.carbon.modules.components.CarbonStateComponent 11 | import viper.silver.{ast => sil} 12 | 13 | case class PMaskDesugaredRep(selectId: Identifier, storeId: Identifier) 14 | 15 | /** 16 | * The permission module determines the encoding of permissions and allows to add or remove 17 | * permission. 18 | */ 19 | trait PermModule extends Module with CarbonStateComponent { 20 | 21 | /** 22 | * The type used to represent permissions. 23 | */ 24 | def permType: Type 25 | 26 | /** 27 | * Translate a permission amount 28 | */ 29 | def translatePerm(e: sil.Exp): Exp 30 | 31 | /** 32 | * Translate a permission comparison 33 | */ 34 | def translatePermComparison(e: sil.Exp): Exp 35 | 36 | /** 37 | * Returns an expression representing that a permission amount is positive 38 | * 39 | * @param permission the permission amount to be checked 40 | * @param zeroOK whether the comparison should (not) be strict, or not 41 | * @return the expression representing the fact that the permission is positive 42 | */ 43 | def permissionPositive(permission: Exp, zeroOK : Boolean = false): Exp 44 | 45 | def conservativeIsPositivePerm(e: sil.Exp): Boolean 46 | 47 | /** 48 | * Returns an expression representing that a permission amount is positive. 49 | * Similar to [[permissionPositive]], but works directly on Viper expressions, *including* ones containing 50 | * wildcards, and performs more aggressive simplifications. 51 | * 52 | * @param e the permission amount to be checked 53 | * @return the expression representing the fact that the permission is positive 54 | */ 55 | def isStrictlyPositivePerm(e: sil.Exp): Exp 56 | 57 | /** 58 | * The current mask. 59 | */ 60 | def currentMask: Seq[Exp] 61 | 62 | /** 63 | * A static reference to the mask. 64 | */ 65 | def staticMask: Seq[LocalVarDecl] 66 | 67 | /** 68 | * Is the permission for a given expression positive (using the static mask). 69 | */ 70 | def staticPermissionPositive(rcv: Exp, loc: Exp): Exp 71 | 72 | /** 73 | * The predicate mask field of a given predicate (as its ghost location). 74 | */ 75 | def predicateMaskField(pred: Exp): Exp 76 | 77 | /** 78 | * The wand mask field of a given wand (as its ghost location). 79 | */ 80 | def wandMaskField(wand: Exp): Exp 81 | 82 | /** 83 | * The type used for masks. 84 | */ 85 | def maskType: Type 86 | 87 | /** 88 | * The type used to for predicate masks. 89 | */ 90 | def pmaskType: Type 91 | 92 | /** 93 | * The desugared poly map version of [[pmaskType]]. 94 | * TODO: It may make sense to move the representation of predicate masks to another module. Right now the representation 95 | * seems to be shared among multiple modules. 96 | */ 97 | def pmaskTypeDesugared: PMaskDesugaredRep 98 | 99 | def zeroPMask: Exp 100 | 101 | def hasDirectPerm(ra: sil.ResourceAccess): Exp 102 | 103 | /** 104 | * The expression for the current permission at a location. 105 | */ 106 | def currentPermission(loc: sil.ResourceAccess): Exp 107 | 108 | def currentPermission(rcv:Exp, loc:Exp):Exp 109 | 110 | /**these methods are for experimental purposes, not yet finalized **/ 111 | /*def beginSumMask : Stmt 112 | 113 | def sumMask : Exp 114 | 115 | def endSumMask: Stmt*/ 116 | /* 117 | def setSummandMask1 118 | def setSummandMask2 119 | def sumMask(assmsToSmt: Exp => Stmt):Stmt 120 | */ 121 | 122 | /** 123 | * 124 | * @param summandMask1 125 | * @param summandMask2 126 | * @return expression for which its validity implies that the current mask is the sum of the two input masks 127 | */ 128 | def sumMask(summandMask1: Seq[Exp], summandMask2: Seq[Exp]): Exp 129 | 130 | /** 131 | * 132 | * @param resultMask 133 | * @param summandMask1 134 | * @param summandMask2 135 | * @return expression for which its validity implies that @{code resultMask} is the sum of the other two input 136 | * masks 137 | */ 138 | def sumMask(resultMask: Seq[Exp], summandMask1: Seq[Exp], summandMask2: Seq[Exp]) : Exp 139 | 140 | /** returns a mask and the returned statement ensures that the mask has non-zero permission at rcv.loc and zero 141 | * permission at all other location 142 | * this should only be used temporarily, i.e. if there are two calls to this then the previous tempMask returned 143 | * will be overwritten in the Boogie code 144 | */ 145 | def tempInitMask(rcv: Exp, loc:Exp):(Seq[Exp], Stmt) 146 | 147 | def getCurrentAbstractReads(): collection.mutable.ListBuffer[String] 148 | 149 | /** 150 | * Checks if expression e contains instances of wildcards 151 | */ 152 | 153 | def containsWildCard(e: sil.Exp): Boolean 154 | 155 | // adds permission to w#ft (footprint of the magic wand) (See Heap module for w#ft description) 156 | def inhaleWandFt(w: sil.MagicWand): Stmt 157 | 158 | // removes permission to w#ft (footprint of the magic wand) (See Heap module for w#ft description) 159 | def exhaleWandFt(w: sil.MagicWand): Stmt 160 | 161 | def setCheckReadPermissionOnly(readOnly: Boolean): Boolean 162 | 163 | def assumePermUpperBounds(doAssume: Boolean): Stmt 164 | 165 | } 166 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/HeapModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import viper.silver.{ast => sil} 10 | import viper.carbon.boogie._ 11 | import viper.carbon.modules.components.CarbonStateComponent 12 | import viper.carbon.utility.PolyMapDesugarHelper 13 | import viper.silver.ast.{LocationAccess, MagicWand} 14 | 15 | /** 16 | * A module for translating heap expressions (access, updating) and determining 17 | * the heap encoding. 18 | */ 19 | trait HeapModule extends Module with CarbonStateComponent { 20 | 21 | /** 22 | * The type used for heaps 23 | */ 24 | def heapType: Type 25 | 26 | /** 27 | * The type used for references. 28 | */ 29 | def refType: Type 30 | 31 | /** 32 | * The type used for fields. 33 | */ 34 | def fieldType: Type 35 | 36 | /** 37 | * The type used for fields of type t. 38 | */ 39 | def fieldTypeOf(t: Type): Type 40 | 41 | 42 | /** 43 | * Represents the Boogie type constructor for fields. 44 | * The first element specifies how many type arguments (n_ty_args) the field type constructor takes and 45 | * the second element provides a function to construct a field type given n_ty_args many type arguments 46 | */ 47 | def fieldTypeConstructor : (Int, Seq[Type] => Type) 48 | 49 | /** 50 | * The type used for predicates. 51 | */ 52 | def predicateVersionFieldType(genericT: String = "A"): Type 53 | 54 | /** 55 | * The type used for predicates mask fields. 56 | */ 57 | def predicateMaskFieldType: Type 58 | 59 | /** 60 | * The type used for predicates mask fields of a given predicate family. 61 | */ 62 | def predicateMaskFieldTypeOf(p: sil.Predicate): Type 63 | 64 | /** 65 | * The type used for predicates of a given family. 66 | */ 67 | def predicateVersionFieldTypeOf(p: sil.Predicate): Type 68 | 69 | /** 70 | * Get a function application representing that one heap-state (as represented by currentStateContributions of HeapModule) is a predecessor of another 71 | */ 72 | def successorHeapState(first: Seq[LocalVarDecl], second: Seq[LocalVarDecl]) : Exp 73 | 74 | /** 75 | * The type used for wands. 76 | */ 77 | def wandFieldType(wandName: String): Type 78 | 79 | /** 80 | * new type introduced for a wand 81 | */ 82 | def wandBasicType(wandName: String):Type 83 | 84 | /** 85 | * Definitions for a field. 86 | */ 87 | def translateField(f: sil.Field): Seq[Decl] 88 | 89 | /** 90 | * Definitions for the ghost field of a predicate. 91 | */ 92 | def predicateGhostFieldDecl(f: sil.Predicate): Seq[Decl] 93 | 94 | /** 95 | * Translation of a field read, predicate instance, or wand instance. 96 | */ 97 | def translateResourceAccess(f: sil.ResourceAccess): Exp 98 | /** 99 | * Translation of a field read. 100 | */ 101 | def translateLocationAccess(rcv: Exp, loc:Exp):Exp 102 | 103 | def translateResource(f: sil.ResourceAccess): Exp 104 | def translateLocation(pred: sil.Predicate, args: Seq[Exp]): Exp 105 | 106 | /** 107 | * Translation of the null literal. 108 | */ 109 | def translateNull: Exp 110 | 111 | /** 112 | * Check that the receiver of a location access is non-null. 113 | */ 114 | def checkNonNullReceiver(loc: sil.LocationAccess): Exp = { 115 | loc match { 116 | case sil.FieldAccess(rcv, _) => 117 | verifier.expModule.translateExp(rcv) !== translateNull 118 | case _ => TrueLit() 119 | } 120 | } 121 | 122 | def checkNonNullReceiver(rcv: Exp):Exp = { 123 | rcv !== translateNull 124 | } 125 | 126 | /** 127 | * Begin of exhale. 128 | */ 129 | def beginExhale: Stmt 130 | 131 | /** 132 | * End of exhale 133 | */ 134 | def endExhale: Stmt 135 | 136 | /** 137 | * Is the given field a predicate field? 138 | */ 139 | def isPredicateField(f: Exp): Exp 140 | 141 | /** 142 | * get Predicate or wand Id (unique for each Predicate or wand) 143 | */ 144 | def getPredicateOrWandId(f:Exp): Exp 145 | 146 | /** 147 | * Predicate or (internal) wand name mapping to Id 148 | */ 149 | def getPredicateOrWandId(s:String):BigInt 150 | /** 151 | * Is the given field a wand field? 152 | */ 153 | def isWandField(f: Exp): Exp 154 | 155 | def predicateTrigger(extras: Seq[Exp], pred: sil.PredicateAccess, anyState: Boolean = false): Exp 156 | 157 | def currentHeap:Seq[Exp] 158 | 159 | /** 160 | * store {@code newVal} at {@code loc} in the current heap 161 | */ 162 | def currentHeapAssignUpdate(loc: sil.LocationAccess, newVal: Exp): Stmt 163 | 164 | def identicalOnKnownLocations(heap:Seq[Exp],mask:Seq[Exp]):Exp 165 | 166 | /** 167 | * Adds assumption that current heap equals heap represented by s 168 | */ 169 | def equateWithCurrentHeap(s: Seq[Var]): Stmt 170 | 171 | // returns wand#sm (secondary mask for the wand) 172 | def wandMaskIdentifier(f: Identifier): Identifier 173 | 174 | // returns wand#ft (footprint of the magic wand) 175 | // this is inhaled at the beginning of packaging a wand to frame fields while the wand being packaged ( 176 | // as the permission to the wand is gained at the end of the package statement) 177 | def wandFtIdentifier(f: Identifier): Identifier 178 | 179 | def predicateMaskFieldTypeOfWand(wand: String): Type 180 | 181 | def predicateVersionFieldTypeOfWand(wand: String): Type 182 | 183 | // adds permission to field e to the secondary mask of the wand 184 | def addPermissionToWMask(wMask: Exp, e: sil.Exp): Stmt 185 | 186 | // If expression evaluates to true then resultHeap is the sum of of heap1, where mask1 is defined, 187 | // and heap2, where mask2 is defined. 188 | def sumHeap(resultHeap: Exp, heap1: Exp, mask1: Exp, heap2: Exp, mask2: Exp): Exp 189 | 190 | } 191 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/map_axioms/MapAxiomatization.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | /** 8 | ; These axioms are based on the DafnyPrelude.bpl file of Microsoft's Dafny tool. 9 | ; See https://github.com/dafny-lang for more information about the Dafny verifier. 10 | ; 11 | ; A snapshot of the corresponding DafnyPrelude.bpl file including the date 12 | ; of the version and its copyright notices can be found in this directory. 13 | ; 14 | ; This file is subject to the terms of the Microsoft Public License 15 | ; (Ms-PL). A copy of the Ms-PL is provided in this directory (LICENCE.TXT) 16 | */ 17 | 18 | package viper.carbon.modules.impls.map_axioms 19 | 20 | object MapAxiomatization { 21 | val value : String = 22 | """ 23 | |type Map U V; 24 | | 25 | |// A Map is defined by three functions, Map#Domain, Map#Elements, and #Map#Card. 26 | | 27 | |function Map#Domain(Map U V) : Set U; 28 | | 29 | |function Map#Elements(Map U V) : [U]V; 30 | | 31 | |function Map#Card(Map U V) : int; 32 | | 33 | |axiom (forall m: Map U V :: { Map#Card(m) } 0 <= Map#Card(m)); 34 | | 35 | |// The set of Keys of a Map are available by Map#Domain, and the cardinality of that 36 | |// set is given by Map#Card. 37 | | 38 | | /* added second trigger set */ 39 | | 40 | |axiom (forall m: Map U V :: { Set#Card(Map#Domain(m)) } { Map#Card(m) } 41 | | Set#Card(Map#Domain(m)) == Map#Card(m)); 42 | | 43 | |// The set of Values of a Map can be obtained by the function Map#Values, which is 44 | |// defined as follows. Remember, a Set is defined by membership (using Boogie's 45 | |// square brackets) and Map#Card, so we need to define what these mean for the Set 46 | |// returned by Map#Values. 47 | | 48 | |function Map#Values(Map U V) : Set V; 49 | | 50 | | /* split axiom into each direction */ 51 | | 52 | |axiom (forall m: Map U V, v: V :: { Map#Values(m)[v] } 53 | | Map#Values(m)[v] ==> 54 | | (exists u: U :: { Map#Domain(m)[u] } { Map#Elements(m)[u] } 55 | | Map#Domain(m)[u] && 56 | | v == Map#Elements(m)[u])); 57 | | 58 | |axiom (forall m: Map U V, u: U :: { Map#Elements(m)[u] } // { Map#Domain(m)[u] } // REMOVED this trigger due to a potential for matching loops 59 | | Map#Domain(m)[u] 60 | | ==> Map#Values(m)[Map#Elements(m)[u]]); 61 | |// There's a potential for matching loops with the extra trigger if two maps have equal domains: 62 | |// v in range(m1); some k in dom(m1) = dom(m2) s.t. m1[k] = v; m2[k] in range(m2); some k' in dom(m2) s.t. m2[k'] = m2[k] 63 | | 64 | |axiom (forall m: Map U V, u: U :: { Map#Domain(m)[u] } { Map#Elements(m)[u] } 65 | | Map#Domain(m)[u] 66 | | ==> Set#Card(Map#Values(m)) > 0); // weaker property than above, with weaker triggers 67 | | 68 | | // Here are the operations that produce Map values. 69 | | 70 | |function Map#Empty(): Map U V; 71 | |axiom (forall u: U :: 72 | | { Map#Domain(Map#Empty(): Map U V)[u] } 73 | | !Map#Domain(Map#Empty(): Map U V)[u]); 74 | | 75 | |axiom (forall m: Map U V :: { Map#Card(m) } 76 | | (Map#Card(m) == 0 <==> m == Map#Empty()) && 77 | | (Map#Card(m) != 0 ==> (exists x: U :: Map#Domain(m)[x])) && 78 | | ((forall x: U :: {Map#Domain(m)[x]} Map#Domain(m)[x] ==> Map#Card(m) != 0))); 79 | | 80 | |//Build is used in displays, and for map updates 81 | |function Map#Build(Map U V, U, V): Map U V; 82 | | 83 | |/* added second trigger set (cf. example3 test case, test3) */ 84 | |axiom (forall m: Map U V, u: U, u': U, v: V :: 85 | | { Map#Domain(Map#Build(m, u, v))[u'] } { Map#Domain(m)[u'],Map#Build(m, u, v) } { Map#Elements(Map#Build(m, u, v))[u'] } 86 | | (u' == u ==> Map#Domain(Map#Build(m, u, v))[u'] && 87 | | Map#Elements(Map#Build(m, u, v))[u'] == v) && 88 | | (u' != u ==> Map#Domain(Map#Build(m, u, v))[u'] == Map#Domain(m)[u'] && 89 | | Map#Elements(Map#Build(m, u, v))[u'] == Map#Elements(m)[u'])); 90 | |/* added second trigger set (not sure of a test case needing it, though) */ 91 | |axiom (forall m: Map U V, u: U, v: V :: { Map#Card(Map#Build(m, u, v)) }{ Map#Card(m),Map#Build(m, u, v) } 92 | | Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m)); 93 | |/* added second trigger set (not sure of a test case needing it, though) */ 94 | |axiom (forall m: Map U V, u: U, v: V :: { Map#Card(Map#Build(m, u, v)) }{ Map#Card(m),Map#Build(m, u, v) } 95 | | !Map#Domain(m)[u] ==> Map#Card(Map#Build(m, u, v)) == Map#Card(m) + 1); 96 | | 97 | |//equality for maps 98 | | // this axiom is only needed in one direction; the other is implied by the next axiom 99 | | 100 | |function Map#Equal(Map U V, Map U V): bool; 101 | |axiom (forall m: Map U V, m': Map U V:: 102 | | { Map#Equal(m, m') } 103 | | (forall u : U :: Map#Domain(m)[u] == Map#Domain(m')[u]) && 104 | | (forall u : U :: Map#Domain(m)[u] ==> Map#Elements(m)[u] == Map#Elements(m')[u]) ==> Map#Equal(m, m')); 105 | |// extensionality 106 | |axiom (forall m: Map U V, m': Map U V:: 107 | | { Map#Equal(m, m') } 108 | | Map#Equal(m, m') ==> m == m'); 109 | | 110 | |function Map#Disjoint(Map U V, Map U V): bool; 111 | |// split in both directions 112 | |axiom (forall m: Map U V, m': Map U V :: 113 | | { Map#Disjoint(m, m') } 114 | | Map#Disjoint(m, m') ==> (forall o: U :: {Map#Domain(m)[o]} {Map#Domain(m')[o]} !Map#Domain(m)[o] || !Map#Domain(m')[o])); 115 | |axiom (forall m: Map U V, m': Map U V :: 116 | | { Map#Disjoint(m, m') } 117 | | !Map#Disjoint(m, m') ==> (exists o: U :: {Map#Domain(m)[o]} {Map#Domain(m')[o]} Map#Domain(m)[o] && Map#Domain(m')[o])); 118 | | 119 | |""".stripMargin 120 | } 121 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultSetModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.modules.SetModule 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie._ 12 | import viper.carbon.verifier.Verifier 13 | import viper.carbon.boogie.Implicits._ 14 | import viper.carbon.modules.impls.dafny_axioms.SetAxiomatization 15 | import viper.carbon.modules.components.DefinednessComponent 16 | 17 | /** 18 | * The default implementation of [[viper.carbon.modules.SetModule]]. 19 | 20 | */ 21 | class DefaultSetModule(val verifier: Verifier) 22 | extends SetModule 23 | with DefinednessComponent { 24 | import verifier._ 25 | import typeModule._ 26 | import expModule._ 27 | 28 | /** 29 | * Have sets been used so far (to determine if we need to include 30 | * the set axiomatization in the prelude. 31 | */ 32 | private var used = false 33 | 34 | def name = "Set module" 35 | implicit val namespace = verifier.freshNamespace("set") 36 | 37 | //override def freeAssumptions(e: sil.Exp): Stmt = { 38 | // e match { 39 | // case _ if e.typ.isInstanceOf[sil.MultisetType] => 40 | // Assume(FuncApp(Identifier("$IsGoodMultiSet"),translateExp(e),Bool)) 41 | // case _ => Nil 42 | // } 43 | // } 44 | 45 | //override def validValue(typ: sil.Type, variable: LocalVar, isParameter: Boolean): Option[Exp] = { 46 | // if (typ.isInstanceOf[sil.MultisetType]) Some(FuncApp(Identifier("$IsGoodMultiSet"),variable,Bool)) else None 47 | // } 48 | 49 | override def preamble = { 50 | if (used || verifier.mapModule.isUsed()) { 51 | LiteralDecl(SetAxiomatization.value) 52 | } else { 53 | Nil 54 | } 55 | } 56 | 57 | override def translateSetExp(e: sil.Exp): Exp = { 58 | def t(e: sil.Exp) = translateExp(e) 59 | val isMultiset = ((x:sil.Exp) => x.typ match { 60 | case _: sil.MultisetType => true 61 | case _: sil.SetType => false 62 | case _ => sys.error("Internal Error: expression " + e + "was expected to be of Set or Multiset type") 63 | }) 64 | used = true 65 | val typ = translateType(e.typ) 66 | e match { 67 | case sil.EmptySet(elemTyp) => 68 | val fa = FuncApp(Identifier("Set#Empty"), Nil, typ) 69 | fa.showReturnType = true // needed (in general) to avoid Boogie complaints about ambiguous type variable 70 | fa 71 | case sil.ExplicitSet(elems) => 72 | def buildSet(es: Seq[sil.Exp]): Exp = { 73 | es.toList match { 74 | case Nil => sys.error("did not expect empty sequence") 75 | case a :: Nil => 76 | FuncApp(Identifier("Set#Singleton"), t(a), typ) 77 | case a :: as => 78 | FuncApp(Identifier("Set#UnionOne"), List(buildSet(as), t(a)), typ) 79 | } 80 | } 81 | buildSet(elems) 82 | case sil.EmptyMultiset(elemTyp) => 83 | val fa = FuncApp(Identifier("MultiSet#Empty"), Nil, typ) 84 | fa.showReturnType = true // needed (in general) to avoid Boogie complaints about ambiguous type variable 85 | fa 86 | case sil.ExplicitMultiset(elems) => 87 | def buildSet(es: Seq[sil.Exp]): Exp = { 88 | es.toList match { 89 | case Nil => sys.error("did not expect empty sequence") 90 | case a :: Nil => 91 | FuncApp(Identifier("MultiSet#Singleton"), t(a), typ) 92 | case a :: as => 93 | FuncApp(Identifier("MultiSet#UnionOne"), List(buildSet(as), t(a)), typ) 94 | } 95 | } 96 | buildSet(elems) 97 | case sil.AnySetUnion(left, right) => 98 | if (isMultiset(e)) FuncApp(Identifier("MultiSet#Union"), List(t(left), t(right)), typ) 99 | else FuncApp(Identifier("Set#Union"), List(t(left), t(right)), typ) 100 | case sil.AnySetIntersection(left, right) => 101 | if (isMultiset(e)) FuncApp(Identifier("MultiSet#Intersection"), List(t(left), t(right)), typ) 102 | else FuncApp(Identifier("Set#Intersection"), List(t(left), t(right)), typ) 103 | case sil.AnySetSubset(left, right) => 104 | if (isMultiset(left)) FuncApp(Identifier("MultiSet#Subset"), List(t(left), t(right)), Bool) 105 | else FuncApp(Identifier("Set#Subset"), List(t(left), t(right)), Bool) 106 | case sil.AnySetMinus(left, right) => 107 | if (isMultiset(e)) FuncApp(Identifier("MultiSet#Difference"), List(t(left), t(right)), typ) 108 | else FuncApp(Identifier("Set#Difference"), List(t(left), t(right)), typ) 109 | case sil.AnySetContains(left, right) => 110 | if (isMultiset(right)) 111 | FuncApp(Identifier("MultiSet#Select"), List(t(right), t(left)), Int) 112 | //MapSelect(t(right),Seq(t(left))) 113 | else 114 | MapSelect(t(right),Seq(t(left))) 115 | case sil.AnySetCardinality(set) => 116 | if (isMultiset(set)) FuncApp(Identifier("MultiSet#Card"), t(set), Bool) 117 | else FuncApp(Identifier("Set#Card"), t(set), Bool) 118 | case sil.EqCmp(left, right) => 119 | if (isMultiset(left)) FuncApp(Identifier("MultiSet#Equal"), List(t(left), t(right)), Bool) 120 | else FuncApp(Identifier("Set#Equal"), List(t(left), t(right)), Bool) 121 | case sil.NeCmp(left, right) => 122 | if (isMultiset(left)) UnExp(Not, FuncApp(Identifier("MultiSet#Equal"), List(t(left), t(right)), Bool)) 123 | else UnExp(Not, FuncApp(Identifier("Set#Equal"), List(t(left), t(right)), Bool)) 124 | case _ => sys.error("not a set expression") 125 | } 126 | } 127 | 128 | override def translateSetType(setType: sil.SetType): Type = { 129 | used = true 130 | NamedType("Set", translateType(setType.elementType)) 131 | } 132 | 133 | override def translateMultisetType(setType: sil.MultisetType): Type = { 134 | used = true 135 | NamedType("MultiSet", translateType(setType.elementType)) 136 | } 137 | 138 | /** 139 | * Reset the state of this module so that it can be used for new program. This method is called 140 | * after verifier gets a new program. 141 | */ 142 | override def reset(): Unit = { 143 | used = false 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/Optimizer.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | /** 10 | * Optimize a given Boogie program or expression. 11 | 12 | */ 13 | object Optimizer { 14 | 15 | /** 16 | * Optimizes a Boogie program or expression by performing the following simplifications: 17 | * - Constant folding for booleans, integers and reals. 18 | * - Removal of dead branches. 19 | * - Removal of assertions known to hold. 20 | * 21 | * Constant folding partly taken from Transformer.simplify from SIL, but added more optimizations. 22 | */ 23 | def optimize(n: Node): Node = { 24 | /* Always optimize children first, then treat parent. */ 25 | Transformer.transform(n)(_ => true, { 26 | case UnExp(Not, BoolLit(literal)) => 27 | BoolLit(!literal) 28 | case UnExp(Not, UnExp(Not, single)) => single 29 | 30 | case BinExp(TrueLit(), And, right) => right 31 | case BinExp(left, And, TrueLit()) => left 32 | case BinExp(FalseLit(), And, _) => FalseLit() 33 | case BinExp(_, And, FalseLit()) => FalseLit() 34 | 35 | case BinExp(FalseLit(), Or, right) => right 36 | case BinExp(left, Or, FalseLit()) => left 37 | case BinExp(TrueLit(), Or, _) => TrueLit() 38 | case BinExp(_, Or, TrueLit()) => TrueLit() 39 | 40 | case BinExp(FalseLit(), Implies, _) => TrueLit() 41 | case BinExp(_, Implies, TrueLit()) => TrueLit() 42 | case BinExp(TrueLit(), Implies, FalseLit()) => FalseLit() 43 | case BinExp(TrueLit(), Implies, consequent) => consequent 44 | 45 | case BinExp(BoolLit(left), EqCmp, BoolLit(right)) => BoolLit(left == right) 46 | case BinExp(FalseLit(), EqCmp, right) => UnExp(Not, right) 47 | case BinExp(left, EqCmp, FalseLit()) => UnExp(Not, left) 48 | case BinExp(TrueLit(), EqCmp, right) => right 49 | case BinExp(left, EqCmp, TrueLit()) => left 50 | case BinExp(IntLit(left), EqCmp, IntLit(right)) => BoolLit(left == right) 51 | case BinExp(RealLit(left), EqCmp, RealLit(right)) => BoolLit(left == right) 52 | 53 | case BinExp(BoolLit(left), NeCmp, BoolLit(right)) => BoolLit(left != right) 54 | case BinExp(FalseLit(), NeCmp, right) => right 55 | case BinExp(left, NeCmp, FalseLit()) => left 56 | case BinExp(TrueLit(), NeCmp, right) => UnExp(Not, right) 57 | case BinExp(left, NeCmp, TrueLit()) => UnExp(Not, left) 58 | case BinExp(IntLit(left), NeCmp, IntLit(right)) => BoolLit(left != right) 59 | case BinExp(RealLit(left), NeCmp, RealLit(right)) => BoolLit(left != right) 60 | 61 | case CondExp(TrueLit(), ifTrue, _) => ifTrue 62 | case CondExp(FalseLit(), _, ifFalse) => ifFalse 63 | case CondExp(_, FalseLit(), FalseLit()) => 64 | FalseLit() 65 | case CondExp(_, TrueLit(), TrueLit()) => 66 | TrueLit() 67 | case CondExp(condition, FalseLit(), TrueLit()) => 68 | UnExp(Not, condition) 69 | case CondExp(condition, TrueLit(), FalseLit()) => condition 70 | case CondExp(condition, FalseLit(), ifFalse) => 71 | BinExp(UnExp(Not, condition), And, ifFalse) 72 | case CondExp(condition, TrueLit(), ifFalse) => 73 | BinExp(condition, Or, ifFalse) 74 | case CondExp(condition, ifTrue, FalseLit()) => 75 | BinExp(condition, And, ifTrue) 76 | case CondExp(condition, ifTrue, TrueLit()) => 77 | BinExp(UnExp(Not, condition), Or, ifTrue) 78 | 79 | case Forall(_, _, BoolLit(literal), _, _) => 80 | BoolLit(literal) 81 | case Exists(_, _, BoolLit(literal), _) => 82 | BoolLit(literal) 83 | 84 | case UnExp(Minus, IntLit(literal)) => IntLit(-literal) 85 | case UnExp(Minus, RealLit(literal)) => RealLit(-literal) 86 | case UnExp(Minus, UnExp(Minus, single)) => single 87 | 88 | case BinExp(IntLit(left), GeCmp, IntLit(right)) => 89 | BoolLit(left >= right) 90 | case BinExp(IntLit(left), GtCmp, IntLit(right)) => 91 | BoolLit(left > right) 92 | case BinExp(IntLit(left), LeCmp, IntLit(right)) => 93 | BoolLit(left <= right) 94 | case BinExp(IntLit(left), LtCmp, IntLit(right)) => 95 | BoolLit(left < right) 96 | 97 | case BinExp(IntLit(left), Add, IntLit(right)) => 98 | IntLit(left + right) 99 | case BinExp(IntLit(left), Sub, IntLit(right)) => 100 | IntLit(left - right) 101 | case BinExp(IntLit(left), Mul, IntLit(right)) => 102 | IntLit(left * right) 103 | // This case was removed - the evaluation as doubles and translation of RealLit can introduce rounding/precision errors 104 | /* case BinExp(IntLit(left), Div, IntLit(right)) if right != 0 => 105 | RealLit(left.toDouble / right.toDouble)*/ 106 | 107 | /* In the general case, Carbon uses the SMT division and modulo. Scala's division is not in-sync with SMT division. 108 | For nonnegative dividends and divisors, all used division and modulo definitions coincide. So, in order to not 109 | not make any assumptions on the SMT division, division and modulo are simplified only if the dividend and divisor 110 | are nonnegative. 111 | */ 112 | case BinExp(IntLit(left), IntDiv, IntLit(right)) if left >= 0 && right > 0 => 113 | IntLit(left / right) 114 | case BinExp(IntLit(left), Mod, IntLit(right)) if left >= 0 && right > 0 => 115 | IntLit(left % right) 116 | 117 | case BinExp(RealLit(left), GeCmp, RealLit(right)) => 118 | BoolLit(left >= right) 119 | case BinExp(RealLit(left), GtCmp, RealLit(right)) => 120 | BoolLit(left > right) 121 | case BinExp(RealLit(left), LeCmp, RealLit(right)) => 122 | BoolLit(left <= right) 123 | case BinExp(RealLit(left), LtCmp, RealLit(right)) => 124 | BoolLit(left < right) 125 | 126 | case BinExp(RealLit(left), Add, RealLit(right)) => 127 | RealLit(left + right) 128 | case BinExp(RealLit(left), Sub, RealLit(right)) => 129 | RealLit(left - right) 130 | case BinExp(RealLit(left), Mul, RealLit(right)) => 131 | RealLit(left * right) 132 | case BinExp(RealLit(left), Div, RealLit(right)) if right != 0 => 133 | RealLit(left / right) 134 | case BinExp(RealLit(left), Mod, RealLit(right)) if right != 0 => 135 | RealLit(left % right) 136 | 137 | case If(TrueLit(), thn, _) => thn 138 | case If(FalseLit(), _, els) => els 139 | 140 | case If(_, thn, els) if thn.children.isEmpty && els.children.isEmpty => 141 | Statements.EmptyStmt 142 | 143 | case Assert(TrueLit(), _) => Statements.EmptyStmt 144 | case Assume(TrueLit()) => Statements.EmptyStmt 145 | }) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/boogie/utility.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.boogie 8 | 9 | 10 | /** 11 | * Utility methods for statements. 12 | */ 13 | object Statements { 14 | /** An empty statement. */ 15 | val EmptyStmt = Seqn(Nil) 16 | 17 | /** 18 | * Returns a list of all actual statements contained in a given statement. That 19 | * is, all statements except `Seqn`, including statements in the branches of 20 | * if's. 21 | * 22 | * Taken from the Viper AST with minimal adaptation. 23 | */ 24 | def children(s: Stmt): Seq[Stmt] = { 25 | s match { 26 | case If(_, thn, els) => Seq(s) ++ children(thn) ++ children(els) 27 | case NondetIf(thn, els) => Seq(s) ++ children(thn) ++ children(els) 28 | case Seqn(ss) => ss flatMap children 29 | case CommentBlock(_, stmt) => Seq(stmt) 30 | case _ => List(s) 31 | } 32 | } 33 | 34 | /** 35 | * Returns a list of all undeclared local variables used in this statement. 36 | * If the same local variable is used with different 37 | * types, an exception is thrown. 38 | * 39 | * Taken from the Viper AST with minimal adaptation. 40 | */ 41 | def undeclLocalVars(s: Stmt): Seq[LocalVar] = { 42 | def extractLocal(n: Node, decls: Seq[LocalVarDecl]) = n match { 43 | case l: LocalVar => decls.find(_.name == l.name) match { 44 | case None => List(l) 45 | case Some(d) if d.typ != l.typ => { 46 | sys.error("Local variable " + l.name + " is declared with type " + d.typ + " but used with type " + l.typ + ".") 47 | } 48 | case _ => Nil 49 | } 50 | case _ => Nil 51 | } 52 | def combineLists(s1: Seq[LocalVar], s2: Seq[LocalVar]) = { 53 | for (l1 <- s1; l2 <- s2) { 54 | if (l1.name == l2.name && l1.typ != l2.typ) { 55 | sys.error("Local variable " + l1.name + " is used with different types " + l1.typ + " and " + l2.typ) 56 | } 57 | } 58 | (s1 ++ s2).distinct 59 | } 60 | def addDecls(n: Node, decls: Seq[LocalVarDecl]) = n match { 61 | case Exists(v, _, _, _) => decls ++ v 62 | case Forall(v, _, _, _, _) => decls ++ v 63 | case _ => decls 64 | } 65 | def combineResults(n: Node, decls: Seq[LocalVarDecl], localss: Seq[Seq[LocalVar]]) = { 66 | localss.fold(extractLocal(n, decls))(combineLists) 67 | } 68 | s.reduce(Nil, addDecls, combineResults) 69 | } 70 | } 71 | 72 | /** 73 | * Utility methods for AST nodes. 74 | 75 | */ 76 | object Nodes { 77 | 78 | /** 79 | * See Node.subnodes. 80 | */ 81 | def subnodes(n: Node): Seq[Node] = { 82 | n match { 83 | case Program(_, decls) => 84 | decls 85 | case LocalVarDecl(_, _, where) => 86 | where match { 87 | case Some(e) => Seq(e) 88 | case None => Nil 89 | } 90 | case Trigger(exps) => exps 91 | case _: Type => Nil 92 | case d: Decl => 93 | d match { 94 | case ConstDecl(_, _, _) => Nil 95 | case TypeDecl(_) => Nil 96 | case TypeAlias(_, _) => Nil 97 | case Func(_, args, _, _) => args 98 | case Axiom(exp) => Seq(exp) 99 | case GlobalVarDecl(_, _) => Nil 100 | case Procedure(_, ins, outs, body) => ins ++ outs ++ Seq(body) 101 | case CommentedDecl(_, ds, _, _) => ds 102 | case DeclComment(_) => Nil 103 | case LiteralDecl(_) => Nil 104 | } 105 | case ss: Stmt => 106 | ss match { 107 | case Assign(lhs, rhs) => Seq(lhs, rhs) 108 | case Assert(e, _) => Seq(e) 109 | case Assume(e) => Seq(e) 110 | case HavocImpl(es) => es 111 | case Comment(_) => Nil 112 | case CommentBlock(_, stmt) => Seq(stmt) 113 | case Seqn(s) => s 114 | case If(cond, thn, els) => Seq(cond, thn, els) 115 | case NondetIf(thn, els) => Seq(thn, els) 116 | case Label(_) => Nil 117 | case Goto(_) => Nil 118 | case LocalVarWhereDecl(_, where) => Seq(where) 119 | } 120 | case e: Exp => 121 | // Note: If you have to update this pattern match to make it exhaustive, it 122 | // might also be necessary to update the PrettyPrinter.toParenDoc method. 123 | e match { 124 | case IntLit(_) => Nil 125 | case BoolLit(_) => Nil 126 | case RealLit(_) => Nil 127 | case RealConv(exp) => Seq(exp) 128 | case LocalVar(_, _) => Nil 129 | case GlobalVar(_, _) => Nil 130 | case Const(_) => Nil 131 | case MapSelect(map, idxs) => Seq(map) ++ idxs 132 | case MapUpdate(map, idxs, value) => Seq(map) ++ idxs ++ Seq(value) 133 | case Old(exp) => Seq(exp) 134 | case CondExp(cond, thn, els) => Seq(cond, thn, els) 135 | case Exists(v, triggers, exp, _) => v ++ triggers ++ Seq(exp) 136 | case Forall(v, triggers, exp, _, _) => v ++ triggers ++ Seq(exp) 137 | case BinExp(left, _, right) => Seq(left, right) 138 | case UnExp(_, exp) => Seq(exp) 139 | case FuncApp(_, args, _) => args 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Transforms an expression using the function `f`; if `f` returns `Some(e)`, then the previous expression 146 | * is replaced by e, and otherwise the previous expression is reused. 147 | * The function `f` must produce expressions that are valid in the given context. For instance, it cannot 148 | * replace an integer literal by a boolean literal. 149 | */ 150 | def transform(exp: Exp, f: PartialFunction[Exp, Option[Exp]]): Exp = { 151 | val func = (e: Exp) => transform(e, f) 152 | val t = if (f.isDefinedAt(exp)) f(exp) else None 153 | t match { 154 | case Some(ee) => ee 155 | case None => 156 | exp match { 157 | case IntLit(i) => exp 158 | case BoolLit(b) => exp 159 | case RealLit(b) => exp 160 | case RealConv(exp) => RealConv(func(exp)) 161 | case LocalVar(n, tt) => exp 162 | case GlobalVar(n, tt) => exp 163 | case Const(i) => exp 164 | case MapSelect(map, idxs) => MapSelect(func(map), idxs map func) 165 | case MapUpdate(map, idxs, value) => MapUpdate(func(map), idxs map func, func(value)) 166 | case Old(e) => Old(func(e)) 167 | case CondExp(cond, thn, els) => CondExp(func(cond), func(thn), func(els)) 168 | case Exists(v, triggers, e, w) => Exists(v, (triggers map (_ match {case Trigger(es) => Trigger(es map func)})), func(e), w) 169 | case Forall(v, triggers, e, tv, w) => Forall(v, (triggers map (_ match {case Trigger(es) => Trigger(es map func)})), func(e), tv, w) 170 | case BinExp(left, binop, right) => BinExp(func(left), binop, func(right)) 171 | case UnExp(unop, e) => UnExp(unop, func(e)) 172 | case f@FuncApp(ff, args, typ) => { 173 | val fa = FuncApp(ff, args map func, typ) 174 | fa.showReturnType = f.showReturnType 175 | fa 176 | } 177 | } 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/test/resources/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/impls/DefaultInhaleModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules.impls 8 | 9 | import viper.carbon.modules._ 10 | import viper.silver.{ast => sil} 11 | import viper.carbon.boogie._ 12 | import viper.carbon.verifier.Verifier 13 | import viper.carbon.boogie.Implicits._ 14 | import viper.silver.verifier.PartialVerificationError 15 | 16 | /** 17 | * The default implementation of a [[viper.carbon.modules.InhaleModule]]. 18 | 19 | */ 20 | class DefaultInhaleModule(val verifier: Verifier) extends InhaleModule with StatelessComponent { 21 | 22 | import verifier._ 23 | import expModule._ 24 | import stateModule._ 25 | import mainModule._ 26 | 27 | def name = "Inhale module" 28 | 29 | override def start(): Unit = { 30 | register(this) 31 | } 32 | 33 | override def inhale(exps: Seq[(sil.Exp, PartialVerificationError)], addDefinednessChecks: Boolean, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt = { 34 | val current_state = stateModule.state 35 | if(insidePackageStmt && !addDefinednessChecks) { // replace currentState with the correct state in which the inhale occurs during packaging the wand 36 | stateModule.replaceState(statesStackForPackageStmt(0).asInstanceOf[StateRep].state) 37 | } 38 | 39 | 40 | val stmt = 41 | (exps map (e => inhaleConnective(e._1.whenInhaling, e._2, addDefinednessChecks = addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt = insidePackageStmt))) ++ 42 | assumeGoodState 43 | 44 | if(insidePackageStmt && !addDefinednessChecks) { 45 | /* all the assumptions made during packaging a wand (except assumptions about the global state before the package statement) 46 | * should be replaced by updates to state booleans (see documentation for 'exchangeAssumesWithBoolean') */ 47 | stateModule.replaceState(current_state) 48 | wandModule.exchangeAssumesWithBoolean(stmt, statesStackForPackageStmt.head.asInstanceOf[StateRep].boolVar) 49 | } else { 50 | stmt 51 | } 52 | } 53 | 54 | def containsFunc(exp: sil.Exp): Boolean = { 55 | var res = false 56 | exp visit { 57 | case _: sil.FuncApp => res = true 58 | } 59 | res 60 | } 61 | 62 | /** 63 | * Inhales Viper expression connectives (such as logical and/or) and forwards the 64 | * translation of other expressions to the inhale components. 65 | */ 66 | private def inhaleConnective(e: sil.Exp, error: PartialVerificationError, addDefinednessChecks: Boolean, statesStackForPackageStmt: List[Any] = null, insidePackageStmt: Boolean = false): Stmt = { 67 | 68 | def maybeDefCheck(eDef: sil.Exp) : Stmt = { if(addDefinednessChecks) checkDefinedness(eDef, error, insidePackageStmt = insidePackageStmt) else Statements.EmptyStmt } 69 | 70 | def maybeFreeAssumptions(eAssm: sil.Exp) : Stmt = { 71 | /* definedness checks include free assumptions, so only add free assumption if no definedness checks were made 72 | GP: Currently, inhale during packaging a wand still requires these additional free assumptions even if definedness 73 | checks are made. For instance, the example wands/regression/issue029.vpr in the Viper test suite requires 74 | this. That's why there is an additional conjunct in the if condition. However, this special case during 75 | packaging a wand needs to be revisited. 76 | */ 77 | if(addDefinednessChecks && !insidePackageStmt) { 78 | Nil 79 | } else { 80 | MaybeCommentBlock("Free assumptions (inhale module)", allFreeAssumptions(eAssm)) 81 | } 82 | } 83 | 84 | val res = 85 | e match { 86 | case sil.And(e1, e2) => 87 | inhaleConnective(e1, error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt) :: 88 | inhaleConnective(e2, error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt) :: 89 | Nil 90 | case sil.Implies(e1, e2) => 91 | val defCheck = maybeDefCheck(e1) 92 | val lhsTranslation = if(insidePackageStmt && addDefinednessChecks) { wandModule.getCurOpsBoolvar() ==> translateExpInWand(e1) } else { translateExp(e1) } 93 | 94 | defCheck ++ 95 | If(lhsTranslation, inhaleConnective(e2, error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt), Statements.EmptyStmt) 96 | case sil.CondExp(c, e1, e2) => 97 | val defCheck = maybeDefCheck(c) 98 | val condTranslation = if(insidePackageStmt && addDefinednessChecks) { wandModule.getCurOpsBoolvar() ==> translateExpInWand(c) } else { translateExp(c) } 99 | 100 | defCheck ++ 101 | If(condTranslation, inhaleConnective(e1, error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt), 102 | inhaleConnective(e2, error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt)) 103 | case sil.Let(declared,boundTo,body) if !body.isPure || addDefinednessChecks => 104 | { 105 | val defCheck = maybeDefCheck(boundTo) 106 | val u = env.makeUniquelyNamed(declared) // choose a fresh binder 107 | env.define(u.localVar) 108 | defCheck :: 109 | Assign(translateLocalVar(u.localVar),translateExp(boundTo)) :: 110 | inhaleConnective(body.replace(declared.localVar, u.localVar), error, addDefinednessChecks, statesStackForPackageStmt, insidePackageStmt) :: 111 | { 112 | env.undefine(u.localVar) 113 | Nil 114 | } 115 | } 116 | case _ => 117 | def transformStmtInsidePackage(s: Stmt): Stmt = { 118 | if(insidePackageStmt && addDefinednessChecks) { 119 | wandModule.exchangeAssumesWithBoolean(s, statesStackForPackageStmt.head.asInstanceOf[StateRep].boolVar) 120 | } else { 121 | s 122 | } 123 | } 124 | val definednessChecks = maybeDefCheck(e) 125 | val freeAssms = maybeFreeAssumptions(e) 126 | val stmt = components map (_.inhaleExp(e, error)) 127 | if (stmt.children.isEmpty) 128 | sys.error(s"missing translation for inhaling of $e") 129 | 130 | //do not transform definednessChecks inside package (backwards compatible with older version) 131 | val retStmt = 132 | transformStmtInsidePackage(if (containsFunc(e)) Seq(assumeGoodState) else Seq()) ++ 133 | definednessChecks ++ 134 | transformStmtInsidePackage(stmt ++ (if (e.isPure) Seq() else Seq(assumeGoodState))) ++ 135 | freeAssms 136 | //(if (containsFunc(e)) assumeGoodState else Seq[Stmt]()) ++ stmt ++ (if (e.isPure) Seq[Stmt]() else assumeGoodState) 137 | 138 | // if we are inside package statement, then all assumptions should be replaced with conjinctions with ops.boolVar 139 | retStmt 140 | } 141 | if(insidePackageStmt && addDefinednessChecks) { 142 | If(wandModule.getCurOpsBoolvar(), res, Statements.EmptyStmt) 143 | } else { 144 | res 145 | } 146 | } 147 | 148 | override def inhaleExp(e: sil.Exp, error: PartialVerificationError): Stmt = { 149 | if (e.isPure) { 150 | Assume(translateExp(e)) 151 | } else { 152 | Nil 153 | } 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/scala/viper/carbon/modules/StateModule.scala: -------------------------------------------------------------------------------- 1 | // This Source Code Form is subject to the terms of the Mozilla Public 2 | // License, v. 2.0. If a copy of the MPL was not distributed with this 3 | // file, You can obtain one at http://mozilla.org/MPL/2.0/. 4 | // 5 | // Copyright (c) 2011-2021 ETH Zurich. 6 | 7 | package viper.carbon.modules 8 | 9 | import components.{CarbonStateComponent, ComponentRegistry} 10 | import viper.silver.components.StatefulComponent 11 | import viper.carbon.boogie.{Exp, LocalVar, LocalVarDecl, Stmt} 12 | 13 | /** 14 | * A module for dealing with the state of a program during execution. Allows other modules 15 | * to register [[viper.carbon.modules.components.CarbonStateComponent]]s that contribute to the 16 | * state. 17 | 18 | */ 19 | trait StateModule extends Module with ComponentRegistry[CarbonStateComponent] with StatefulComponent { 20 | 21 | /** 22 | * Returns an assumption that the current state is 'good', or well-formed. 23 | */ 24 | def assumeGoodState: Stmt 25 | 26 | /** 27 | * Returns a static invocation of the 'is good state' function with the arguments from 28 | * `stateContributions`. 29 | */ 30 | def staticGoodState: Exp 31 | 32 | /** 33 | * Returns invocation of the 'is good state' function with the arguments from 34 | * `currentStateContributions`. 35 | */ 36 | def currentGoodState: Exp 37 | 38 | /** 39 | * The statements necessary to initialize the Boogie state of all CarbonStateComponent modules. 40 | * This will also set the variable names used in the current state to the initial ones. 41 | */ 42 | def initBoogieState: Stmt 43 | 44 | /** 45 | * The statements necessary to reset the Boogie state of all CarbonStateComponent modules. 46 | * Note that this should modify the *current* state (i.e. reassign all Boogie state in use), not create a fresh state 47 | */ 48 | def resetBoogieState: Stmt 49 | 50 | def reset() = {initBoogieState; initOldState} // make the call to reset all Boogie state (e.g. variable names) - the latter call is probably not necessary, since it always gets called before relevant, but the former can affect the translation of many features (e.g. axioms) 51 | 52 | /** 53 | * The statements necessary to initialize old(state). 54 | */ 55 | def initOldState: Stmt 56 | 57 | /** 58 | * The name and type of the static contribution of the state components registered with this module to the state. The returned value should remain the 59 | * same even if the state is changed. 60 | */ 61 | def staticStateContributions(withHeap : Boolean = true, withPermissions : Boolean = true): Seq[LocalVarDecl] 62 | 63 | /** 64 | * The current values for all registered components' state contributions. The number of elements 65 | * in the list and the types must correspond to the ones given in `stateContributions`. 66 | * 67 | * Compared to the currentStateContributions [ALEX: maybe to be deprecated], these expressions may include e.g. old(..) around a heap variable. 68 | */ 69 | def currentStateContributionValues: Seq[Exp] = { 70 | // TODO: This implementation does not return the values of the old or pure state if we're currently using one of those; 71 | // instead it only wraps expressions into old(..) if we're using the old state. 72 | stateContributionValues(state) 73 | } 74 | 75 | def stateContributionValues(snap : StateSnapshot): Seq[Exp] 76 | 77 | type StateSnapshot// used to abstractly capture the Boogie variables, old expressions etc. used to represent a current state in the translation 78 | 79 | /** 80 | * Backup the current state and return enough information such that it can 81 | * be restored again at a later point. 82 | * 83 | * Replace the current state with a version named after the provided String (the returned Stmt includes the necessary setup code) 84 | * The returned state is the *previous* state (useful for restoring this later) 85 | * 86 | * This code is typically used in the following pattern: 87 | * val tmpStateName = // choose some name 88 | * val (stmt, state) = stateModule.freshTempState(tmpStateName) 89 | * val resultingStatements = stmt ++ // something which generates a statement with respect to the temp state 90 | * stateModule.replaceState(state) // go back to the original state 91 | * 92 | * 93 | */ 94 | def freshTempState(name: String, discardCurrent: Boolean = false, initialise: Boolean = false): (Stmt, StateSnapshot) 95 | 96 | /** 97 | * Returns a fresh state that is not an old state. This method has no side-effects on the current state. 98 | */ 99 | def freshTempStateKeepCurrent(name: String) : StateSnapshot 100 | 101 | /** 102 | * Returns the statement that initializes the input state to the current state. This method has no side-effects on 103 | * the current state. 104 | */ 105 | def initToCurrentStmt(snapshot: StateSnapshot) : Stmt 106 | 107 | /** 108 | * Create a state without any information and return a snapshot of the created state. 109 | * if init is true then the Stmt returned will contain the initialization according to the state 110 | * components 111 | * Note: the current state is not affected by this in contrast to "freshTempState" 112 | * 113 | * ALEX: to get rid of - this seems redundant 114 | */ 115 | def freshEmptyState(name: String,init:Boolean): (Stmt, StateSnapshot) 116 | 117 | /** 118 | * Restore the state to a given snapshot. 119 | */ 120 | def replaceState(snapshot: StateSnapshot): Unit 121 | 122 | /** 123 | * Get the current old state. 124 | */ 125 | def oldState: StateSnapshot 126 | 127 | 128 | /** 129 | * Get a pure state without a heap. 130 | */ 131 | def pureState: StateSnapshot 132 | 133 | /** 134 | * Replace the old state with a given snapshot. 135 | */ 136 | def replaceOldState(snapshot: StateSnapshot): Unit 137 | 138 | /** 139 | * Get the current state. 140 | */ 141 | def state: StateSnapshot 142 | 143 | /** 144 | * Get a copy of the current state's snapshot. Should guarantee that if the current state changes then the 145 | * returned copy is not affected. 146 | * Gaurav: I think "def state" should do this, since it doesn't make sense to me that the client sees updates of 147 | * the current state without making additional queries. 148 | * ALEX: I agree, and in practice it does do this, I believe - the underlying map is reassigned before any updates, and the "state" method just returns an alias of this map. 149 | * We could try refactoring this to just be one method (I'm not sure whether the explicit copy is necessary or not). 150 | */ 151 | def getCopyState:StateSnapshot 152 | 153 | /** 154 | * Are we currently using an 'old' state? Implies that we should wrap relevant state components in "Old" 155 | */ 156 | def stateModuleIsUsingOldState: Boolean 157 | 158 | /** 159 | * Are we currently using a pure state with no heap? 160 | */ 161 | def stateModuleIsUsingPureState: Boolean 162 | 163 | /** 164 | * * Store a StateSnapshot in a retrievable map, with the given name as key 165 | * @param name the key to associate with this StateSnapshot 166 | * @param snapshot the StateSnapshot to store 167 | */ 168 | def stateRepositoryPut(name:String, snapshot: StateSnapshot): Unit 169 | 170 | /* 171 | * Analogous get operation to the put above. 172 | */ 173 | def stateRepositoryGet(name:String) : Option[StateSnapshot] 174 | 175 | /* 176 | * is used to store relevant blocks needed to use a newly created state in executing package statement 177 | */ 178 | 179 | /** 180 | * returns statement of equating heap represented by snapshot to current heap 181 | * e.g. the returned statement is in the form of: Assume Heap1 == Heap2 182 | */ 183 | def equateHeaps(snapshot: StateSnapshot, c: CarbonStateComponent):Stmt 184 | 185 | /** 186 | * Representation of state used in wandModule. Pair of stateSnapshot and boolean variable carrying assumptions about this state. 187 | */ 188 | case class StateRep(state: StateSnapshot, boolVar: LocalVar) 189 | 190 | case class StateSetup(usedState: StateRep, initStmt: Stmt) 191 | } 192 | --------------------------------------------------------------------------------