Skip to content

Commit

Permalink
fix: Don't consider := * to be definite assignment for non-ghost vari…
Browse files Browse the repository at this point in the history
…ables of a (00) type (#5024)

Fixes #5023

### Description

Previously, the verifier allowed `:= *` to satisfy the
definite-assignment obligation for any known-to-be-nonempty type,
regardless of context. The fix is to use `(0)`, not `(00)`, for
non-ghost assignments.

<small>By submitting this pull request, I confirm that my contribution
is made under the terms of the [MIT
license](https://github.com/dafny-lang/dafny/blob/master/LICENSE.txt).</small>
  • Loading branch information
RustanLeino authored Jan 30, 2024
1 parent dcc302a commit a5fd6f3
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 9 deletions.
4 changes: 4 additions & 0 deletions Source/DafnyCore/AST/Types/Types.cs
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,10 @@ public bool KnownToHaveToAValue(bool ghostContext) {

public enum AutoInitInfo { MaybeEmpty, Nonempty, CompilableValue }

public bool HavocCountsAsDefiniteAssignment(bool inGhostContext) {
return inGhostContext ? IsNonempty : HasCompilableValue;
}

/// <summary>
/// This property returns
/// - CompilableValue, if the type has a known compilable value
Expand Down
6 changes: 3 additions & 3 deletions Source/DafnyCore/Verifier/BoogieGenerator.TrStatement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ private void TrStmt(Statement stmt, BoogieStmtListBuilder builder, List<Variable
}

// Mark off the simple variables as having definitely been assigned AND THEN havoc their values. By doing them
// in this order, they type antecedents will in effect be assumed.
// in this order, the type antecedents will in effect be assumed.
var bHavocLHSs = new List<Bpl.IdentifierExpr>();
foreach (var lhs in simpleLHSs) {
MarkDefiniteAssignmentTracker(lhs, builder);
Expand Down Expand Up @@ -2402,7 +2402,7 @@ void ProcessLhss(List<Expression> lhss, bool rhsCanAffectPreviouslyKnownExpressi
bldr.Add(cmd);
}

if (!origRhsIsHavoc || ie.Type.IsNonempty) {
if (!origRhsIsHavoc || ie.Type.HavocCountsAsDefiniteAssignment(ie.Var.IsGhost)) {
MarkDefiniteAssignmentTracker(ie, bldr);
}
});
Expand Down Expand Up @@ -2434,7 +2434,7 @@ void ProcessLhss(List<Expression> lhss, bool rhsCanAffectPreviouslyKnownExpressi
bldr.Add(cmd);
}

if (!origRhsIsHavoc || field.Type.IsNonempty) {
if (!origRhsIsHavoc || field.Type.HavocCountsAsDefiniteAssignment(field.IsGhost)) {
MarkDefiniteAssignmentTracker(lhs.tok, nm, bldr);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ class MyClass<G(00)> {
} // error: x was never assigned, neither was oxA or oxB
constructor C4(g: G)
{
this.y := *;
x := y;
this.y := *; // note, the type of y is G, so this does not discharge the definite-assignment proof obligation on the next line
x := y; // error: y has not been properly assigned
}
}

Expand Down Expand Up @@ -87,7 +87,7 @@ method DontForgetHavoc<G(00)>(a: G, h: int) returns (k: G) {
if h < 10 {
k := x; // fine
} else if h < 20 {
k := y;
k := y; // error: y is used but not assigned
} else {
z := *;
return z; // this is fine, since z was explicitly assigned by the havoc
Expand Down Expand Up @@ -145,7 +145,7 @@ method MM<G(00)>(ghost x: int, g: G) returns (vv: G, ww: G)
vv := v; // fine
var w: G := *;
w := *;
ww := w;
ww := w; // error: w has not been properly assigned
}

// ----- iterators ----------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ DefiniteAssignment.dfy(11,9): Error: field 'y', which is subject to definite-ass
DefiniteAssignment.dfy(18,4): Error: field 'y', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
DefiniteAssignment.dfy(23,2): Error: field 'y', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
DefiniteAssignment.dfy(27,2): Error: field 'x', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
DefiniteAssignment.dfy(31,9): Error: field 'y', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(45,0): Error: out-parameter 'y', which is subject to definite-assignment rules, might be uninitialized at this return point
DefiniteAssignment.dfy(53,9): Error: variable 'z', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(61,15): Error: variable 'j', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(64,4): Error: out-parameter 'y', which is subject to definite-assignment rules, might be uninitialized at this return point
DefiniteAssignment.dfy(68,0): Error: out-parameter 'z', which is subject to definite-assignment rules, might be uninitialized at this return point
DefiniteAssignment.dfy(90,9): Error: variable 'y', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(99,5): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
DefiniteAssignment.dfy(99,14): Error: variable 'e1', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(132,7): Error: variable 'b', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(133,0): Error: out-parameter 'g', which is subject to definite-assignment rules, might be uninitialized at this return point
DefiniteAssignment.dfy(148,8): Error: variable 'w', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(242,12): Error: variable 'a', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(243,12): Error: variable 'b', which is subject to definite-assignment rules, might be uninitialized here
DefiniteAssignment.dfy(281,13): Error: variable 'f', which is subject to definite-assignment rules, might be uninitialized here
Expand All @@ -33,4 +36,4 @@ DefiniteAssignment.dfy(438,13): Error: variable 'g', which is subject to definit
DefiniteAssignment.dfy(441,2): Error: out-parameter 'r'', which is subject to definite-assignment rules, might be uninitialized at this return point
DefiniteAssignment.dfy(449,2): Error: out-parameter 'r', which is subject to definite-assignment rules, might be uninitialized at this return point

Dafny program verifier finished with 17 verified, 34 errors
Dafny program verifier finished with 14 verified, 37 errors
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ git-issue-1619.dfy(171,11): Error: variable 'm', which is subject to definite-as
git-issue-1619.dfy(173,9): Error: assertion might not hold
git-issue-1619.dfy(178,11): Error: variable 'm', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(180,9): Error: assertion might not hold
git-issue-1619.dfy(185,12): Error: variable 'u', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(203,11): Error: variable 'p', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(222,11): Error: variable 'x', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(223,11): Error: variable 'p', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(266,27): Error: variable 'p', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(271,6): Error: cannot establish the existence of LHS values that satisfy the such-that predicate
git-issue-1619.dfy(284,27): Error: variable 'p', which is subject to definite-assignment rules, might be uninitialized here
git-issue-1619.dfy(289,9): Error: cannot establish the existence of LHS values that satisfy the such-that predicate

Dafny program verifier finished with 19 verified, 14 errors
Dafny program verifier finished with 18 verified, 16 errors
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
// RUN: %testDafnyForEachResolver --expect-exit-code=4 "%s"

// ---------- compiled variables/fields ----------

module GhostWitness {
type Five = x: int | x == 5 ghost witness 5

class ContainsFive {
var five: Five

constructor () {
five := *;
} // error: five not defined
}

method M() {
var five: Five := *;
var c := new ContainsFive();

if
case true =>
print five, "\n"; // error: five used before defined
case true =>
var x :=
var f: Five := five; // error: five used before defined
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
print c.five, "\n";
}
}

module CompiledWitness {
type Five = x: int | x == 5 witness 5

class ContainsFive {
var five: Five

constructor () {
five := *;
}
}

method M() {
var five: Five := *;
var c := new ContainsFive();

if
case true =>
print five, "\n";
case true =>
var x :=
var f: Five := five;
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
print c.five, "\n";
}
}

module NoWitness {
type Five = x: int | x == 5 witness *

class ContainsFive {
var five: Five

constructor () {
five := *;
} // error: five not defined
}

method M() {
var five: Five := *;
var c := new ContainsFive();

if
case true =>
print five, "\n"; // error: five used before defined
case true =>
var x :=
var f: Five := five; // error: five used before defined
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
print c.five, "\n";
}
}

// ---------- ghost variables/fields ----------

module XGhostWitness {
type Five = x: int | x == 5 ghost witness 5

class ContainsFive {
ghost var five: Five

constructor () {
five := *;
}
}

method M() returns (ghost g: int) {
g := 0;
ghost var five: Five := *;
var c := new ContainsFive();

if
case true =>
g := five;
case true =>
ghost var x :=
var f: Five := five;
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
g := c.five;
}
}

module XCompiledWitness {
type Five = x: int | x == 5 witness 5

class ContainsFive {
ghost var five: Five

constructor () {
five := *;
}
}

method M() returns (ghost g: int) {
g := 0;
ghost var five: Five := *;
var c := new ContainsFive();

if
case true =>
g := five;
case true =>
ghost var x :=
var f: Five := five;
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
g := c.five;
}
}

module XNoWitness {
type Five = x: int | x == 5 witness *

class ContainsFive {
ghost var five: Five

constructor () {
five := *;
} // error: five not defined
}

method M() returns (ghost g: int) {
g := 0;
ghost var five: Five := *;
var c := new ContainsFive();

if
case true =>
g := five; // error: five used before defined
case true =>
ghost var x :=
var f: Five := five; // error: five used before defined
assert f == 5;
if f == 5 then 200 else 200/0;
case true =>
g := c.five;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
git-issue-5023.dfy(13,4): Error: field 'five', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
git-issue-5023.dfy(22,12): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here
git-issue-5023.dfy(25,23): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here
git-issue-5023.dfy(69,4): Error: field 'five', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
git-issue-5023.dfy(78,12): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here
git-issue-5023.dfy(81,23): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here
git-issue-5023.dfy(157,4): Error: field 'five', which is subject to definite-assignment rules, might be uninitialized at this point in the constructor body
git-issue-5023.dfy(167,11): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here
git-issue-5023.dfy(170,23): Error: variable 'five', which is subject to definite-assignment rules, might be uninitialized here

Dafny program verifier finished with 7 verified, 9 errors
1 change: 1 addition & 0 deletions docs/dev/news/5024.fix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Don't consider `:= *` to be a definite assignment for non-ghost variables of a `(00)` type

0 comments on commit a5fd6f3

Please sign in to comment.