From 5fabb48326c5f8ad1d4508264209e67123e997a8 Mon Sep 17 00:00:00 2001 From: Vitek Karas <10670590+vitek-karas@users.noreply.github.com> Date: Fri, 12 May 2023 13:31:25 -0700 Subject: [PATCH] Add tests for RUC warnings on override methods (#86015) These are tests for bug https://github.com/dotnet/runtime/issues/86008. I also restructured the test file into nested classes some more as it's easier to navigate this way. Also adds a test for https://github.com/dotnet/runtime/issues/86032. --- .../RequiresCapability/RequiresAccessedThrough.cs | 54 ++++ .../RequiresOnVirtualsAndInterfaces.cs | 316 ++++++++++++++------- 2 files changed, 260 insertions(+), 110 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs index c345c9c..15c0993 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresAccessedThrough.cs @@ -22,6 +22,7 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability { TestRequiresOnlyThroughReflection (); AccessedThroughReflectionOnGenericType.Test (); + AccessedThroughGenericParameterAnnotation.Test (); AccessThroughSpecialAttribute.Test (); AccessThroughPInvoke.Test (); AccessThroughNewConstraint.Test (); @@ -73,6 +74,59 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability } } + class AccessedThroughGenericParameterAnnotation + { + class TypeWithRequiresMethod + { + [RequiresUnreferencedCode("--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--")] + [RequiresDynamicCode ("--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--")] + [RequiresAssemblyFiles ("--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--")] + public static void MethodWhichRequires () { } + } + + class TypeWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + public TypeWithPublicMethods () { } + } + + [ExpectedWarning ("IL2026", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--")] + [ExpectedWarning ("IL3002", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + static void TestAccessOnGenericType () + { + new TypeWithPublicMethods (); + } + + static void MethodWithPublicMethods<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () { } + + [ExpectedWarning ("IL2026", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--")] + [ExpectedWarning ("IL3002", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + static void TestAccessOnGenericMethod () + { + MethodWithPublicMethods (); + } + + static void MethodWithPublicMethodsInference<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> (T instance) { } + + // https://github.com/dotnet/runtime/issues/86032 + // IL2026 should be produced by the analyzer as well, but it has a bug around inferred generic arguments + [ExpectedWarning ("IL2026", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.Trimmer | Tool.NativeAot)] + [ExpectedWarning ("IL3002", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--AccessedThroughGenericParameterAnnotation.TypeWithRequiresMethod.MethodWhichRequires--", ProducedBy = Tool.NativeAot)] + static void TestAccessOnGenericMethodWithInferenceOnMethod () + { + MethodWithPublicMethodsInference (new TypeWithRequiresMethod ()); + } + + public static void Test () + { + TestAccessOnGenericType (); + TestAccessOnGenericMethod (); + TestAccessOnGenericMethodWithInferenceOnMethod (); + } + } + class AccessThroughSpecialAttribute { // https://github.com/dotnet/linker/issues/1873 diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnVirtualsAndInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnVirtualsAndInterfaces.cs index 79273bd..5470aa1 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnVirtualsAndInterfaces.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/RequiresOnVirtualsAndInterfaces.cs @@ -18,154 +18,250 @@ namespace Mono.Linker.Tests.Cases.RequiresCapability { public static void Main () { - TestBaseTypeVirtualMethodRequires (); - TestTypeWhichOverridesMethodVirtualMethodRequires (); - TestTypeWhichOverridesMethodVirtualMethodRequiresOnBase (); - TestTypeWhichOverridesVirtualPropertyRequires (); - TestInterfaceMethodWithRequires (); - TestCovariantReturnCallOnDerived (); + VirtualMethod.Test (); + VirtualProperty.Test (); + InterfaceMethod.Test (); + CovariantReturn.Test (); CovariantReturnViaLdftn.Test (); NewSlotVirtual.Test (); StaticInterfaces.Test (); } - class BaseType + class VirtualMethod { - [RequiresUnreferencedCode ("Message for --BaseType.VirtualMethodRequires--")] - [RequiresAssemblyFiles ("Message for --BaseType.VirtualMethodRequires--")] - [RequiresDynamicCode ("Message for --BaseType.VirtualMethodRequires--")] - public virtual void VirtualMethodRequires () + class BaseType { + [RequiresUnreferencedCode ("Message for --BaseType.VirtualMethodRequires--")] + [RequiresAssemblyFiles ("Message for --BaseType.VirtualMethodRequires--")] + [RequiresDynamicCode ("Message for --BaseType.VirtualMethodRequires--")] + public virtual void VirtualMethodRequires () + { + } } - } - class TypeWhichOverridesMethod : BaseType - { - [RequiresUnreferencedCode ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] - [RequiresAssemblyFiles ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] - [RequiresDynamicCode ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] - public override void VirtualMethodRequires () + class TypeWhichOverridesMethod : BaseType { + [RequiresUnreferencedCode ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] + [RequiresAssemblyFiles ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] + [RequiresDynamicCode ("Message for --TypeWhichOverridesMethod.VirtualMethodRequires--")] + public override void VirtualMethodRequires () + { + } } - } - [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] - [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestBaseTypeVirtualMethodRequires () - { - var tmp = new BaseType (); - tmp.VirtualMethodRequires (); - } + [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void TestCallOnBase () + { + var tmp = new BaseType (); + tmp.VirtualMethodRequires (); + } - [LogDoesNotContain ("TypeWhichOverridesMethod.VirtualMethodRequires")] - [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] - [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestTypeWhichOverridesMethodVirtualMethodRequires () - { - var tmp = new TypeWhichOverridesMethod (); - tmp.VirtualMethodRequires (); - } + [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void TestCallOnOverride () + { + var tmp = new TypeWhichOverridesMethod (); + tmp.VirtualMethodRequires (); + } - [LogDoesNotContain ("TypeWhichOverridesMethod.VirtualMethodRequires")] - [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] - [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestTypeWhichOverridesMethodVirtualMethodRequiresOnBase () - { - BaseType tmp = new TypeWhichOverridesMethod (); - tmp.VirtualMethodRequires (); - } + [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void TestCallOnOverrideViaBase () + { + BaseType tmp = new TypeWhichOverridesMethod (); + tmp.VirtualMethodRequires (); + } - class PropertyBaseType - { - public virtual int VirtualPropertyRequires { - [RequiresUnreferencedCode ("Message for --PropertyBaseType.VirtualPropertyRequires--")] - [RequiresAssemblyFiles ("Message for --PropertyBaseType.VirtualPropertyRequires--")] - [RequiresDynamicCode ("Message for --PropertyBaseType.VirtualPropertyRequires--")] - get; + // https://github.com/dotnet/runtime/issues/86008 + // This is the "direct reflection" case, which actually behaves differently from indirect (DAM annotation) + // in this case even trimmer will warn on both methods. + [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL2026", "--TypeWhichOverridesMethod.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--TypeWhichOverridesMethod.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--TypeWhichOverridesMethod.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + static void TestDirectReflectionAccess () + { + BaseType tmp = new TypeWhichOverridesMethod (); + typeof (TypeWhichOverridesMethod).GetMethod ("VirtualMethodRequires").Invoke (tmp, Array.Empty ()); } - } - class TypeWhichOverridesProperty : PropertyBaseType - { - public override int VirtualPropertyRequires { - [RequiresUnreferencedCode ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] - [RequiresAssemblyFiles ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] - [RequiresDynamicCode ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] - get { return 1; } + static void CallMethodWithRequiresOnInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(T instance) + { + typeof(T).GetMethod("VirtualMethodRequires").Invoke(instance, Array.Empty ()); } - } - [LogDoesNotContain ("TypeWhichOverridesProperty.VirtualPropertyRequires")] - [ExpectedWarning ("IL2026", "--PropertyBaseType.VirtualPropertyRequires--")] - [ExpectedWarning ("IL3002", "--PropertyBaseType.VirtualPropertyRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--PropertyBaseType.VirtualPropertyRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestTypeWhichOverridesVirtualPropertyRequires () - { - var tmp = new TypeWhichOverridesProperty (); - _ = tmp.VirtualPropertyRequires; + // https://github.com/dotnet/runtime/issues/86008 + [ExpectedWarning ("IL2026", "--BaseType.VirtualMethodRequires--")] + [ExpectedWarning ("IL3002", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--BaseType.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL2026", "--TypeWhichOverridesMethod.VirtualMethodRequires--", ProducedBy = Tool.Analyzer)] + //[ExpectedWarning ("IL3002", "--TypeWhichOverridesMethod.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + //[ExpectedWarning ("IL3050", "--TypeWhichOverridesMethod.VirtualMethodRequires--", ProducedBy = Tool.NativeAot)] + static void TestAnnotatedReflectionAccess() + { + CallMethodWithRequiresOnInstance(new TypeWhichOverridesMethod ()); + } + + public static void Test() + { + TestCallOnBase (); + TestCallOnOverride (); + TestCallOnOverrideViaBase (); + TestDirectReflectionAccess (); + TestAnnotatedReflectionAccess (); + } } - [LogDoesNotContain ("ImplementationClass.MethodWithRequires")] - [ExpectedWarning ("IL2026", "--IRequires.MethodWithRequires--")] - [ExpectedWarning ("IL3002", "--IRequires.MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--IRequires.MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestInterfaceMethodWithRequires () + class VirtualProperty { - IRequires inst = new ImplementationClass (); - inst.MethodWithRequires (); + class PropertyBaseType + { + public virtual int VirtualPropertyRequires { + [RequiresUnreferencedCode ("Message for --PropertyBaseType.VirtualPropertyRequires--")] + [RequiresAssemblyFiles ("Message for --PropertyBaseType.VirtualPropertyRequires--")] + [RequiresDynamicCode ("Message for --PropertyBaseType.VirtualPropertyRequires--")] + get; + } + } + + class TypeWhichOverridesProperty : PropertyBaseType + { + public override int VirtualPropertyRequires { + [RequiresUnreferencedCode ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] + [RequiresAssemblyFiles ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] + [RequiresDynamicCode ("Message for --TypeWhichOverridesProperty.VirtualPropertyRequires--")] + get { return 1; } + } + } + + [ExpectedWarning ("IL2026", "--PropertyBaseType.VirtualPropertyRequires--")] + [ExpectedWarning ("IL3002", "--PropertyBaseType.VirtualPropertyRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--PropertyBaseType.VirtualPropertyRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void CallOnDerived () + { + var tmp = new TypeWhichOverridesProperty (); + _ = tmp.VirtualPropertyRequires; + } + + public static void Test() + { + CallOnDerived (); + } } class BaseReturnType { } class DerivedReturnType : BaseReturnType { } - interface IRequires + class InterfaceMethod { - [RequiresUnreferencedCode ("Message for --IRequires.MethodWithRequires--")] - [RequiresAssemblyFiles ("Message for --IRequires.MethodWithRequires--")] - [RequiresDynamicCode ("Message for --IRequires.MethodWithRequires--")] - public void MethodWithRequires (); - } + interface IRequires + { + [RequiresUnreferencedCode ("Message for --IRequires.MethodWithRequires--")] + [RequiresAssemblyFiles ("Message for --IRequires.MethodWithRequires--")] + [RequiresDynamicCode ("Message for --IRequires.MethodWithRequires--")] + public void MethodWithRequires (); + } - class ImplementationClass : IRequires - { - [RequiresUnreferencedCode ("Message for --ImplementationClass.RequiresMethod--")] - [RequiresAssemblyFiles ("Message for --ImplementationClass.RequiresMethod--")] - [RequiresDynamicCode ("Message for --ImplementationClass.RequiresMethod--")] - public void MethodWithRequires () + class ImplementationClass : IRequires { + [RequiresUnreferencedCode ("Message for --ImplementationClass.RequiresMethod--")] + [RequiresAssemblyFiles ("Message for --ImplementationClass.RequiresMethod--")] + [RequiresDynamicCode ("Message for --ImplementationClass.RequiresMethod--")] + public void MethodWithRequires () + { + } } - } - abstract class CovariantReturnBase - { - [RequiresUnreferencedCode ("Message for --CovariantReturnBase.GetRequires--")] - [RequiresAssemblyFiles ("Message for --CovariantReturnBase.GetRequires--")] - [RequiresDynamicCode ("Message for --CovariantReturnBase.GetRequires--")] - public abstract BaseReturnType GetRequires (); - } + [ExpectedWarning ("IL2026", "--IRequires.MethodWithRequires--")] + [ExpectedWarning ("IL3002", "--IRequires.MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--IRequires.MethodWithRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void TestCallViaInterface () + { + IRequires inst = new ImplementationClass (); + inst.MethodWithRequires (); + } - class CovariantReturnDerived : CovariantReturnBase - { - [RequiresUnreferencedCode ("Message for --CovariantReturnDerived.GetRequires--")] - [RequiresAssemblyFiles ("Message for --CovariantReturnDerived.GetRequires--")] - [RequiresDynamicCode ("Message for --CovariantReturnDerived.GetRequires--")] - public override DerivedReturnType GetRequires () + [ExpectedWarning ("IL2026", "--ImplementationClass.RequiresMethod--")] + [ExpectedWarning ("IL3002", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void TestCallViaImplementationClass () + { + ImplementationClass inst = new ImplementationClass (); + inst.MethodWithRequires (); + } + + [ExpectedWarning ("IL2026", "--ImplementationClass.RequiresMethod--")] + [ExpectedWarning ("IL3002", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.NativeAot)] + static void TestDirectReflectionAccess () + { + typeof (ImplementationClass).GetMethod ("MethodWithRequires").Invoke (new ImplementationClass (), Array.Empty ()); + } + + static void CallMethodWithRequiresOnInstance<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(T instance) + { + typeof (T).GetMethod ("MethodWithRequires").Invoke (new ImplementationClass (), Array.Empty ()); + } + + // https://github.com/dotnet/runtime/issues/86008 + // This is a bug in illink, the fact that there's no warning is an analysis hole + [ExpectedWarning ("IL2026", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.NativeAot | Tool.Analyzer)] + [ExpectedWarning ("IL3002", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--ImplementationClass.RequiresMethod--", ProducedBy = Tool.NativeAot)] + static void TestAnnotatedReflectionAccess () + { + CallMethodWithRequiresOnInstance (new ImplementationClass ()); + } + + public static void Test() { - return null; + TestCallViaInterface (); + TestCallViaImplementationClass (); + TestDirectReflectionAccess (); + TestAnnotatedReflectionAccess (); } } - [LogDoesNotContain ("--CovariantReturnBase.GetRequires--")] - [ExpectedWarning ("IL2026", "--CovariantReturnDerived.GetRequires--")] - [ExpectedWarning ("IL3002", "--CovariantReturnDerived.GetRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - [ExpectedWarning ("IL3050", "--CovariantReturnDerived.GetRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] - static void TestCovariantReturnCallOnDerived () + class CovariantReturn { - var tmp = new CovariantReturnDerived (); - tmp.GetRequires (); + abstract class CovariantReturnBase + { + [RequiresUnreferencedCode ("Message for --CovariantReturnBase.GetRequires--")] + [RequiresAssemblyFiles ("Message for --CovariantReturnBase.GetRequires--")] + [RequiresDynamicCode ("Message for --CovariantReturnBase.GetRequires--")] + public abstract BaseReturnType GetRequires (); + } + + class CovariantReturnDerived : CovariantReturnBase + { + [RequiresUnreferencedCode ("Message for --CovariantReturnDerived.GetRequires--")] + [RequiresAssemblyFiles ("Message for --CovariantReturnDerived.GetRequires--")] + [RequiresDynamicCode ("Message for --CovariantReturnDerived.GetRequires--")] + public override DerivedReturnType GetRequires () + { + return null; + } + } + + [ExpectedWarning ("IL2026", "--CovariantReturnDerived.GetRequires--")] + [ExpectedWarning ("IL3002", "--CovariantReturnDerived.GetRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + [ExpectedWarning ("IL3050", "--CovariantReturnDerived.GetRequires--", ProducedBy = Tool.Analyzer | Tool.NativeAot)] + static void CallOnDerived () + { + var tmp = new CovariantReturnDerived (); + tmp.GetRequires (); + } + + public static void Test() + { + CallOnDerived (); + } } class CovariantReturnViaLdftn -- 2.7.4