Skip to content
This repository was archived by the owner on Jul 15, 2023. It is now read-only.

Conversation

@SergeyTeplyakov
Copy link
Contributor

This PR contains fixes for #235 and #275. Main change in this PR was made in EmitAsyncClosure class.

Special generic type mapping was added to correlate potentially different generic arguments from enclosing generic method to closure generic class.

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.
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

@SergeyTeplyakov
Copy link
Contributor Author

Any other comments on this stuff? The change was relatively major, so I would like to get additional feedback on this...

/cc @sharwell

Copy link
Contributor

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 ?

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.

Copy link
Contributor Author

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 &lt; which is annoying...

@hubuk
Copy link
Contributor

hubuk commented Nov 24, 2015

@SergeyTeplyakov I have found a few cases for which BadImageFormatException is thrown.
Some of them are reproducible before fix and some were introduced by the fix. I have prepared 192 combinations of class generic arguments and method arguments. With class and struct constraints. I will present a list of failing scenarios ASAP.

EDIT:
Async versions of some test cases did not throw an exception before fix. But as you observed in another example the generated IL was invalid anyway.

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();

@hubuk
Copy link
Contributor

hubuk commented Nov 25, 2015

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.Object

Second T1 generic type parameter is probably not needed.

@hubuk
Copy link
Contributor

hubuk commented Nov 25, 2015

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?

@SergeyTeplyakov
Copy link
Contributor Author

@hubuk About last comment: the chnage is intented, because async postconditions are all about multi threading where thread static checks like __ContracatsRuntime.insideContractEvaluation just makes no sense.

@SergeyTeplyakov
Copy link
Contributor Author

@hubuk Thanks a lot for clear test cases, I'll fix them and will update PR before pushing it to master.

@SergeyTeplyakov
Copy link
Contributor Author

@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.

@hubuk
Copy link
Contributor

hubuk commented Jan 24, 2016

It seems that issue with public static Task<T> DefaultValue<T>(T obj) method is not reproducible even without the fix. Second issue is fixed by 2163f15.
LGTM 👍

@SergeyTeplyakov
Copy link
Contributor Author

@hubuk Great. Moving forward to 1.10 release!

SergeyTeplyakov added a commit that referenced this pull request Jan 25, 2016
Async postconditions in generic methods
@SergeyTeplyakov SergeyTeplyakov merged commit 24168d9 into microsoft:master Jan 25, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants