Skip to content

Unsafe evolution: handle extern members#81786

Merged
jjonescz merged 3 commits into
dotnet:features/UnsafeEvolutionfrom
jjonescz:Unsafe-09-Extern
Jan 7, 2026
Merged

Unsafe evolution: handle extern members#81786
jjonescz merged 3 commits into
dotnet:features/UnsafeEvolutionfrom
jjonescz:Unsafe-09-Extern

Conversation

@jjonescz

Copy link
Copy Markdown
Member

Test plan: #81207

Follow up on #81581 (i.e., still only for methods and properties).

Implements this speclet section: https://github.com/dotnet/csharplang/blob/d4a7252e082927c9e6d6f1386d0bb46fa690db8b/proposals/unsafe-evolution.md#extern

@jjonescz jjonescz changed the title Make extern members caller-unsafe Unsafe evolution: handle extern members Dec 22, 2025
@jjonescz jjonescz marked this pull request as ready for review December 22, 2025 13:05
@jjonescz jjonescz requested a review from a team as a code owner December 22, 2025 13:05
@jjonescz jjonescz requested review from 333fred and jcouv December 22, 2025 13:05
@jcouv jcouv self-assigned this Dec 22, 2025
@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor
    private void ReportDiagnosticsIfUnsafeMemberAccess(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNode node)

Diagnostic bag is usually the last parameter. #Closed


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs:24 in 8569af4. [](commit_id = 8569af4, deletion_comment = False)

/// The member is explicitly marked as <see langword="extern"/> under the updated memory safety rules.
/// This can only appear for source symbols (after metadata roundtrip, this turns into <see cref="Explicit"/>).
/// </summary>
Extern,

@AlekseyTs AlekseyTs Dec 23, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Extern

In general, we prefer to treat source and metadata symbols uniformly. There are exceptions, but they usually happen when an information isn't preserved in metadata. In this case, however, it looks like the information is preserved "The member is explicitly marked", whether by unsafe or extern modifier. It is not clear to me why do we want to separate extern, this looks fragile and bug prone. I think explicit in source should get CallerUnsafeMode.Explicit value as it gets it when imported from metadata. #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

when an information isn't preserved in metadata

See the speclet section and the corresponding discussion about this. extern is not preserved in ref assemblies apparently.

It is not clear to me why do we want to separate extern

I guess I can merge the enum members (I originally separated them to have a different error message, but got rid of that since then for uniformity as you are saying).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

See the speclet section and the corresponding discussion about this. extern is not preserved in ref assemblies apparently.

I am not saying that extern members should be treated as safe. I am saying that the CallerUnsafeMode API should produce the same value from source and metadata.

@jjonescz jjonescz Dec 23, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Yes, I agree. That part was replying to "There are exceptions, but they usually happen when an information isn't preserved in metadata" which technically is the case here (extern is not preserved in metadata)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

which technically is the case here (extern is not preserved in metadata)

No, this is not the case. The information is preserved, there is an attribute on the member, it really doesn't matter what syntax triggered its application.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If there were no attribute, then the safety information wouldn't be preserved.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right, I will unify the Extern and Explicit members, thanks.

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Done with a quick glance over the changes (commit 1) #Closed

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

Please make sure we have tests that consume APIs through compilation references from a compilation that is using old rules and doesn't emit new attributes etc. The expectation is that the experience should match the experience of consuming metadata references produced from the same compilation. Retargeting scenarios are probably interesting too.

To clarify, "from a compilation that is using old rules" refers to the referenced compilation rather than to the consuming compilation. #Closed

@jjonescz

jjonescz commented Dec 23, 2025

Copy link
Copy Markdown
Member Author

consume APIs through compilation references

We have that. All the tests use common helpers that always test both image and compilation references.

Retargeting scenarios are probably interesting too.

I can look into that (or maybe I'll write it to test plan), thanks. #Resolved

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

We have that.

Could you confirm that the safety mode is different between compilations? Perhaps you could point me to an example. #Closed

@jjonescz

jjonescz commented Dec 23, 2025

Copy link
Copy Markdown
Member Author

Could you confirm that the safety mode is different between compilations? Perhaps you could point me to an example.

// When compiling the lib under legacy rules, extern members are not unsafe.

(This isn't using a common helper after all since it's sufficiently different from the common scenarios, but still verifies both types of references.) #Resolved

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

The scenario in referenced test doesn't look like one I have in mind. I am talking about scenarios where consumer uses new rules and the library uses old rules. It looks like the referenced test does the opposite. And such tests should not be limited to extern members. #Closed

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor

I guess my original comment was hard to "parse". I tried to clarify it #Closed

@AlekseyTs

AlekseyTs commented Dec 23, 2025

Copy link
Copy Markdown
Contributor
private void CompileAndVerify(

Overloading existing common APIs hurts readability of tests. Instead of overloading consider using distinct name #Closed


Refers to: src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs:23 in 8569af4. [](commit_id = 8569af4, deletion_comment = False)

ReadOnlySpan<string> additionalSources = default,
Verification verify = default)
Verification verify = default,
CallerUnsafeMode expectedUnsafeMode = CallerUnsafeMode.Explicit)

@AlekseyTs AlekseyTs Dec 23, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

CallerUnsafeMode expectedUnsafeMode = CallerUnsafeMode.Explicit

I think that new optional parameters hurt readability of tests, especially when the API is used a lot. No one remembers the defaults. #Closed

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I believe the default here makes sense, it should be rarely different.

@jjonescz

jjonescz commented Jan 5, 2026

Copy link
Copy Markdown
Member Author
private void CompileAndVerify(

This is a pre-existing helper and I'd rather not bloat the diff by renaming it here, I can do it in a follow up PR. Nevermind, I guess I can rename to help clarify the other comments too.


In reply to: 3687547136


Refers to: src/Compilers/CSharp/Test/CSharp15/UnsafeEvolutionTests.cs:23 in 8569af4. [](commit_id = 8569af4, deletion_comment = False)

@jjonescz

jjonescz commented Jan 5, 2026

Copy link
Copy Markdown
Member Author

I am talking about scenarios where consumer uses new rules and the library uses old rules. It looks like the referenced test does the opposite. And such tests should not be limited to extern members.

I think the example I posted does exactly that - the library uses old rules (old rules are the default and the compilation in var lib = CreateCompilation(...) doesn't overwrite that) and the consumer uses new rules (via options: TestOptions.UnsafeReleaseExe.WithUpdatedMemorySafetyRules()). We test this in many places (not just for extern members) via the custom CompileAndVerify helper (now renamed to CompileAndVerifyUnsafe) defined at the top of the file.


In reply to: 3687334849

@jjonescz

jjonescz commented Jan 5, 2026

Copy link
Copy Markdown
Member Author
    private void ReportDiagnosticsIfUnsafeMemberAccess(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNode node)

This was modeled after ReportDiagnosticsIfObsolete for consistency (it should be also called at the same callsites).


In reply to: 3687194228


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Unsafe.cs:24 in 8569af4. [](commit_id = 8569af4, deletion_comment = False)

@AlekseyTs AlekseyTs left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM (commit 3)

@jjonescz jjonescz merged commit 24f9053 into dotnet:features/UnsafeEvolution Jan 7, 2026
25 checks passed
@jjonescz jjonescz deleted the Unsafe-09-Extern branch January 7, 2026 09:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants