Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Enable generic attributes #9189

Merged
merged 20 commits into from
Apr 17, 2018
Merged

Enable generic attributes #9189

merged 20 commits into from
Apr 17, 2018

Conversation

AviAvni
Copy link

@AviAvni AviAvni commented Jan 28, 2017

@@ -695,10 +694,6 @@ FCIMPL2(Object*, RuntimeTypeHandle::CreateCaInstance, ReflectClassBaseObject* pC
PRECONDITION(
(!pCtor && gc.refCaType->GetType().IsValueType() && !gc.refCaType->GetType().GetMethodTable()->HasDefaultConstructor()) ||
(pCtor == gc.refCaType->GetType().GetMethodTable()->GetDefaultConstructor()));

// If we relax this, we need to insure custom attributes construct properly for Nullable<T>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this work for Nullable types that this comment is talking about?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you mean that the generic argument is nullable so I tried with int? and it works great too

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@jkotas Do I need to do more things to get this PR merged?

@@ -671,7 +671,6 @@ FCIMPL2(Object*, RuntimeTypeHandle::CreateCaInstance, ReflectClassBaseObject* pC
CONTRACTL {
FCALL_CHECK;
PRECONDITION(CheckPointer(pCaTypeUNSAFE));
PRECONDITION(!pCaTypeUNSAFE->GetType().IsGenericVariable());
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this precondition is still valid. This is guarding against something like this:

 class G<T>
 {
      [T]   // <-- still disallowed, correct?
      class Inner {}
 }

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atsushikan You right sorry I revert this delete and validated it's still work

@jkotas
Copy link
Member

jkotas commented Jan 30, 2017

@atsushikan Could you please take a look at this?

There will need to be tests added or updated in corefx.

@ghost
Copy link

ghost commented Jan 30, 2017

What I see here LGTM but the framework has a bunch of overlapping custom-attribute fetching apis and the code isn't as factored for sharing as it could be.

Attribute.GetCustomAttribute(*), IsDefined, etc.
CustomAttributeExtensions.GetCustomAttributes() (mostly wraps Attribute, but should check.)
ICustomAttributeProvider.GetCustomAttributes() (implemented on MemberInfo, Assembly, Module, etc. - this is largely its own codepath, I recall.)
CustomAttributeData.GetCustomAttributes(),
MemberInfo.CustomAttributes properties (and on Assemblies, Modules)

Can you test some of these as well to make sure all our code paths have been updated?

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@atsushikan what is the best place to write this tests?
I'll check them
@jkotas do you mean the tests of System.Reflection.* on CoreFX repository I'll check them too

@ghost
Copy link

ghost commented Jan 30, 2017

System.Runtime\tests is where you'd add the tests, but this assumes that this new language feature is already enabled in the corefx toolset. If not, you should leave an issue open for writing tests once the feature is available.

For now, I just meant test it privately as you've been doing...

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@atsushikan I also made PR for Roslyn dotnet/roslyn#16814 so when this will merge hope also roslyn merge the PR

@jkotas
Copy link
Member

jkotas commented Jan 30, 2017

Should I be able to say myType.GetCustomAttribute(MyCustomAttribute<>) and expect it to work for any T?

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@jkotas I'll check this too

@ghost
Copy link

ghost commented Jan 30, 2017

Should I be able to say myType.GetCustomAttribute(MyCustomAttribute<>) and expect it to work for any T?

This sounds like appropriate material for an api proposal. It's a bit iffy - it conflates the concept of assignability with instantiation.

Note that this would be a breaking change if we don't do it in NS2.0 as in some instances (the GetCustomAttribute() apis off MemberInfo (the ones not "added" by CustomAttributeExtensions) don't do any validation on the passed in attribute type, so this kind of call today would return an empty array (as in "Yessir, none of the attributes on this member are assignable to MyCustomAttribute<>")

@MichalStrehovsky
Copy link
Member

How will this work for serialized custom attribute data? Suppose we have:

class FooAttribute<T> : Attribute
{
    public T Field;
}

Now, I would expect this to work:

[Foo<int>(Field = 123)]
class X { }

But this shouldn't:

[Foo<int?>(Field = 123)]
class Y { }

Will the class library fail appropriately? Will Roslyn be checking for this too?

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@MichalStrehovsky roslyn already check this

@MichalStrehovsky
Copy link
Member

@MichalStrehovsky roslyn already check this

But the runtime needs to check for this too. There could be a buggy compiler that emits such metadata blob. We need to make sure it fails as expected (and make sure we have test coverage for that).

It's possible the runtime already does handle it, but since this PR is removing some validation that would previously cover it, we need to ensure the new failure modes the removed validation opens us up to are covered as well.

@AviAvni
Copy link
Author

AviAvni commented Jan 30, 2017

@MichalStrehovsky I'm working on tests I'll add this scenario thanks

@AviAvni
Copy link
Author

AviAvni commented Jan 31, 2017

@atsushikan Please look at my tests https://gist.github.com/AviAvni/3ee62f4c93d33485db880cf01011e0b6

@MichalStrehovsky I tried to make tests that you mentioned by taking the IL from the tests exe with ildasm
https://gist.github.com/AviAvni/388b8a3023181bd78f9d56e85c2a4b31
and change it and compile back with ilasm but I need help what to change I tried to make line 138 value like 137 value not sure if this correct

@ghost
Copy link

ghost commented Jan 31, 2017

Be careful about distinguishing beween the instance member GetCustomAttributes() and the extension method CustomAttributes() - they're called using the same C# syntax but for reasons that escape me, aren't the same at all internally.

It's probably safest to write the tests so that the instance member version is always invoked through the ICustomAttributeProvider interface rather directly from the member, while the extension method is invoke without using the extension method syntax, i.e. rather than writing

memberInfo.GetCustomAttribute(typeof(D), inherit: true),

write

((ICustomAttributeProvider)memberInfo).GetCustomAttributes(typeof(D), inherit: true)

and

CustomAttributeExtensions.GetCustomAttribute(memberInfo, typeof(D), inherit: true).

That way, it's always clear which one you mean.

@AviAvni
Copy link
Author

AviAvni commented Jan 31, 2017

@atsushikan thanks I updated the gist
now I'm investigating why https://gist.github.com/AviAvni/3ee62f4c93d33485db880cf01011e0b6#file-genericattributetests-cs-L171 have different behavior

@AviAvni
Copy link
Author

AviAvni commented Jan 31, 2017

@atsushikan now the difference is in line https://gist.github.com/AviAvni/3ee62f4c93d33485db880cf01011e0b6#file-genericattributetests-cs-L187
and still don't understand why it's happend

@ghost
Copy link

ghost commented Jan 31, 2017

CustomAttributeExtensions.GetCustomAttributes() should never return null. If there are no matching attributes, it should return a 0-length enumeration.

@AviAvni AviAvni closed this Jan 31, 2017
@@ -0,0 +1,72 @@
using System;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please add license header?

@AviAvni
Copy link
Author

AviAvni commented Feb 4, 2018

@jkotas I added the license header and the missing spaces sorry

@jkotas
Copy link
Member

jkotas commented Feb 5, 2018

@AviAvni The change looks good to me now. Thanks!

We are too close to snapping 2.1 release and I do not think there is runway to verify this together with the matching C# changes. This will need to wait after 2.1.

AssertAny(b8, a => (a as MultiAttribute<bool?>)?.Value == null);

Assert(CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), false) == null);
Assert(CustomAttributeExtensions.GetCustomAttributes(programTypeInfo, typeof(MultiAttribute<>), true) == null);
Copy link

@ghost ghost Feb 7, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you pass in an open attribute type to this api (typeof(MultiAttribute<)), I think it should either:

  • Throw a NotSupportedException saying explicitly that the attribute type cannot be an open type (and for consistency, make Attribute.GetCustomAttributes work the same way) I think this is the preferable option as I can't think of a good use case for passing in an open type here.

or

  • Return a zero-length array of type Attribute[].

We shouldn't be returning null - there is no precedent for this api returning null.

Edit: Changed ArgumentException to NotSupportedException since that's the behavior you see today with the api.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atsushikan I implemented this on Attribute.GetCustomAttributes and with CustomAttributeExtensions api it throws NotSupportedException as expected
but it not work for ICustomAttributeProvider api is this api need also to throw?

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing in an open type as the attribute type should continue to throw NotSupportedException. See my last comment.

Edit: My bad - returning null is the NETFX compatible behavior. It was CoreRT that was incompatible.

@ghost
Copy link

ghost commented Feb 8, 2018 via email

@AviAvni
Copy link
Author

AviAvni commented Feb 8, 2018

@atsushikan I made the change you asked

@@ -459,6 +459,9 @@ public static Attribute[] GetCustomAttributes(MemberInfo element, Type type, boo
if (type == null)
throw new ArgumentNullException(nameof(type));

if (type.IsGenericTypeDefinition)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The api you want is Type.ContainsGenericParameters. This will catch open types such as G<int, T> in addition to G<>.

{
static int Main(string[] args)
{
new MultiAttribute<string>("a");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line looks like an accidental leftover.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry

@ghost
Copy link

ghost commented Feb 8, 2018

Ah - I just checked the behavior of Attribute.GetCA and CustomAttributeExtensions.GetCA on NETFX and it seems they do return null when passed in an open generic type. It was the CoreRT framework that was throwing NotSupportedException. As much as returning null feels wrong to me, we should stay compatible with NETFX. Your original version (11c28a8) is fine as is. I'll make the change to CoreRT so it matches.

Sorry about the bother.

@jkotas
Copy link
Member

jkotas commented Apr 16, 2018

@AviAvni Could you please resolve the conflict? This can be merged now.

@AviAvni
Copy link
Author

AviAvni commented Apr 17, 2018

@jkotas conflict resolved build is green

@jkotas
Copy link
Member

jkotas commented Apr 17, 2018

@dotnet-bot test Windows_NT x64 Checked corefx_baseline
@dotnet-bot test Ubuntu x64 Checked corefx_baseline

@AviAvni
Copy link
Author

AviAvni commented Apr 17, 2018

@jkotas build is green

@jkotas jkotas merged commit 1d406b1 into dotnet:master Apr 17, 2018
@jkotas
Copy link
Member

jkotas commented Apr 17, 2018

@AviAvni Thank you for your persistence in pushing this through!

@AviAvni
Copy link
Author

AviAvni commented Apr 17, 2018

@jkotas thank you and the team very much for helping me I'll follow if there is more things from me
If there are more things needed on this ping me

@AviAvni
Copy link
Author

AviAvni commented Jun 7, 2019

Hiii @jkotas I'm working on the Roslyn PR and testing with .net core sdk preview 6
I found that in case the generic attribute parameter is String and the ctor is not the default one then there is a bug that the attribute generic parameter that return from GetCustomAttribute is System.__Canon and not String I'll try to debug this on monday and check what is missing in the tests

@jkotas
Copy link
Member

jkotas commented Jun 7, 2019

@AviAvni Thank you! If you won't be able to get to this soon, could you please at least file an issue in CoreCLR repo so that we have it tracked?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
7 participants