-
Notifications
You must be signed in to change notification settings - Fork 153
Async postconditions in generic methods #278
Async postconditions in generic methods #278
Conversation
Add type mapping to change generic type reference in async closure that is called from generic async methods.
Conflicts: Foxtrot/Tests/FoxtrotTests10.csproj
Add additional logic for type comparison to cover generic async methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This delegate seems not to be used anywhere else so the calculation can be performed without it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I thought to extract method but left with "local" function. Will extract function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if method is generic, but returned task uses declaring type generic parameter?
Something like this:
class DeclaringType<T> where T : class {
public async Task<T> MethodAsync<U>() {
Contract.Ensures(Contract.Result<T>() != null);
}
}or scenario with nested type:
class DeclaringType<T> where T : class {
private class NestedType<U> where U : class {
public async Task<T> MethodAsync<V>() {
Contract.Ensures(Contract.Result<T>() != null);
}
}
}Will this also work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure. I'll test this code.
|
Any other comments on this stuff? The change was relatively major, so I would like to get additional feedback on this... /cc @sharwell |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just out of curiosity. Why do you used CDATA here? is there any difference in just plain text in ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume, that the would be parsed as xml tag otherwise.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hubuk Yep, @AlexanderTaeschner is right. Otherwise we would have to encode all this < with < which is annoying...
|
@SergeyTeplyakov I have found a few cases for which BadImageFormatException is thrown. EDIT: I was able to narrow down all the failing cases to the following two: public class Outer<T> {
public class Inner {
public static Task<T> OuterClassTypeArgument(T obj) {
Contract.Ensures(Contract.Result<T>() != null);
return Task.FromResult(obj);
}
}
}
public static Task<T> DefaultValue<T>(T obj) {
// Does not throw when null is used instead of default(T)
Contract.Ensures(!object.Equals(Contract.Result<T>(), default(T)));
return Task.FromResult(obj);
}// Constraint on Outer<T> class does not matter.
// Reproducible also before fix.
// System.BadImageFormatException thrown for:
// Program.Outer`1.Inner.OuterClassTypeArgument(T obj) method
Outer<object>.Inner.OuterClassTypeArgument(new object()).Wait();
// Constraint on method does not matter.
// Reproducible also before fix.
// Also fails (but only after the fix) when made async.
// Aggregated System.BadImageFormatException thrown for
// Program.<DefaultValue>AsyncContractClosure_0`1.CheckPost(Task`1 task)
DefaultValue(new object()).Wait(); |
|
There is also some questionable behavior for closure class generation. public class OuterClassGeneric<T0> where T0 : class {
public class InnerGeneric<T1> {
public static async Task<T0> AsyncTest1(T0 v0, T1 v1) {
Contract.Ensures(Contract.Result<T0>() != null);
return await Task.FromResult(v0);
}
}
}rewriter generates the following closure class for the method above: .class nested private auto ansi '<AsyncTest1>AsyncContractClosure_0`1'<class T0, T1, T1>
extends [mscorlib]System.ObjectSecond T1 generic type parameter is probably not needed. |
|
Previously CheckPost method content was: TaskStatus status = task.Status;
if (status == TaskStatus.RanToCompletion && __ContractsRuntime.insideContractEvaluation <= 4)
{
try
{
__ContractsRuntime.insideContractEvaluation++;
__ContractsRuntime.Ensures(task.Result != null, null, "Contract.Result<T0>() != null");
}
finally
{
__ContractsRuntime.insideContractEvaluation--;
}
}
return task;now it looks like: TaskStatus status = task.Status;
if (status == TaskStatus.RanToCompletion)
{
__ContractsRuntime.Ensures(task.Result != null, null, "Contract.Result<T0>() != null");
}
return task;Is it intended change? |
|
@hubuk About last comment: the chnage is intented, because async postconditions are all about multi threading where thread static checks like |
|
@hubuk Thanks a lot for clear test cases, I'll fix them and will update PR before pushing it to master. |
|
@hubuk FIxed everything that you've mentioned. Including two test cases that you've provde. Once you'll sign off I'll move forward and will prepare new release of the CodeContracts. |
|
It seems that issue with |
|
@hubuk Great. Moving forward to 1.10 release! |
Async postconditions in generic methods
This PR contains fixes for #235 and #275. Main change in this PR was made in
EmitAsyncClosureclass.Special generic type mapping was added to correlate potentially different generic arguments from enclosing generic method to closure generic class.