runtime: System.Security.VerificationException (type argument violates the constraint of type parameter) when running code on net 5.0
Description
Hi, we have encountered strange exception after one of ours project’s target framework has been upgraded from netcoreapp3.1 to net5.0.
This is the exception message:
Unhandled exception. System.Security.VerificationException: Method ConsoleApp1.ComponentRegistry`1[ConsoleApp1.NamedObjectComponent`1[TNamedObject]].Register: type argument 'TComponentA' violates the constraint of type parameter 'TComponent'.
at ConsoleApp1.SingleTypeNamedObjectContainer`1.Register[TComponentA]()
at ConsoleApp1.NamedObjectContainer.Register[TNamedObject,TComponent]() in /Users/gureev/RiderProjects/ConsoleApp1/ConsoleApp1/Program.cs:line 87
at ConsoleApp1.Program.Main(String[] args) in /Users/gureev/RiderProjects/ConsoleApp1/ConsoleApp1/Program.cs:line 108
I made simple console app to reproduce it:
class Program
{
public abstract class NamedObject { }
public class FooNamedObject : NamedObject { }
public abstract class NamedObjectComponent { }
public abstract class NamedObjectComponent<TNamedObject> : NamedObjectComponent where TNamedObject : NamedObject { }
public class FooNamedObjectComponent : NamedObjectComponent<FooNamedObject> { }
public abstract class SingleTypeNamedObjectContainer
{
internal SingleTypeNamedObjectContainer() { }
internal abstract Type NamedObjectType { get; }
}
public class SingleTypeNamedObjectContainer<TNamedObject> : SingleTypeNamedObjectContainer
where TNamedObject : NamedObject
{
private readonly ComponentRegistry<NamedObjectComponent<TNamedObject>> components =
new ComponentRegistry<NamedObjectComponent<TNamedObject>>();
internal override Type NamedObjectType => typeof(TNamedObject);
public void Register<TComponentA>()
where TComponentA : NamedObjectComponent<TNamedObject>, new()
{
components.Register<TComponentA>();
}
}
public class ComponentRegistry<TBaseComponent> where TBaseComponent : class
{
private readonly HashSet<Type> componentTypes = new HashSet<Type>();
private readonly Dictionary<Type, Func<TBaseComponent>> componentFactories =
new Dictionary<Type, Func<TBaseComponent>>();
public void Register<TComponent>()
where TComponent : class, TBaseComponent, new()
{
Register(() => new TComponent());
}
public void Register<TComponent>(Func<TComponent> componentFactory)
where TComponent : class, TBaseComponent
{
componentTypes.Add(typeof(TComponent));
componentFactories.Add(typeof(TComponent), componentFactory);
}
}
public class NamedObjectContainer
{
private readonly Dictionary<Type, SingleTypeNamedObjectContainer> subcontainersByNamedObjectType =
new Dictionary<Type, SingleTypeNamedObjectContainer>();
private readonly Dictionary<Type, SingleTypeNamedObjectContainer> subcontainersByRegisteredType =
new Dictionary<Type, SingleTypeNamedObjectContainer>();
public SingleTypeNamedObjectContainer<TNamedObject> RegisterNamedObjectType<TNamedObject>()
where TNamedObject : NamedObject
{
return RegisterSubcontainer(new SingleTypeNamedObjectContainer<TNamedObject>());
}
public TSubcontainer RegisterSubcontainer<TSubcontainer>(TSubcontainer subcontainer)
where TSubcontainer : SingleTypeNamedObjectContainer
{
subcontainersByNamedObjectType.Add(subcontainer.NamedObjectType, subcontainer);
subcontainersByRegisteredType.Add(typeof(TSubcontainer), subcontainer);
return subcontainer;
}
internal void Register<TNamedObject, TComponent>()
where TNamedObject : NamedObject
where TComponent : NamedObjectComponent<TNamedObject>, new()
{
GetSubcontainerFor<TNamedObject>().Register<TComponent>();
}
public SingleTypeNamedObjectContainer<TNamedObject> GetSubcontainerFor<TNamedObject>()
where TNamedObject : NamedObject
{
return (SingleTypeNamedObjectContainer<TNamedObject>)GetSubcontainerFor(typeof(TNamedObject));
}
public SingleTypeNamedObjectContainer GetSubcontainerFor(Type baseNamedObjectType)
{
return subcontainersByNamedObjectType[baseNamedObjectType];
}
}
static void Main(string[] args)
{
var contaner = new NamedObjectContainer();
contaner.RegisterNamedObjectType<FooNamedObject>();
contaner.Register<FooNamedObject, FooNamedObjectComponent>();
}
}
If you run this code with target netcoreapp3.1 - it works. But if you run with target netcoreapp5.0 - exception throws.
Configuration
Dotnet sdk 5.0.100 OS: MacOS Catalina (x64), Windows 10 1903 (x64)
Regression?
Yes, works on 3.1.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Reactions: 12
- Comments: 16 (12 by maintainers)
I have debugged the problem and discovered a weird behavior in MethodDesc::SatisfiesMethodConstraints where it somehow incorrectly ends up checking
NamedObjectComponent<NamedObjectComponent<TNamedObject>>
instead ofNamedObjectComponent<TNamedObject>
. It turns out it was caused by PR #34889 that added passing instantiation context to the SatisfiesConstraints method for the purpose of default interface methods. The instantiatoon context is wrong in this specific case. Reverting the change from that PR fixes the problem. cc: @MichalStrehovskyI have looked into it three weeks ago, but I wasn’t able to figure out what’s wrong right away and then got side tracked by other high priority work and Christmas holidays. It is still on my list to get to as soon as possible.
Let me run the repro locally and see where the exception stems from in the native runtime.
I’ve come up with what appears to be a fix although I think it needs a bit more testing, and the test case needs some additional work.
I’m taking a look today. Hopefully, I’ll have something for the thread by EOD.
cc: @mangod9 - I’ve thought the change was made by Michal, but I’ve just noticed it was your change.