


class C
{
  var c:C?
  ghost var g:C?
}

datatype D = D(c:C, ghost g:C)

newtype N = i:int | 0 <= i < 100
type T = i:int | 0 <= i < 100

function f1(c:C):C
function method fm1(c:C):C

function fBad(c:C, d:D, a:array<C>, s:set<C>):C


{
  // each of the following lines fails:
  assert true by { assert allocated(c); }
  assert true by { assert allocated(d); }
  assert true by { assert allocated(a); }
  assert true by { var x := c.c; }
  assert true by { assert allocated(d.c); }
  assert true by { assert allocated(d.g); }
  assert true by { assert allocated(c) ==> allocated(c.g); }
  assert true by { assert allocated(c) ==> allocated(f1(c)); }
  assert true by { assert allocated(c) ==> allocated(fm1(c)); }
  assert true by { forall x | x in s ensures true { assert allocated(x); } }
  assert allocated(c);
  assert allocated(d);
  assert allocated(a);
  var x:C :| true; assert allocated(x);
  c // ok
}

function method fmBad(c:C, d:D, a:array<C>, s:set<C>):C


{
  // each of the following lines fails:
  assert true by { assert allocated(c); }
  assert true by { assert allocated(d); }
  assert true by { assert allocated(a); }
  assert true by { var x := c.c; }
  assert true by { assert allocated(d.c); }
  assert true by { assert allocated(d.g); }
  assert true by { assert allocated(c) ==> allocated(c.g); }
  assert true by { assert allocated(c) ==> allocated(f1(c)); }
  assert true by { assert allocated(c) ==> allocated(fm1(c)); }
  assert true by { forall x | x in s ensures true { assert allocated(x); } }
  assert allocated(c);
  assert allocated(d);
  assert allocated(a);
  var x:C :| true; assert allocated(x);
  c // ok
}

lemma LGood(c:C, d:D, a:array<C>, s:set<C>)


{
  assert true by { assert allocated(null); }
  assert true by { assert allocated(c) ==> allocated(c.c); }
  assert true by { assert allocated(a) && a.Length > 0 ==> allocated(a[0]); }
}

lemma LCall() returns(c:C)

lemma LBad(c:C, d:D, a:array<C>, s:set<C>)
  returns(r:C)


{
  var l:C;
  // each of the following lines fails:
  assert true by { assert allocated(c); }
  assert true by { assert allocated(d); }
  assert true by { assert allocated(a); }
  assert true by { var x := c.c; }
  assert true by { assert allocated(d.c); }
  assert true by { assert allocated(d.g); }
  assert true by { assert allocated(c) ==> allocated(c.g); }
  assert true by { assert allocated(c) ==> allocated(f1(c)); }
  assert true by { assert allocated(c) ==> allocated(fm1(c)); }
  assert true by { forall x | x in s ensures true { assert allocated(x); } }
  assert true by { assert (match d case D(x, y) => allocated(x)); }
  if (*) { match d case D(x, y) => { assert allocated(x); } }
  if (*) { var x:C :| true; assert allocated(x); }
  assert allocated(c);
  assert allocated(d);
  assert allocated(a);
  assert allocated(l);
  assert allocated(r);
  l := LCall(); assert allocated(l);
}

ghost method GGood(c:C, d:D, a:array<C>, s:set<C>)


{
  assert true by { assert allocated(c) ==> allocated(c.c); }
  assert true by { assert allocated(a) && a.Length > 0 ==> allocated(a[0]); }
}

ghost method GBad(c:C, d:D, a:array<C>, s:set<C>)
  returns(r:C)


{
  var l:C;
  // each of the following lines fails:
  assert true by { assert allocated(c); }
  assert true by { assert allocated(d); }
  assert true by { assert allocated(a); }
  assert true by { var x := c.c; }
  assert true by { assert allocated(d.c); }
  assert true by { assert allocated(d.g); }
  assert true by { assert allocated(c) ==> allocated(c.g); }
  assert true by { assert allocated(c) ==> allocated(f1(c)); }
  assert true by { assert allocated(c) ==> allocated(fm1(c)); }
  assert true by { forall x | x in s ensures true { assert allocated(x); } }
  assert true by { assert (match d case D(x, y) => allocated(x)); }
  if (*) { match d case D(x, y) => { assert allocated(x); } }
  if (*) { var x:C :| true; assert allocated(x); }
  assert allocated(c);
  assert allocated(d);
  assert allocated(a);
  assert allocated(l);
  assert allocated(r);
  l := LCall(); assert allocated(l);
}

method MCall() returns(c:C, ghost gc:C)

method MGood(c:C, ghost gc:C, d:D, ghost gd:D, a:array<C>, ghost ga:array<C>, s:set<C>, ghost gs:set<C>)
  returns(r:C?, ghost gr:C?)








{
  var l:C?;
  ghost var gl:C?;
  assert true by { assert allocated(c); }
  assert true by { assert allocated(d); }
  assert true by { assert allocated(a); }
  assert true by { assert allocated(c.c); }
  assert true by { assert allocated(d.c); }
  assert true by { assert allocated(l); }
  assert true by { assert allocated(r); }
  assert true by { assert a.Length > 0 ==> allocated(a[0]); }
  assert true by { assert allocated(gc) ==> allocated(gc.c); }
  assert true by { assert allocated(gd) ==> allocated(gd.c); }
  assert true by { ghost var x := gd.c; }
  assert true by { assert allocated(ga) && ga.Length > 0 ==> allocated(ga[0]); }
  assert true by { forall x | x in s ensures true { assert allocated(x); } }
  assert true by { assert (match d case D(x, y) => allocated(x)); }
  if (*) { match d case D(x, y) => { assert allocated(x); } }
  if (*) { var x:C :| true; assert allocated(x); }
  l, gl := MCall(); assert allocated(l);
}

method MBad(c:C, ghost gc:C, d:D, ghost gd:D, a:array<C>, ghost ga:array<C>, s:set<C>, ghost gs:set<C>, f:C~>C)
  returns(r:C?, ghost gr:C?)
{
  var l:C;
  ghost var gl:C;
  // each of the following lines fails:
  assert true by { assert allocated(gc); }
  assert true by { assert allocated(gd); }
  assert true by { assert allocated(ga); }
  assert true by { var x := gc.c; }
  assert true by { assert allocated(gd.c); }
  assert true by { assert allocated(c.g); }
  assert true by { assert allocated(d.g); }
  assert true by { assert allocated(f1(c)); }
  assert true by { assert allocated(fm1(c)); }
  assert true by { forall x | x in gs ensures true { assert allocated(x); } }
  assert true by { assert (match d case D(x, y) => allocated(y)); }
  assert true by { assert (match gd case D(x, y) => allocated(x)); }
  if (*) { match d case D(x, y) => { assert allocated(y); } }
  if (*) { match gd case D(x, y) => { assert allocated(x); } }
  if (*) { ghost var x:C :| true; assert allocated(x); }
  assert f.requires(c) ==> allocated(f(c));
  assert f.requires(c) && gc in f.reads(c) ==> allocated(gc);
  assert allocated(gc);
  assert allocated(gd);
  assert allocated(ga);
  assert allocated(gl);
  assert allocated(gr);
  gl := LCall(); assert allocated(gl);
  l, gl := MCall(); assert allocated(gl);
}

function fReadGood(c1:C, c2:C):C?
  requires allocated(c1)
  requires allocated(c2)
  reads c1
{
  c1.c // succeeds
}

function fReadBad1(c1:C, c2:C):C?
  requires allocated(c1)
  requires allocated(c2)
  reads c1
{
  c2.c // fails, c2 not in reads set
}

function fReadBad2(c1:C, c2:C):C?
  requires allocated(c1)
  requires allocated(c2)
{
  c2.c // fails, c2 not in reads set
}

function fId1(c1:C, c2:C):C
  reads c1
{
  c1
}

function fId2(c1:C, c2:C):C
  reads c1
  ensures fId2(c1, c2) == c1

lemma LId(c1:C, c2:C)
{
  assert fId1(c1, c2) == c1; // succeeds: the definition axiom applies even when c1 and c2 may be unallocated
  assert fId2(c1, c2) == c1; // succeeds: the consequence axiom applies even when c1 and c2 may be unallocated
}

method MModifiesGood(c1:C, c2:C)
  modifies c1
{
  c1.c := c2;
  c1.g := c2;
}

method MModifiesFunGood1(c1:C, c2:C, ghost g1:C, ghost g2:C, f:C~>C)
  requires f.requires(g1) && f(g1) == g2 && f.reads(g1) == {}
  modifies c1
{
  MModifiesGood(c1, c2);
  assert f(g1) == g2; // succeeds
}

method MModifiesFunGood2(c1:C, c2:C, ghost g1:C, ghost g2:C, f:C~>C)
  requires f.requires(g1) && f(g1) == g2 && (forall o :: o in f.reads(g1) ==> allocated(o))
  requires c1 !in f.reads(g1)
  modifies c1
{
  MModifiesGood(c1, c2);
  assert f(g1) == g2; // succeeds: everything f reads from is allocated, so if g1 is unallocated, f can't read from it
}











method MModifiesFunBad(c1:C, c2:C, ghost g1:C, ghost g2:C, f:C~>C)
  requires f.requires(g1) && f(g1) == g2
  requires c1 !in f.reads(g1)
  modifies c1
{
  MModifiesGood(c1, c2);
  assert f(g1) == g2; // fails: f might read from g1, g1 might have been unallocated, and therefore may have changed
}

class Reader
{
  ghost var g:set<C>
  function method f():C reads this reads g
}

method MReader(r:Reader)
  requires allocated(r)
{
  var f := r.f; // ok
  var c := f(); // ok
  assert allocated(f); // succeeds because f is not ghost
  // Just because f is allocated does not imply that everything f reads is allocated.
  // f is allowed to include unallocated references in its reads set, so the following must fail:
  assert true by { assert forall o :: o in f.reads() ==> allocated(o); } // fails
  assert true by { assert forall o :: o in r.g ==> allocated(o); } // fails
}

method MComprehension(c:C, ghost gc:C)
{
  var s := set x | x in {c};
  assert forall x :: x in s ==> allocated(x); // succeeds
  ghost var gs := set x | x == gc;
  assert forall x :: x in gs ==> allocated(x); // fails
}

