Skip to content
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

Generate custom attributes from metadata instead of type system #100763

Merged
merged 8 commits into from
Apr 16, 2024

Conversation

MichalStrehovsky
Copy link
Member

@MichalStrehovsky MichalStrehovsky commented Apr 8, 2024

Fixes #100688

Fixes a bit more than just what was reported (see the test). We were losing type information in attribute.DecodeValue and could no longer distinguish between new object[] { SomeEnum.Val } and new SomeEnum[] { SomeEnum.Val }.

The fix required a complete rewrite of attribute emission using the more low level API.

Cc @dotnet/ilc-contrib

Copy link
Contributor

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Member Author

/azp run runtime-nativeaot-outerloop

Copy link

Azure Pipelines successfully started running 1 pipeline(s).

@MichalStrehovsky
Copy link
Member Author

@dotnet/ilc-contrib could someone have a look? This is switching custom attribute metadata generation from attribute.DecodeValue convenience decoder provided by S.R.Metadata to decoding the CA blob manually. The convenience decoder loses fidelity and we're not able to distinguish between object[] { ... } and SomeEnum[] { ... } being assigned to a location typed as System.Object.

The changes to *Gen.cs files are autogenerated because I touched SchemaDef.cs (the native metadata schema).

Copy link
Member

@agocke agocke left a comment

Choose a reason for hiding this comment

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

LGTM, minor questions

Ecma.SerializationTypeCode.Single => new ConstantSingleValue { Value = valueReader.ReadSingle() },
Ecma.SerializationTypeCode.Double => new ConstantDoubleValue { Value = valueReader.ReadDouble() },
Ecma.SerializationTypeCode.SZArray => HandleCustomAttributeConstantArray(module, valueReader.ReadSerializationTypeCode(), ref valueReader),
_ => throw new System.Exception()
Copy link
Member

Choose a reason for hiding this comment

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

Cts.ThrowHelper.ThrowBadImageFormatException or throw new UnreachableException() ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Cts.ThrowHelper.ThrowBadImageFormatException is not a throw for C#, so it won't work.

Any opinion about simply disabling warning CS8509 ILCompiler-project-wide?

We keep having to work around it by adding artificial throws, or worse, by adding _ => for something that could be one concrete value (SomeFoo =>), but the right-hand side happens to throw for all the other possibilities, so it's as good as an extra _ => throw new Blah() line.

Copy link
Member

Choose a reason for hiding this comment

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

adding _ => for something that could be one concrete value (SomeFoo =>)

The compiler is going to add throw for cases that are not covered by the switch. The difference here is whether the code is explicitly handling it with a throw or whether you let the compiler to generate the throw implicitly. Many of the compiler warnings and our own coding conventions rules force you to write more verbose and explicit code, so I see the CS8509 warning as just another one of those.

I think we should throw BIF for invalid input files when we have a choice. If these default switch cases are reachable for invalid input (it is not obvious to me whether it is the case), we may want to add Cts.ThrowHelper.CreateBadImageFormatException and change them to throw Cts.ThrowHelper.CreateBadImageFormatException().

Copy link
Member Author

Choose a reason for hiding this comment

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

The thing is that I would be fine with the SwitchExpressionException the compiler throws. Anything that kills the compilation is fine by me.

I agree that our coding standards err on the more verbose side, so throw new UnrechableException sounds like the best fix.

Throwing a Cts.BadImageException wouldn't make a difference here. This spot is non-recoverable; we're in the middle of generating metadata and can't change our mind about it. We already inspected the attribute for dependencies during dependency analysis so at this point we know it doesn't refer to something that doesn't exist (we'd skip it if it does). If it has some other structural issue we didn't detect during dependency analysis, I'm fine with a compiler crash. We try to be resilient for invalid inputs that could be the result of bad assembly version management, but we don't try to be resilient against source-to-IL compiler bugs. Too much resilience makes it difficult to surface our own bugs because this would also paper over those.

Copy link
Member

Choose a reason for hiding this comment

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

We try to be resilient for invalid inputs that could be the result of bad assembly version management,

Parsing of custom attribute blobs can hit BadImageFormat exception due to user error. If the custom attribute references enum from a different assembly and the enum underlying type changes due to user error, parsing of the custom attribute blob will get lost and it will likely end up hitting one of these unreachable cases.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's a good point but it still needs to go to the other spot that parses it first (at the time we analyze dependencies). #101164 has the fix.

Ecma.SerializationTypeCode.UInt64 => new ConstantUInt64Array { Value = GetCustomAttributeConstantArrayElements<ulong>(ref valueReader, count) },
Ecma.SerializationTypeCode.Single => new ConstantSingleArray { Value = GetCustomAttributeConstantArrayElements<float>(ref valueReader, count) },
Ecma.SerializationTypeCode.Double => new ConstantDoubleArray { Value = GetCustomAttributeConstantArrayElements<double>(ref valueReader, count) },
_ => throw new System.Exception()
Copy link
Member

Choose a reason for hiding this comment

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

Dtto

@MichalStrehovsky MichalStrehovsky merged commit 1a820cb into dotnet:main Apr 16, 2024
124 of 127 checks passed
matouskozak pushed a commit to matouskozak/runtime that referenced this pull request Apr 30, 2024
…et#100763)

Fixes dotnet#100688

Fixes a bit more than just what was reported (see the test). We were losing type information in `attribute.DecodeValue` and could no longer distinguish between `new object[] { SomeEnum.Val }` and `new SomeEnum[] { SomeEnum.Val }`.

The fix required a complete rewrite of attribute emission using the more low level API.

Cc @dotnet/ilc-contrib
@github-actions github-actions bot locked and limited conversation to collaborators May 17, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Attributes taking object as a parameter have unexpected behavior in AOT
3 participants