Skip to content

Commit 062a11c

Browse files
authored
Merge pull request #666 from viperproject/meilers_qp_finite_domains
Adjusting the QP encoding to deal with quantification over finite types
2 parents f94e56a + 3a7c187 commit 062a11c

3 files changed

Lines changed: 56 additions & 24 deletions

File tree

silver

src/main/scala/rules/QuantifiedChunkSupport.scala

Lines changed: 54 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,13 @@ case class InverseFunctions(condition: Term,
3232
additionalArguments: Vector[Var],
3333
axiomInversesOfInvertibles: Quantification,
3434
axiomInvertiblesOfInverses: Quantification,
35-
qvarsToInverses: Map[Var, Function]) {
35+
qvarsToInverses: Map[Var, Function],
36+
qvarsToImages: Map[Var, Function]) {
3637

3738
val inverses: Iterable[Function] = qvarsToInverses.values
3839

40+
val images: Iterable[Function] = qvarsToImages.values
41+
3942
val definitionalAxioms: Vector[Quantification] =
4043
Vector(axiomInversesOfInvertibles, axiomInvertiblesOfInverses)
4144

@@ -93,6 +96,8 @@ trait QuantifiedChunkSupport extends SymbolicExecutionRules {
9396
/** Creates `n` fresh (partial) inverse functions `inv_i` that invert an `n`-ary
9497
* function `fct`, where `n == qvars.length`, and returns the inverse functions as
9598
* well as the definitional axioms.
99+
* If the types of the quantified variables could be finite, additionally creates n fresh
100+
* boolean functions `img_i` denoting the domain of the respective inverse functions.
96101
*
97102
* Let
98103
* - `x_1: T_1`, ..., `x_n: T_n` denote the quantified variables (argument `qvars`)
@@ -103,18 +108,21 @@ trait QuantifiedChunkSupport extends SymbolicExecutionRules {
103108
* the partial inverses are defined
104109
*
105110
* The following definitional axioms will be returned, in addition to the fresh
106-
* inverse functions `inv_1`, ..., `inv_n`:
111+
* inverse functions `inv_1`, ..., `inv_n` and the respective image functions:
107112
*
108113
* forall x_1: T_1, ..., x_n: T_n :: {fct(x_1, ..., x_n)}
109114
* c(x_1, ..., x_n) ==>
110-
* inv_1(fct(x_1, ..., x_n)) == x_1
115+
* inv_1(fct(x_1, ..., x_n)) == x_1 && img_1(fct(x_1, ..., x_n))
111116
* && ...
112-
* && inv_n(fct(x_1, ..., x_n)) == x_n
117+
* && inv_n(fct(x_1, ..., x_n)) == x_n && img_n(fct(x_1, ..., x_n))
113118
*
114119
* forall r: R :: {inv_1(r), ..., inv_n(r)}
115-
* c(inv_1(r), ..., inv_n(r)) ==>
120+
* c(inv_1(r), ..., inv_n(r)) && img_1(r) && ... && img_n(r) ==>
116121
* fct(inv_1(r), ..., inv_n(r)) == r
117122
*
123+
* For all i where x_i is of type Ref or Int, we do not generate the img_i constraints in
124+
* either axiom, since those types are known to have the same cardinality as Ref.
125+
*
118126
* @param qvars Quantified variables that occur in the invertible function and for
119127
* which partial inverse functions are to be defined..
120128
* @param condition A condition (containing the quantified variables) determining
@@ -125,7 +133,10 @@ trait QuantifiedChunkSupport extends SymbolicExecutionRules {
125133
* @param additionalInvArgs Additional arguments on which `inv` depends (typically
126134
* quantified variables bound by some surrounding scope).
127135
* Currently omitted from the axioms shown above.
128-
* @return The generated partial inverse functions and corresponding definitional axioms.
136+
* @return The generated partial inverse functions and corresponding definitional axioms, and
137+
* the images of the given codomain variables (returned separately, since nothing else
138+
* in the returned InverseFunctions object references or contains the codomain
139+
* variables, and thus they only have meaning for the caller).
129140
*/
130141
def getFreshInverseFunctions(qvars: Seq[Var],
131142
condition: Term,
@@ -135,7 +146,7 @@ trait QuantifiedChunkSupport extends SymbolicExecutionRules {
135146
userProvidedTriggers: Option[Seq[Trigger]],
136147
qidPrefix: String,
137148
v: Verifier)
138-
: InverseFunctions
149+
: (InverseFunctions, Seq[Term])
139150

140151
def injectivityAxiom(qvars: Seq[Var],
141152
condition: Term,
@@ -247,7 +258,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
247258
program: ast.Program)
248259
: (QuantifiedBasicChunk, InverseFunctions) = {
249260

250-
val inverseFunctions =
261+
val (inverseFunctions, imagesOfCodomain) =
251262
getFreshInverseFunctions(
252263
qvars,
253264
And(condition, IsPositive(permissions)),
@@ -262,7 +273,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
262273

263274
val conditionalizedPermissions =
264275
Ite(
265-
condition.replace(qvarsToInversesOfCodomain),
276+
And(And(imagesOfCodomain), condition.replace(qvarsToInversesOfCodomain)),
266277
permissions.replace(qvarsToInversesOfCodomain),
267278
NoPerm())
268279

@@ -1014,7 +1025,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
10141025
(Q: (State, Heap, Term, Verifier) => VerificationResult)
10151026
: VerificationResult = {
10161027

1017-
val inverseFunctions =
1028+
val (inverseFunctions, imagesOfFormalQVars) =
10181029
quantifiedChunkSupporter.getFreshInverseFunctions(
10191030
qvars,
10201031
And(tCond, IsPositive(tPerm)),
@@ -1124,13 +1135,13 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
11241135
s2,
11251136
relevantChunks,
11261137
formalQVars,
1127-
condOfInvOfLoc,
1138+
And(condOfInvOfLoc, And(imagesOfFormalQVars)),
11281139
resource,
11291140
rPerm,
11301141
chunkOrderHeuristics,
11311142
v2)
11321143
val optSmDomainDefinitionCondition2 =
1133-
if (s3.smDomainNeeded) Some(And(condOfInvOfLoc, IsPositive(lossOfInvOfLoc)))
1144+
if (s3.smDomainNeeded) Some(And(condOfInvOfLoc, IsPositive(lossOfInvOfLoc), And(imagesOfFormalQVars)))
11341145
else None
11351146
val (smDef2, smCache2) =
11361147
quantifiedChunkSupporter.summarisingSnapshotMap(
@@ -1170,7 +1181,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
11701181
s.copy(smCache = smCache1),
11711182
relevantChunks,
11721183
formalQVars,
1173-
condOfInvOfLoc,
1184+
And(condOfInvOfLoc, And(imagesOfFormalQVars)),
11741185
resource,
11751186
lossOfInvOfLoc,
11761187
chunkOrderHeuristics,
@@ -1180,7 +1191,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
11801191
case (Complete(), s2, remainingChunks) =>
11811192
val h3 = Heap(remainingChunks ++ otherChunks)
11821193
val optSmDomainDefinitionCondition2 =
1183-
if (s2.smDomainNeeded) Some(And(condOfInvOfLoc, IsPositive(lossOfInvOfLoc)))
1194+
if (s2.smDomainNeeded) Some(And(condOfInvOfLoc, IsPositive(lossOfInvOfLoc), And(And(imagesOfFormalQVars))))
11841195
else None
11851196
val (smDef2, smCache2) =
11861197
quantifiedChunkSupporter.summarisingSnapshotMap(
@@ -1577,7 +1588,7 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
15771588
userProvidedTriggers: Option[Seq[Trigger]],
15781589
qidPrefix: String,
15791590
v: Verifier)
1580-
: InverseFunctions = {
1591+
: (InverseFunctions, Seq[Term]) = {
15811592

15821593
assert(
15831594
invertibles.length == codomainQVars.length,
@@ -1593,6 +1604,9 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
15931604
val qvarsWithIndices = qvars.zipWithIndex
15941605

15951606
val inverseFunctions = Array.ofDim[Function](qvars.length) /* inv_i */
1607+
val imageFunctions = Array.ofDim[Function](qvars.length) /* img_i */
1608+
val imagesOfFcts = Array.ofDim[Term](qvars.length) // /* img_i(f_1(xs), ..., f_m(xs)) */
1609+
val imagesOfCodomains = Array.ofDim[Term](qvars.length) /* img_i(rs) */
15961610
val inversesOfFcts = Array.ofDim[Term](qvars.length) /* inv_i(f_1(xs), ..., f_m(xs)) */
15971611
val inversesOfCodomains = Array.ofDim[Term](qvars.length) /* inv_i(rs) */
15981612

@@ -1603,6 +1617,20 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
16031617
inverseFunctions(idx) = fun
16041618
inversesOfFcts(idx) = inv(invertibles)
16051619
inversesOfCodomains(idx) = inv(codomainQVars)
1620+
1621+
if (qvar.sort != sorts.Int && qvar.sort != sorts.Ref) {
1622+
// Types known to be infinite, thus there is no need to constrain the domain using image functions.
1623+
val imgFun = v.decider.fresh("img", (additionalInvArgs map (_.sort)) ++ invertibles.map(_.sort), sorts.Bool)
1624+
val img = (ts: Seq[Term]) => App(imgFun, additionalInvArgs ++ ts)
1625+
1626+
imageFunctions(idx) = imgFun
1627+
imagesOfFcts(idx) = img(invertibles)
1628+
imagesOfCodomains(idx) = img(codomainQVars)
1629+
} else {
1630+
// imageFunctions(idx) remains null, will be filtered out later.
1631+
imagesOfFcts(idx) = True()
1632+
imagesOfCodomains(idx) = True()
1633+
}
16061634
}
16071635

16081636
/* f_1(inv_1(rs), ..., inv_n(rs)), ..., f_m(inv_1(rs), ..., inv_n(rs)) */
@@ -1614,14 +1642,15 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
16141642
condition.replace(qvars, ArraySeq.unsafeWrapArray(inversesOfCodomains))
16151643

16161644
/* c(xs) ==>
1617-
* inv_1(f_1(xs), ..., f_m(xs)) == x_1
1645+
* inv_1(f_1(xs), ..., f_m(xs)) == x_1 && img_1(f_1(xs), ..., f_m(xs)) &&
16181646
* && ...
1619-
* && inv_n(f_1(xs), ..., f_m(xs)) == x_n
1647+
* && inv_n(f_1(xs), ..., f_m(xs)) == x_n && img_n(f_1(xs), ..., f_m(xs))
16201648
*/
16211649
val axInvsOfFctsBody =
16221650
Implies(
16231651
condition,
1624-
And(qvarsWithIndices map { case (qvar, idx) => inversesOfFcts(idx) === qvar }))
1652+
And(And(qvarsWithIndices map { case (qvar, idx) => inversesOfFcts(idx) === qvar }),
1653+
And(qvarsWithIndices map { case (_, idx) => imagesOfFcts(idx) })))
16251654

16261655
val axInvsOfFct =
16271656
userProvidedTriggers match {
@@ -1644,12 +1673,12 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
16441673
s"$qidPrefix-invOfFct")
16451674
}
16461675

1647-
/* c(inv_1(rs), ..., inv_n(rs)) ==>
1676+
/* c(inv_1(rs), ..., inv_n(rs)) && img_1(rs) && ... && img_n(rs) ==>
16481677
* f_1(inv_1(rs), ..., inv_n(rs)) == r_1
16491678
*/
16501679
val axFctsOfInvsBody =
16511680
Implies(
1652-
conditionOfInverses,
1681+
And(And(imagesOfCodomains), conditionOfInverses),
16531682
And(
16541683
fctsOfInversesOfCodomain
16551684
.zip(codomainQVars)
@@ -1669,13 +1698,16 @@ object quantifiedChunkSupporter extends QuantifiedChunkSupport {
16691698
isGlobal = true,
16701699
v.axiomRewriter)
16711700

1672-
InverseFunctions(
1701+
val res = InverseFunctions(
16731702
condition,
16741703
invertibles,
16751704
additionalInvArgs.toVector,
16761705
axInvsOfFct,
16771706
axFctsOfInvs,
1678-
qvars.zip(inverseFunctions).to(Map))
1707+
qvars.zip(inverseFunctions).to(Map),
1708+
qvars.zip(imageFunctions).filter(_._2 != null).to(Map)
1709+
)
1710+
(res, imagesOfCodomains)
16791711
}
16801712

16811713
def hintBasedChunkOrderHeuristic(hints: Seq[Term])

src/main/scala/supporters/functions/FunctionData.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class FunctionData(val programFunction: ast.Function,
124124
freshSymbolsAcrossAllPhases ++= freshPathSymbols map FunctionDecl
125125
freshSymbolsAcrossAllPhases ++= freshArps.map(pair => FunctionDecl(pair._1))
126126
freshSymbolsAcrossAllPhases ++= freshSnapshots map FunctionDecl
127-
freshSymbolsAcrossAllPhases ++= freshFieldInvs.flatMap(_.inverses map FunctionDecl)
127+
freshSymbolsAcrossAllPhases ++= freshFieldInvs.flatMap(i => (i.inverses ++ i.images) map FunctionDecl)
128128
freshSymbolsAcrossAllPhases ++= freshMacros
129129

130130
freshSymbolsAcrossAllPhases ++= freshFvfsAndDomains map (fvfDef =>

0 commit comments

Comments
 (0)