Code Contracts: Ensures Unproven & Requires Unproven
Asked Answered
T

2

6

I'm not sure if I'm doing something wrong here or if it needs to be fixed...

I have a custom Dictionary wrapper class and here is a snippet of the code that is necessary.

public int Count
{
    get
    {
        Contract.Ensures(Contract.Result<int>() >= 0);

        return InternalDictionary.Count;
    }
}

public bool ContainsKey(TKey key)
{
    //This contract was suggested by the warning message, if I remove it
    //I still get the same warning...
    Contract.Ensures(!Contract.Result<bool>() || Count > 0);

    return InternalDictionary.ContainsKey(key);
}

The only reason I added the line for the ContainsKey is because I got the following warning message (and still do): Codecontracts: ensures unproven: !Contract.Result<bool>() || @this.Count > 0. I can remove this line and still get the SAME ISSUE!

What do I do here to get rid of these issues?


Update:

I also tried (as suggested)...

public Boolean ContainsKey(TKey key)
{
    Contract.Requires(Count == 0 || InternalDictionary.ContainsKey(key));
    Contract.Ensures(!Contract.Result<bool>() || Count > 0);

    return InternalDictionary.ContainsKey(key);
}

Warning 5 Method 'My.Collections.Generic.ReadOnlyDictionary2.ContainsKey(type parameter.TKey)' implements interface method 'System.Collections.Generic.IDictionary2.ContainsKey(type parameter.TKey)', thus cannot add Requires.

Tertial answered 14/9, 2011 at 15:42 Comment(1)
Note that the root of your problem is that this method is promising that every key will be found, and it has really no control over that.Herron
P
5

"I have a custom Dictionary wrapper class" - that implements IDictionary<TKey, TValue>. Interface methods can specify contracts, and the class methods that implement them must meet the contracts. In this case, IDictionary<TKey, TValue>.ContainsKey(TKey) has the contract you're asking about:

Contract.Ensures(!Contract.Result<bool>() || this.Count > 0);

Logically, !a || b can be read as a ===> b (a implies b), and using that, we can translate this to English:

If ContainsKey() returns true, the dictionary must not be empty.

This is a perfectly sensible requirement. An empty dictionary must not claim to contain keys. This is what you need to prove.

Here's a sample DictionaryWrapper class that adds Contract.Ensures to promise that the implementation detail of Count being equal to innerDictionary.Count is a hard guarantee that other methods can rely upon. It adds a similar Contract.Ensures to ContainsKey so that the IDictionary<TKey, TValue>.TryGetValue contract is also verifiable.

public class DictionaryWrapper<TKey, TValue> : IDictionary<TKey, TValue>
{
    IDictionary<TKey, TValue> innerDictionary;

    public DictionaryWrapper(IDictionary<TKey, TValue> innerDictionary)
    {
        Contract.Requires<ArgumentNullException>(innerDictionary != null);
        this.innerDictionary = innerDictionary;
    }

    [ContractInvariantMethod]
    private void Invariant()
    {
        Contract.Invariant(innerDictionary != null);
    }

    public void Add(TKey key, TValue value)
    {
        innerDictionary.Add(key, value);
    }

    public bool ContainsKey(TKey key)
    {
        Contract.Ensures(Contract.Result<bool>() == innerDictionary.ContainsKey(key));
        return innerDictionary.ContainsKey(key);
    }

    public ICollection<TKey> Keys
    {
        get
        {
            return innerDictionary.Keys;
        }
    }

    public bool Remove(TKey key)
    {
        return innerDictionary.Remove(key);
    }

    public bool TryGetValue(TKey key, out TValue value)
    {
        return innerDictionary.TryGetValue(key, out value);
    }

    public ICollection<TValue> Values
    {
        get
        {
            return innerDictionary.Values;
        }
    }

    public TValue this[TKey key]
    {
        get
        {
            return innerDictionary[key];
        }
        set
        {
            innerDictionary[key] = value;
        }
    }

    public void Add(KeyValuePair<TKey, TValue> item)
    {
        innerDictionary.Add(item);
    }

    public void Clear()
    {
        innerDictionary.Clear();
    }

    public bool Contains(KeyValuePair<TKey, TValue> item)
    {
        return innerDictionary.Contains(item);
    }

    public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
    {
        innerDictionary.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get
        {
            Contract.Ensures(Contract.Result<int>() == innerDictionary.Count);
            return innerDictionary.Count;
        }
    }

    public bool IsReadOnly
    {
        get
        {
            return innerDictionary.IsReadOnly;
        }
    }

    public bool Remove(KeyValuePair<TKey, TValue> item)
    {
        return innerDictionary.Remove(item);
    }

    public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
    {
        return innerDictionary.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return innerDictionary.GetEnumerator();
    }
}
Partlow answered 28/2, 2012 at 21:28 Comment(1)
+1 for the great logic class. I stumbled upon those conditional contracts before and forgot that identity about implies from my discrete math classes xD.Allopathy
R
1

Frankly I don't understand the point of the contract. The contract is

 Contract.Ensures(!Contract.Result<bool>() || Count > 0);

What are you trying to say? You can neither guarantee that the dictionary contains the key, nor that the dictionary contains any values at all. So this contract can't always be met. That's what the verifier is telling you: it can't prove that this statement you are promising to be true is true.

The best you can ensure that the return value is true or the return value is false and that Count is greater than zero or equal to zero But what's the point of such a contract? The caller already knows this.

Given that, I wouldn't bother with a contract here at all.

Reservist answered 14/9, 2011 at 16:57 Comment(6)
In my question I stated that the only reason I added that contract was because the warning message told me to. I get the same warning with or without it, and quite frankly I don't even understand what it's trying to say.Tertial
@michael: start again. Wasn't this the first contract?Herron
Originally, I had no contract in the code. CodeContracts complained and I placed the contract in there because of what it stated. Afterwards, I get the same warning message. Basically, even if the method does not have ANY contract code it still gives the same warning.Tertial
Wait, if Code Contracts is telling you that, then surely somewhere you have a requirement that that be true. Where is it? Find it, and eliminate the analyzer from thinking that it needs to be true.Reservist
I'm getting it too and I think its because of the internal collection has this tagged and this collection is a core one.Varus
@Jason The contract is actually saying that the result of the function cannot be true if Count <= 0Varus

© 2022 - 2024 — McMap. All rights reserved.