I'm trying to understand Code Contracts in a bit more detail. I've got the following contrived example, where I'm trying to assert the invariant of a try/get pattern that if it returns true
then the out
object is non-null, otherwise if it returns false
.
public static bool TryParseFruit(string maybeFruit, out Fruit fruit)
{
Contract.Requires(maybeFruit != null);
Contract.Ensures((Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) != null) ||
(!Contract.Result<bool>() && Contract.ValueAtReturn(out fruit) == null));
// Elided for brevity
if (ICanParseIt())
{
fruit = SomeNonNullValue;
return true;
}
else
{
fruit = null;
return false;
}
}
I don't like the duplication inside Contract.Ensures
so I wanted to factor out my own method for this.
[Pure]
public static bool Implies(bool a, bool b)
{
return (a && b) || (!a && !b);
}
Then I changed my invariant in TryParseFruit
to
Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
But this generates warnings that the "ensures is unproven". If I then perform the inline refactoring on my Implies
method then everything is OK again.
Could someone explain to me why this happens? I'm guessing it's because Contract.ValueAtReturn
is used magically in some way that means I can't just pass it's result off to another function and expect it to work.
(Update #1)
I think that all of the following Contract.Ensures
express the same thing (namely that if the function returns true
then fruit
is non-null, otherwise fruit
is null
). Note that I am only using one of these at a time :)
Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null));
Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out fruit) != null));
Contract.Ensures(Contract.Result<bool>() ^ (Contract.ValueAtReturn(out fruit) == null));
However, none of the above Contract.Ensures
lead to a clean compile of the code below. I want the Code.Contracts to express that fruit.Name
cannot be a null reference.
Fruit fruit;
while (!TryGetExample.TryParseFruit(ReadLine(), out fruit))
{
Console.WriteLine("Try again...");
}
Console.WriteLine(fruit.Name);
I am only able to get the completely clean compile with code contracts if I use the long winded way of expressing this detailed above. My question is why!
Of course, you can also try Contract.Ensures(Contract.Result<bool>() == (Contract.ValueAtReturn(out fruit) != null));
I have a vague recollection that the analyzer prefers == to other operators.
I've had some success tracing these things with Contract.Assert
which helps you find out where the hole in the analysis lies. I have also found cases where Contract.Assert
allows the analysis to succeed. In other words, you might fix this with a couple of assertions:
public static bool TryParseFruit(string maybeFruit, out Fruit fruit)
{
Contract.Requires(maybeFruit != null);
Contract.Ensures(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
// Elided for brevity
if (ICanParseIt())
{
fruit = SomeNonNullValue;
Contract.Assert(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
return true;
}
else
{
fruit = null;
Contract.Assert(Implies(Contract.Result<bool>(), Contract.ValueAtReturn(out fruit) != null);
return false;
}
}
Messy, I know. On the other hand, if any assertion fails, you can look, for example, at other aspects of the logic. For example, you can litter your code with Contract.Assert(SomeNonNullValue != null);
to see where the analyzer is losing its certainty about the non-nullness of SomeNonNullValue
.
EDIT
If the Assert
is unproven, but you know that it should be provable, then you can use that to help isolate the problem. I suspect that the problem (or at least part of it) is your lack of Ensures
in the Implies
method. Try adding Contract.Ensures(Contract.Result<bool>() == (Contract.OldValue(a) == Contract.OldValue(b)));
Also, as I again have vague recollections about different handling for different logical operators, try restating the return of that method. For example: return a == b;