From 7631bfacb340285e5552da9ad8c103c03d46a309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michal=20Strehovsk=C3=BD?= Date: Wed, 10 Apr 2024 06:06:06 +0900 Subject: [PATCH] Do not warn when accessing fields on T constrained to be enum (#100814) Fixes #97737. Trimming ensures we keep all the public fields on enums. `typeof(T).GetFields()` is safe when the `T` is constrained to be `System.Enum` or a derived type. --- .../Compiler/Dataflow/GenericParameterProxy.cs | 11 +++++++++++ .../TrimAnalysis/GenericParameterProxy.cs | 10 ++++++++++ .../RequireDynamicallyAccessedMembersAction.cs | 4 ++++ .../TypeSystemProxy/GenericParameterProxy.cs | 2 ++ .../linker/Linker.Dataflow/GenericParameterProxy.cs | 12 ++++++++++++ .../DataFlow/GenericParameterDataFlow.cs | 7 +++++++ 6 files changed, 46 insertions(+) diff --git a/src/coreclr/tools/Common/Compiler/Dataflow/GenericParameterProxy.cs b/src/coreclr/tools/Common/Compiler/Dataflow/GenericParameterProxy.cs index b04e245ee50e58..cb9bbeadbc7726 100644 --- a/src/coreclr/tools/Common/Compiler/Dataflow/GenericParameterProxy.cs +++ b/src/coreclr/tools/Common/Compiler/Dataflow/GenericParameterProxy.cs @@ -15,6 +15,17 @@ internal readonly partial struct GenericParameterProxy internal partial bool HasDefaultConstructorConstraint() => GenericParameter.HasDefaultConstructorConstraint; + internal partial bool HasEnumConstraint() + { + foreach (TypeDesc constraint in GenericParameter.TypeConstraints) + { + if (constraint.IsWellKnownType(Internal.TypeSystem.WellKnownType.Enum)) + return true; + } + + return false; + } + public readonly GenericParameterDesc GenericParameter; public override string ToString() => GenericParameter.ToString(); diff --git a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericParameterProxy.cs b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericParameterProxy.cs index 6418e85b18575f..b51c2c6ecce019 100644 --- a/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericParameterProxy.cs +++ b/src/tools/illink/src/ILLink.RoslynAnalyzer/TrimAnalysis/GenericParameterProxy.cs @@ -14,6 +14,16 @@ internal partial bool HasDefaultConstructorConstraint () => TypeParameterSymbol.HasValueTypeConstraint | TypeParameterSymbol.HasUnmanagedTypeConstraint; + internal partial bool HasEnumConstraint () + { + foreach (ITypeSymbol constraintType in TypeParameterSymbol.ConstraintTypes) { + if (constraintType.SpecialType == SpecialType.System_Enum) + return true; + } + + return false; + } + public readonly ITypeParameterSymbol TypeParameterSymbol; public override string ToString () => TypeParameterSymbol.ToString (); diff --git a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs index 50166c803be30c..dd8e5891c37bce 100644 --- a/src/tools/illink/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs +++ b/src/tools/illink/src/ILLink.Shared/TrimAnalysis/RequireDynamicallyAccessedMembersAction.cs @@ -28,6 +28,10 @@ public void Invoke (in MultiValue value, ValueWithDynamicallyAccessedMembers tar && uniqueValue is GenericParameterValue genericParam && genericParam.GenericParameter.HasDefaultConstructorConstraint ()) { // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor + } else if (targetValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.PublicFields + && uniqueValue is GenericParameterValue maybeEnumConstrainedGenericParam + && maybeEnumConstrainedGenericParam.GenericParameter.HasEnumConstraint ()) { + // We allow a System.Enum constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicFields } else if (uniqueValue is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { if (uniqueValue is NullableValueWithDynamicallyAccessedMembers nullableValue) { MarkTypeForDynamicallyAccessedMembers (nullableValue.NullableType, nullableValue.DynamicallyAccessedMemberTypes); diff --git a/src/tools/illink/src/ILLink.Shared/TypeSystemProxy/GenericParameterProxy.cs b/src/tools/illink/src/ILLink.Shared/TypeSystemProxy/GenericParameterProxy.cs index 09ddbc4ae0521e..1e9e21c24f0fa4 100644 --- a/src/tools/illink/src/ILLink.Shared/TypeSystemProxy/GenericParameterProxy.cs +++ b/src/tools/illink/src/ILLink.Shared/TypeSystemProxy/GenericParameterProxy.cs @@ -9,5 +9,7 @@ namespace ILLink.Shared.TypeSystemProxy internal readonly partial struct GenericParameterProxy { internal partial bool HasDefaultConstructorConstraint (); + + internal partial bool HasEnumConstraint (); } } diff --git a/src/tools/illink/src/linker/Linker.Dataflow/GenericParameterProxy.cs b/src/tools/illink/src/linker/Linker.Dataflow/GenericParameterProxy.cs index 1c92df4559c5cc..5eb73751b4c921 100644 --- a/src/tools/illink/src/linker/Linker.Dataflow/GenericParameterProxy.cs +++ b/src/tools/illink/src/linker/Linker.Dataflow/GenericParameterProxy.cs @@ -13,6 +13,18 @@ internal readonly partial struct GenericParameterProxy internal partial bool HasDefaultConstructorConstraint () => GenericParameter.HasDefaultConstructorConstraint; + internal partial bool HasEnumConstraint () + { + if (GenericParameter.HasConstraints) { + foreach (GenericParameterConstraint? constraint in GenericParameter.Constraints) { + if (constraint.ConstraintType.Name == "Enum" && constraint.ConstraintType.Namespace == "System") + return true; + } + } + + return false; + } + public readonly GenericParameter GenericParameter; public override string ToString () => GenericParameter.ToString (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs index fb4088a0e27f41..e7b96db55e6893 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/GenericParameterDataFlow.cs @@ -37,6 +37,8 @@ public static void Main () TestStructConstraintSatisfiesParameterlessConstructor (); TestUnmanagedConstraintSatisfiesParameterlessConstructor (); + TestEnumConstraintSatisfiesPublicFields (); + TestGenericParameterFlowsToField (); TestGenericParameterFlowsToReturnValue (); @@ -798,6 +800,11 @@ static void TestUnmanagedConstraintSatisfiesParameterlessConstructor () where { } + static void TestEnumConstraintSatisfiesPublicFields () where T : Enum + { + typeof (T).RequiresPublicFields (); + } + // Warn about calls to static methods: [ExpectedWarning ("IL2026", "TypeRequiresPublicFields", "RUCTest()", "message")] [ExpectedWarning ("IL2026", "RUCMethodRequiresPublicMethods", "message")]