Tuesday, August 09, 2005 2:11 AM bart

An introduction to Spec#

Introduction

I've been blogging about the Comega language on my blog earlier (and will continue to do so in the near future), which is a superset of the C# language that focuses primarily on object/relational/hierarchical data manipulation and mapping on the one hand and concurrency language extensions on the other hand. Part of the features of Comega are already available in C# 2.0 (e.g. the yield statement, anonymous delegates and more deeply closures).

In this post I'm introducing you to another Microsoft Research project called Spec#, which is a superset of the C# language too. The idea of Spec# is to introduce the concept of pre-conditions, post-conditions and object invariants in the code (much like Eiffel does). More information can be found on:

Today's software is typically of a large complexity and therefore a specification of the software is needed in order to be successful. However, specifications are far too often words or diagrams and are not reflected in a clear way in the code of the application itself. Of course, there are approaches today that can assist to bring clearness to a software development project, using UML tools or even unit testing which can serve as a kind of minor specification of the software. Spec# however introduces specification declarations and enforcement inside the code itself, making the code more robust and self-describing.

Generally spoken, Spec# focuses on four fields:

  • non-null types
  • enhanced exception support
  • pre- and post-conditions
  • object invariants

I'll cover these in the rest of this post in somewhat more detail. Just to wrap up the introduction, let's stress on the fact that Spec# is a superset of the C# language and therefore any valid C# application should compile in Spec# too (if there are no conflicts with the new keywords introduced in Spec#).

To summarize Spec# in three words, remember these: "Design by contract"

 

How to start?

Go to the MSR homepage for Spec# and download the Visual Studio .NET 2003 or Visual Studio 2005 plug-in to support the Spec# language inside the VS IDE. When you start VS after the installation of Spec# a new set of project types should appear in the list. Spec# projects have an extension of .sscproj and the code files have an .ssc extension. Included with the Spec# package are some samples which are also accessible through the New Project dialog window in VS.

 

Non-null types

One classic problem that occurs in a lot of applications is the possiblity that a reference type variable is null. In such a case, it's not valid to access or call members of the variable through the dot (.) operator. Assume you have a class like the following:

class A
{
   B b;

   public A(B b)
   {
      this.b = b;
   }

   public int C()
   {
      return b.D(); //what if b == null?
   }
}

When you try to compile this in Spec#, you'll get a warning by the Spec# compiler, telling there is a "possible null dereference" on the line return b.D();. Adding code like

if (b != null) { ... }

will make the warning disappear for the guarded statements (that is, the block that is protected by the null-check). Another way to "solve" the problem is to throw an exception:

if (b == null) throw new NullArgumentException();

However, Spec# allows you to enforce the "non-nullness" of a variable by using a precondition (will cover this in more detail later). The basic code looks like this:

   public A(B b)
   requires b != null;
   {
      this.b = b;
   }

An alternative syntax in Spec# is the use of the "bang" symbol (!) as an annotation for a declaration, as follows:

   B! b;

   public A(B! b)
   {
      this.b = b;
   }

which means "it should never be possible to get null in this variable". The runtime will guarantee against exceptions by enforcing the preconditions for you, by having the compiler add additional statements to ensure this and by generating compile-time errors ("unsatified requires ...").

Let's show a far more extensive and concrete sample. Go to the Spec# command line and create a file called test.ssc in which you paste the following code:

using System;

class Test
{
   public static void Main()
   {
      Do("test");
   }

   private static void Do(string s)
   {
      Console.WriteLine(s.Length);
   }
}

When compiling this using ssc.exe test.ssc, you'll get the following warning:

C:\temp\specsharp\test>ssc test.ssc
test.ssc(12,19): warning CS2614: Receiver might be null

Which obviously is true. However, when you run the application, you'll get a correct result of course:

C:\temp\specsharp\test>test
4

Now, change the code as follows:

using System;

class Test
{
   public static void Main()
   {
      Do(null);
   }

   private static void Do(string s)
   {
      Console.WriteLine(s.Length);
   }
}

When you do compile, you'll still get the same warning message, but when you run an exception is thrown.

C:\temp\specsharp\test>ssc test.ssc
test.ssc(12,25): warning CS2614: Receiver might be null

C:\temp\specsharp\test>test

Unhandled Exception: System.NullReferenceException: Object reference not set to
an instance of an object.
   at System.String.get_Length()
   at Test.Main()

Nothing special at first sight, C# has the same behavior at runtime except for the compile-time warning about a "possibly null value".

Now, change the code as follows:

using System;

class Test
{
   public static void Main()
   {
      Do("test");
   }

   private static void Do(string! s)
   {
      Console.WriteLine(s.Length);
   }
}

And compile. All warnings are gone now and running the application goes well, as we expect. However, when you change the code like this:

using System;

class Test
{
   public static void Main()
   {
      Do(null);
   }

   private static void Do(string! s)
   {
      Console.WriteLine(s.Length);
   }
}

The compiler tells us were making a mistake against the contract of the target method Do: a non-null value is expected.

C:\temp\specsharp\test>ssc test.ssc
test.ssc(7,10): warning CS2612: Null cannot be used where a non-null value is expected

Now, how does Spec# enforce this not-null requirement at runtime? The answer can be found using ildasm.exe. The Do method itself does not contain something special, exception for the method header:

.method private hidebysig static void  Do(string modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType) s) cil managed
{
  // Code size       12 (0xc)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance int32 [mscorlib]System.String::get_Length()
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_000b:  ret
} // end of method Test::Do

This indicates the Spec# compiler to insert not-null enforcement code before calling the Do method. When you take a look at Main, you can see the result of this:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  8
  IL_0000:  ldnull
  IL_0001:  call       object [System.Compiler.Runtime]Microsoft.Contracts.NonNullType::IsNonNull(object)
  IL_0006:  castclass  string modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType)
  IL_000b:  call       void Test::Do(string modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType))
  IL_0010:  ret
} // end of method Test::Main

As the matter in fact, what I've shown you is pretty straightforward. There are however more complicated cases where the use of a non-null type system is not as simple as this. Consider for example this:

class B : A
{
   C! c;

   public B(...) : base(...)
   {
      c = ...;
   }
}

In here, c needs to be not null. But when the constructor is being executed, c is temporarily null because of the first call to the base constructor which doesn't know about the existence of c. This clearly is a violation against the type safety of Spec#'s non-null type system. For this very reason, Spec# allows this:

class B : A
{
   C! c;

   public B(...) : c(...), base(...)
   {
   }
}

Which is derived from the C++ syntax. However, the base constructor call is happening after field initialization statements now. For every not-null field, such an initializer is needed in order to be able to guarantee type safety. Spec# allows the use of non-null types for local variables, class attributes, parameters and return types but not for elements inside an array (the array itself can be declared as non-null though).

<PermissionToRead Groups="Geeks">

You might wonder why we need to be able to do stuff before calling the base class constructor when using non-null types in a class hierarchy. Well, the answer is the following. Assume you have some base class A that looks like this:

abstract class A
{
   public A()
   {
      Init();
   }

   public abstract void Init();
}

Okay for now. Now let's create a subclass of A, call it B:

class B : A
{
   C! c; //non-null value of type C

   public B(C! c) : base()
   {
      this.c = c;
   }

   public override void Init()
   {
      c.DoSomething();
   }
}

You thought this is type safe? Well, it isn't. Track down the execution path for "new B(some_c)". First we do initialize A because of the base() call (which needs to be the first call in a subtype's constructor in C#). Inside the constructor of A, we do call the abstract method Init, so we end up in B::Init. Over there, c is used to call DoSomething, but c isn't initialized. Crash boom bang :-). This is why we need to be able to defer the base constructor call to a later point and why non-null values need to be initialized upfront, before calling base(). Note that this is only valid in Spec#. C# simply doesn't allow deferred base() constructor calls. A sample looks like this:

   public B(C! c)
   {
      this.c = c;
      base();
   }

</PermissionToRead>

 

Preconditions

As mentioned in the introduction, Spec# is all about design by contract. One crucial aspect of this are so-called "method contracts". In such as contract, the developer specifies a contract between the caller and the implementation of a method, a constructor, a property or an indexer (which are in the end all translated to methods in IL). Spec# focuses on different fields to accomplish this, including preconditions (in which state can the method be called?), postconditions (in which state is the method allowed to return?), extended exception handling mechanisms (see further), frame conditions (see further).

Let's kick off with preconditions. In the previous sample, I already did show a precondition through the ! syntax and using the "requires" keyword. However, these predconditions can take far more complex forms:

public void AssignToPos(int pos, string! val)
requires 0 <= pos && pos < max;
requires string != "";
{ ... }

and you can have more than one requires statement as you can see. However, the Base Class Library does not contain this kind of contracts today. Examples of where this might be useful include the Array class, where bound checking is important (e.g. Array.Copy). In order to support this kind of scenarios, Spec# ships with an assembly Mscorlib.Contracts that contains this kind of checks on the BCL classes. Therefore, wrong calls to e.g. Array.Copy will result in compile-time warnings.

Spec# realizes this very powerful program verification system to validate preconditions using a built-in theorem prover, which is fed by information from the code.

Let's show some examples, starting with a pretty simple one first. Assume we want to count the number of occurrences of a certain value in an array, let's say an array of ints for simplicity's sake. Consider the following piece of code. Just past it into a Notepad and try to find the first compiler warning you should get without running ssc.exe:

using System;

class Test2
{
   public static void Main()
   {
      Console.WriteLine(Do (new int[] {1,2,3,4,2,3,4,3,4,4}, 2));
   }

   private static int Do(int[] array, int val)
   {
      int n = 0;
      for (int i = 0; i < array.Length; i++)
         if (array[i] == val) n++;
      return n;
   }
}

Got it? Indeed, array.Length will cause a warning to be generated about the possibility for array to be null. So, you should change the code as follows:

using System;

class Test2
{
   public static void Main()
   {
      Console.WriteLine(Do (new int[] {1,2,3,4,2,3,4,3,4,4}, 2));
   }

   private static int Do(int[]! array, int val)
   {
      int n = 0;
      for (int i = 0; i < array.Length; i++)
         if (array[i] == val) n++;
      return n;
   }
}

Nothing new yet. Now, let's say we only want to search some region of the array by specifying bounds in the Do method's parameter list.

using System;

class Test2
{
   public static void Main()
   {
      Console.WriteLine(Do (new int[] {1,2,3,4,2,3,4,3,4,4}, 2, 1, 5));
   }

   private static int Do(int[]! array, int val, int from, int to)
   {
      int n = 0;
      for (int i = from; i < to; i++)
         if (array[i] == val) n++;
      return n;
   }
}

This will certainly fail with an IndexOutOfRangeException if the specified values for the third and fourth parameter are invalid. So, Spec# allows us to specify some conditions:

using System;

class Test2
{
   public static void Main()
   {
      Console.WriteLine(Do (new int[] {1,2,3,4,2,3,4,3,4,4}, 2, 1, 5));
   }

   private static int Do(int[]! array, int val, int from, int to)
   requires to >= 0;
   requires from >= 0 && from < array.Length;

   {
      int n = 0;
      for (int i = from; i < to; i++)
         if (array[i] == val) n++;
      return n;
   }
}

Now, when you run this code but with a call to Do with the following arguments:

      Console.WriteLine(Do (new int[] {1,2,3,4,2,3,4,3,4,4}, 2, -1, 20));

You should get something like this:

C:\temp\specsharp\test>test2

Unhandled Exception: Microsoft.Contracts.RequiresException: Precondition violated from method 'Test2.Do(optional(Microsoft.Contracts.NonNullType) System.Int32[],System.Int32,System.Int32,System.Int32)'
   at Test2.Do(Int32[] array, Int32 val, Int32 from, Int32 to)
   at Test2.Main()

Hey, no IndexOutOfRangeException? Instead, we get an exception of the type Microsoft.Contracts.RequiresException. What's going on in the IL code? Let's take a look at Do's IL (personal comments in green):

.method private hidebysig static int32  Do(int32[] modopt([System.Compiler.Runtime]Microsoft.Contracts.NonNullType) 'array',
                                           int32 val,
                                           int32 from,
                                           int32 'to') cil managed
{
  .custom instance void [System.Compiler.Runtime]Microsoft.Contracts.RequiresAttribute::.ctor(string) = ( 01 00 13 3A 3A 3E 3D 28 69 33 32 2C 69 33 32 29   // ...::>=(i32,i32)
                                                                                                          7B 24 33 2C 30 7D 06 00 53 0E 08 46 69 6C 65 6E   // {$3,0}..S..Filen
                                                                                                          61 6D 65 20 43 3A 5C 74 65 6D 70 5C 73 70 65 63   // ame C:\temp\spec
                                                                                                          73 68 61 72 70 5C 74 65 73 74 5C 74 65 73 74 32   // sharp\test\test2
                                                                                                          2E 73 73 63 53 08 09 53 74 61 72 74 4C 69 6E 65   // .sscS..StartLine
                                                                                                          0B 00 00 00 53 08 0B 53 74 61 72 74 43 6F 6C 75   // ....S..StartColu
                                                                                                          6D 6E 0D 00 00 00 53 08 07 45 6E 64 4C 69 6E 65   // mn....S..EndLine
                                                                                                          0B 00 00 00 53 08 09 45 6E 64 43 6F 6C 75 6D 6E   // ....S..EndColumn
                                                                                                          14 00 00 00 53 0E 0A 53 6F 75 72 63 65 54 65 78   // ....S..SourceTex
                                                                                                          74 11 72 65 71 75 69 72 65 73 20 74 6F 20 3E 3D   // t.requires to >=
                                                                                                          20 30 3B )                                        //  0;
  .custom instance void [System.Compiler.Runtime]Microsoft.Contracts.RequiresAttribute::.ctor(string) = ( 01 00 5F 3A 3A 26 26 28 62 6F 6F 6C 2C 62 6F 6F   // .._::&&(bool,boo
                                                                                                          6C 29 7B 3A 3A 3E 3D 28 69 33 32 2C 69 33 32 29   // l){::>=(i32,i32)
                                                                                                          7B 24 32 2C 30 7D 2C 3A 3A 3C 28 69 33 32 2C 69   // {$2,0},::<(i32,i
                                                                                                          33 32 29 7B 24 32 2C 24 30 40 5B 6D 73 63 6F 72   // 32){$2,$0@[mscor
                                                                                                          6C 69 62 5D 53 79 73 74 65 6D 2E 41 72 72 61 79   // lib]System.Array
                                                                                                          3A 3A 67 65 74 5F 4C 65 6E 67 74 68 28 29 7B 7D   // ::get_Length(){}
                                                                                                          7D 7D 06 00 53 0E 08 46 69 6C 65 6E 61 6D 65 20   // }}..S..Filename
                                                                                                          43 3A 5C 74 65 6D 70 5C 73 70 65 63 73 68 61 72   // C:\temp\specshar
                                                                                                          70 5C 74 65 73 74 5C 74 65 73 74 32 2E 73 73 63   // p\test\test2.ssc
                                                                                                          53 08 09 53 74 61 72 74 4C 69 6E 65 0C 00 00 00   // S..StartLine....
                                                                                                          53 08 0B 53 74 61 72 74 43 6F 6C 75 6D 6E 0D 00   // S..StartColumn..
                                                                                                          00 00 53 08 07 45 6E 64 4C 69 6E 65 0C 00 00 00   // ..S..EndLine....
                                                                                                          53 08 09 45 6E 64 43 6F 6C 75 6D 6E 2D 00 00 00   // S..EndColumn-...
                                                                                                          53 0E 0A 53 6F 75 72 63 65 54 65 78 74 2A 72 65   // S..SourceText*re
                                                                                                          71 75 69 72 65 73 20 66 72 6F 6D 20 3E 3D 20 30   // quires from >= 0
                                                                                                          20 26 26 20 66 72 6F 6D 20 3C 20 61 72 72 61 79   //  && from < array
                                                                                                          2E 4C 65 6E 67 74 68 3B )                         // .Length;
  // Code size       218 (0xda)
  .maxstack  6
  .locals init (class [mscorlib]System.Exception V_0,
           object V_1,
           class [mscorlib]System.Exception V_2,
           object V_3,
           class [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException V_4,
           int32 V_5,
           int32 V_6,
           int32 V_7,
           int32 V_8,
           int32 V_9,
           int32 V_10)
  .try
  {
    .try
    {
      IL_0000:  ldarg.3             //parameter to
      IL_0001:  ldc.i4.0            //0

      IL_0002:  blt        IL_000c  //requires to >= 0
      IL_0007:  leave      IL_003f
      IL_000c:  leave      IL_0034  //check not valid, leave .try block and jump to IL_0034 to throw exception
    }  // end .try
    catch [mscorlib]System.Exception
    {
      IL_0011:  stloc.0
      IL_0012:  ldstr      "Exception occurred during evaluation of preconditi"
      + "on in method 'Test2.Do(optional(Microsoft.Contracts.NonNullType) System"
      + ".Int32[],System.Int32,System.Int32,System.Int32)'"
      IL_0017:  ldloc.0
      IL_0018:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string,
                                                                                                                      class [mscorlib]System.Exception)
      IL_001d:  throw
      IL_001e:  leave      IL_0034
    }  // end handler
    catch [mscorlib]System.Object
    {
      IL_0023:  stloc.1
      IL_0024:  ldstr      "Exception occurred during evaluation of preconditi"
      + "on in method 'Test2.Do(optional(Microsoft.Contracts.NonNullType) System"
      + ".Int32[],System.Int32,System.Int32,System.Int32)'"
      IL_0029:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string)
      IL_002e:  throw
      IL_002f:  leave      IL_0034
    }  // end handler
    IL_0034:  ldstr      "Precondition violated from method 'Test2.Do(option"
    + "al(Microsoft.Contracts.NonNullType) System.Int32[],System.Int32,System."
    + "Int32,System.Int32)'"
    IL_0039:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.RequiresException::.ctor(string)
    IL_003e:  throw
    IL_003f:  nop
    .try
    {
      IL_0040:  ldarg.2                                                         //parameter from
      IL_0041:  ldc.i4.0                                                        //0
      IL_0042:  blt        IL_0058                                              //requires from >= 0 --> on failure goto IL_0058 to throw exception
      IL_0047:  ldarg.2                                                         //parameter from
      IL_0048:  ldarg.0                                                         //parameter array
      IL_0049:  call       instance int32 [mscorlib]System.Array::get_Length()  //find the length of the array (first variable on stack is passed as argument to get_Length)
      IL_004e:  bge        IL_0058                                              //requires from < array.Length
      IL_0053:  leave      IL_008b
      IL_0058:  leave      IL_0080                                              //check not valid, leave .try block and jump to IL_0034 to throw exception
    }  // end .try
    catch [mscorlib]System.Exception
    {
      IL_005d:  stloc.2
      IL_005e:  ldstr      "Exception occurred during evaluation of preconditi"
      + "on in method 'Test2.Do(optional(Microsoft.Contracts.NonNullType) System"
      + ".Int32[],System.Int32,System.Int32,System.Int32)'"
      IL_0063:  ldloc.2
      IL_0064:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string,
                                                                                                                      class [mscorlib]System.Exception)
      IL_0069:  throw
      IL_006a:  leave      IL_0080
    }  // end handler
    catch [mscorlib]System.Object
    {
      IL_006f:  stloc.3
      IL_0070:  ldstr      "Exception occurred during evaluation of preconditi"
      + "on in method 'Test2.Do(optional(Microsoft.Contracts.NonNullType) System"
      + ".Int32[],System.Int32,System.Int32,System.Int32)'"
      IL_0075:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string)
      IL_007a:  throw
      IL_007b:  leave      IL_0080
    }  // end handler
    IL_0080:  ldstr      "Precondition violated from method 'Test2.Do(option"
    + "al(Microsoft.Contracts.NonNullType) System.Int32[],System.Int32,System."
    + "Int32,System.Int32)'"
    IL_0085:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.RequiresException::.ctor(string)
    IL_008a:  throw
    IL_008b:  nop
    IL_008c:  leave      IL_0095
  }  // end .try
  catch [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException
  {
    IL_0091:  stloc.s    V_4
    IL_0093:  rethrow
  }  // end handler
  IL_0095:  nop
  IL_0096:  ldc.i4.0
  IL_0097:  stloc.s    V_5
  IL_0099:  ldarg.2
  IL_009a:  stloc.s    V_6
  IL_009c:  ldloc.s    V_6
  IL_009e:  ldarg.3
  IL_009f:  bge        IL_00ca
  IL_00a4:  ldarg.0
  IL_00a5:  ldloc.s    V_6
  IL_00a7:  ldelem.i4
  IL_00a8:  ldarg.1
  IL_00a9:  bne.un     IL_00bb
  IL_00ae:  ldloc.s    V_5
  IL_00b0:  stloc.s    V_7
  IL_00b2:  ldloc.s    V_7
  IL_00b4:  ldc.i4.1
  IL_00b5:  add
  IL_00b6:  stloc.s    V_5
  IL_00b8:  ldloc.s    V_7
  IL_00ba:  pop
  IL_00bb:  ldloc.s    V_6
  IL_00bd:  stloc.s    V_8
  IL_00bf:  ldloc.s    V_8
  IL_00c1:  ldc.i4.1
  IL_00c2:  add
  IL_00c3:  stloc.s    V_6
  IL_00c5:  ldloc.s    V_8
  IL_00c7:  pop
  IL_00c8:  br.s       IL_009c
  IL_00ca:  ldloc.s    V_5
  IL_00cc:  stloc.s    V_9
  IL_00ce:  br         IL_00d3
  IL_00d3:  ldloc.s    V_9
  IL_00d5:  stloc.s    V_10
  IL_00d7:  ldloc.s    V_9
  IL_00d9:  ret
} // end of method Test2::Do

The orange part of the IL code shown above, contains the preconditions itself as metadata for further reference. Now, what's the big idea of this? We still do get an exception, so what's the benefit? The answer is that Spec# supports program verification at the side of the caller to see whether the contract is fulfilled or not. If not, developers are warned at development time that they're not working conform the contract. The technology to make these checks is called Boogie and comes with Spec# in the boogie.exe executable. More information about how Boogie works can be found on http://research.microsoft.com/~leino/papers/Leino-CASSIS2004.ppt. In order to run Boogie you'll need to install a tool called Simplify, as explained on http://www.research.microsoft.com/specsharp/simplify.htm. Link to the Module-3 source code download: http://www.hpl.hp.com/downloads/crl/jtk/download-simplify.html. Link to the release containing the .exe file: http://www.hpl.hp.com/downloads/crl/jtk/download-escjava.html. The use of Simplify is likely going to change in the future of the project, as mentioned in "The Spec# Programming System: An Overview" (PDF on the Spec# research home page):

Currently, Boogie uses the Simplify theorem prover [18], but we intend to switch to a new experimental theorem prover being developed at Microsoft Research.

To install Simplify, go to the Escjava\release\master\bin folder where you unzipped (e.g. using WinRAR) the download of EscJava from HP Labs and copy the Simplify-1.5.1.exe.win file to the bin folder of Spec# (typically located in %programfiles%\Microsoft\Spec#\bin) where you should rename it to Simplify.exe.

Now you can go to the Spec# command prompt and run Boogie against test2.exe:

C:\temp\specsharp\test>boogie test2.exe
Spec# Program Verifier Version 0.6, Copyright (c) 2003-2005, Microsoft.
Call of Test2.Do(int[]! array, int val, int from, int to): unsatisfied requires from >= 0 && from < array.Length;
Array index possibly below lower bound

Spec# Program Verifier finished with 2 errors

Now, go to Visual Studio and create a Spec# project, importing the same code as test2.ssc. You should see the result of verification in there too. Try to mess around with the System.Array class of the BLC too in order to see the contracts included with the Spec# installation (use the arraylist sample project of Spec#, e.g. line 66 or 221).

To make preconditions even more powerful, you can also specify an alternative exception to signal precondition violations at runtime, by means of the otherwise keyword:

<returntype> <methodname>(<paramlist>)
requires <precondition> otherwise <exceptiontype>;
...
{ ... }

Important note for people who know languages such as Ada. Preconditions are not the same as guards on procedures in Ada applications. Ada's guards are focusing on support for concurrency and can queue up the calls to a certain "entry" (till the guard "opens the door"), whileas Spec# is just about checking conditions to ensure execution correctness. If the criteria are not met, the runtime will throw an exception.

 

Postconditions

Where preconditions can be used to make sure an object is in a certain state ready to accept an incoming message for a certain method, postconditions can be seen as guard conditions that are checked when a method returns. Spec#'s postconditions are very powerful, as I'll show you using some samples right now. Let's start with some basic sample:

using System;

class Test3
{
   int i;

   public Test3(int i)
   requires i > 0;
   {
      this.i = i;
   }

   public void Increment()
   ensures I > 0;
   {
      i++;
   }

   public int I
   {
      get
      {
         return i;
      }
   }

   public static void Main()
   {
      Test3 t = new Test3(1);
      t.Increment();

      Console.WriteLine(t.I);
   }
}

In here we define a class called Test3 that accepts an integer valued parameter, that is required to be strictly positive. In the postcondition for the method Increment, we specify that this condition should still hold (I'll come back to this later on, when talking about invariants). When you compile and run this, it should print - as expected - the value 2 to the screen. In the code, notice the use of capital I in the postcondition whileas lower i is used in the precondition. Now, let's take a look at how this is implemented in IL, so jump to ildasm and open up test3.exe's Increment method definition:

.method public hidebysig instance void  Increment() cil managed
{
  .custom instance void [System.Compiler.Runtime]Microsoft.Contracts.EnsuresAttribute::.ctor(string) = ( 01 00 23 3A 3A 3E 28 69 33 32 2C 69 33 32 29 7B   // ..#::>(i32,i32){
                                                                                                         74 68 69 73 40 54 65 73 74 33 3A 3A 67 65 74 5F   //
this@Test3::get_
                                                                                                         49 7B 7D 2C 30 7D 06 00 53 0E 08 46 69 6C 65 6E   // I{},0}..S..Filen
                                                                                                         61 6D 65 20 43 3A 5C 74 65 6D 70 5C 73 70 65 63   // ame C:\temp\spec
                                                                                                         73 68 61 72 70 5C 74 65 73 74 5C 74 65 73 74 33   // sharp\test\test3
                                                                                                         2E 73 73 63 53 08 09 53 74 61 72 74 4C 69 6E 65   // .sscS..StartLine
                                                                                                         0E 00 00 00 53 08 0B 53 74 61 72 74 43 6F 6C 75   // ....S..StartColu
                                                                                                         6D 6E 0C 00 00 00 53 08 07 45 6E 64 4C 69 6E 65   // mn....S..EndLine
                                                                                                         0E 00 00 00 53 08 09 45 6E 64 43 6F 6C 75 6D 6E   // ....S..EndColumn
                                                                                                         11 00 00 00 53 0E 0A 53 6F 75 72 63 65 54 65 78   // ....S..SourceTex
                                                                                                         74 0E 65 6E 73 75 72 65 73 20 49 20 3E 20 30 3B ) // t.ensures I > 0;
  // Code size       97 (0x61)
  .maxstack  8
  .locals init (int32 V_0,
           class [mscorlib]System.Exception V_1,
           object V_2,
           class [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException V_3)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      int32 Test3::i
  IL_0006:  stloc.0
  IL_0007:  ldarg.0
  IL_0008:  ldloc.0
  IL_0009:  ldc.i4.1
  IL_000a:  add
  IL_000b:  stfld      int32 Test3::i
  IL_0010:  ldloc.0
  IL_0011:  pop
  .try
  {
    .try
    {
      IL_0012:  ldarg.0
      IL_0013:  call       instance int32 Test3::get_I() //get the (new) I value
      IL_0018:  ldc.i4.0
      IL_0019:  ble        IL_0023                       //check the postcondition ensures I > 0
      IL_001e:  leave      IL_0056
      IL_0023:  leave      IL_004b                       //throw the exception
    }  // end .try
    catch [mscorlib]System.Exception
    {
      IL_0028:  stloc.1
      IL_0029:  ldstr      "Exception occurred during evaluation of postcondit"
      + "ion in method 'Test3.Increment'"
      IL_002e:  ldloc.1
      IL_002f:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string,
                                                                                                                      class [mscorlib]System.Exception)
      IL_0034:  throw
      IL_0035:  leave      IL_004b
    }  // end handler
    catch [mscorlib]System.Object
    {
      IL_003a:  stloc.2
      IL_003b:  ldstr      "Exception occurred during evaluation of postcondit"
      + "ion in method 'Test3.Increment'"
      IL_0040:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string)
      IL_0045:  throw
      IL_0046:  leave      IL_004b
    }  // end handler
    IL_004b:  ldstr      "Postcondition violated from method 'Test3.Increment'"
    IL_0050:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.EnsuresException::.ctor(string)
    IL_0055:  throw
    IL_0056:  nop
    IL_0057:  leave      IL_005f
  }  // end .try
  catch [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException
  {
    IL_005c:  stloc.3
    IL_005d:  rethrow
  }  // end handler
  IL_005f:  nop
  IL_0060:  ret
} // end of method Test3::Increment

In order to see the exception being thrown, we'll have to introduce a little hack because we simply can't create an instance of Test3 with a negative value (because of the constructor's precondition). So, change the code like this:

using System;

class Test3
{
   int i;

   public Test3(int i)
   requires i > 0;
   {
      this.i = i;
   }

   public void Increment()
   ensures I > 0;
   {
      i++;
   }

   public int I
   {
      get
      {
         return i;
      }
   }

   public void Hack()
   {
      i = -1;
   }

   public static void Main()
   {
      Test3 t = new Test3(1);

      t.Increment();
      Console.WriteLine(t.I);

      t.Hack();

      t.Increment();
      Console.WriteLine(t.I);
   }
}

Compile and run and you should see this:

C:\temp\specsharp\test>test3
2

Unhandled Exception: Microsoft.Contracts.EnsuresException: Postcondition violated from method 'Test3.Increment'
   at Test3.Increment()
   at Test3.Main()

Just as we expected.

Now, in quite some cases we want to do more, relying on the old and new value of some attribute for example. In order to support this, Spec# has a keyword called old that allows you to save original values for postcondition evaluation purposes. Here we go for a simple sample:

using System;

class Test4
{
   int i;

   public Test4(int i)
   {
      this.i = i;
   }

   public void Increment()
   ensures I == old(I) + 1;
   {
      i++;
   }

   public int I
   {
      get
      {
         return i;
      }
   }

   public static void Main()
   {
      Test4 t = new Test4(1);
      t.Increment();

      Console.WriteLine(t.I);
   }
}

No preconditions anymore, just a little sample of a postcondition that checks the Increment method for correctness in a trivial case. Run and compile, it should execute just fine. What's interesting to see now, is how the old operator affects the IL code being generated by the Spec# compiler. Let's take a look again:

.method public hidebysig instance void  Increment() cil managed
{
  .custom instance void [System.Compiler.Runtime]Microsoft.Contracts.EnsuresAttribute::.ctor(string) = ( 01 00 4C 3A 3A 3D 3D 28 69 33 32 2C 69 33 32 29   // ..L::==(i32,i32)
                                                                                                         7B 74 68 69 73 40 54 65 73 74 34 3A 3A 67 65 74   //
{this@Test4::get
                                                                                                         5F 49 7B 7D 2C 3A 3A 2B 28 69 33 32 2C 69 33 32   // _I{},::+(i32,i32
                                                                                                         29 7B 5C 6F 6C 64 28 74 68 69 73 40 54 65 73 74   // ){\old(
this@Test
                                                                                                         34 3A 3A 67 65 74 5F 49 7B 7D 29 2C 31 7D 7D 06   // 4::get_I{}),1}}.
                                                                                                         00 53 0E 08 46 69 6C 65 6E 61 6D 65 20 43 3A 5C   // .S..Filename C:\
                                                                                                         74 65 6D 70 5C 73 70 65 63 73 68 61 72 70 5C 74   // temp\specsharp\t
                                                                                                         65 73 74 5C 74 65 73 74 34 2E 73 73 63 53 08 09   // est\test4.sscS..
                                                                                                         53 74 61 72 74 4C 69 6E 65 0D 00 00 00 53 08 0B   // StartLine....S..
                                                                                                         53 74 61 72 74 43 6F 6C 75 6D 6E 0C 00 00 00 53   // StartColumn....S
                                                                                                         08 07 45 6E 64 4C 69 6E 65 0D 00 00 00 53 08 09   // ..EndLine....S..
                                                                                                         45 6E 64 43 6F 6C 75 6D 6E 1B 00 00 00 53 0E 0A   // EndColumn....S..
                                                                                                         53 6F 75 72 63 65 54 65 78 74 18 65 6E 73 75 72   // SourceText.ensur
                                                                                                         65 73 20 49 20 3D 3D 20 6F 6C 64 28 49 29 20 2B   // es I == old(I) +
                                                                                                         20 31 3B )                                        //  1;
  // Code size       117 (0x75)
  .maxstack  7
  .locals init (int32 V_0,                                                                  //space to keep original value
           class [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException V_1,
           int32 V_2,
           class [mscorlib]System.Exception V_3,
           object V_4,
           class [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException V_5)
  .try
  {
    IL_0000:  ldarg.0
    IL_0001:  call       instance int32 Test4::get_I()  //retrieve the original value
    IL_0006:  stloc.0                                   //save it
    IL_0007:  leave      IL_000f
  }  // end .try
  catch [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException
  {
    IL_000c:  stloc.1
    IL_000d:  rethrow
  }  // end handler
  IL_000f:  nop
  IL_0010:  ldarg.0
  IL_0011:  ldfld      int32 Test4::i
  IL_0016:  stloc.2
  IL_0017:  ldarg.0
  IL_0018:  ldloc.2
  IL_0019:  ldc.i4.1
  IL_001a:  add
  IL_001b:  stfld      int32 Test4::i
  IL_0020:  ldloc.2
  IL_0021:  pop
  .try
  {
    .try
    {
      IL_0022:  ldarg.0
      IL_0023:  call       instance int32 Test4::get_I() //get current (new) value
      IL_0028:  ldloc.0

      IL_0029:  ldc.i4.1
      IL_002a:  add                                      //old value + 1

      IL_002b:  bne.un     IL_0035                       //compare
      IL_0030:  leave      IL_0069
      IL_0035:  leave      IL_005e                       //jump to throw exception
    }  // end .try
    catch [mscorlib]System.Exception
    {
      IL_003a:  stloc.3
      IL_003b:  ldstr      "Exception occurred during evaluation of postcondit"
      + "ion in method 'Test4.Increment'"
      IL_0040:  ldloc.3
      IL_0041:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string,
                                                                                                                      class [mscorlib]System.Exception)
      IL_0046:  throw
      IL_0047:  leave      IL_005e
    }  // end handler
    catch [mscorlib]System.Object
    {
      IL_004c:  stloc.s    V_4
      IL_004e:  ldstr      "Exception occurred during evaluation of postcondit"
      + "ion in method 'Test4.Increment'"
      IL_0053:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.InvalidContractException::.ctor(string)
      IL_0058:  throw
      IL_0059:  leave      IL_005e
    }  // end handler
    IL_005e:  ldstr      "Postcondition violated from method 'Test4.Increment'"
    IL_0063:  newobj     instance void [System.Compiler.Runtime]Microsoft.Contracts.EnsuresException::.ctor(string)
    IL_0068:  throw
    IL_0069:  nop
    IL_006a:  leave      IL_0073
  }  // end .try
  catch [System.Compiler.Runtime]Microsoft.Contracts.ContractMarkerException
  {
    IL_006f:  stloc.s    V_5
    IL_0071:  rethrow
  }  // end handler
  IL_0073:  nop
  IL_0074:  ret
} // end of method Test4::Increment

As you can see, the old value is evaluated and kept aside for later comparison. Again, Boogie can be used to check a lot of this stuff already at compile time. There is far more that you can do however, e.g. using the forall function. Let's show you using an array sort class:

using System;

class Test5
{
   int[]! array;
   int n;

   public Test5(int[]! a)
   {
      this.array = a;
      this.n = a.Length;
      base(); //needed for proper initialization of array attribute
   }

   public int this[int i]
   {
      get
      {
         return array[i];
      }
   }

   public void Sort()
   ensures forall { int i in (1:N); this[i-1] <= this[i] };
   {
      Array.Sort(array);
   }

   public int N
   {
      get
      {
         return n;
      }
   }

   public static void Main()
   {
      new Test5(new int[] {3,2,1} ).Sort();
   }
}

The ensures postcondition statement for the Sort method in here is pretty straightforward. I just loops over the array on positions 1 ... N-1 (the last parameter to the ':' range operator in forall denotes the upper bound, not included in the range) and checks the element in front of it to be lower than or equal than the current element. In this sample, I just used the Array.Sort method of the BCL to show the sorting. If you comment out the Array.Sort call and compile again, the application should not run anymore and throw an EnsuresException. In IL, the forall-check looks like this:

.method public hidebysig instance void  Sort() cil managed
{
  .custom instance void [System.Compiler.Runtime]Microsoft.Contracts.EnsuresAttribute::.ctor(string) = ( 01 00 80 D0 66 6F 72 61 6C 6C 7B 7C 7B 69 33 32   // ....forall{|{i32
                                                                                                         2C 24 62 6C 6F 63 6B 56 61 72 28 69 33 32 29 7B   // ,$blockVar(i32){
                                                                                                         69 7D 2C 28 3A 3A 3A 28 69 33 32 2C 69 33 32 29   // i},(:::(i32,i32)
                                                                                                         7B 31 2C 3A 3A 2D 28 69 33 32 2C 69 33 32 29 7B   // {1,::-(i32,i32){
                                                                                                         74 68 69 73 40 54 65 73 74 35 3A 3A 67 65 74 5F   //
this@Test5::get_
                                                                                                         4E 7B 7D 2C 31 7D 7D 29 7D 3B 3A 3A 3C 3D 28 69   // N{},1}})};::<=(i
                                                                                                         33 32 2C 69 33 32 29 7B 74 68 69 73 40 54 65 73   // 32,i32){this@Tes
                                                                                                         74 35 3A 3A 67 65 74 5F 49 74 65 6D 28 69 33 32   // t5::get_Item(i32
                                                                                                         29 7B 3A 3A 2D 28 69 33 32 2C 69 33 32 29 7B 24   // ){::-(i32,i32){$
                                                                                                         62 6C 6F 63 6B 56 61 72 28 69 33 32 29 7B 69 7D   // blockVar(i32){i}
                                   &nb