0

I think I may have found an issue with either the C# 8.0 compiler or the .NET Core run-time regarding default interface member implementations and generic type parameter constraints.

The general gist of it is that I implemented a very simple design which you can use to reproduce the run-time VerificiationException I get when running a piece of code that compiles just fine and actually should be fine.

So let's get to the code. I created a blank solution with two projects: one C# library targeting .NETStandard 2.1 and one C# test project targeting .NET Core 3.1, where the test-project references the library.

Then in the library project I added the following code:

// In library project
namespace SomeLibrary
{
    public interface IMessageHandler<TMessage> { }

    public interface ISomeInterface<TMessage>
    {
        void DoSomething<TMessageHandler>() where TMessageHandler : class, IMessageHandler<TMessage> =>
            DoSomething<TMessageHandler>("Something");

        void DoSomething<TMessageHandler>(string value) where TMessageHandler : class, IMessageHandler<TMessage>;
    }

    public sealed class SomeClass<TMessage> : ISomeInterface<TMessage>
    {
        public void DoSomething<TMessageHandler>(string value) where TMessageHandler : class, IMessageHandler<TMessage> { }
    }
}

Note how the DoSomething<TMessageHandler>-methods declare a generic type constraint on TMessageHandler that also references the interface's generic type parameter TMessage.

In the test-project, I added a stub-implementation of the IMessageHandler<TMessage> interface (SomeHandler) to have some type that satisfies the generic type parameter constraint. Then I implemented the following simple test that invokes the ISomeInterface<object>.DoSomething<SomeHandler>'s overload that has the default implementation (note: I use MS Test):

// In test-project.
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace SomeLibrary.Tests
{
    [TestClass]
    public sealed class SomeClassTest
    {
        [TestMethod]
        public void DoSomething_DoesSomething()
        {
            CreateSomeClass<object>().DoSomething<SomeHandler>();
        }

        private static ISomeInterface<TMessage> CreateSomeClass<TMessage>() =>
            new SomeClass<TMessage>();
    }

    public sealed class SomeHandler : IMessageHandler<object> { }
}

As you would expect, this all compiles just fine.

However, when you run this test, the CLR throws a VerificationException at the point of invoking the DoSomething<...>-method:

System.Security.VerificationException: Method ISomeInterface`1[System.Object].DoSomething: type argument 'TMessageHandler' violates the constraint of type parameter 'TMessageHandler'.

VerificationException

It's as if the run-time cannot see that class SomeHandler actually does satisfy this constraint - which is already checked by the compiler.

After experimenting a bit, I noticed the issue goes away if I change the type parameter constraint to something that doesn't depend on/use the interface's type parameter TMessage. For example, if I simply omit the requirement that TMessageHandler implements IMessageHandler<TMessage>, the code runs just fine:

public interface ISomeInterface<TMessage>
{
    void DoSomething<TMessageHandler>() where TMessageHandler : class =>
        DoSomething<TMessageHandler>("Something");

    void DoSomething<TMessageHandler>(string value) where TMessageHandler : class;
}

It's also possible to add other constraints, as long as they don't use TMessage.

Also note that if I keep the generic type parameter constraint intact but move the method's implementation to SomeClass<TMessage> - which is what you would do before C# 8.0 - then the code also runs fine, so it's this particular combination of constraint and default interface method implementation that makes the system crack.

Is this a bug in the compiler or CLR, or am I missing a vital step in my thought-process?

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Wim.van.Gool
  • 1,290
  • 1
  • 10
  • 19
  • 1
    I'm having a hard time following these reproduction steps. What is `IMyInterface`? I don't see `IMyInterface` defined anywhere. It seems like the steps mix up "My" and "Some" class / interface. What is actually in `Main` that triggers the runtime exception? – vcsjones Mar 31 '20 at 14:47
  • Indeed. It would make things simpler if you'd provide all the code in a single code block per project, so it's a matter of "copy, paste, compile". – Jon Skeet Mar 31 '20 at 14:50
  • @vcsjones Yeah sorry about that - I mixed up the naming as I went through a bunch of iterations in my code - I fixed it now. I also added all code into two separate blocks so you can just copy-paste and run. I use MS Test to run the code. – Wim.van.Gool Apr 01 '20 at 06:27
  • @JonSkeet thnx for the tip - see my comment above. – Wim.van.Gool Apr 01 '20 at 06:28
  • Does this actually depend on MS Test? While this is now not *too* hard to do, with there being various test frameworks around, many people aren't familiar with any one specific framework. Unless this is actually about the test, it would be simpler to have one class library and one console app. Will give that a try now. – Jon Skeet Apr 01 '20 at 07:13
  • Okay, I've reproduced it now - and it definitely looks like it's a known bug. – Jon Skeet Apr 01 '20 at 07:21

2 Answers2

1

I believe this is a duplicate of the bug initially reported against Roslyn which was then resolved to be a bug in the CLR.

It looks like it's not yet fixed, but has a milestone to be fixed in .NET 5.0.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • It looks like the same issue indeed. At least there's a simple work-around by avoiding default implementations in these types of scenarios. :) Thnx for the feedback. – Wim.van.Gool Apr 01 '20 at 12:09
1

This issue has been recently resolved in .net 5.0

Manish
  • 36
  • 1