Skip to content

Commit 5f9404e

Browse files
authored
Merge pull request #614 from viperproject/meilers_parser_fixes
Parser and consistency fixes
2 parents dd8b8c1 + d5a180c commit 5f9404e

8 files changed

Lines changed: 186 additions & 25 deletions

File tree

src/main/scala/viper/silver/ast/Expression.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,7 @@ case class Forall(variables: Seq[LocalVarDecl], triggers: Seq[Trigger], exp: Exp
558558
//require(isValid, s"Invalid quantifier: { $this } .")
559559
override lazy val check : Seq[ConsistencyError] =
560560
(if(!(exp isSubtype Bool)) Seq(ConsistencyError(s"Body of universal quantifier must be of Bool type, but found ${exp.typ}", exp.pos)) else Seq()) ++
561+
(if (variables.isEmpty) Seq(ConsistencyError("Quantifier must have at least one quantified variable.", pos)) else Seq()) ++
561562
Consistency.checkAllVarsMentionedInTriggers(variables, triggers) ++
562563
checkNoNestedQuantsForQuantPermissions ++
563564
checkQuantifiedPermission
@@ -624,7 +625,8 @@ case class Forall(variables: Seq[LocalVarDecl], triggers: Seq[Trigger], exp: Exp
624625
/** Existential quantification. */
625626
case class Exists(variables: Seq[LocalVarDecl], triggers: Seq[Trigger], exp: Exp)(val pos: Position = NoPosition, val info: Info = NoInfo, val errT: ErrorTrafo = NoTrafos) extends QuantifiedExp {
626627
override lazy val check : Seq[ConsistencyError] = Consistency.checkPure(exp) ++
627-
(if(!(exp isSubtype Bool)) Seq(ConsistencyError(s"Body of existential quantifier must be of Bool type, but found ${exp.typ}", exp.pos)) else Seq())
628+
(if(!(exp isSubtype Bool)) Seq(ConsistencyError(s"Body of existential quantifier must be of Bool type, but found ${exp.typ}", exp.pos)) else Seq()) ++
629+
(if (variables.isEmpty) Seq(ConsistencyError("Quantifier must have at least one quantified variable.", pos)) else Seq())
628630

629631
/** Returns an identical forall quantification that has some automatically generated triggers
630632
* if necessary and possible.

src/main/scala/viper/silver/parser/FastParser.scala

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -799,11 +799,11 @@ class FastParser {
799799

800800
def result[_: P]: P[PResultLit] = FP(keyword("result")).map { case (pos, _) => PResultLit()(pos) }
801801

802-
def unExp[_: P]: P[PUnExp] = FP(CharIn("\\-\\!").! ~~ !" " ~~ suffixExpr).map { case (pos, (a, b)) => PUnExp(a, b)(pos) }
802+
def unExp[_: P]: P[PUnExp] = FP(CharIn("\\-\\!").! ~ suffixExpr).map { case (pos, (a, b)) => PUnExp(a, b)(pos) }
803803

804804
def strInteger[_: P]: P[String] = P(CharIn("0-9").rep(1)).!
805805

806-
def integer[_: P]: P[PIntLit] = FP(strInteger.filter(s => !s.contains(' '))).map { case (pos, s) => PIntLit(BigInt(s))(pos) }
806+
def integer[_: P]: P[PIntLit] = FP(strInteger.filter(s => s.matches("\\S+"))).map { case (pos, s) => PIntLit(BigInt(s))(pos) }
807807

808808
def booltrue[_: P]: P[PBoolLit] = FP(keyword("true")).map {case (pos, _) => PBoolLit(b = true)(pos)}
809809

@@ -1034,11 +1034,11 @@ class FastParser {
10341034

10351035
def setTypedEmpty[_: P]: P[PExp] = collectionTypedEmpty("Set", (a, b) => PEmptySet(a)(b))
10361036

1037-
def explicitSetNonEmpty[_: P]: P[PExp] = P(FP("Set" ~ "(" ~/ exp.rep(sep = ",", min = 1) ~ ")").map {
1037+
def explicitSetNonEmpty[_: P]: P[PExp] = P(FP("Set" ~ "(" ~ exp.rep(sep = ",", min = 1) ~ ")").map {
10381038
case (pos, exps) => PExplicitSet(exps)(pos)
10391039
})
10401040

1041-
def explicitMultisetNonEmpty[_: P]: P[PExp] = P(FP("Multiset" ~ "(" ~/ exp.rep(min = 1, sep = ",") ~ ")").map {
1041+
def explicitMultisetNonEmpty[_: P]: P[PExp] = P(FP("Multiset" ~ "(" ~ exp.rep(min = 1, sep = ",") ~ ")").map {
10421042
case (pos, exps) => PExplicitMultiset(exps)(pos)
10431043
})
10441044

@@ -1050,16 +1050,16 @@ class FastParser {
10501050
case (pos, e) => PSize(e)(pos)
10511051
})
10521052

1053-
def explicitSeqNonEmpty[_: P]: P[PExp] = P(FP("Seq" ~ "(" ~/ exp.rep(min = 1, sep = ",") ~ ")").map {
1053+
def explicitSeqNonEmpty[_: P]: P[PExp] = P(FP("Seq" ~ "(" ~ exp.rep(min = 1, sep = ",") ~ ")").map {
10541054
case (pos, exps) => PExplicitSeq(exps)(pos)
10551055
})
10561056

10571057
private def collectionTypedEmpty[_: P](name: String, typeConstructor: (PType, (Position, Position)) => PExp): P[PExp] =
1058-
FP(`name` ~ ("[" ~/ typ ~ "]").? ~ "(" ~ ")").map{ case (pos, typ) => typeConstructor(typ.getOrElse(PTypeVar("#E")), pos)}
1058+
FP(`name` ~ ("[" ~ typ ~ "]").? ~ "(" ~ ")").map{ case (pos, typ) => typeConstructor(typ.getOrElse(PTypeVar("#E")), pos)}
10591059

10601060
def seqRange[_: P]: P[PExp] = FP("[" ~ exp ~ ".." ~ exp ~ ")").map { case (pos, (a, b)) => PRangeSeq(a, b)(pos) }
10611061

1062-
def mapTypedEmpty[_: P] : P[PMapLiteral] = P(FP("Map" ~ ("[" ~/ typ ~ "," ~ typ ~ "]").? ~ "(" ~ ")").map {
1062+
def mapTypedEmpty[_: P] : P[PMapLiteral] = P(FP("Map" ~ ("[" ~ typ ~ "," ~ typ ~ "]").? ~ "(" ~ ")").map {
10631063
case (pos, Some((keyType, valueType))) => PEmptyMap(keyType, valueType)(pos)
10641064
case (pos, None) => PEmptyMap(PTypeVar("#K"), PTypeVar("#E"))(pos)
10651065
})
@@ -1068,7 +1068,7 @@ class FastParser {
10681068
case (pos, (key, value)) => PMaplet(key, value)(pos)
10691069
})
10701070

1071-
def explicitMapNonEmpty[_: P]: P[PMapLiteral] = P(FP("Map" ~ "(" ~/ maplet.rep(sep = ",", min = 1) ~ ")").map {
1071+
def explicitMapNonEmpty[_: P]: P[PMapLiteral] = P(FP("Map" ~ "(" ~ maplet.rep(sep = ",", min = 1) ~ ")").map {
10721072
case (pos, maplets) => PExplicitMap(maplets)(pos)
10731073
})
10741074

src/test/resources/all/issues/silver/0271-1.vpr

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/test/resources/all/issues/silver/0271-2.vpr

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
// Any copyright is dedicated to the Public Domain.
2+
// http://creativecommons.org/publicdomain/zero/1.0/
3+
4+
method t1(x: Int, xs: Seq[Int]) {
5+
var n: Seq[Int] := Seq[Int]()
6+
assert |n| == 0
7+
assert n != Seq(x)
8+
assert |Seq(1)| == 1
9+
assert |Seq(0)| == 0
10+
}
11+
12+
method t2() {
13+
assert 1 in Seq(1,2,3)
14+
assert |[-1..10)| == 11
15+
assert Seq(1) ++ Seq(2) == Seq(1,2)
16+
17+
var a: Seq[Int] := Seq(0,1,-11,22)
18+
assert a[2] == -11
19+
20+
assert a[..1] == Seq(0)
21+
assert a[1..] == Seq(1,-11,22)
22+
assert a[1..2] == Seq(1)
23+
24+
assert a[1 := 22] == (a[1 := -1][1 := 22])
25+
assert a[1 := 22] == Seq(0,22,-11,22)
26+
assert |a[1 := 22]| == 4
27+
assert a[1 := 22][1] == 22
28+
assert a[1 := 22][2] == -11
29+
assert a[1 := 22][0] == 22
30+
}
31+
32+
method test3() {
33+
var xs: Seq[Int] := Seq(0, 1, 2, 3, 4, 5, 6, 7)
34+
var bs: Seq[Bool] := Seq(true, true, false, true) ++ Seq(false, true)
35+
36+
assert |xs[1..][..6]| == |bs|
37+
assert |xs[1..]| == |xs|
38+
}
39+
40+
method test4(s:Seq[Int], i : Int, j:Int)
41+
requires 0 <= i
42+
requires i <= j
43+
{
44+
assert s == s[..i] ++ s[i..]
45+
assert s == s[..i] ++ s[i..j] ++ s[j..]
46+
assert (s[..i] ++ s[i..j]) ++ s[j..] assert a[1..2] == Seq(1)
47+
48+
assert a[1 := 22] == (a[1 := -1][1 := 22])
49+
assert a[1 := 22] == Seq(0,22,-11,22)
50+
assert |a[1 := 22]| == 4
51+
assert a[1 := 22][1] == 22
52+
assert a[1 := 22][2] == -11
53+
assert a[1 := 22][0] == 22
54+
}
55+
56+
method test3() {
57+
var xs: Seq[Int] := Seq(0, 1, 2, 3, 4, 5, 6, 7)
58+
var bs: Seq[Bool] := Seq(true, true, false, true) ++ Seq(false, true)
59+
60+
assert |xs[1..][..6]| == |bs|
61+
assert |xs[1..]| == |xs|
62+
}
63+
64+
method test4(s:Seq[Int], i : Int, j:Int)
65+
requires 0 <= i
66+
requires i <= j
67+
{
68+
assert s == s[..i] ++ s[i..]
69+
assert s == s[..i] ++ s[i..j] ++ s[j..]
70+
assert (s[..i] ++ s[i..j]) ++ s[j..] == s[..i] ++ (s[i..j] ++ s[j..])
71+
assert |s[j..]| == |s| - j
72+
}
73+
74+
method test5(s:Seq[Int], i : Int, j:Int)
75+
{
76+
assert s == s[..i] ++ s[i..]
77+
}
78+
79+
method test6() {
80+
assert Seq(3,4,5,6)[0] == 3
81+
//:: ExpectedOutput(parser.error)
82+
assert Seq(3,4,5,6)[1] == 4
83+
5,6)[i])
84+
}
85+
86+
me== s[..i] ++ (s[i..j] ++ s[j..])
87+
assert |s[j..]| == |s| - j
88+
}
89+
90+
method test5(s:Seq[Int], i : Int, j:Int)
91+
{
92+
assert s == s[..i] ++ s[i..]
93+
}
94+
95+
method test6() {
96+
assert Seq(3,4,5,6)[0] == 3
97+
assert Seq(3,4,5,6)[1] == 4
98+
assert Seq(3,4,5,6)[2] == 5
99+
assert Seq(3,4,5,6)[3] == 6
100+
assert Seq(3,4,5,6)[3] == 5
101+
}
102+
103+
function trivial(i:Int) : Bool { true }
104+
105+
method test_index_definedness_small(i : Int)
106+
requires i < 4
107+
{
108+
assert trivial(Seq(3,4,5,6)[i])
109+
}
110+
111+
method test_index_definedness_large(i : Int)
112+
requires i >= 0
113+
{
114+
assefrt trivial(Seq(3,4,5,6)[i])
115+
}
116+
117+
method test_build_index_definedness_small(i : Int)
118+
requires i < 4
119+
{
120+
(assert.failed:seq.index.negative, /carbon/issue/232/)
121+
assert trivial(Seq(3,4,5,6)[i := 3][0])
122+
}
123+
124+
method test_build_index_definedness_large(] : Int)
125+
requires i >= 0
126+
{
127+
var s : Seq[Int] := Seq(3,4,5,6)[i := 3]
128+
assert trivial(s[0])
129+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
domain Either[T, V] {
2+
function left(t: T): Either[T, V]
3+
function right(v: V): Either[T, V]
4+
}
5+
6+
method client(s: Seq[Int])
7+
{
8+
var x: Either[Seq[Int], Ref]
9+
// okay
10+
x := (left(s): Either[Seq[Int], Ref])
11+
x := (left(Seq[Int]()))
12+
// parse error
13+
x := (left(Seq[Int]()): Either[Seq[Int], Ref])
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
method test(n: Int, d: Int) returns (q: Int, r: Int)
2+
requires n >= 0 && d > 0;
3+
ensures n == q * d + r && r >= 0 && r < d;
4+
{
5+
r := n;
6+
q := 0;
7+
8+
while(r >= 0)
9+
invariant r >= - d && n == q * d + r;
10+
{
11+
r := r - d;
12+
q := q + 1;
13+
}
14+
15+
r := r + d;
16+
q := q - 1;
17+
}

src/test/scala/ConsistencyTests.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,4 +256,19 @@ class ConsistencyTests extends AnyFunSuite with Matchers {
256256
ConsistencyError("Quantified permissions must have an implication as expression, with the access predicate in its right-hand side.", NoPosition)
257257
)
258258
}
259+
260+
test("Testing if Forall AST nodes have at least one quantified variable, and all variables are mentioned in the trigger.") {
261+
262+
val f = Function("f", Seq(LocalVarDecl("i", Int)()), Int, Seq(), Seq(), None)()
263+
val forallNoVar = Forall(Seq(), Seq(), TrueLit()())()
264+
val forallUnusedVar = Forall(Seq(LocalVarDecl("i", Int)()), Seq(Trigger(Seq(FuncApp(f, Seq(IntLit(0)()))()))()), TrueLit()())()
265+
266+
forallNoVar.checkTransitively shouldBe Seq(
267+
ConsistencyError("Quantifier must have at least one quantified variable.", NoPosition)
268+
)
269+
270+
forallUnusedVar.checkTransitively shouldBe Seq(
271+
ConsistencyError("Variable i is not mentioned in one or more triggers.", NoPosition)
272+
)
273+
}
259274
}

0 commit comments

Comments
 (0)