Skip to content

Commit

Permalink
Fix sealing of types that were not concretely instantiated (#73218)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichalStrehovsky authored Aug 2, 2022
1 parent eb4f246 commit a2a5668
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 3 deletions.
12 changes: 9 additions & 3 deletions src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -372,10 +372,15 @@ public ScannedDevirtualizationManager(ImmutableArray<DependencyNodeCore<NodeFact
{
foreach (var node in markedNodes)
{
if (node is ConstructedEETypeNode eetypeNode)
TypeDesc type = node switch
{
TypeDesc type = eetypeNode.Type;
ConstructedEETypeNode eetypeNode => eetypeNode.Type,
CanonicalEETypeNode canoneetypeNode => canoneetypeNode.Type,
_ => null,
};

if (type != null)
{
if (!type.IsInterface)
{
//
Expand All @@ -391,7 +396,8 @@ public ScannedDevirtualizationManager(ImmutableArray<DependencyNodeCore<NodeFact
// didn't scan the virtual methods on them.
//

_constructedTypes.Add(type);
if (!type.IsCanonicalSubtype(CanonicalFormKind.Any))
_constructedTypes.Add(type);

TypeDesc canonType = type.ConvertToCanonForm(CanonicalFormKind.Specific);

Expand Down
114 changes: 114 additions & 0 deletions src/tests/nativeaot/SmokeTests/UnitTests/Devirtualization.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
// 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.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

class Devirtualization
{
internal static int Run()
{
RegressionBug73076.Run();
DevirtualizationCornerCaseTests.Run();

return 100;
}

class RegressionBug73076
{
interface IFactory
{
BaseFtnn<T> Make<T>();
}

class Factory : IFactory
{
[MethodImpl(MethodImplOptions.NoInlining)]
public BaseFtnn<T> Make<T>() => new DerivedFtnn<T>();
}

class BaseFtnn<T>
{
public virtual string GetId() => "Base";
}

class DerivedFtnn<T> : BaseFtnn<T>
{
public override string GetId() => "Derived";
}

public static void Run()
{
IFactory factory = new Factory();

// This is a generic virtual method call so we'll only ever see BaseFtnn and DerivedFtnn instantiated
// over __Canon at compile time.
var made = factory.Make<object>();
if (made.GetId() != "Derived")
throw new Exception();
}
}

class DevirtualizationCornerCaseTests
{
interface IIntf1
{
int GetValue();
}

class Intf1Impl : IIntf1
{
public virtual int GetValue() => 123;
}

[DynamicInterfaceCastableImplementation]
interface IIntf1Impl : IIntf1
{
int IIntf1.GetValue() => 456;
}

class Intf1CastableImpl : IDynamicInterfaceCastable
{
public RuntimeTypeHandle GetInterfaceImplementation(RuntimeTypeHandle interfaceType) => typeof(IIntf1Impl).TypeHandle;
public bool IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) => true;
}

interface IIntf2
{
int GetValue();
}

class Intf2Impl1 : IIntf2
{
public virtual int GetValue() => 123;
}

class Intf2Impl2<T> : IIntf2
{
public virtual int GetValue() => 456;
}

static void AssertEqual<T>(T expected, T actual)
{
if (!object.Equals(expected, actual))
throw new Exception();
}

[MethodImpl(MethodImplOptions.NoInlining)]
static void TestIntf1(IIntf1 o, int expected) => AssertEqual(expected, o.GetValue());

[MethodImpl(MethodImplOptions.NoInlining)]
static void TestIntf2(IIntf2 o, int expected) => AssertEqual(expected, o.GetValue());

public static void Run()
{
TestIntf1(new Intf1Impl(), 123);
TestIntf1((IIntf1)new Intf1CastableImpl(), 456);

TestIntf2(new Intf2Impl1(), 123);
TestIntf2((IIntf2)Activator.CreateInstance(typeof(Intf2Impl2<>).MakeGenericType(typeof(object))), 456);
}
}
}
1 change: 1 addition & 0 deletions src/tests/nativeaot/SmokeTests/UnitTests/Main.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
success &= RunTest(Generics.Run);
success &= RunTest(Interfaces.Run);
success &= RunTest(Threading.Run);
success &= RunTest(Devirtualization.Run);

return success ? 100 : 1;

Expand Down
1 change: 1 addition & 0 deletions src/tests/nativeaot/SmokeTests/UnitTests/UnitTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ItemGroup>
<Compile Include="BasicThreading.cs" />
<Compile Include="Delegates.cs" />
<Compile Include="Devirtualization.cs" />
<Compile Include="Generics.cs" />
<Compile Include="Interfaces.cs" />
<Compile Include="Threading.cs" />
Expand Down

0 comments on commit a2a5668

Please sign in to comment.