diff --git a/src/coreclr/vm/genmeth.cpp b/src/coreclr/vm/genmeth.cpp index 62f413ada24e67..300e2873682764 100644 --- a/src/coreclr/vm/genmeth.cpp +++ b/src/coreclr/vm/genmeth.cpp @@ -474,10 +474,19 @@ InstantiatedMethodDesc::NewInstantiatedMethodDesc(MethodTable *pExactMT, // The canonical instantiation is exempt from constraint checks. It's used as the basis // for all other reference instantiations so we can't not load it. The Canon type is // not visible to users so it can't be abused. + // + // Instantiations containing generic variables (type parameters) are also exempt. + // Such instantiations arise during JIT access checks when the caller is a generic + // method and the EE resolves method specs using the caller's formal type parameters. + // Constraint checking for these instantiations would unnecessarily load types + // instantiated over the type variables (e.g. IList). The actual constraint + // check will happen when the method is instantiated with concrete types. BOOL fExempt = TypeHandle::IsCanonicalSubtypeInstantiation(methodInst) || - TypeHandle::IsCanonicalSubtypeInstantiation(pNewMD->GetClassInstantiation()); + TypeHandle::IsCanonicalSubtypeInstantiation(pNewMD->GetClassInstantiation()) || + MethodTable::ComputeContainsGenericVariables(methodInst) || + MethodTable::ComputeContainsGenericVariables(pNewMD->GetClassInstantiation()); if (!fExempt) { diff --git a/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.cs b/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.cs new file mode 100644 index 00000000000000..5084fdc64831d8 --- /dev/null +++ b/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.cs @@ -0,0 +1,50 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Regression test: access checks triggered by the JIT for generic methods with +// constraints should not unnecessarily load types instantiated over type variables. +// The JIT uses the typical method definition (with formal type params) when doing +// security checks for generic callers, which was triggering constraint checks that +// loaded types like IList where TMethod is a type variable. + +using System; +using System.Collections.Generic; +using Xunit; + +public class TypeLoadWithGenericVars +{ + static void Method() where TMethod1_1 : IList + { + Method2(); + } + + static void Method2() where TMethod2_1 : IList + { + } + + static void MethodA() + where T1 : IList + where T2 : IList + { + MethodB(); + } + + static void MethodB() + where U1 : IList + where U2 : IList + { + } + + [Fact] + public static void TestEntryPoint() + { + // Call with concrete types that satisfy the constraints. + // The JIT will use the typical method definition for access checks, + // which previously caused unnecessary loading of IList, etc. + Method, int>(); + Method(); + + // Test with chained generic constraints + MethodA>, List, int>(); + } +} diff --git a/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.csproj b/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.csproj new file mode 100644 index 00000000000000..30d4804022360c --- /dev/null +++ b/src/tests/Loader/classloader/generics/Constraints/Regressions/TypeLoadWithGenericVars/TypeLoadWithGenericVars.csproj @@ -0,0 +1,11 @@ + + + 1 + + + + + + + +