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

Implement support for UnsafeAccessor in the trimmer #88268

Merged
merged 53 commits into from
Jul 18, 2023

Conversation

vitek-karas
Copy link
Member

The core of the change is that UnsafeAccessor creates a code dependency from the accessor method to the target specified by the attribute. The trimmer needs to follow this dependency and preserve the target. Additionally, because the trimmer operates at the IL level, it needs to make sure that the target will keep its name and some other properties intact (so that the runtime implementation of the UnsafeAccessor can still work).

Implementation choices:

  • The trimmer will mark the target as "accessed via reflection", this is a simple way to make sure that name and other properties about the target are preserved. This could be optimized in the future, but the savings are probably not that interesting.
  • The implementation ran into a problem when trying to precisely match the signature overload resolution. Due to Cecil issues and the fact that Cecil's resolution algorithm is not extensible, it was not possible to match the runtime's behavior without adding lot more complexity (currently it seems we would have to reimplement method resolution in the trimmer). So, to simplify the implementation, trimmer will mark all methods of a given name. This means it will mark more than necessary. This is fixable by adding more complexity to the code base if we think there's a good reason for it.
  • Due to the above choices, there are some behavioral differences:
    • Trimmer will warn if the target has data flow annotations, always. There's no way to "fix" this in the code without a suppression.
    • Trimmer will produce different warning codes even if there is a true data flow mismatch - this is because it treats the access as "reflection access" which produces different warning codes from direct access.
    • These differences are fixable, but it was not deemed necessary right now.
  • We decided that analyzer will not react to the attribute at all, and thus will not produce any diagnostics around it.

The guiding reason to keep the implementation simple is that we don't expect the unsafe accessor to be used by developers directly, instead we assume that vast majority of its usages will be from source generators. So, developer UX is not as important.

Test changes:

  • Adds directed tests for the marking behavior
  • Adds tests to verify that Requires* attributes behave correctly
  • Adds tests to verify that data flow annotations behave as expected (described above)
  • The tests are effectively a second validation of the NativeAOT implementation as they cover NativeAOT as well.

Fixes in CoreCLR/NativeAOT:
This change fixes one bug in the CoreCLR/NativeAOT implementation, unsafe accessor on a instance method of a value type must use "by-ref" parameter for the this parameter. Without the "by-ref" the accessor is considered invalid and will throw.
This change also adds some tests to the CoreCLR/NativeAOT test suite.

Part of #86161.
Related to #86438.
Feature design in #81741.

@vitek-karas vitek-karas added the area-Tools-ILLink .NET linker development as well as trimming analyzers label Jun 30, 2023
@vitek-karas vitek-karas added this to the 8.0.0 milestone Jun 30, 2023
@vitek-karas vitek-karas self-assigned this Jun 30, 2023
@ghost
Copy link

ghost commented Jun 30, 2023

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

Issue Details

The core of the change is that UnsafeAccessor creates a code dependency from the accessor method to the target specified by the attribute. The trimmer needs to follow this dependency and preserve the target. Additionally, because the trimmer operates at the IL level, it needs to make sure that the target will keep its name and some other properties intact (so that the runtime implementation of the UnsafeAccessor can still work).

Implementation choices:

  • The trimmer will mark the target as "accessed via reflection", this is a simple way to make sure that name and other properties about the target are preserved. This could be optimized in the future, but the savings are probably not that interesting.
  • The implementation ran into a problem when trying to precisely match the signature overload resolution. Due to Cecil issues and the fact that Cecil's resolution algorithm is not extensible, it was not possible to match the runtime's behavior without adding lot more complexity (currently it seems we would have to reimplement method resolution in the trimmer). So, to simplify the implementation, trimmer will mark all methods of a given name. This means it will mark more than necessary. This is fixable by adding more complexity to the code base if we think there's a good reason for it.
  • Due to the above choices, there are some behavioral differences:
    • Trimmer will warn if the target has data flow annotations, always. There's no way to "fix" this in the code without a suppression.
    • Trimmer will produce different warning codes even if there is a true data flow mismatch - this is because it treats the access as "reflection access" which produces different warning codes from direct access.
    • These differences are fixable, but it was not deemed necessary right now.
  • We decided that analyzer will not react to the attribute at all, and thus will not produce any diagnostics around it.

The guiding reason to keep the implementation simple is that we don't expect the unsafe accessor to be used by developers directly, instead we assume that vast majority of its usages will be from source generators. So, developer UX is not as important.

Test changes:

  • Adds directed tests for the marking behavior
  • Adds tests to verify that Requires* attributes behave correctly
  • Adds tests to verify that data flow annotations behave as expected (described above)
  • The tests are effectively a second validation of the NativeAOT implementation as they cover NativeAOT as well.

Fixes in CoreCLR/NativeAOT:
This change fixes one bug in the CoreCLR/NativeAOT implementation, unsafe accessor on a instance method of a value type must use "by-ref" parameter for the this parameter. Without the "by-ref" the accessor is considered invalid and will throw.
This change also adds some tests to the CoreCLR/NativeAOT test suite.

Part of #86161.
Related to #86438.
Feature design in #81741.

Author: vitek-karas
Assignees: vitek-karas
Labels:

area-Tools-ILLink

Milestone: 8.0.0

Copy link
Member

@AaronRobinsonMSFT AaronRobinsonMSFT left a comment

Choose a reason for hiding this comment

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

NativeAOT/VM changes and new runtime tests LGTM.

Thank you!

@lambdageek
Copy link
Member

@vitek-karas I resolved the merge conflict

Copy link
Member

@sbomer sbomer left a comment

Choose a reason for hiding this comment

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

ILLink changes LGTM!

// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

// Copy of the enum from CoreLib - once that is merged this should be deleted.
Copy link
Member

Choose a reason for hiding this comment

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

Based on the build failures I think this can be removed now

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes - I'm testing this locally - there are also some merge conflicts in the runtime test. I need to test it on all 3 runtimes now :-)

@vitek-karas
Copy link
Member Author

Test failures are unrelated.

@vitek-karas vitek-karas merged commit ac3979a into dotnet:main Jul 18, 2023
@vitek-karas vitek-karas deleted the TrimmerUnsafeAccessor branch July 18, 2023 07:33
@ghost ghost locked as resolved and limited conversation to collaborators Aug 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-Tools-ILLink .NET linker development as well as trimming analyzers
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants