When I run my code, I get an index out of range error. The same problem also occurs at the ensures statement.
My code:
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
class class_0 {
var
Data : DATA,
Cmd : MSG_CMD
}
class class_1 {
var
Data : DATA,
State : CACHE_STATE
}
class TopC{
var
AuxData : DATA,
MemData : DATA,
CurPtr : NODE,
CurCmd : MSG_CMD,
ExGntd : boolean,
ShrSet : array<boolean>,
InvSet : array<boolean>,
Chan3 : array<class_0 > ,
Chan2 : array<class_0 > ,
Chan1 : array<class_0 > ,
Cache : array<class_1 > }
method n_RecvInvAck(top:TopC,i:nat, N0:nat,p1:nat,p2:nat )
requires 0<= i<N0
requires top.Chan3.Length ==N0
requires top.ShrSet.Length ==N0
requires N0>0
requires 0<= p1 <top.Chan3.Length
requires 0<= p2 <top.Chan3.Length
requires p1
requires N0>0
requires (i==p2)
requires ((top.Chan3[i].Cmd == InvAck) && (!(top.CurCmd == Empty)))
modifies top.Chan3[i]
modifies top.ShrSet
modifies top
ensures (!((top.ShrSet[p1] == true) && (top.ExGntd == true) &&
(top.ShrSet[p2] == true)))
{
top.Chan3[i].Cmd := Empty;
top.ShrSet[i] := false;
if (top.ExGntd == true) {
top.ExGntd := false;
top.MemData := top.Chan3[i].Data;
}
}
The problem is that the method modifies top, which means it might allocated a totally different array for top.ShrSet, which might have a different length.
You can add the line
ensures top.ShrSet == old(top.ShrSet)
before the other ensures clause to fix this problem.
Related
Is there a built-in data type in Dafny like List in Java (or any type for dynamic list)?
I've looked for it in Dafny Reference Manual, but nothing found.
It seems that a self-defined class must be defined for it.
If it is the fact, then how can the performance be assured for the generated java program and how can the gernerality of Dafny as a programming language be assured?
Not criticism, just curious.
Dafny's first collection is undoubtedly seq, which is an immutable list.
function sum(s: seq<int>): int {
if |s| == 0 then 0 else s[0] + sum(s[1..])
}
For anything else, the Dafny team is working on a standard library, but you might be interestest by the first example given in the Dafny documentation that also explain why lists are non trivial objects to verify:
https://dafny.org/dafny/DafnyRef/DafnyRef#sec-example
In short, to define a list, you want to write a class and store a ghost model of all the elements to ensure there is no cycle, and possibly write this node into another data structure. But the proofs are not obvious. Here is what I got so far.
class ListNode<T> {
var head: T
var tail: ListNode?<T>
ghost var Repr: seq<ListNode<T>>
constructor(h: T, t: ListNode?<T>) requires t != null ==> t.Valid() ensures Valid()
{
head:= h;
tail := t;
Repr := [this] + (if t == null then [] else t.Repr);
}
predicate Valid() reads this, Repr decreases |Repr|
{
&& |Repr| > 0
&& Repr[0] == this
&& (if tail == null then |Repr| == 1 else
&& |Repr| > 1
&& tail == Repr[1]
&& tail.Repr == Repr[1..]
&& tail.Valid())
}
lemma ReprAreDecreasing(i: int)
requires Valid()
requires 0 <= i < |Repr|
ensures Repr[i].Repr == Repr[i..]
ensures Repr[i].Valid()
{
if i == 0 {
} else {
tail.ReprAreDecreasing(i-1);
}
}
}
class List<T> {
var head: ListNode?<T>
var last: ListNode?<T>
ghost var Repr: seq<ListNode<T>>
constructor() ensures Valid() {
head := null;
last := null;
Repr := [];
}
lemma ValidImpliesAllNodesValid(n: ListNode<T>)
requires Valid()
requires n in Repr
ensures n.Valid() {
if n == head {
assert n.Valid();
} else {
var i :| 0 <= i < |Repr| && Repr[i] == n;
head.ReprAreDecreasing(i);
}
}
method Append(node: ListNode<T>)
{
...
}
predicate Valid() reads this, Repr
{
(if head != null then
&& last != null
&& head in Repr
&& head.Repr == Repr
&& head.Valid()
&& last == head.Repr[|head.Repr|-1]
&& assert last.Repr == head.Repr[|head.Repr|-1..] by {
head.ReprAreDecreasing(|head.Repr|-1);
} last.Valid()
else
&& last == null
&& |Repr| == 0)
}
}
References:
https://dafny.org/dafny/DafnyRef/DafnyRef#sec-seq-comprehension
The final commented out assert will not validate but when run the if statement above will prints.
OUTPUT
ohb= true
ohx= false
palin(xe) == false
ohx ==false
function method palin(a:seq<int>) :bool {
forall i:int :: (0<=i && i<(|a|/2)) ==> a[i]==a[|a|-i -1]
}
method Main() {
var xe:seq<int> := [0,1,2,3,0];
var se:seq<int> := [0,1,2,1,0];
var ohb := palin(se);
var ohx :bool := palin(xe);
print "ohb= ",ohb,"\n";
print "ohx= ",ohx,"\n";
assert palin(se);
if (palin(xe) == false) {print "palin(xe) == false\n";}
if (!ohx) {print "ohx ==false\n";}
//assert !ohx;
}
A failing assert means the verifier cannot automatically find a proof. If you think the property holds, you need to write the proof (or part of the proof).
For your program, proving that something is not a palindrome comes down to showing a position that violates the palindrome property. In terms of logic, you're trying to prove the negation of a forall, which is an exists, and to prove an exists you'll need to supply the witness for the bound variable i.
In your example, the following suffices:
predicate method palin(a: seq<int>) {
forall i :: 0 <= i < |a| / 2 ==> a[i] == a[|a| - i - 1]
}
method Main() {
var xe := [0,1,2,3,0];
var se := [0,1,2,1,0];
var ohb := palin(se);
var ohx := palin(xe);
print "ohb= ", ohb, "\n";
print "ohx= ", ohx, "\n";
assert palin(se);
if palin(xe) == false { print "palin(xe) == false\n"; }
if !ohx { print "ohx == false\n"; }
assert !ohx by {
assert xe[1] != xe[3];
}
}
datatype CACHE_STATE = CACHE_I| CACHE_S| CACHE_E
datatype NODE_CMD = NODE_None| NODE_Get| NODE_GetX
datatype UNI_CMD = UNI_None| UNI_Get| UNI_GetX| UNI_Put| UNI_PutX| UNI_Nak
datatype INV_CMD = INV_None| INV_Inv| INV_InvAck
datatype RP_CMD = RP_None| RP_Replace
datatype WB_CMD = WB_None| WB_Wb
datatype SHWB_CMD = SHWB_None| SHWB_ShWb| SHWB_FAck
datatype NAKC_CMD = NAKC_None| NAKC_Nakc
type NODE=nat
type boolean=bool
class class_0 {
var
Cmd : NAKC_CMD
}
class class_1 {
var
HomeProc : boolean,
Proc : NODE,
Cmd : SHWB_CMD
}
class class_2 {
var
HomeProc : boolean,
Proc : NODE,
Cmd : WB_CMD
}
class class_3 {
var
Cmd : RP_CMD
}
class class_4 {
var
Cmd : INV_CMD
}
class class_5 {
var
HomeProc : boolean,
Proc : NODE,
Cmd : UNI_CMD
}
class class_6 {
var
HomeInvSet : boolean,
InvSet : array<boolean>,
HomeShrSet : boolean,
ShrSet : array<boolean>,
ShrVld : boolean,
HomeHeadPtr : boolean,
HeadPtr : NODE,
HeadVld : boolean,
Dirty : boolean,
Local : boolean,
Pending : boolean
}
class class_7 {
var
CacheState : CACHE_STATE,
InvMarked : boolean,
ProcCmd : NODE_CMD
}
class class_8 {
var
NakcMsg : class_0 ,
ShWbMsg : class_1 ,
WbMsg : class_2 ,
HomeRpMsg : class_3 ,
RpMsg : array<class_3 >,
HomeInvMsg : class_4 ,
InvMsg : array<class_4 >,
HomeUniMsg : class_5 ,
UniMsg : array<class_5 >,
Dir : class_6 ,
HomeProc : class_7 ,
Proc : array<class_7 >;
constructor(invmsg:array<class_4 >,unimsg:array<class_5 >,rpmsg:array<class_3 >,proc:array<class_7 >){
NakcMsg := new class_0 ;
ShWbMsg := new class_1 ;
WbMsg := new class_2 ;
HomeRpMsg := new class_3 ;
RpMsg := rpmsg;
HomeInvMsg := new class_4 ;
InvMsg := invmsg;
HomeUniMsg := new class_5 ;
UniMsg := unimsg;
Dir := new class_6 ;
HomeProc := new class_7 ;
Proc := proc;
}
}
class TopC{
var
Sta : class_8 ;
constructor (){
var
invmsg:array<class_4 >,unimsg:array<class_5 >,rpmsg:array<class_3 >,proc:array<class_7 >;
Sta := new class_8
(invmsg, unimsg, rpmsg, proc);
}
}
method n_NI_ShWbinv__4_0(top:TopC,N0:nat,p__Inv4:nat)
//requires top!=null
//requires top.Sta!=null
//requires top.Sta.Dir!=null
requires N0>0
requires top.Sta.Dir.InvSet.Length==N0
ensures top.Sta.Dir.InvSet==old(top.Sta.Dir.InvSet)
requires N0>0
requires top.Sta.Dir.ShrSet.Length==N0
ensures top.Sta.Dir.ShrSet==old(top.Sta.Dir.ShrSet)
//ensures top.Sta.Dir==old(top.Sta.Dir)
requires N0>0
requires top.Sta.UniMsg.Length==N0
requires forall i,j::0<=i<top.Sta.UniMsg.Length&&0<=j<top.Sta.UniMsg.Length==>top.Sta.UniMsg[i]!=top.Sta.UniMsg[j]
// ensures top.Sta.UniMsg==old(top.Sta.UniMsg)top.Sta.Dir.HomeShrSet
requires forall i::0<=i<top.Sta.Dir.ShrSet.Length
==>top.Sta.Dir.ShrSet[i]!=top.Sta.Dir.HomeShrSet
requires p__Inv4<N0
requires (!((top.Sta.HomeProc.CacheState == CACHE_E) && (top.Sta.UniMsg[p__Inv4].Cmd == UNI_PutX)))//statement has nothing with prop--it guranttee itself
//guard condition
requires (top.Sta.ShWbMsg.Cmd == SHWB_ShWb);
// ensures (!((top.Sta.HomeProc.CacheState == CACHE_E) && (top.Sta.UniMsg[p__Inv4].Cmd == UNI_PutX)))
modifies top.Sta.Dir.InvSet
modifies top.Sta.Dir.ShrSet
modifies top.Sta.Dir
modifies top.Sta.ShWbMsg
modifies top.Sta
modifies top
{
top.Sta.ShWbMsg.Cmd := SHWB_None;
top.Sta.Dir.Pending := false;
top.Sta.Dir.Dirty := false;
top.Sta.Dir.ShrVld := true;
var p:=0;
while(p<N0)
decreases N0-p
invariant top.Sta.Dir.ShrSet==old(top.Sta.Dir.ShrSet)
invariant top.Sta.Dir.InvSet==old(top.Sta.Dir.InvSet)
{ //assume 0<p<=top.Sta.Dir.ShrSet.Length;
//assume 0<p<=top.Sta.Dir.InvSet.Length;
aux1(p,N0,top.Sta.Dir.ShrSet.Length);
aux1(p,N0,top.Sta.Dir.InvSet.Length);
if (((p == top.Sta.ShWbMsg.Proc) && (top.Sta.ShWbMsg.HomeProc == false)) || (top.Sta.Dir.ShrSet[p] == true)) {
top.Sta.Dir.ShrSet[p] := true;
top.Sta.Dir.InvSet[p] := true;}
else{
top.Sta.Dir.ShrSet[p] := false;
top.Sta.Dir.InvSet[p] := false;
}
p:=p+1;
}
//assert top==old(top);
// assert top.Sta ==old(top.Sta );
//assert top.Sta.Dir==old(top.Sta.Dir);
if ((top.Sta.ShWbMsg.HomeProc == true) || (top.Sta.Dir.HomeShrSet == true)){
top.Sta.Dir.HomeShrSet := true;
top.Sta.Dir.HomeInvSet := true;}
else{
top.Sta.Dir.HomeShrSet := false;
top.Sta.Dir.HomeInvSet := false;
}
}
lemma aux1(p:int, N0: int, M:int)
requires 0<=p<N0
requires N0==M
ensures 0<=p<M
{}
Dafny tell me that "top.Sta.Dir.HomeShrSet := true; " statement assignment may update an object not in the enclosing context's modifies clause. I guess that
the error occurs due to the fact top.Sta.Dir.HomeShrSet is not in the range that modifies cause defines;
I have defined that modifies top.Sta.Dir.InvSet
modifies top.Sta.Dir.ShrSet
modifies top.Sta.Dir
modifies top.Sta.ShWbMsg
modifies top.Sta
modifies top
how to define modifies clause to avoid this error?
Your modifies clause is fine. To fix this problem, you need to add this line to your while loop:
invariant top.Sta.Dir == old(top.Sta.Dir)
The line modifies top.Sta.Dir tells Dafny that whatever object top.Sta.Dir refers to at the beginning of the method may be modified. However, without this extra invariant, Dafny doesn't know that the value top.Sta.Dir hasn't changed by the point in the program where you execute top.Sta.Dir.HomeShrSet := true.
This section reminders me "Related location 1. This is the postcondition that might not hold."
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
method n_RecvGntSinv__2_0(Cache_Data:array<DATA>, Cache_State:array<CACHE_STATE>, Chan2_Cmd:array<MSG_CMD>, Chan2_Data:array<DATA>,i:nat, N0:nat,p__Inv0:nat,p__Inv2:nat)
requires Cache_Data.Length==N0
requires Cache_State.Length==N0
requires Chan2_Cmd.Length==N0
requires Chan2_Data.Length==N0
requires 0<= i<N0
requires p__Inv0!=p__Inv2&&p__Inv2<N0&& p__Inv0<N0
requires i==p__Inv2
requires (!((Cache_State[p__Inv0] == E) && (Chan2_Cmd[p__Inv2] == GntS)))//3
//guard condition
requires (Chan2_Cmd[i] == GntS)
ensures !((Cache_State[p__Inv2] == S) && (!(Cache_State[p__Inv0] == I)) && (!(Cache_State[p__Inv0] == S)))
modifies Cache_Data
modifies Cache_State
modifies Chan2_Cmd
modifies Chan2_Data
{
Cache_State[i] := S;
Cache_Data[i] := Chan2_Data[i];
Chan2_Cmd[i] := Empty;
}
When i change the condition of ensure,like the below fragment
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
method n_RecvGntSinv__2_0(Cache_Data:array<DATA>, Cache_State:array<CACHE_STATE>, Chan2_Cmd:array<MSG_CMD>, Chan2_Data:array<DATA>,i:nat, N0:nat,p__Inv0:nat,p__Inv2:nat)
requires Cache_Data.Length==N0
requires Cache_State.Length==N0
requires Chan2_Cmd.Length==N0
requires Chan2_Data.Length==N0
requires 0<= i<N0
requires p__Inv0!=p__Inv2&&p__Inv2<N0&& p__Inv0<N0
requires i==p__Inv2
requires (!((Cache_State[p__Inv0] == E) && (Chan2_Cmd[p__Inv2] == GntS)))//3
//guard condition
requires (Chan2_Cmd[i] == GntS)
ensures !((Cache_State[p__Inv2] == S) && (Cache_State[p__Inv0] == E ))modifies Cache_Data
modifies Cache_State
modifies Chan2_Cmd
modifies Chan2_Data
{
Cache_State[i] := S;
Cache_Data[i] := Chan2_Data[i];
Chan2_Cmd[i] := Empty;
}
This compiles success.Whether there are some place i don't understand Dafny?
I think Dafny is no longer maintained by people, it is dead! No need to use it!
datatype CACHE_STATE = I| S| E
datatype MSG_CMD = Empty| ReqS| ReqE| Inv| InvAck| GntS| GntE
type NODE=nat
type DATA=nat
type boolean=bool
class class_0 {
var
Data : DATA,
Cmd : MSG_CMD
}
class class_1 {
var
Data : DATA,
State : CACHE_STATE
}
method n_SendGntEinv__5_0(Cache_State:array<CACHE_STATE>, Chan2_Cmd:array<MSG_CMD>, Chan2_Data:array<DATA>, CurCmd:MSG_CMD, CurPtr:NODE, ExGntd:boolean, MemData:DATA, ShrSet:array<boolean>,
i:nat,N0:nat,p__Inv0:nat,p__Inv2:nat)
requires 0<= i < N0
requires Chan2_Cmd.Length==N0
requires Chan2_Data.Length==N0
requires ShrSet.Length==N0
requires p__Inv0!=p__Inv2&&p__Inv2<N0&& p__Inv0<N0
requires i==p__Inv2
//1
//guard condition
requires ((Chan2_Cmd[i] == Empty) && (CurCmd == ReqE) && (CurPtr == i) && (ExGntd == false) && (forall j |0<= j<N0 :: (ShrSet[j] == false) ))
ensures (!((Cache_State[p__Inv0] == E) && (Chan2_Cmd[p__Inv2] == GntS)))
modifies Chan2_Cmd
modifies Chan2_Data
modifies ShrSet
{
Chan2_Cmd[i] := GntE;
Chan2_Data[i] := MemData;
ShrSet[i] := true;
ExGntd := true;
CurCmd := Empty;
}
I try to compile this code, however tip mentions me
LHS of assignment must denote a mutable variable
and I don't know how to solve the problem. Does the CurCMD and the EXGntd need to be array value, or there has other solution to this problem?
In-parameters are immutable. The error message points out that you're trying to assign to them.
You can introduce (mutable) local variables, if you want. In your example, that would look like:
var ExGntd', CurCmd' := ExGntd, CurCmd;
...
ExGntd' := true;
CurCmd' := Empty;