-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid Attribute.GetCustomAttributes() returning null for open generic type #65237
Conversation
Tagging subscribers to this area: @dotnet/area-system-reflection |
src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
Outdated
Show resolved
Hide resolved
src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeCustomAttributeData.cs
Show resolved
Hide resolved
Previously, this test asserted that GetCustomAttributes on an open generic type would return null. Now we return empty instead.
Could you please also fix it for NativeAOT here: https://github.com/dotnet/runtime/blob/main/src/coreclr/nativeaot/System.Private.CoreLib/src/System/Reflection/Attribute.CoreRT.cs#L149 |
@jkotas is your proposal to return Also, do you know if there is test coverage that should be updated for this? |
src/coreclr/System.Private.CoreLib/src/System/Attribute.CoreCLR.cs
Outdated
Show resolved
Hide resolved
I think it needs to return Attribute array of the right size.
It would be nice to use non-exception based control flow, same as CoreCLR.
We are working to get the existing tests enabled for NativeAOT. No need to add special test coverage. |
Enable open generic attribute type requests for parameters, break out larger tests into separate cases.
public static void GetCustomAttributesBehaviorMemberInfoReturnsNull() | ||
{ | ||
FieldInfo field = new CustomFieldInfo(null!); | ||
if (PlatformDetection.IsMonoRuntime) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we fix Mono to return null in this corner case as well to avoid this condition?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jkotas I'll defer to your decision but I'm not sure it makes sense. Looking through the code it seems like null vs throw in both CoreCLR and Mono are going to be inconsistent depending on the member type and other factors (e. g. whether inherit is true or false). It would take quite a bit more test coverage and probably quite a few changes to get to a consistent state across all the different call flows.
Another option would just be to stop testing this case, since it is so niche so perhaps undefined behavior is fine.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option would just be to stop testing this case, since it is so niche so perhaps undefined behavior is fine.
I agree. We either care about this corner case and the behavior should be consistent across runtimes; or we do not care about this corner case and we should not be testing it at all.
return (object[])Array.CreateInstance(elementType, elementCount); | ||
return (object[])Array.CreateInstance(caType, elementCount); | ||
|
||
T[] CreateArray<T>() => elementCount == 0 ? Array.Empty<T>() : new T[elementCount]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Non-static) local methods with captured arguments are inefficient. The compiler will create display type to pass the captured locals around that comes with a lot of ceremony.
I think I would inline it. The local method is not worth it. Also, I think it may be worth it to check for Attribute and use the faster non-reflection based path for it. Something like:
bool useAttributeArray = false;
bool useObjectArray = false;
if (caType == typeof(Attribute))
{
useAttributeArray = true;
}
else if (caType.IsValueType)
{
useObjectArray = true;
}
else if (caType.ContainsGenericParameters)
{
if (caType.IsSubclassOf(typeof(Attribute)))
{
useAttributeArray = true;
}
else
{
useObjectArray = true;
}
}
if (useAttributeArray) (elementCount != 0) ? new Attribute[elementCount] : Array.Empty<Attribute>();
if (useObjectArray) (elementCount != 0) ? new object[elementCount] : Array.Empty<object>();
return (elementCount != 0) ? (object[])Array.CreateInstance(caType, elementCount) : caType.GetEmptyArray();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM otherwise. Thank you!
Looks like Mono is crashing on the newly added tests even though they are disabled:
|
if (attributeType.UnderlyingSystemType is not RuntimeType attributeRuntimeType) | ||
throw new ArgumentException(SR.Arg_MustBeType, nameof(attributeType)); | ||
|
||
if (MdToken.IsNullToken(m_tkParamDef)) | ||
return CustomAttribute.CreateAttributeArrayHelper(attributeRuntimeType, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This change was required to prevent a failure in Microsoft.CSharp.RuntimeBinder.Tests.AccessTests.PublicMethod().
I feel a bit weird exposing the CreateAttributeArrayHelper method as internal, but this is really the exact logic we want here. It makes this branch consistent with other GetCustomAttribute methods which all allow the consumer to cast the array to the requested attribute type.
Moving this after the argument check presumably has some slight perf cost but ultimately feels more correct (and unless we change CreateAttributeArrayHelper to take Type
instead of RuntimeType
we need the check to happen first regardless).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok. Do we need a test to directly verify the behavior of ParamerInfo.GetCustomAttributes for this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added!
@jkotas @buyaa-n From some experimentation, it seems that this is caused by adding the generic assembly attribute. Any thoughts on how best to address this? I tried putting the assembly/module generic attributes behind |
I do not think that it makes sense to keep pilling workarounds for Mono bugs. @SamMonoRT @lambdageek Could you please take a look and suggest fix for the Mono crash that the CI is hitting? |
…for assembly and module into CoreCLR-only tests.
I see some infrastructure related failures (the logs that talk about deadlettered queues). I also see number of failures related to the change in the PR:
or
|
@@ -283,27 +283,6 @@ private static void GenericAttributesTestHelper<TGenericParameter>(Func<Type, At | |||
Assert.Equal(1, closedGenericAttributes.Length); | |||
Assert.Equal(typeof(GenericAttribute<TGenericParameter>[]), closedGenericAttributes.GetType()); | |||
} | |||
|
|||
[Fact] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just a test, or are you actually removing tests because they fail on Mono?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the discussion here it seemed like these weren't worth it given the failures on Mono. They're all tests I added in earlier commits, not existing tests.
Many of the remaining errors from the checks are like this one Example trace:
Any thoughts on where to start looking? |
If the failure is on Windows 7 x86, it is most likely #65890 (ie not related to your changes). |
It seems this is the only blocker for this PR, @jkotas should we open an issue for this and unblock the PR? Seems @madelson commented out the tests that causing mono failure:
|
Fine with me. |
// Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<long>)).GetEnumerator().MoveNext()); | ||
// Assert(CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<long>)).GetEnumerator().MoveNext()); | ||
// Assert(!CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); | ||
// Assert(!CustomAttributeExtensions.GetCustomAttributes(module, typeof(SingleAttribute<>)).GetEnumerator().MoveNext()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we include these tests within the above mulitilne comment? Seems related to dotnet/msbuild#6734
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't sure if it was related and wanted to ask about that. It's possible that these tests could be brought back.
Do you know what the preferred approach is for iterating on this particular test locally? [https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/coreclr/testing.md#build-individual-test](this page) suggests that I should run
dotnet.cmd build c:\dev\runtime\src\tests\reflection\GenericAttribute\GenericAttributeMetadata.ilproj -c Release
but that just builds a dll and doesn't seem to actually invoke this main method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry idk how to run them locally, @davidwrighton @jkotas might help with that, probably it is not ready to be brought back
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The instructions for running these tests are at https://github.com/dotnet/runtime/blob/main/docs/workflow/testing/coreclr/windows-test-instructions.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this @madelson!
An issue created for mono failure, CI failure looks related to this issue and unrelated to this change => merging. |
…for open generic type (dotnet#65237)" (dotnet#66508)" This reverts commit f99ba2e.
… type * Revert "Revert "Avoid Attribute.GetCustomAttributes() returning null for open generic type (#65237)" (#66508)" This reverts commit f99ba2e. * Make DynamicMethod.GetCustomAttributes() return well-typed arrays. This change makes DynamicMethod.GetCustomAttributes() compatible with Attribute.GetCustomAttributes().
… type * Revert "Revert "Avoid Attribute.GetCustomAttributes() returning null for open generic type (dotnet#65237)" (dotnet#66508)" This reverts commit f99ba2e. * Make DynamicMethod.GetCustomAttributes() return well-typed arrays. This change makes DynamicMethod.GetCustomAttributes() compatible with Attribute.GetCustomAttributes().
Fix #64335