E N D
1. Introduction to Spec# Programming System Yossi Peery
Advanced SW Tools Seminar
TAU Nov 2006
2. Spec# Spec# = C# + contracts
3 levels of checking
static type checking
runtime checking
program verification
Boogie verifier
Simplify theorem prover
Tool support integrated with MS Visual Studio
3. Non-null types Each reference type T includes the value null
Spec#s type T! contains only references to objects of type T (not null).
4. Types versus Assertions Without non-null types:
Person(string name)
requires name != null;
With non-null types:
Person(string/*^!^*/ name)
[Q] What is the difference?
5. Non-null types are flow-sensitive The non-null type of an expression is flow-sensitive.
void Foo(T o) {
if (o != null)
T! p = o; // OK!
}
That is, it does not follow uniquely from the declared types of the variables and members mentioned in the expression.
6. Non-null Fields and Object Creation abstract class C {
public C() { this.M(); }
public abstract int M();
}
class D : C {
T! f;
D(T! x) : base() {
f = x;
}
public override int M{ return f.g; }
}
7. Non-nullness of Fields Common coding pattern:
if (o.f != null)
o.f.Foo();
[Q] How can this go wrong?
8. Non-nullness of Properties Common coding pattern:
if (o.P != null)
o.P.Foo();
[Q] How can this go wrong?
9. Fields and Properties For the non-null dataflow analysis, it is assumed that non-nullness of fields and properties is preserved in the absence of intervening heap-modifying operations
Property reads are not considered heap-modifying operations
This is checked at run time because of the possibility of
Data races
Impure property getters
Array element types cannot be non-null
10. Contracts today
11. Preconditions Public virtual void Insert(int index, object value)
requires 0 <= index && index <= Count;
requires !IsReadOnly && !IsFixedSize;
{ }
Run-time checks are inserted by the compiler to validate that preconditions hold
RequiresViolationExecption is the default exception
12. Otherwise clauses Public virtual void Insert(int index, object value)
requires 0 <= index && index <= Count;
otherwise ArgumentOutOfRangeException
requires !IsReadOnly && !IsFixedSize;
otherwise NotSupportedException
{ }
Otherwise clause used to specify what happens when requirements are not met in runtime
13. Postconditions Public virtual void Insert(int index, object value)
ensures Count == old(Count) +1
ensures value == this[index];
ensures Forall { int i in 0 : index; old(this[i]) == this[i] };
ensures Forall { int i in index : old(Count);
old(this[i]) == this[i] };
Static verification is attempted first
Run-time checks inserted by compiler, when required, to check that preconditions hold
EnsuresViolationExecption is the default exception of a postcondition failure
14. Exceptional Postconditions void ReadToken(ArrayList a)
throws EndOfFileException ensures a.Count == old(a.Count)
Exceptions that can legally occur can be part of a the method contract
Can be used only for exceptions that appear in the throws set
Can be used only for checked exceptions
15. Inheriting contracts Method contracts can be inherited
Postconditions can be added
No changes allowed for preconditions
Interface methods can also have specifications
Multiple inheritance raises problem of combining contracts
Preconditions can be combined only if they are the same
Otherwise, only explicit interface method implementation allowed
interface I { void M(int x) requires x <= 10; }
interface I { void M(int x) requires x <= 10; }
class C : I,J { void I.M(int x) {}
void J.M(int x) {}}
16. 0. When do invariants hold? class Car {
int speed;int windResistance;
invariant windResistance == K * speed * speed;
public Car() { speed = 0; windResistance = 0; }
public void SetSpeed(int kmph) { speed = kmph; windResistance = K * speed * speed;}
17. 0. When do invariants hold? class Car {
int speed;int windResistance;
invariant windResistance == K * speed * speed;
public Car() { speed = 0; windResistance = 0; }
public void SetSpeed(int kmph) { speed = kmph; windResistance = K * speed * speed;}
18. When do invariants hold? class Car {
int speed;int windResistance;
invariant windResistance == K * speed * speed;
public Car() { speed = 0; windResistance = 0; }
public void SetSpeed(int kmph) { speed = kmph; P( ); windResistance = K * speed * speed;}
19. Object states Mutable
Object invariant might be violated
Field updates are allowed
Valid
Object invariant holds
Field updates not allowed
20. The heap (the object store)
21. The heap (the object store)
22. To mutable and back: expose class Car {
int speed;int windResistance;
invariant windResistance == K * speed * speed;
public void SetSpeed(int kmph) requires this.valid;{ expose (this) { speed = kmph; windResistance = K * speed * speed; }}
23. Summary for simple objects:
24. Summary for simple objects:
25. Aggregate objects class Seat { public void Move(int pos) requires this.valid; }
class Car {
Seat s;
public void Adjust(Profile p) requires this.valid ? p.valid;{ s.Move(p.SeatPosition);}
26. Ownership
27. Ownership domains
28. Ownership domains
29. An object is only as valid as its components
30. Representation (rep) fields class Seat { public void Move(int pos) requires this.Consistent; }
class Car {
rep Seat s;
public void Adjust(Profile p) requires this.Consistent ? p.Consistent;{ expose (this) { s.Move(p.SeatPosition); }}
31. Peer fields and peer validity class Seat { public void Move(int pos) requires this.PeerConsistent; }
class Car {
rep Seat s; peer Seat s;
public void Adjust(Profile p) public void Adjust(Position p) requires this.PeerConsistent ? requires this.PeerConsistent ? p.PeerConsistent; p.PeerConsistent; { { expose (this) { s.Move(p.SeatPosition); s.Move(p.SeatPosition); }} }
32. Summary for aggregate objects:
33. Summary for aggregate objects:
34. Immutable types
35. Ever-peer-consistent (immutable) objects
36. Summary for immutable types:
37. Summary for immutable types:
38. Immutable is determined from static type (except for object) [Immutable] class C extends B { }
[Immutable] allowed on C if either
B is [Immutable] or
B is object
[Immutable] required on C if
B is [Immutable]
39. Subclasses class Car {
int speed; invariant 0 = speed; }
class LuxuryCar extends Car { Radio r; invariant 6 = r.CDCapacity; }
40. Owners are pairs To support subclasses with invariants, we change owners to be pairs:
(object reference, class frame)
41. Invariants and subclasses
42. Summary for subclasses:
43. Summary for subclasses:
44. Thank You !
45. Backup Slides
46. Static field initialization class C {
static S ! s ;
static T ! t ;
static C() { s = new S(); t = new T();}
}
47. Additive invariants class Car {
int speed; }
class LuxuryCar extends Car { Radio r; invariant speed > 60 ? r.SoundBooster=true;
overrides void SetSpeed(int kmph) { expose (this) { base.SetSpeed(kmph); if (speed > 60) { } } }}
48. An additive frame is only as valid as its subclass frames
49. Summary for additive invariants:
50. Summary for additive invariants:
51. Object invariants in Spec# Spec# syntactically checks that invariants are admissible
Ownership is specified with the [Owned] attribute
We first supported only rep ownership relations
peer relationships are often useful too
we now use PeerConsistent as the default method precondition
owners are set automatically on assignments of rep and peer fields
An immutable class/interface is specified with [Immutable]
We first supported only additive invariants in Spec#
non-additive invariants are easier to work with
non-additive expose is now the default
implementation restriction: no further expose allowed on an object while a non-additive expose is in progress
Additive methods (those that update the additive fields mentioned in additive invariants) require dynamic dispatch and use precondition Consistent
52. From Spec#... static int Abs(int x)
ensures 0 <= x ==> result == x;
ensures x < 0 ==> result == -x;
{ if (x < 0) x = -x; return x; }
53. via BoogiePL procedure Abs(x$in: int) returns ($result: int);
ensures 0 <= x$in ==> $result == x$in;
ensures x$in < 0 ==> $result == -x$in;
{ var x1, x2: int, b: bool;
entry: x1 := x$in; b := x < 0; goto t, f;
t: assume b; x := -x; goto end;
f: assume !b; goto end;
end: $result := x; return; }
54. via BoogiePL-DSA procedure Abs(x$in: int) returns ($result: int);
ensures 0 <= x$in ==> $result == x$in;
ensures x$in < 0 ==> $result == -x$in;
{ var x1, x2: int, b: bool;
entry: x1 := x$in; b := x1 < 0; goto t, f;
t: assume b; x2 := -x1; goto end;
f: assume !b; x2 := x1; goto end;
end: $result := x2; return; }
55. via Passive BoogiePL procedure Abs(x$in: int) returns ($result: int);
ensures 0 <= x$in ==> $result == x$in;
ensures x$in < 0 ==> $result == -x$in;
{ var x1, x2: int, b: bool;
entry: assume x1 == x$in;
assume b == x1 < 0; goto t, f;
t: assume b; assume x2 == -x1; goto end;
f: assume !b; assume x2 == x1; goto end;
end: assume $result == x2; return; }
56. without contracts procedure Abs(x$in: int) returns ($result: int);
ensures 0 <= x$in ==> $result == x$in;
ensures x$in < 0 ==> $result == -x$in;
{ var x1, x2: int, b: bool;
entry: assume x1 == x$in;
assume b == x1 < 0; goto t, f;
t: assume b; assume x2 == -x1; goto end;
f: assume !b; assume x2 == x1; goto end;
end: assume $result == x2; return; }
57. without contracts procedure Abs(x$in: int) returns ($result: int);
{ var x1, x2: int, b: bool;
entry: assume x1 == x$in;
assume b == x1 < 0; goto t, f;
t: assume b; assume x2 == -x1; goto end;
f: assume !b; assume x2 == x1; goto end;
end: assume $result == x2;
assert 0 <= x$in ==> $result == x$in;
assert x$in < 0 ==> $result == -x$in;
return; }
58. to Logic [M. Barnett, K. R. M. Leino, in preparation]
entry &&
(entry <== (x1 == x$in ==>
b == x1 < 0 ==> t && f)) &&
(t <== (b ==> x2 == -x1 ==> end)) &&
(f <== (!b ==> x2 == x1 ==> end)) &&
(end <== ($result == x2 ==>
(0 <= x$in ==> $result == x$in) &&
(x$in < 0 ==> $result == -x$in) &&
true))