├── 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 |
--------------------------------------------------------------------------------