Skip to content

Commit 72b02b1

Browse files
authored
Fix issue 943 (#947)
* add bug witness * add fix * fix wrong stub in the std lib * Update src/main/scala/viper/gobra/frontend/info/implementation/typing/ghost/GhostExprTyping.scala
1 parent f805530 commit 72b02b1

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

src/main/resources/stubs/net/ip.gobra

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func (ip IP) To4() (res IP) {
143143
}
144144

145145
pure
146-
preserves forall i int :: 0 <= i && i < len(s) ==> acc(&s[i], 1/100000)
146+
requires forall i int :: 0 <= i && i < len(s) ==> acc(&s[i])
147147
decreases _
148148
func isZeros(s []byte) bool
149149

src/main/scala/viper/gobra/frontend/info/implementation/typing/ghost/GhostExprTyping.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,11 @@ trait GhostExprTyping extends BaseTyping { this: TypeInfoImpl =>
462462
case _: PUnfolding => true
463463
case _: PLet => true // the well-definedness check makes sure that both sub-expressions are pure.
464464
case _: POld | _: PLabeledOld | _: PBefore => true
465-
case _: PForall => true
466-
case _: PExists => true
465+
case f: PForall => go(f.body)
466+
case e: PExists =>
467+
// The type checker currently enforces that all existential quantifiers must be strongly pure, so we wouldn't need to recurse here.
468+
// Nonetheless, this implementation is safer in case that ever changes.
469+
go(e.body)
467470

468471
case PConditional(cond, thn, els) => Seq(cond, thn, els).forall(go)
469472

src/main/scala/viper/gobra/frontend/info/implementation/typing/ghost/GhostMemberTyping.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,22 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl =>
5252
isSingleResultArg(member) ++
5353
isSinglePureReturnExpr(member) ++
5454
isPurePostcondition(member.spec) ++
55+
pureMembersCannotHavePreserves(member.spec) ++
5556
nonVariadicArguments(member.args) ++
5657
error(member, pureFunctionsDoNotNeedMayInitMsg, member.spec.mayBeUsedInInit)
5758
} else noMessages
5859
}
5960

61+
// Preserves clauses are unnecessary in pure functions. Any condition that holds on entry is guaranteed to
62+
// hold on exit, thus it is redundant to make properties both pre- and postconditions.
63+
private[typing] def pureMembersCannotHavePreserves(member: PFunctionSpec): Messages = {
64+
assert(member.isPure)
65+
member.preserves flatMap { c =>
66+
error(c, "Pure functions and pure methods cannot have preserves clauses." +
67+
"Considering replacing this preserves clause with a precondition.")
68+
}
69+
}
70+
6071
private[typing] def wellDefIfPureMethodImplementationProof(implProof: PMethodImplementationProof): Messages = {
6172
if (implProof.isPure) {
6273
isSinglePureReturnExpr(implProof) // all other checks are taken care of by super implementation
@@ -68,6 +79,7 @@ trait GhostMemberTyping extends BaseTyping { this: TypeInfoImpl =>
6879
isSingleResultArg(member) ++
6980
isSinglePureReturnExpr(member) ++
7081
isPurePostcondition(member.spec) ++
82+
pureMembersCannotHavePreserves(member.spec) ++
7183
nonVariadicArguments(member.args) ++
7284
error(member, pureFunctionsDoNotNeedMayInitMsg, member.spec.mayBeUsedInInit)
7385
} else noMessages
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
package issue000943
5+
6+
//:: ExpectedOutput(type_error)
7+
preserves forall i int :: 0 <= i && i < len(b) ==> acc(&b[i])
8+
decreases
9+
pure func test1(b []int) bool
10+
11+
//:: ExpectedOutput(type_error)
12+
ensures forall i int :: 0 <= i && i < len(b) ==> acc(&b[i])
13+
decreases
14+
pure func test2(b []int) bool
15+
16+
// Preserves clauses are always unnecessary in pure functions.
17+
// One should always prefer writing 'requires A' over 'preserves A',
18+
// as preconditions of pure functions are guaranteed to hold after the
19+
// call anyway. Gobra now actively rejects the use of preserves clauses
20+
// in pure functions.
21+
//:: ExpectedOutput(type_error)
22+
preserves true
23+
decreases
24+
pure func test3(b []int) bool

0 commit comments

Comments
 (0)