diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs index d2be67138b5d4..893331bcc141b 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Attributes.OnlyKeepUsedTests.g.cs @@ -64,7 +64,13 @@ public Task MethodWithUnmanagedConstraint () } [Fact] - public Task NullableOnConstraints () + public Task NullableOnConstraintsKept () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task NullableOnConstraintsRemoved () { return RunTest (allowMissingWarnings: true); } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs index 90356bd4df1ee..b61127a3e5542 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/GenericsTests.g.cs @@ -15,6 +15,12 @@ public Task ArrayVariantCasting () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task ByRefLike () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task CorrectOverloadedMethodGetsStrippedInGenericClass () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeOnConstraintAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeOnConstraintAttribute.cs new file mode 100644 index 0000000000000..ee2eca3bc6099 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptAttributeOnConstraintAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.GenericParameter, Inherited = false)] + public class KeptAttributeOnConstraintAttribute : KeptAttribute + { + public KeptAttributeOnConstraintAttribute (Type constraintType, Type attributeType) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptGenericParamAttributesAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptGenericParamAttributesAttribute.cs new file mode 100644 index 0000000000000..bb3992586b686 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptGenericParamAttributesAttribute.cs @@ -0,0 +1,16 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Reflection; + +namespace Mono.Linker.Tests.Cases.Expectations.Assertions +{ + [AttributeUsage (AttributeTargets.All, Inherited = false)] + public class KeptGenericParamAttributesAttribute : KeptAttribute + { + public KeptGenericParamAttributesAttribute (GenericParameterAttributes attributes) + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/MethodWithUnmanagedConstraint.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/MethodWithUnmanagedConstraint.cs index 713ed17db49d5..6b58c1dbf0de9 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/MethodWithUnmanagedConstraint.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/MethodWithUnmanagedConstraint.cs @@ -1,3 +1,4 @@ +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -19,7 +20,10 @@ public static void Main () /// /// [Kept] - static void Method () where T : unmanaged + static void Method< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : unmanaged { } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsKept.cs new file mode 100644 index 0000000000000..2d7f9d0df6b55 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsKept.cs @@ -0,0 +1,59 @@ +#nullable enable + +using System.Reflection; +using System.Runtime.CompilerServices; + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Attributes.OnlyKeepUsed +{ + [SetupCSharpCompilerToUse ("csc")] + [SetupLinkerTrimMode ("link")] + public class NullableOnConstraintsKept + { + public static void Main () + { + Test.Run (); + } + + [Kept] + [KeptInterface (typeof (I))] + class Test : I + { + [Kept] + public static void Run () + { + new C (); + Method (); + } + + [Kept] + [KeptAttributeAttribute (typeof (NullableContextAttribute))] + static T? Method< + [KeptGenericParamAttributes (GenericParameterAttributes.ReferenceTypeConstraint)] + [KeptAttributeAttribute (typeof (NullableAttribute))] + T + > () + where T : class, I? + { + return default; + } + } + + [Kept] + interface I + { + } + + [Kept] + [KeptMember (".ctor()")] + class C< + [KeptAttributeOnConstraint (typeof (I), typeof (NullableAttribute))] + T + > + where T : I? + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraints.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsRemoved.cs similarity index 63% rename from src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraints.cs rename to src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsRemoved.cs index 0151df2caa1ac..dca7d00cc0ef4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraints.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Attributes/OnlyKeepUsed/NullableOnConstraintsRemoved.cs @@ -1,5 +1,7 @@ #nullable enable +using System.Reflection; +using System.Runtime.CompilerServices; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -9,7 +11,7 @@ namespace Mono.Linker.Tests.Cases.Attributes.OnlyKeepUsed [SetupLinkerArgument ("--used-attrs-only", "true")] [SetupLinkerTrimMode ("link")] [IgnoreDescriptors (false)] - public class NullableOnConstraints + public class NullableOnConstraintsRemoved { public static void Main () { @@ -28,21 +30,24 @@ public static void Run () } [Kept] - static T? Method () where T : class, I? + static T? Method< + [KeptGenericParamAttributes (GenericParameterAttributes.ReferenceTypeConstraint)] + T + > () where T : class, I? { return default; } } - } - [Kept] - interface I - { - } + [Kept] + interface I + { + } - [Kept] - [KeptMember (".ctor()")] - class C where T : I? - { + [Kept] + [KeptMember (".ctor()")] + class C where T : I? + { + } } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs index e1764eb986fa5..8f11a98281eee 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/NullableAnnotations.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics.CodeAnalysis; +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Helpers; using DAM = System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute; @@ -131,14 +132,22 @@ static void UnderlyingTypeOfUnannotatedGenericParameterRequiresProperties () where T : struct + static void NullableOfAnnotatedGenericParameterRequiresPublicProperties< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + [KeptAttributeAttribute (typeof (DAM))] + [DAM (DAMT.PublicProperties)] + T + > () where T : struct { Nullable.GetUnderlyingType (typeof (Nullable)).RequiresPublicProperties (); } [Kept] [ExpectedWarning ("IL2087")] - static void NullableOfUnannotatedGenericParameterRequiresPublicProperties () where T : struct + static void NullableOfUnannotatedGenericParameterRequiresPublicProperties< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : struct { Nullable.GetUnderlyingType (typeof (Nullable)).RequiresPublicProperties (); } @@ -240,7 +249,12 @@ static void DamOnNullableKeepsUnderlyingMembers () } [Kept] - static void UnderlyingTypeOfCreatedNullableOfAnnotatedTRequiresPublicProperties<[KeptAttributeAttribute (typeof (DAM))][DAM (DAMT.PublicProperties)] T> () where T : struct + static void UnderlyingTypeOfCreatedNullableOfAnnotatedTRequiresPublicProperties< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + [KeptAttributeAttribute (typeof (DAM))] + [DAM (DAMT.PublicProperties)] + T + > () where T : struct { Type t = typeof (Nullable); t = Nullable.GetUnderlyingType (t); @@ -262,13 +276,20 @@ struct StructWithFieldsReferencedThroughDamOnNullable [Kept] [ExpectedWarning ("IL2091")] - static void NullableOfUnannotatedGenericParamPassedAsGenericParamRequiresPublicFields () where T : struct + static void NullableOfUnannotatedGenericParamPassedAsGenericParamRequiresPublicFields< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : struct { RequirePublicFieldsOnGenericParam> (); } [Kept] - static void NullableOfAnnotatedGenericParamPassedAsGenericParamRequiresPublicFields<[KeptAttributeAttribute (typeof (DAM))][DAM (DAMT.PublicFields)] T> () where T : struct + static void NullableOfAnnotatedGenericParamPassedAsGenericParamRequiresPublicFields< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + [KeptAttributeAttribute (typeof (DAM))] + [DAM (DAMT.PublicFields)] T + > () where T : struct { RequirePublicFieldsOnGenericParam> (); } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/ByRefLike.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/ByRefLike.cs new file mode 100644 index 0000000000000..af6de6f7fdb4c --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/ByRefLike.cs @@ -0,0 +1,44 @@ +using System; +using System.Reflection; +using System.Runtime.CompilerServices; + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Generics +{ + [IgnoreTestCase ("Ignore in NativeAOT, see https://github.com/dotnet/runtime/issues/82447", IgnoredBy = Tool.NativeAot)] + [KeptAttributeAttribute (typeof (IgnoreTestCaseAttribute), By = Tool.Trimmer)] + class ByRefLike + { + static void Main () + { + Test (); + } + + [Kept] + static void Test () + { + G g = new (); + } + + [Kept] + [KeptAttributeAttribute (typeof (IsByRefLikeAttribute))] + [KeptAttributeAttribute (typeof (ObsoleteAttribute))] // Signals this is unsupported to older compilers + [KeptAttributeAttribute (typeof (CompilerFeatureRequiredAttribute))] + ref struct RefStruct { + } + + [Kept] + [KeptAttributeAttribute (typeof (IsByRefLikeAttribute))] + [KeptAttributeAttribute (typeof (ObsoleteAttribute))] // Signals this is unsupported to older compilers + [KeptAttributeAttribute (typeof (CompilerFeatureRequiredAttribute))] + ref struct G< + [KeptGenericParamAttributes (GenericParameterAttributes.AllowByRefLike)] + T + > where T : allows ref struct { + [Kept] + public T t; + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/NewConstraintOnClass.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/NewConstraintOnClass.cs index 0acdf9191b65c..eda9e3e09dabe 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/NewConstraintOnClass.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/NewConstraintOnClass.cs @@ -1,4 +1,5 @@ -using System.Runtime.CompilerServices; +using System.Reflection; +using System.Runtime.CompilerServices; using Mono.Linker.Tests.Cases.Expectations.Assertions; namespace Mono.Linker.Tests.Cases.Generics @@ -36,7 +37,10 @@ public void Foo () [Kept] [KeptMember (".ctor()")] - class WithConstraint where T : new() + class WithConstraint< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] + T + > where T : new() { } @@ -70,7 +74,10 @@ public void Foo () [Kept] [KeptMember (".ctor()")] - class WithConstraint where T : struct + class WithConstraint< + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + T + > where T : struct { } @@ -104,7 +111,11 @@ public void Foo () [Kept] [KeptMember (".ctor()")] - class WithConstraint<[KeptAttributeAttribute (typeof (IsUnmanagedAttribute))] T> where T : unmanaged + class WithConstraint< + [KeptAttributeAttribute (typeof (IsUnmanagedAttribute))] + [KeptGenericParamAttributes (GenericParameterAttributes.NotNullableValueTypeConstraint | GenericParameterAttributes.DefaultConstructorConstraint)] + T + > where T : unmanaged { } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/VariantCasting.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/VariantCasting.cs index bc82aa51c4bfb..cc8bd7c637565 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/VariantCasting.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Generics/VariantCasting.cs @@ -1,11 +1,16 @@ -using Mono.Linker.Tests.Cases.Expectations.Assertions; +using System.Reflection; + +using Mono.Linker.Tests.Cases.Expectations.Assertions; namespace Mono.Linker.Tests.Cases.Generics { public class VariantCasting { [Kept] - interface IVariant { } + interface IVariant< + [KeptGenericParamAttributes (GenericParameterAttributes.Covariant)] + out T + > { } [Kept] interface IFoo { } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructWithNestedStructImplementingInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructWithNestedStructImplementingInterface.cs index 0bdc4a2a4dbb2..f4c06c40e1b11 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructWithNestedStructImplementingInterface.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructWithNestedStructImplementingInterface.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnValueType @@ -33,7 +34,10 @@ public interface IBuilder } [Kept] - public interface IBuildable : IBuildable where T : IBuilder, new() + public interface IBuildable< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] + T + > : IBuildable where T : IBuilder, new() { } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs index 7fcc3bf3c6e08..370a5c7dd538d 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Helpers; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -410,7 +411,10 @@ internal interface IStaticAndInstanceMethodsInternalUsed } [Kept] - internal static void CallExplicitImplMethod () where T : IStaticAndInstanceMethods, new() + internal static void CallExplicitImplMethod< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : IStaticAndInstanceMethods, new() { T.StaticMethodExplicitImpl (); IStaticAndInstanceMethods x = new T (); @@ -418,7 +422,10 @@ internal interface IStaticAndInstanceMethodsInternalUsed } [Kept] - internal static void CallExplicitImplMethodInternalUsed () where T : IStaticAndInstanceMethodsInternalUsed, new() + internal static void CallExplicitImplMethodInternalUsed< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : IStaticAndInstanceMethodsInternalUsed, new() { T.StaticMethodExplicitImpl (); IStaticAndInstanceMethodsInternalUsed x = new T (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/VarianceBasic.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/VarianceBasic.cs index 75ae157dc1848..727780e2434d4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/VarianceBasic.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/VarianceBasic.cs @@ -1,4 +1,5 @@ using System; +using System.Reflection; using Mono.Linker.Tests.Cases.Expectations.Assertions; namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods @@ -11,13 +12,19 @@ static void Main () TestEntrypoint.Test (); } [Kept] - public interface InterfaceScenario1 + public interface InterfaceScenario1< + [KeptGenericParamAttributes (GenericParameterAttributes.Contravariant)] + in T + > { [Kept] static abstract int Method (); } [Kept] - public interface InterfaceScenario2 + public interface InterfaceScenario2< + [KeptGenericParamAttributes (GenericParameterAttributes.Contravariant)] + in T + > { [Kept] static abstract int Method (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs index 4aca2d5186185..70289934fe536 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ActivatorCreateInstance.cs @@ -541,7 +541,10 @@ public TestCreateInstanceOfTWithNewConstraintType (int i) } [Kept] - private static void TestCreateInstanceOfTWithNewConstraint () where T : new() + private static void TestCreateInstanceOfTWithNewConstraint< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] + T + > () where T : new() { Activator.CreateInstance (); } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs index 3dcd1b7fd1175..451cf28afbde4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Reflection/ObjectGetType.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Reflection; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using Mono.Linker.Tests.Cases.Expectations.Assertions; @@ -195,9 +196,11 @@ class TestType [Kept] [KeptMember (".cctor()")] class Generic< + [KeptGenericParamAttributes (GenericParameterAttributes.DefaultConstructorConstraint)] [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] - [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] - TWithMethods> where TWithMethods : new() + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + TWithMethods + > where TWithMethods : new() { [Kept] static TWithMethods ReturnAnnotated () { return new TWithMethods (); } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 6e45fbadcdb52..7d984ef2e4863 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -1151,10 +1151,14 @@ IEnumerable VerifyGenericParameters (IGenericParameterProvider src, IGen Assert.AreEqual (src.HasGenericParameters, linked.HasGenericParameters); if (src.HasGenericParameters) { for (int i = 0; i < src.GenericParameters.Count; ++i) { - // TODO: Verify constraints var srcp = src.GenericParameters[i]; var lnkp = linked.GenericParameters[i]; + foreach (var err in VerifyGenericParameterConstraints (srcp, lnkp)) + yield return err; + foreach (var err in VerifyGenericParameterAttributes (srcp, lnkp)) + yield return err; + if (!compilerGenerated) { foreach (var err in VerifyCustomAttributes (srcp, lnkp)) yield return err; @@ -1176,6 +1180,67 @@ IEnumerable VerifyGenericParameters (IGenericParameterProvider src, IGen } } + IEnumerable VerifyGenericParameterConstraints (GenericParameter src, GenericParameter linked) + { + if (src.HasConstraints != linked.HasConstraints) { + yield return $"Mismatch in generic parameter constraints on {src} of {src.Owner}. Input has constraints?: {src.HasConstraints}, Output has constraints?: {linked.HasConstraints}"; + yield break; + } + + if (!src.HasConstraints) + yield break; + + if (src.Constraints.Count != linked.Constraints.Count) { + yield return $"Mismatch in generic parameter constraint count on {src} of {src.Owner}. Input has {src.Constraints.Count} constraints, Output has {linked.Constraints.Count} constraints"; + yield break; + } + + // ILLink doesn't rewrite generic parameter constraint types, so just check they are identical to inputs. + for (int i = 0; i < src.Constraints.Count; i++) { + var srcConstraint = src.Constraints[i]; + var linkedConstraint = linked.Constraints[i]; + if (srcConstraint.ConstraintType.FullName != linkedConstraint.ConstraintType.FullName) { + yield return $"Mismatch in generic parameter constraint type. {src} constraint {i} is {srcConstraint.ConstraintType.FullName}, {linked} constraint {i} is {linkedConstraint.ConstraintType.FullName}"; + } + } + + // C# doesn't have syntax for annotating generic parameter constraints with arbitrary attributes, + // so expected attributes on generic parameter constraints are specified on the generic parameter itself. + HashSet<(string ConstraintType, string AttributeType)> expectedConstraintAttributes = src.CustomAttributes + .Where (a => IsKeptAttributeOnConstraint (a)) + .Select (a => (a.ConstructorArguments[0].Value.ToString (), a.ConstructorArguments[1].Value.ToString ())) + .ToHashSet (); + + HashSet<(string ConstraintType, string AttributeType)> linkedConstraintAttributes = linked.Constraints + .Where (c => c.HasCustomAttributes) + .SelectMany (c => c.CustomAttributes.Select (a => (c.ConstraintType.FullName, a.AttributeType.FullName))) + .ToHashSet (); + + if (!expectedConstraintAttributes.SetEquals (linkedConstraintAttributes)) { + var missing = $"Missing: {string.Join (", ", expectedConstraintAttributes.Except (linkedConstraintAttributes).Select (c => $"{c.AttributeType} on {c.ConstraintType}"))}"; + var extra = $"Extra: {string.Join (", ", linkedConstraintAttributes.Except (expectedConstraintAttributes).Select (c => $"{c.AttributeType} on {c.ConstraintType}"))}"; + yield return string.Join (Environment.NewLine, $"Custom attributes on `{src}' generic parameter constraints are not matching:", missing, extra); + } + + static bool IsKeptAttributeOnConstraint (CustomAttribute attr) { + if (attr.AttributeType.Name != nameof (KeptAttributeOnConstraintAttribute)) + return false; + + if (attr.ConstructorArguments.Count != 2) + throw new NotImplementedException ("Unexpected KeptCustomAttributeOnConstraintAttribute ctor variant"); + + return true; + } + } + + IEnumerable VerifyGenericParameterAttributes (GenericParameter src, GenericParameter linked) + { + var expectedAttributes = (System.Reflection.GenericParameterAttributes) (GetCustomAttributeCtorValues (src, nameof (KeptGenericParamAttributesAttribute)).FirstOrDefault () ?? System.Reflection.GenericParameterAttributes.None); + var linkedAttributes = (System.Reflection.GenericParameterAttributes) linked.Attributes; + if (expectedAttributes != linkedAttributes) + yield return $"Mismatch in generic parameter attributes on {src} of {src.Owner}. Expected: {expectedAttributes}, Output: {linkedAttributes}"; + } + IEnumerable VerifyParameters (IMethodSignature src, IMethodSignature linked, bool compilerGenerated) { if (src.HasParameters != linked.HasParameters)