-
Notifications
You must be signed in to change notification settings - Fork 40
Expand file tree
/
Copy pathPathConditions.scala
More file actions
402 lines (309 loc) · 12.8 KB
/
Copy pathPathConditions.scala
File metadata and controls
402 lines (309 loc) · 12.8 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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
// 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.decider
import viper.silicon.common.collections.immutable.InsertionOrderedSet
import viper.silicon.Stack
import viper.silicon.state.terms._
import viper.silicon.utils.Counter
import viper.silver.ast
/*
* Interfaces
*/
/* TODO: 'contains' functionality currently not needed. If removed, 'allAssumptions' could
* probably removed as well.
* Benchmark runtime difference!
*/
trait RecordedPathConditions {
def branchConditions: Stack[Term]
def branchConditionExps: Stack[Option[ast.Exp]]
def assumptions: InsertionOrderedSet[Term]
def declarations: InsertionOrderedSet[Decl]
def contains(assumption: Term): Boolean
def conditionalized: Seq[Term]
def quantified(quantifier: Quantifier,
qvars: Seq[Var],
triggers: Seq[Trigger],
name: String,
isGlobal: Boolean,
ignore: Term /* TODO: Hack, implement properly */)
: (Seq[Term], Seq[Quantification])
}
trait PathConditionStack extends RecordedPathConditions {
def setCurrentBranchCondition(condition: Term, conditionExp: Option[ast.Exp]): Unit
def add(assumption: Term): Unit
def add(declaration: Decl): Unit
def pushScope(): Unit
def popScope(): Unit
def mark(): Mark
def popUntilMark(mark: Mark): Unit
def after(mark: Mark): RecordedPathConditions
def isEmpty: Boolean
def duplicate(): PathConditionStack
/* Public method 'clone' impossible, see https://issues.scala-lang.org/browse/SI-6760 */
}
/*
* Implementations (mostly mutable!)
*/
private class PathConditionStackLayer
extends Cloneable {
private var _branchCondition: Option[Term] = None
private var _branchConditionExp: Option[Option[ast.Exp]] = None
private var _globalAssumptions: InsertionOrderedSet[Term] = InsertionOrderedSet.empty
private var _nonGlobalAssumptions: InsertionOrderedSet[Term] = InsertionOrderedSet.empty
private var _declarations: InsertionOrderedSet[Decl] = InsertionOrderedSet.empty
def branchCondition: Option[Term] = _branchCondition
def branchConditionExp: Option[Option[ast.Exp]] = _branchConditionExp
def globalAssumptions: InsertionOrderedSet[Term] = _globalAssumptions
def nonGlobalAssumptions: InsertionOrderedSet[Term] = _nonGlobalAssumptions
def declarations: InsertionOrderedSet[Decl] = _declarations
def assumptions: InsertionOrderedSet[Term] = globalAssumptions ++ nonGlobalAssumptions
def pathConditions: InsertionOrderedSet[Term] = assumptions ++ branchCondition
def branchCondition_=(condition: Term): Unit = {
assert(_branchCondition.isEmpty,
s"Branch condition is already set (to ${_branchCondition.get}), "
+ s"won't override (with $condition).")
_branchCondition = Some(condition)
}
def branchConditionExp_=(condition: Option[ast.Exp]): Unit = {
assert(_branchConditionExp.isEmpty,
s"Branch condition is already set (to ${_branchConditionExp.get}), "
+ s"won't override (with $condition).")
_branchConditionExp = Some(condition)
}
def add(assumption: Term): Unit = {
assert(
!assumption.isInstanceOf[And],
s"Unexpectedly found a conjunction (should have been split): $assumption")
/* TODO: Don't record branch conditions as assumptions */
if (PathConditions.isGlobal(assumption))
_globalAssumptions += assumption
else
_nonGlobalAssumptions += assumption
}
def add(declaration: Decl): Unit = _declarations += declaration
def contains(pathCondition: Term): Boolean = {
assert(
!pathCondition.isInstanceOf[And],
s"Unexpectedly found a conjunction (should have been split): $pathCondition")
if (PathConditions.isGlobal(pathCondition))
/* Assumption: globals are never used as branch conditions */
_globalAssumptions.contains(pathCondition)
else
_nonGlobalAssumptions.contains(pathCondition) || _branchCondition.contains(pathCondition)
}
override def clone(): AnyRef = {
/* Attention: the original and its clone must not share any mutable data! */
super.clone()
}
}
private trait LayeredPathConditionStackLike {
protected def branchConditions(layers: Stack[PathConditionStackLayer]): Stack[Term] =
layers.flatMap(_.branchCondition)
protected def branchConditionExps(layers: Stack[PathConditionStackLayer]): Stack[Option[ast.Exp]] =
layers.flatMap(_.branchConditionExp)
protected def assumptions(layers: Stack[PathConditionStackLayer]): InsertionOrderedSet[Term] =
InsertionOrderedSet(layers.flatMap(_.assumptions)) // Note: Performance?
protected def declarations(layers: Stack[PathConditionStackLayer]): InsertionOrderedSet[Decl] =
InsertionOrderedSet(layers.flatMap(_.declarations)) // Note: Performance?
protected def contains(layers: Stack[PathConditionStackLayer], assumption: Term): Boolean =
layers exists (_.contains(assumption))
protected def conditionalized(layers: Stack[PathConditionStackLayer]): Seq[Term] = {
var unconditionalTerms = Vector.empty[Term]
var conditionalTerms = Vector.empty[Term]
var implicationLHS: Term = True
for (layer <- layers.reverseIterator) {
unconditionalTerms ++= layer.globalAssumptions
layer.branchCondition match {
case Some(condition) =>
implicationLHS = And(implicationLHS, condition)
case None =>
}
conditionalTerms :+=
Implies(implicationLHS, And(layer.nonGlobalAssumptions))
}
unconditionalTerms ++ conditionalTerms
}
protected def quantified(layers: Stack[PathConditionStackLayer],
quantifier: Quantifier,
qvars: Seq[Var],
triggers: Seq[Trigger],
name: String,
isGlobal: Boolean,
ignore: Term)
: (Seq[Term], Seq[Quantification]) = {
var globals = Vector.empty[Term]
var nonGlobals = Vector.empty[Quantification]
val ignores = ignore.topLevelConjuncts
for (layer <- layers) {
globals ++= layer.globalAssumptions
nonGlobals :+=
Quantification(
quantifier,
qvars,
Implies(layer.branchCondition.getOrElse(True), And(layer.nonGlobalAssumptions -- ignores)),
triggers,
name,
isGlobal)
}
(globals, nonGlobals)
}
}
private class DefaultRecordedPathConditions(from: Stack[PathConditionStackLayer])
extends LayeredPathConditionStackLike
with RecordedPathConditions {
val branchConditions: Stack[Term] = branchConditions(from)
val branchConditionExps: Stack[Option[ast.Exp]] = branchConditionExps(from)
val assumptions: InsertionOrderedSet[Term] = assumptions(from)
val declarations: InsertionOrderedSet[Decl] = declarations(from)
def contains(assumption: Term): Boolean = contains(from, assumption)
val conditionalized: Seq[Term] = conditionalized(from)
def quantified(quantifier: Quantifier,
qvars: Seq[Var],
triggers: Seq[Trigger],
name: String,
isGlobal: Boolean,
ignore: Term)
: (Seq[Term], Seq[Quantification]) = {
quantified(from, quantifier, qvars, triggers, name, isGlobal, ignore)
}
}
private[decider] class LayeredPathConditionStack
extends LayeredPathConditionStackLike
with PathConditionStack
with Cloneable {
/* private */ var layers: Stack[PathConditionStackLayer] = Stack.empty
private var markToLength: Map[Mark, Int] = Map.empty
private var scopeMarks: List[Mark] = List.empty
private var markCounter = new Counter(0)
/* Set of assumptions across all layers. Maintained separately to improve performance. */
private var allAssumptions = InsertionOrderedSet.empty[Term]
pushScope() /* Create an initial layer on the stack */
def setCurrentBranchCondition(condition: Term, conditionExp: Option[ast.Exp]): Unit = {
/* TODO: Split condition into top-level conjuncts as well? */
layers.head.branchCondition = condition
layers.head.branchConditionExp = conditionExp
}
def add(assumption: Term): Unit = {
/* TODO: Would be cleaner to not add assumptions that are already set as branch conditions */
val tlcs = assumption.topLevelConjuncts
tlcs foreach layers.head.add
allAssumptions ++= tlcs
}
def add(declaration: Decl): Unit = {
layers.head.add(declaration)
}
def pushScope(): Unit = {
val scopeMark = pushLayer()
scopeMarks = scopeMark :: scopeMarks
}
def popScope(): Unit = {
val scopeMark = scopeMarks.head
scopeMarks = scopeMarks.tail
popLayersAndRemoveMark(scopeMark)
}
private def pushLayer(): Mark = {
val mark = markCounter.next()
markToLength += (mark -> layers.length)
layers = new PathConditionStackLayer() +: layers
mark
}
def popUntilMark(mark: Mark): Unit = {
assert(markToLength.contains(mark), "Cannot pop unknown mark")
popLayersAndRemoveMark(mark)
}
private def popLayersAndRemoveMark(mark: Mark): Unit = {
val targetLength = markToLength(mark)
val dropLength = layers.length - targetLength
markToLength = markToLength - mark
// /* Remove marks pointing to popped layers (including mark itself) */
// markToLength = markToLength filter (_._2 < targetLength)
// /* TODO: Performance? Do lazily, e.g. when isEmpty is called? */
var i = 0
layers =
layers.dropWhile(layer => {
i += 1
allAssumptions --= layer.assumptions
i < dropLength
/* If i < dropLength is false, the current - and last-to-drop - layer won't be
* dropped, but its assumptions have already been removed from allAssumptions.
* Subsequently taking the tail of the remaining layers results in also
* dropping the last layer that needs to be dropped.
*/
}).tail
}
def branchConditions: Stack[Term] = layers.flatMap(_.branchCondition)
override def branchConditionExps: Stack[Option[ast.Exp]] = layers.flatMap(_.branchConditionExp)
def assumptions: InsertionOrderedSet[Term] = allAssumptions
def declarations: InsertionOrderedSet[Decl] =
InsertionOrderedSet(layers.flatMap(_.declarations)) // Note: Performance?
def contains(assumption: Term): Boolean = allAssumptions.contains(assumption)
def conditionalized: Seq[Term] = conditionalized(layers)
def quantified(quantifier: Quantifier,
qvars: Seq[Var],
triggers: Seq[Trigger],
name: String,
isGlobal: Boolean,
ignore: Term)
: (Seq[Term], Seq[Quantification]) = {
quantified(layers, quantifier, qvars, triggers, name, isGlobal, ignore)
}
def mark(): Mark = pushLayer()
def after(mark: Mark): RecordedPathConditions = {
val afterLength = layers.length - markToLength(mark)
val afterLayers = layers.take(afterLength)
new DefaultRecordedPathConditions(afterLayers)
}
def isEmpty: Boolean = (
layers.forall(_.branchCondition.isEmpty)
&& allAssumptions.isEmpty
&& (markToLength.keySet -- scopeMarks).isEmpty)
override def duplicate(): LayeredPathConditionStack = {
/* Attention: The original and its clone must not share any mutable data! */
val clonedStack = new LayeredPathConditionStack
/* Sharing immutable data is safe */
clonedStack.allAssumptions = allAssumptions
clonedStack.markToLength = markToLength
clonedStack.scopeMarks = scopeMarks
/* Mutable data is cloned */
clonedStack.markCounter = markCounter.clone()
clonedStack.layers = layers map (_.clone().asInstanceOf[PathConditionStackLayer])
clonedStack
}
override def toString: String = {
val sb = new StringBuilder(s"${this.getClass.getSimpleName}:\n")
val sep = s" ${"-" * 10}\n"
sb.append(sep)
sb.append(s" height: ${layers.length}\n")
sb.append(s" allAssumptions:\n")
for (assumption <- allAssumptions) {
sb.append(s" $assumption\n")
}
sb.append(sep)
for (layer <- layers) {
sb.append(s" branch condition: ${layer.branchCondition}\n")
sb.append( " assumptions:\n")
for (assumption <- layer.assumptions) {
sb.append(s" $assumption\n")
}
}
sb.append(sep)
val marks = markToLength.keySet -- scopeMarks
sb.append(" marks:\n")
marks foreach (m => {
sb.append(s" $m -> ${markToLength(m)} (${scopeMarks.contains(m)})\n")
})
sb.result()
}
}
private object PathConditions {
def isGlobal(assumption: Term): Boolean = {
assumption match {
case quantification: Quantification => quantification.isGlobal
case _: IsReadPermVar => true
case _ => false
}
}
}