Skip to content

Unsafe evolution: check overrides and implementations#82172

Merged
jjonescz merged 8 commits into
dotnet:features/UnsafeEvolutionfrom
jjonescz:Unsafe-12-OHI
Feb 3, 2026
Merged

Unsafe evolution: check overrides and implementations#82172
jjonescz merged 8 commits into
dotnet:features/UnsafeEvolutionfrom
jjonescz:Unsafe-12-OHI

Conversation

@jjonescz

Copy link
Copy Markdown
Member

Test plan: #81207
Implements this part of the speclet: Overriding, inheritance, and implementation

Note: the first commit can be reviewed separately.

@jjonescz jjonescz marked this pull request as ready for review January 27, 2026 15:37
@jjonescz jjonescz requested a review from a team as a code owner January 27, 2026 15:37
@jjonescz jjonescz requested review from 333fred and jcouv January 27, 2026 15:37
@jcouv jcouv self-assigned this Jan 27, 2026
// (7,1): error CS9502: 'I2.M2()' must be used in an unsafe context because it is marked as 'unsafe' or 'extern'
// i2.M2();
Diagnostic(ErrorCode.ERR_UnsafeMemberOperation, "i2.M2()").WithArguments("I2.M2()").WithLocation(7, 1),
// (12,24): error CS9504: Unsafe member 'C3.M2()' overrides safe member 'I1.M2()'

@jcouv jcouv Jan 27, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider using a dedicated error message for implementation scenarios, since it's not an override
FWIW, nullability check uses CS8767 #Closed

// (39,19): error CS9504: Unsafe member 'C6.I.M2()' overrides safe member 'I.M2()'
// extern void I.M2();
Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M2").WithArguments("C6.I.M2()", "I.M2()").WithLocation(39, 19),
]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Consider testing:

  • interceptor scenario (we do nullability checks there, but intentionally not caller-unsafe checks)
  • test effect of LeastOverridenMember with interesting hierarchy from metadata
  • test hiding scenarios (the caller-unsafe bit should not count as part of the signature for hiding)

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.

test effect of LeastOverridenMember with interesting hierarchy from metadata

The initially added tests (e.g., Member_Method_Override) should already test interesting hierarchy from both metadata and source.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The initially added tests (e.g., Member_Method_Override) should already test interesting hierarchy from both metadata and source.

Do we test metadata that cannot be produced from C#? For instance, metadata has base method is safe and derived method that is requires-unsafe; then source overrides

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.

Added Member_Method_Override_Metadata, thanks.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably add a virtual impl of an interface method too.

@jcouv jcouv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Done with review pass (commit 2)

.VerifyDiagnostics();
var libRef = AsReference(lib, useCompilationReference);

// PROTOTYPE: Should a method like `I<T>.M(T)` be considered caller-unsafe under compat rules when substituted for `T = int*[]`?

@jjonescz jjonescz Jan 28, 2026

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.

Removed this prototype comment as I've realized it doesn't make sense to handle this. Corresponding speclet update: dotnet/csharplang#9962

@jcouv jcouv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM Thanks (commit 5). One test suggestion remains

@jcouv jcouv left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM Thanks (commit 6)

<value>'{0}' must be used in an unsafe context because it has pointers in its signature</value>
</data>
<data name="ERR_CallerUnsafeOverridingSafe" xml:space="preserve">
<value>Unsafe member '{0}' overrides safe member '{1}'</value>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Think these would be phrased better as "cannot". IE:

Suggested change
<value>Unsafe member '{0}' overrides safe member '{1}'</value>
<value>Unsafe member '{0}' cannot override safe member '{1}'</value>

@@ -8,6 +8,7 @@
using System.Reflection.Metadata.Ecma335;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is this used?

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 in Member_Interceptor for InvocationExpressionSyntax.

// (39,19): error CS9504: Unsafe member 'C6.I.M2()' overrides safe member 'I.M2()'
// extern void I.M2();
Diagnostic(ErrorCode.ERR_CallerUnsafeOverridingSafe, "M2").WithArguments("C6.I.M2()", "I.M2()").WithLocation(39, 19),
]);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should probably add a virtual impl of an interface method too.

@jjonescz jjonescz requested a review from 333fred January 30, 2026 12:25
@jjonescz

jjonescz commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

@333fred for another look, thanks

@jjonescz jjonescz merged commit 86c2b0f into dotnet:features/UnsafeEvolution Feb 3, 2026
24 checks passed
@jjonescz jjonescz deleted the Unsafe-12-OHI branch February 3, 2026 10:57
jjonescz added a commit to dotnet/csharplang that referenced this pull request Feb 3, 2026
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.

3 participants