Skip to content
Merged
3 changes: 3 additions & 0 deletions src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2030,6 +2030,9 @@ internal void MarkRelevantToVariantCasting(TypeDefinition type)
{
_typeMapHandler.ProcessType(type);
Annotations.MarkRelevantToVariantCasting(type);

if (Context.TryResolve(type.BaseType) is TypeDefinition baseType && !Annotations.IsRelevantToVariantCasting(baseType))
MarkRelevantToVariantCasting(baseType);
}

internal void MarkMethodVisibleToReflection(MethodReference method, in DependencyInfo reason, in MessageOrigin origin, bool suppressTrimAnalysisWarnings = false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using System;
using System.Reflection;

using Mono.Linker.Tests.Cases.Expectations.Assertions;

Expand Down Expand Up @@ -59,11 +60,61 @@ class Boring<T> : Interesting<T>
public Boring() { }
}

[Kept]
interface IInner
{
[Kept(By = Tool.Trimmer)] // NativeAOT removes interface methods that no constructed type implements
void InnerMethod();
}

[Kept]
[KeptInterface(typeof(IInner))]
interface IOuter<T> : IInner
{
[Kept(By = Tool.Trimmer)]
void OuterMethod();
}

interface IUnused<T> : IOuter<T>
{
}

[Kept(By = Tool.Trimmer)] // NativeAOT does not need the metadata of an unconstructed base type
[KeptInterface(typeof(IOuter<>), "T", By = Tool.Trimmer)]
[KeptInterface(typeof(IInner), By = Tool.Trimmer)]
abstract class UninstantiatedBaseWithInterfaces<T> : IOuter<T>
{
[Kept(By = Tool.Trimmer)]
public void OuterMethod() { }

[Kept(By = Tool.Trimmer)]
void IInner.InnerMethod() { }
}

// Only reachable through typeof. The interfaces of the base type must be kept together with the ones on this type.
[Kept]
[KeptBaseType(typeof(UninstantiatedBaseWithInterfaces<>), "T")]
[KeptInterface(typeof(IOuter<>), "T")]
[KeptInterface(typeof(IInner))]
class UninstantiatedDerivedWithInterfaces<T> : UninstantiatedBaseWithInterfaces<T>, IUnused<T>
{
}

[Kept]
static void UseInterfaceMethods(IOuter<int> outer)
{
outer.OuterMethod();
((IInner)outer).InnerMethod();
}

public static void Main()
{
// These casts need to succeed after trimming
IVariant<IFoo> foos = (IVariant<IFoo>)(object)new Boring<Foo>();
IVariant<Base> bases = (IVariant<Base>)(object)new Boring<Derived>();

Type type = typeof(UninstantiatedDerivedWithInterfaces<>);
UseInterfaceMethods(null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute<UsedTypeMap>), "ProxyTargetIsAlsoTrimTarget", typeof(ProxyTargetIsAlsoTrimTargetTarget), typeof(ProxyTargetIsAlsoTrimTarget))]
[assembly: KeptAttributeAttribute(typeof(TypeMapAssociationAttribute<UsedTypeMap>), typeof(ProxyTargetIsAlsoTrimTargetSource), typeof(ProxyTargetIsAlsoTrimTarget))]

// Verify that a type only reachable through typeof (and therefore relevant to variant casting) also processes the type map entries of its base types.
[assembly: TypeMap<UsedTypeMap>("BaseOfTypeofOnlyDerived", typeof(BaseOfTypeofOnlyDerivedTarget), typeof(BaseOfTypeofOnlyDerived))] // Kept
[assembly: KeptAttributeAttribute(typeof(TypeMapAttribute<UsedTypeMap>), "BaseOfTypeofOnlyDerived", typeof(BaseOfTypeofOnlyDerivedTarget), typeof(BaseOfTypeofOnlyDerived), By = Tool.Trimmer)]

namespace Mono.Linker.Tests.Cases.Reflection
{
[SetupLinkerAction("link", "System.Private.CoreLib")] // Needed to get the RemoveAttributeInstances in embedded xml
Expand Down Expand Up @@ -256,6 +260,10 @@ static void ConstrainedStaticCall<T>(T t) where T : IStaticInterface
// is processed first via MarkTypeMapAttribute → MarkRequirementsForInstantiatedTypes.
_ = new ProxyTargetIsAlsoTrimTargetSource();
Console.WriteLine(new ProxyTargetIsAlsoTrimTarget());

// Neither type is instantiated, but typeof makes TypeofOnlyDerived relevant to variant casting,
// which makes its base type relevant to variant casting too, so the base type's type map entries must be kept.
Console.WriteLine(typeof(TypeofOnlyDerived));
}

[ExpectBodyModified]
Expand Down Expand Up @@ -615,4 +623,12 @@ class ProxyTargetIsAlsoTrimTargetTarget;
[Kept]
[KeptMember(".ctor()")]
class ProxyTargetIsAlsoTrimTargetSource;

[Kept]
class BaseOfTypeofOnlyDerived;
[Kept]
class BaseOfTypeofOnlyDerivedTarget;
[Kept]
[KeptBaseType(typeof(BaseOfTypeofOnlyDerived))]
class TypeofOnlyDerived : BaseOfTypeofOnlyDerived;
}
Loading