-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathVerification.scala
More file actions
232 lines (198 loc) · 8.7 KB
/
Copy pathVerification.scala
File metadata and controls
232 lines (198 loc) · 8.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
//
// Copyright (c) 2011-2019 ETH Zurich.
package viper.silicon.interfaces
import viper.silicon.interfaces.state.Chunk
import viper.silicon.reporting.{Converter, DomainEntry, ExtractedFunction, ExtractedModel, ExtractedModelEntry, GenericDomainInterpreter, ModelInterpreter, NullRefEntry, RefEntry, UnprocessedModelEntry, VarEntry}
import viper.silicon.state.{State, Store}
import viper.silver.verifier.{ApplicationEntry, ConstantEntry, Counterexample, FailureContext, Model, ValueEntry, VerificationError}
import viper.silicon.state.terms.Term
import viper.silicon.verifier.Verifier
import viper.silver.ast
import viper.silver.ast.Program
/*
* Results
*/
/* TODO: Extract appropriate interfaces and then move the implementations
* outside of the interfaces package.
*/
/* TODO: Make VerificationResult immutable */
sealed abstract class VerificationResult {
var previous: Vector[VerificationResult] = Vector() //Sets had problems with equality
val continueVerification: Boolean = true
def isFatal: Boolean
def &&(other: => VerificationResult): VerificationResult
/* Attention: Parameter 'other' of 'combine' is a function! That is, the following
* statements
* println(other)
* println(other)
* will invoke the function twice, which might not be what you really want!
*/
def combine(other: => VerificationResult): VerificationResult = {
if (this.continueVerification){
val r: VerificationResult = other
/* Result of combining a failure with a non failure should be a failure.
* When combining two failures, the failure with 'continueVerification'
* set to false (if any) should be the 'head' result */
(this, r) match {
case (_: FatalResult, _: FatalResult) | (_: FatalResult, _: NonFatalResult) if r.continueVerification =>
this.previous = (this.previous :+ r) ++ r.previous
this
case _ =>
r.previous = (r.previous :+ this) ++ this.previous
r
}
} else this
}
}
sealed abstract class FatalResult extends VerificationResult {
val isFatal = true
def &&(other: => VerificationResult): VerificationResult = this
}
sealed abstract class NonFatalResult extends VerificationResult {
val isFatal = false
/* Attention: Parameter 'other' of '&&' is a function! That is, the following
* statements
* println(other)
* println(other)
* will invoke the function twice, which might not be what you really want!
*/
def &&(other: => VerificationResult): VerificationResult = {
val r: VerificationResult = other
r.previous = (r.previous :+ this) ++ this.previous
r
}
}
case class Success() extends NonFatalResult {
override val toString = "Success"
}
case class Unreachable() extends NonFatalResult {
override val toString = "Unreachable"
}
case class Failure/*[ST <: Store[ST],
H <: Heap[H],
S <: State[ST, H, S]]*/
(message: VerificationError, override val continueVerification: Boolean = true)
extends FatalResult {
override lazy val toString: String = message.readableMessage
}
case class SiliconFailureContext(branchConditions: Seq[ast.Exp], counterExample: Option[Counterexample]) extends FailureContext {
lazy val branchConditionString: String = {
if(branchConditions.nonEmpty) {
val branchConditionsString =
branchConditions
.map(bc => s"$bc [ ${bc.pos} ] ")
.mkString("\t\t"," ~~> ","")
s"\n\t\tunder branch conditions:\n$branchConditionsString"
} else {
""
}
}
lazy val counterExampleString: String = {
counterExample.fold("")(ce => s"\n\t\tcounterexample:\n$ce")
}
override lazy val toString: String = branchConditionString + counterExampleString
}
trait SiliconCounterexample extends Counterexample {
val internalStore: Store
lazy val store: Map[String, Term] = internalStore.values.map{case (k, v) => k.name -> v}
def withStore(s: Store) : SiliconCounterexample
}
case class SiliconNativeCounterexample(internalStore: Store, heap: Iterable[Chunk], oldHeaps: Map[String,Iterable[Chunk]], model: Model) extends SiliconCounterexample {
override def withStore(s: Store): SiliconCounterexample = {
SiliconNativeCounterexample(s, heap, oldHeaps, model)
}
}
case class SiliconVariableCounterexample(internalStore: Store, nativeModel: Model) extends SiliconCounterexample {
override val model: Model = {
Model(internalStore.values.filter{
case (_,v) => nativeModel.entries.contains(v.toString)
}.map{
case (k, v) => k.name -> nativeModel.entries(v.toString)
})
}
override def withStore(s: Store): SiliconCounterexample = {
SiliconVariableCounterexample(s, nativeModel)
}
}
case class SiliconMappedCounterexample(internalStore: Store,
heap: Iterable[Chunk],
oldHeaps: State.OldHeaps,
nativeModel: Model,
program: Program)
extends SiliconCounterexample {
val converter: Converter =
Converter(nativeModel, internalStore, heap, oldHeaps, program)
val model: Model = nativeModel
val interpreter: ModelInterpreter[ExtractedModelEntry, Seq[ExtractedModelEntry]] = GenericDomainInterpreter(converter)
private var heapNames: Map[ValueEntry, String] = Map()
override lazy val toString: String = {
val extractedModel = converter.extractedModel
val bufModels = converter.modelAtLabel
.map { case (label, model) => s"model at label $label:\n${interpret(model).toString}"}
.mkString("\n")
val (bufDomains, bufNonDomainFunctions) = functionsToString()
s"$bufModels\non return: \n${interpret(extractedModel).toString} $bufDomains $bufNonDomainFunctions"
}
private def interpret(t: ExtractedModel): ExtractedModel =
ExtractedModel(t.entries.map{ case (name, entry) => (name, interpret(entry)) })
private def interpret(t: ExtractedModelEntry): ExtractedModelEntry = interpreter.interpret(t, Seq())
private def functionsToString() : (String, String) = {
//extractedModel.entries should be a bijection so we can invert the map
//Since we only care about ref entries we can remove everything else
val invEntries = converter.extractedModel.entries.flatMap{ case (name, entry) =>
entry match {
case RefEntry(symName, _) => Some(symName -> name)
case NullRefEntry(symName)=> Some(symName -> name)
case _ => None
}}
val replaceDomainFunction = converter.domains.map{
case DomainEntry(names, types, functions) =>
DomainEntry(names, types, renameFunctions(functions, invEntries))
}
val bufDomains = replaceDomainFunction.nonEmpty match {
case true => "\nDomain: \n" + replaceDomainFunction.map(domain => domain.toString()).mkString("\n")
case false => ""
}
val replaceNonDomainFunctions = renameFunctions(converter.nonDomainFunctions, invEntries)
val bufNonDomainFunctions = replaceNonDomainFunctions.nonEmpty match {
case true => "\nFunctions: \n" + replaceNonDomainFunctions.map(fn => fn.toString()).mkString("\n")
case false => ""
}
(bufDomains, bufNonDomainFunctions)
}
private def renameFunctions(fn: Seq[ExtractedFunction], invEntries: Map[String, String]) : Seq[ExtractedFunction] = {
fn.map{
case ExtractedFunction(fname, argtypes, returnType, options, default) => {
val optionsNew = options.map(x => x._1.map(y => renameFunction(y, invEntries)) -> renameFunction(x._2, invEntries))
val defaultNew = renameFunction(default, invEntries)
ExtractedFunction(fname, argtypes, returnType, optionsNew, defaultNew)
}
}
}
private def renameFunction(entry:ExtractedModelEntry, invEntries: Map[String, String]): ExtractedModelEntry = {
entry match {
case VarEntry(name, sort) => VarEntry(invEntries.get(name).getOrElse(name), sort)
case UnprocessedModelEntry(unproEntry) => {
unproEntry match {
case ApplicationEntry("$Snap.combine", Seq(_, _)) => renameSnap(unproEntry)
case ConstantEntry("$Snap.unit") => renameSnap(unproEntry)
case _ => entry
}
}
case _ => entry
}
}
private def renameSnap(entry: ValueEntry): ExtractedModelEntry = {
if (!heapNames.contains(entry)) {
heapNames += (entry -> ("heap_" + heapNames.size.toString))
}
UnprocessedModelEntry(ConstantEntry(
heapNames.get(entry).orNull))
}
override def withStore(s: Store): SiliconCounterexample = {
SiliconMappedCounterexample(s, heap, oldHeaps, nativeModel, program)
}
}