diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs index d649c6a6c1faba..be57f7d1b0b9f6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Compilation.cs @@ -317,6 +317,11 @@ public ISymbolNode ComputeConstantLookup(ReadyToRunHelperId lookupKind, object t public GenericDictionaryLookup ComputeGenericLookup(MethodDesc contextMethod, ReadyToRunHelperId lookupKind, object targetOfLookup) { + if (targetOfLookup is TypeSystemEntity typeSystemEntity) + { + _nodeFactory.TypeSystemContext.DetectGenericCycles(contextMethod, typeSystemEntity); + } + GenericContextSource contextSource; if (contextMethod.RequiresInstMethodDescArg()) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs index 0c944395502d0a..3baab6e689fe62 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/LazyGenerics/ModuleCycleInfo.cs @@ -148,6 +148,12 @@ internal class GenericCycleDetector public void DetectCycle(TypeSystemEntity owner, TypeSystemEntity referent) { + // Not clear if generic recursion through fields is a thing + if (referent is FieldDesc) + { + return; + } + var ownerType = owner as TypeDesc; var ownerMethod = owner as MethodDesc; var ownerDefinition = ownerType?.GetTypeDefinition() ?? (TypeSystemEntity)ownerMethod.GetTypicalMethodDefinition(); diff --git a/src/tests/nativeaot/SmokeTests/Generics/Generics.cs b/src/tests/nativeaot/SmokeTests/Generics/Generics.cs index 8132859b94fd8c..0badf2be9bbe3d 100644 --- a/src/tests/nativeaot/SmokeTests/Generics/Generics.cs +++ b/src/tests/nativeaot/SmokeTests/Generics/Generics.cs @@ -43,6 +43,7 @@ static int Main() TestSimpleGenericRecursion.Run(); TestGenericRecursionFromNpgsql.Run(); TestRecursionInGenericVirtualMethods.Run(); + TestRecursionThroughGenericLookups.Run(); #if !CODEGEN_CPP TestNullableCasting.Run(); TestVariantCasting.Run(); @@ -3005,7 +3006,7 @@ struct GenStruct { } class GenClass { } - private static object RecurseOverStruct(int count) where T: new() + private static object RecurseOverStruct(int count) where T : new() { if (count > 0) RecurseOverStruct>(count - 1); @@ -3113,4 +3114,33 @@ public static void Run() s_derived.Get(); } } + + class TestRecursionThroughGenericLookups + { + abstract class Handler + { + public abstract void Write(object val); + } + + class RangeHandler : Handler> + { + public override void Write(object val) { if (val is ArrayHandler> h) h.Write(default); } + } + + class ArrayHandler + { + public virtual void Write(object val) { if (val is RangeHandler h) h.Write(default); } + } + + struct Gen + { + } + + public static void Run() + { + // There is a generic recursion in the above hierarchy. This just tests that we can compile. + new ArrayHandler().Write(null); + new RangeHandler().Write(default); + } + } }