Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArgumentException: "Cannot create an instance of TEnum because Type.ContainsGenericParameters is true" caused by Enum constraint on method out parameter #656

Closed
abagonhishead opened this issue Aug 21, 2023 · 4 comments · Fixed by #658
Assignees
Labels
Milestone

Comments

@abagonhishead
Copy link

abagonhishead commented Aug 21, 2023

Hi there,
I encountered this using Moq but, from what I could see in the stack trace, it appears to be an issue with Castle.DynamicProxy. Hopefully raising this here is the correct thing to do.
Let me know if I'm wrong and I can raise it with them instead!

I'm not familiar with DynamicProxy so hopefully it's easy enough to suss out.

This is on .NET 6, Moq version 4.20.69 which is using Castle.Core 5.1.1

Example reproduction using Moq on dotnetfiddle

using System;
using Moq;

public class Program
{
	public static void Main()
	{
		var mock1 = new Mock<ITestInterface1>();
		var mock2 = new Mock<ITestInterface2>();

		// Throws
		var instance1 = mock1.Object;

		// Does not throw
		var instance2 = mock2.Object;
	}

	public interface ITestInterface1
	{
		bool TestMethod<TEnum>(out TEnum? someValue)
			where TEnum : Enum;
	}

	public interface ITestInterface2
	{
		bool TestMethod<TEnum>(out TEnum? someValue);
	}
}

Exception:

Unhandled exception. System.ArgumentException: Cannot create an instance of TEnum because Type.ContainsGenericParameters is true.

Stack trace:

   at System.RuntimeType.CreateInstanceCheckThis()
   at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type)
   at Castle.DynamicProxy.Generators.Emitters.OpCodeUtil.GetUnderlyingTypeOfEnum(Type enumType)
   at Castle.DynamicProxy.Generators.Emitters.OpCodeUtil.EmitLoadIndirectOpCodeForType(ILGenerator gen, Type type)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.IndirectReference.LoadReference(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.ReferencesToObjectArrayExpression.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.NewInstanceExpression.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.AssignStatement.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.CodeBuilder.Generate(ILGenerator il)
   at Castle.DynamicProxy.Generators.Emitters.MethodEmitter.Generate()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.EnsureBuildersAreInAValidState()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
   at Castle.DynamicProxy.Generators.BaseInterfaceProxyGenerator.GenerateType(String typeName, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass13_0.<GetProxyType>b__0(CacheKey cacheKey)
   at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.GetProxyType()
   at Castle.DynamicProxy.DefaultProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, IInterceptor[] interceptors)
   at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 98
   at Moq.Mock`1.InitializeInstance() in /_/src/Moq/Mock`1.cs:line 502
   at Moq.Mock`1.OnGetObject() in /_/src/Moq/Mock`1.cs:line 516
   at Moq.Mock.get_Object() in /_/src/Moq/Mock.cs:line 180
   at Moq.Mock`1.get_Object() in /_/src/Moq/Mock`1.cs:line 453
   at Program.Main()
@abagonhishead
Copy link
Author

I should also have pointed out that this throws the same exception:

var proxy = new Castle.DynamicProxy.ProxyGenerator().CreateInterfaceProxyWithoutTarget(typeof(ITestInterface1));

So I'm guessing this is a DynamicProxy issue -- either that or both Moq and I are calling the wrong method.

@abagonhishead abagonhishead changed the title Enum constraint on interface method out parameter causes ArgumentException Enum constraint on method out parameter causes ArgumentException Aug 21, 2023
@abagonhishead
Copy link
Author

This also affects classes:
Example on dotnetfiddle

using System;
using Moq;

public class Program
{
	public static void Main()
	{
		var mock1 = new Mock<TestClass1>();
		var mock2 = new Mock<TestClass2>();

		// Throws
		var instance1 = mock1.Object;

		// Does not throw
		var instance2 = mock2.Object;
	}

	public class TestClass1
	{
		public virtual bool TestMethod<TEnum>(out TEnum? someValue)
			where TEnum : Enum
		{
			someValue = default(TEnum?);
			return false;
		}
	}

	public class TestClass2
	{
		public virtual bool TestMethod<TEnum>(out TEnum? someValue)
		{
			someValue = default(TEnum?);
			return false;
		}
	}
}

Stack trace:

Unhandled exception. System.ArgumentException: Cannot create an instance of TEnum because Type.ContainsGenericParameters is true.
   at System.RuntimeType.CreateInstanceCheckThis()
   at System.RuntimeType.ActivatorCache..ctor(RuntimeType rt)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
   at System.Activator.CreateInstance(Type type)
   at Castle.DynamicProxy.Generators.Emitters.OpCodeUtil.GetUnderlyingTypeOfEnum(Type enumType)
   at Castle.DynamicProxy.Generators.Emitters.OpCodeUtil.EmitLoadIndirectOpCodeForType(ILGenerator gen, Type type)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.IndirectReference.LoadReference(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.ReferencesToObjectArrayExpression.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.NewInstanceExpression.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.SimpleAST.AssignStatement.Emit(ILGenerator gen)
   at Castle.DynamicProxy.Generators.Emitters.CodeBuilder.Generate(ILGenerator il)
   at Castle.DynamicProxy.Generators.Emitters.MethodEmitter.Generate()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.EnsureBuildersAreInAValidState()
   at Castle.DynamicProxy.Generators.Emitters.AbstractTypeEmitter.BuildType()
   at Castle.DynamicProxy.Generators.BaseClassProxyGenerator.GenerateType(String name, INamingScope namingScope)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.<>c__DisplayClass13_0.<GetProxyType>b__0(CacheKey cacheKey)
   at Castle.Core.Internal.SynchronizedDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   at Castle.DynamicProxy.Generators.BaseProxyGenerator.GetProxyType()
   at Castle.DynamicProxy.DefaultProxyBuilder.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
   at Castle.DynamicProxy.ProxyGenerator.CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, Object[] constructorArguments, IInterceptor[] interceptors)
   at Moq.CastleProxyFactory.CreateProxy(Type mockType, IInterceptor interceptor, Type[] interfaces, Object[] arguments) in /_/src/Moq/Interception/CastleProxyFactory.cs:line 110
   at Moq.Mock`1.InitializeInstance() in /_/src/Moq/Mock`1.cs:line 502
   at Moq.Mock`1.OnGetObject() in /_/src/Moq/Mock`1.cs:line 516
   at Moq.Mock.get_Object() in /_/src/Moq/Mock.cs:line 180
   at Moq.Mock`1.get_Object() in /_/src/Moq/Mock`1.cs:line 453
   at Program.Main()

@stakx
Copy link
Member

stakx commented Aug 21, 2023

@abagonhishead, thanks for reporting, I'm going to take a look at it.

@stakx stakx added the bug label Aug 21, 2023
@stakx stakx changed the title Enum constraint on method out parameter causes ArgumentException Enum constraint on method out parameter causes ArgumentException Aug 23, 2023
@stakx stakx added this to the vNext milestone Aug 23, 2023
@stakx
Copy link
Member

stakx commented Aug 23, 2023

This is apparently caused by type checks inside OpCodeUtil.EmitLoadIndirectOpCodeForType and OpCodeUtil.EmitStoreIndirectOpCodeForType running in the wrong order: there is a check for type.IsEnum which runs before type.IsGenericParameter. If we switch the ordering of those type checks, the reported problem disappears.

@stakx stakx changed the title Enum constraint on method out parameter causes ArgumentException ArgumentException : "Cannot create an instance of TEnum because Type.ContainsGenericParameters is true" caused by Enum constraint on method out parameter Aug 23, 2023
@stakx stakx changed the title ArgumentException : "Cannot create an instance of TEnum because Type.ContainsGenericParameters is true" caused by Enum constraint on method out parameter ArgumentException: "Cannot create an instance of TEnum because Type.ContainsGenericParameters is true" caused by Enum constraint on method out parameter Aug 23, 2023
@stakx stakx self-assigned this Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants