Skip to content

Commit 88fb966

Browse files
authored
Support Requires on type in NativeAot (#68978)
Support Requires on type in NativeAot - Adds new overload for MessageOrigin creation that uses MethodIL and an offset as parameters. Eventually, this will be used to produce warnings - Share methods used in linker/analyzer for asking questions about members being in RequiresScope, members Requiring and Requires warning generation. These methods include handling of Requires on a type. - The ReflectionMethodBodyScanner on field access now verifies for requires on type, since implicitly can call the static constructor - Updated the mismatch attribute rules to take into account Requires on type - Added support for RequiresAssemblyFilesAttribute, now the code checks for RAF and warns about it - Added support to check and retrieve attributes in a property - Added overloads to IsInRequiresScope that verify associated properties - Add RequiresOnStaticConstructor id
1 parent d837ea4 commit 88fb966

File tree

7 files changed

+261
-94
lines changed

7 files changed

+261
-94
lines changed

src/coreclr/tools/Common/Compiler/Logging/MessageOrigin.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections.Generic;
56
using System.Text;
6-
7+
using Internal.IL;
78
using Internal.TypeSystem;
89

910
namespace ILCompiler.Logging
@@ -35,6 +36,29 @@ public MessageOrigin(TypeSystemEntity memberDefinition, string fileName = null,
3536
SourceColumn = sourceColumn;
3637
}
3738

39+
public MessageOrigin(MethodIL origin, int ilOffset)
40+
{
41+
string document = null;
42+
int? lineNumber = null;
43+
44+
IEnumerable<ILSequencePoint> sequencePoints = origin.GetDebugInfo()?.GetSequencePoints();
45+
if (sequencePoints != null)
46+
{
47+
foreach (var sequencePoint in sequencePoints)
48+
{
49+
if (sequencePoint.Offset <= ilOffset)
50+
{
51+
document = sequencePoint.Document;
52+
lineNumber = sequencePoint.LineNumber;
53+
}
54+
}
55+
}
56+
FileName = document;
57+
MemberDefinition = origin.OwningMethod;
58+
SourceLine = lineNumber;
59+
SourceColumn = null;
60+
}
61+
3862
public override string ToString()
3963
{
4064
if (FileName == null)

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs

Lines changed: 133 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
5+
using System.Diagnostics;
6+
using System.Diagnostics.CodeAnalysis;
7+
using System.Reflection.Metadata;
8+
using ILCompiler.Logging;
9+
using ILLink.Shared;
410
using Internal.TypeSystem;
511
using Internal.TypeSystem.Ecma;
612

@@ -44,40 +50,147 @@ internal static string GetGenericParameterDeclaringMemberDisplayName(GenericPara
4450
return ((TypeDesc)parent).GetDisplayName();
4551
}
4652

47-
internal static string GetRequiresAttributeMessage(MethodDesc method, string requiresAttributeName)
53+
internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string requiresAttributeName, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
4854
{
49-
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
50-
if (ecmaMethod == null)
51-
return null;
55+
attribute = default;
56+
CustomAttributeValue<TypeDesc>? decoded = default;
57+
switch (member)
58+
{
59+
case MethodDesc method:
60+
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
61+
if (ecmaMethod == null)
62+
return false;
63+
decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
64+
break;
65+
case MetadataType type:
66+
var ecmaType = type as EcmaType;
67+
if (ecmaType == null)
68+
return false;
69+
decoded = ecmaType.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
70+
break;
71+
case PropertyPseudoDesc property:
72+
decoded = property.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
73+
break;
74+
default:
75+
Debug.Fail("Trying to operate with unsupported TypeSystemEntity " + member.GetType().ToString());
76+
break;
77+
}
78+
if (!decoded.HasValue)
79+
return false;
80+
81+
attribute = decoded.Value;
82+
return true;
83+
}
5284

53-
var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
54-
if (decoded == null)
85+
public static CustomAttributeValue<TypeDesc>? GetDecodedCustomAttribute(this PropertyPseudoDesc prop, string attributeNamespace, string attributeName)
86+
{
87+
var ecmaType = prop.OwningType as EcmaType;
88+
var metadataReader = ecmaType.MetadataReader;
89+
90+
var attributeHandle = metadataReader.GetCustomAttributeHandle(prop.GetCustomAttributes,
91+
attributeNamespace, attributeName);
92+
93+
if (attributeHandle.IsNil)
5594
return null;
5695

57-
var decodedValue = decoded.Value;
96+
return metadataReader.GetCustomAttribute(attributeHandle).DecodeValue(new CustomAttributeTypeProvider(ecmaType.EcmaModule));
97+
}
5898

59-
if (decodedValue.FixedArguments.Length != 0)
60-
return (string)decodedValue.FixedArguments[0].Value;
99+
internal static string GetRequiresAttributeMessage(CustomAttributeValue<TypeDesc> attribute)
100+
{
101+
if (attribute.FixedArguments.Length != 0)
102+
return (string)attribute.FixedArguments[0].Value;
61103

62104
return null;
63105
}
64106

65-
internal static string GetRequiresAttributeUrl(MethodDesc method, string requiresAttributeName)
107+
internal static string GetRequiresAttributeUrl(CustomAttributeValue<TypeDesc> attribute)
66108
{
67-
var ecmaMethod = method.GetTypicalMethodDefinition() as EcmaMethod;
68-
if (ecmaMethod == null)
69-
return null;
109+
if (attribute.NamedArguments.Length != 0 && attribute.NamedArguments[0].Name == "Url")
110+
return (string)attribute.NamedArguments[0].Value;
70111

71-
var decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName);
72-
if (decoded == null)
73-
return null;
112+
return null;
113+
}
114+
115+
/// <summary>
116+
/// Determines if method is within a declared Requires scope - this typically means that trim analysis
117+
/// warnings should be suppressed in such a method.
118+
/// </summary>
119+
/// <remarks>Unlike <see cref="DoesMemberRequire(TypeSystemEntity, string, out CustomAttributeValue{TypeDesc}?)"/>
120+
/// if a declaring type has Requires, all methods in that type are considered "in scope" of that Requires. So this includes also
121+
/// instance methods (not just statics and .ctors).</remarks>
122+
internal static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute) =>
123+
method.IsInRequiresScope(requiresAttribute, true);
124+
125+
/// <summary>
126+
/// True if member of a call is considered to be annotated with the Requires... attribute.
127+
/// Doesn't check the associated symbol for overrides and virtual methods because we should warn on mismatched between the property AND the accessors
128+
/// </summary>
129+
/// <param name="method">
130+
/// MethodDesc that is either an overriding member or an overriden/virtual member
131+
/// </param>
132+
internal static bool IsOverrideInRequiresScope(this MethodDesc method, string requiresAttribute) =>
133+
method.IsInRequiresScope(requiresAttribute, false);
134+
135+
private static bool IsInRequiresScope(this MethodDesc method, string requiresAttribute, bool checkAssociatedSymbol)
136+
{
137+
if (method.HasCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttribute) && !method.IsStaticConstructor)
138+
return true;
74139

75-
var decodedValue = decoded.Value;
140+
if (method.OwningType is TypeDesc type && TryGetRequiresAttribute(type, requiresAttribute, out _))
141+
return true;
76142

77-
if (decodedValue.NamedArguments.Length != 0 && decodedValue.NamedArguments[0].Name == "Url")
78-
return (string)decodedValue.NamedArguments[0].Value;
143+
if (checkAssociatedSymbol && method.GetPropertyForAccessor() is PropertyPseudoDesc property && TryGetRequiresAttribute(property, requiresAttribute, out _))
144+
return true;
79145

80-
return null;
146+
return false;
147+
}
148+
149+
internal static bool DoesMethodRequire(this MethodDesc method, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
150+
{
151+
attribute = null;
152+
if (method.IsStaticConstructor)
153+
return false;
154+
155+
if (TryGetRequiresAttribute(method, requiresAttribute, out attribute))
156+
return true;
157+
158+
if ((method.Signature.IsStatic || method.IsConstructor) && method.OwningType is TypeDesc owningType &&
159+
!owningType.IsArray && TryGetRequiresAttribute(owningType, requiresAttribute, out attribute))
160+
return true;
161+
162+
return false;
163+
}
164+
165+
internal static bool DoesFieldRequire(this FieldDesc field, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
166+
{
167+
if (!field.IsStatic || field.OwningType is not TypeDesc owningType || owningType.IsArray)
168+
{
169+
attribute = null;
170+
return false;
171+
}
172+
173+
return TryGetRequiresAttribute(field.OwningType, requiresAttribute, out attribute);
174+
}
175+
176+
internal static bool DoesPropertyRequire(this PropertyPseudoDesc property, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute) =>
177+
TryGetRequiresAttribute(property, requiresAttribute, out attribute);
178+
179+
/// <summary>
180+
/// Determines if member requires (and thus any usage of such method should be warned about).
181+
/// </summary>
182+
/// <remarks>Unlike <see cref="IsInRequiresScope(MethodDesc, string)"/> only static methods
183+
/// and .ctors are reported as requires when the declaring type has Requires on it.</remarks>
184+
internal static bool DoesMemberRequire(this TypeSystemEntity member, string requiresAttribute, [NotNullWhen(returnValue: true)] out CustomAttributeValue<TypeDesc>? attribute)
185+
{
186+
attribute = null;
187+
return member switch
188+
{
189+
MethodDesc method => DoesMethodRequire(method, requiresAttribute, out attribute),
190+
FieldDesc field => DoesFieldRequire(field, requiresAttribute, out attribute),
191+
PropertyPseudoDesc property => DoesPropertyRequire(property, requiresAttribute, out attribute),
192+
_ => false
193+
};
81194
}
82195
}
83196
}

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/EcmaExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,8 @@ public static PropertyPseudoDesc GetProperty(this MetadataType mdType, string na
6666

6767
public static PropertyPseudoDesc GetPropertyForAccessor(this MethodDesc accessor)
6868
{
69-
var ecmaAccessor = (EcmaMethod)accessor.GetTypicalMethodDefinition();
69+
if (accessor.GetTypicalMethodDefinition() is not EcmaMethod ecmaAccessor)
70+
return null;
7071
var type = (EcmaType)ecmaAccessor.OwningType;
7172
var reader = type.MetadataReader;
7273
var module = type.EcmaModule;

0 commit comments

Comments
 (0)