12

There are numerous information that static checking of Contract.ForAll has only limited or no support.

I did lot of experimenting and found it can work with:

  • Contract.ForAll(items, i => i != null)
  • Contract.ForAll(items, p) where p is of type Predicate<T>

it cannot work with:

  • Field access
  • Property access
  • Method group (I think delegate is allocated here anyway)
  • Instance method call

My questions are:

  • What are other types of code that ForAll can work with?
  • Does the Code Contracts undertand that after Contract.ForAll(items, i => i != null) is proven, that when taking one item from the list later in code (i.e. by indexing), the item is not null?

Here is full test code:

public sealed class Test
{
    public bool Field;
    public static Predicate<Test> Predicate;

    [Pure]
    public bool Property
    {
        get { return Field; }
    }    

    [Pure]
    public static bool Method(Test t)
    {
        return t.Field;
    }

    [Pure]
    public bool InstanceMethod()
    {
        return Field;
    }

    public static void Test1()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i != null));
        Contract.Assert(Contract.ForAll(items, i => i != null)); // OK
    }

    public static void Test2()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, Predicate));
        Contract.Assert(Contract.ForAll(items, Predicate)); // OK
    }

    public static void Test3()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.Field));
        Contract.Assert(Contract.ForAll(items, i => i.Field)); // assert unproven
    }

    public static void Test4()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.Property));
        Contract.Assert(Contract.ForAll(items, i => i.Property)); // assert unproven
    }

    public static void Test5()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, Method));
        Contract.Assert(Contract.ForAll(items, Method)); // assert unproven
    }

    public static void Test6()
    {
        var items = new List<Test>();
        Contract.Assume(Contract.ForAll(items, i => i.InstanceMethod()));
        Contract.Assert(Contract.ForAll(items, i => i.InstanceMethod()));// assert unproven
    }
}
Michal Minich
  • 2,387
  • 1
  • 22
  • 30

2 Answers2

3

I was not able to find more working expressions, in fact I found that even Contract.ForAll(items, i => i != null) is not working reliably (but it understands that the item is not null when later used inside foreach in the same function). Finally, I gave up on possibility to use more complex ForAll contracts with static checker.

Instead I devised a way to control which contract are for static checker, and which are for runtime checker. I present this method here, in hope that it might be useful for people interesting in original question. The benefit is ability to be write more complex contracts, which can be checked at runtime only, and leave only easily provable contracts for static checker (and easily keep warnings at low count).

For that, 2 builds Debug are needed (If you don't already have them), Debug and Debug + Static Contracts, The Debug build has conditional compilation symbol MYPROJECT_CONTRACTS_RUNTIME defined. In this way it receives all Contract. and RtContract. contracts. Other builds receive only Contract. contracts.

public static class RtContract
{
    [Pure] [ContractAbbreviator] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Requires(bool condition)
    {
        Contract.Requires(condition);
    }

    [Pure] [ContractAbbreviator] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Ensures(bool condition)
    {
        Contract.Ensures(condition);
    }

    [Pure] [Conditional("MYPROJECT_CONTRACTS_RUNTIME")]
    public static void Assume(bool condition)
    {
        Contract.Assume(condition);
    }
}

public class Usage
{
   void Test (int x)
   {
        Contract.Requires(x >= 0);  // for static and runtime
        RtContract.Requires(x.IsFibonacci());  // for runtime only
   }
}
Michal Minich
  • 2,387
  • 1
  • 22
  • 30
0

By decompiling mscorelib.dll System.Diagnostics.Contracts we can easely see how Contract.ForAll is built: It takes collection and predicate.

public static bool ForAll<T>(IEnumerable<T> collection, Predicate<T> predicate)
{
    if (collection == null)
    {
        throw new ArgumentNullException("collection");
    }
    if (predicate == null)
    {
        throw new ArgumentNullException("predicate");
    }
    foreach (T current in collection)
    {
        if (!predicate(current))
        {
            return false;
        }
    }
    return true;
}

So when you say Contract.ForAll(items, i => i.Field) in this case i => i.Field is predicate

Then by following your example in all test methods, we can see that you provide an empty list to Contract.ForAll method which will return true as it will never enter the foreach block.

Taking it further, if you add item to your list var items = new List<Test>() {new Test()}; and run the test again it will return false as your public bool Field; is false by default

The goal of Contract.ForAll is to

Determines whether all the elements in a collection exist within a function

So my conclusion is that it is not about Contarct.ForAll can't work with something, it is rather at least one element in your collection returns false or is null

jekcom
  • 2,065
  • 2
  • 24
  • 34
  • 2
    Thank you for the effort, but my question is about capabilities of static analysis done by Code Contract engine. It is not at all about runtime characteristic as implemented in the code of the ForAll function. – Michal Minich Jun 03 '15 at 10:25