Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1546,4 +1546,32 @@ static void Validate(ReadyToRunReader reader)
Assert.True(R2RAssert.HasCompiledMethod(reader, "ITest7`1<int>", "ITest7Base.Test7Method", out diag, ["int"]), diag);
}
}

[Fact]
public void VirtualMethodGenericsGenericLookup()
{
var genericLookupLib = new CompiledAssembly
{
AssemblyName = nameof(VirtualMethodGenericsGenericLookup),
SourceResourceNames = ["VirtualMethodGenerics/GenericLookup.cs"],
};

new R2RTestRunner(_output).Run(new R2RTestCase(
nameof(VirtualMethodGenericsGenericLookup),
[
new(nameof(VirtualMethodGenericsGenericLookup), [new CrossgenAssembly(genericLookupLib)])
{
Validate = Validate,
},
]));

static void Validate(ReadyToRunReader reader)
{
string diag;

// The generic type instantiation is reached only through a GenericLookupSignature
// fixup, so its virtual method must still be discovered and compiled.
Assert.True(R2RAssert.HasCompiledMethod(reader, "TestA`2<__Canon,int>", "TestMethod", out diag), diag);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Runtime.CompilerServices;

// Virtual method discovery for a generic type instantiation that is only ever
// reached through a GenericLookupSignature fixup.
//
// When compiling the shared TestB<__Canon, int>.TestCreate, the instance of
// TestA<T, int> is obtained through a generic dictionary lookup (a
// GenericLookupSignature fixup referencing TestA<__Canon, int>) rather than via
// a TypeFixupSignature. The virtual TestMethod on TestA<__Canon, int> must still
// be discovered and compiled.

public class TestA<T, U>
{
public virtual void TestMethod(T item) => Console.WriteLine($"{item} / {typeof(U)}");
}

public class TestB<T, U>
{
[MethodImpl(MethodImplOptions.NoInlining)]
public TestA<T, U> TestCreate() => new TestA<T, U>();
}

static class GenericLookupTests
{
[MethodImpl(MethodImplOptions.NoInlining)]
static TestB<T, U> CreateTestB<T, U>() => new TestB<T, U>();

static void Run()
{
TestB<string, int> obj = CreateTestB<string, int>();
obj.TestCreate().TestMethod("hello");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,30 @@ public override ObjectData GetData(NodeFactory factory, bool relocsOnly = false)
protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFactory factory)
{
DependencyList dependencies = null;

if (_fixupKind == ReadyToRunFixupKind.TypeHandle)
{
TypeFixupSignature.AddDependenciesForAsyncStateMachineBox(ref dependencies, factory, _typeArgument);

// In shared generic code, newobj uses a generic dictionary lookup for the type handle
// rather than a direct READYTORUN_FIXUP_TypeHandle (TypeFixupSignature). Mirror the
// creation of InheritedVirtualMethodsNode as its done in TypeFixupSignature, so we
// scan the virtual methods on this type for dependency analysis.
Comment on lines +141 to +144
if (_typeArgument != null)
{
TypeDesc canonType = _typeArgument.ConvertToCanonForm(CanonicalFormKind.Specific);
if (!canonType.IsGenericDefinition &&
!canonType.IsInterface &&
canonType.IsDefType &&
(factory.CompilationCurrentPhase == 0) &&
factory.CompilationModuleGroup.VersionsWithType(canonType))
{
dependencies ??= new DependencyList();
dependencies.Add(factory.InheritedVirtualMethods(canonType), "Generic lookup type discovery for virtual dispatch");
}
}
}

return dependencies;
}

Expand Down
Loading