Skip to content

Update library change rules to reflect C# 12–14 language features#52976

Open
Copilot wants to merge 5 commits intomainfrom
copilot/update-compatibility-article-new-features
Open

Update library change rules to reflect C# 12–14 language features#52976
Copilot wants to merge 5 commits intomainfrom
copilot/update-compatibility-article-new-features

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 10, 2026

The compatibility rules article (library-change-rules.md) had no guidance for several newer C# language features that affect binary/source compatibility for library authors.

New rules added

Members section

  • ✔️ ALLOWED — refref readonly parameter (C# 12): Existing ref call sites compile without change; unlike refin, ref readonly doesn't silently accept rvalues—it issues a warning instead.
  • ❌ DISALLOWED — inref readonly parameter: Call sites passing arguments without the in modifier (valid for in) will receive a compiler warning with ref readonly. Callers that treat warnings as errors experience a source breaking change.
  • ❓ REQUIRES JUDGMENT — OverloadResolutionPriorityAttribute (C# 13): Intended for new overloads; applying to existing overloads or changing an existing priority value can be a source break.
  • ✔️ ALLOWED — Adding allows ref struct to a type parameter (C# 13): Expands accepted type arguments; existing callers unaffected.
  • ❌ DISALLOWED — Removing allows ref struct from a type parameter (C# 13): Restricts accepted type arguments; breaks callers using ref struct type arguments.

Code changes section

  • ❌ DISALLOWED — Changing the collection type of a params parameter (C# 13): Recognized collection types include Span<T>, ReadOnlySpan<T>, specific interface types, and struct/class types that implement IEnumerable<T> with a parameterless constructor and instance Add method. Changing the collection type alters the IL signature—binary break.
  • ✔️ ALLOWED — Converting extension methods to extension block syntax (C# 14): Both forms emit identical IL; binary and source compatible.

Updated guidance

  • ❓ REQUIRES JUDGMENT — Adding a member to an interface: Updated to explicitly address C# 13, where ref struct types can implement interfaces but can't fall back to default implementations. Adding any default instance member to an interface implemented by a ref struct is a source breaking change for those callers.

Internal previews

📄 File 🔗 Preview link
docs/core/compatibility/library-change-rules.md docs/core/compatibility/library-change-rules

Copilot AI changed the title [WIP] Update compatibility article for new C# language features Update library change rules to reflect C# 12–14 language features Apr 10, 2026
Copilot AI requested a review from gewarren April 10, 2026 21:24
@gewarren gewarren marked this pull request as ready for review April 10, 2026 21:26
@gewarren gewarren requested a review from a team as a code owner April 10, 2026 21:26
@gewarren gewarren requested review from BillWagner and Copilot April 10, 2026 21:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Updates the .NET compatibility guidance for library authors by adding explicit change rules for newer C# language features that affect source and binary compatibility.

Changes:

  • Adds guidance for ref readonly parameters, OverloadResolutionPriorityAttribute, and allows ref struct generic anti-constraints in the Members section.
  • Adds guidance for C# 13 params collections signature changes, and C# 14 extension block conversions in the Code changes section.

Comment on lines +138 to +140
- ✔️ **ALLOWED: Changing a `ref` parameter to [`ref readonly`](../../csharp/language-reference/keywords/method-parameters.md#ref-readonly-modifier)**

Changing a parameter from `ref` to `ref readonly` doesn't require callers to change their code. Call sites that pass arguments by `ref` work without modification. Unlike changing `ref` to `in`, this change doesn't allow callers to pass rvalues (non-variables), so existing call patterns remain valid.
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

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

The explanation says changing ref to ref readonly "doesn't allow callers to pass rvalues (non-variables)", but ref readonly parameters do allow passing non-variables at the call site (the compiler issues a warning, per the language reference). Consider rewording to clarify that ref readonly still requires an lvalue for ref/in arguments, and that passing an rvalue is possible only without a modifier (with a warning), so existing ref call sites remain valid.

Copilot uses AI. Check for mistakes.
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.

This is valid feedback. The clarification would be that changing from ref to ref readonly isn't breaking. But, changing from in to ref readonly can introduce warnings. That change from in should be considered a source breaking change because callers might set warnings as errors.

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.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in commit 105c954. Added a new ❌ DISALLOWED rule immediately after the refref readonly ALLOWED rule: "Changing an in parameter to ref readonly". The explanation notes that call sites passing in arguments without the modifier (valid for in) will receive a compiler warning with ref readonly, and callers that treat warnings as errors will see a source breaking change.

Copilot AI requested a review from gewarren April 13, 2026 20:24
Copy link
Copy Markdown
Member

@BillWagner BillWagner left a comment

Choose a reason for hiding this comment

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

There's one other new rule that should be considered:

In C# 13, ref struct types can implement interfaces. However, they can't be cast to those interfaces. So, as a result, adding an interface method with a default implementation is a breaking change for any ref struct that implements that interface.

It's probably a small risk, but should be at least "Requires Judgement"

Comment on lines +138 to +140
- ✔️ **ALLOWED: Changing a `ref` parameter to [`ref readonly`](../../csharp/language-reference/keywords/method-parameters.md#ref-readonly-modifier)**

Changing a parameter from `ref` to `ref readonly` doesn't require callers to change their code. Call sites that pass arguments by `ref` work without modification. Unlike changing `ref` to `in`, this change doesn't allow callers to pass rvalues (non-variables), so existing call patterns remain valid.
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.

This is valid feedback. The clarification would be that changing from ref to ref readonly isn't breaking. But, changing from in to ref readonly can introduce warnings. That change from in should be considered a source breaking change because callers might set warnings as errors.


- ❓ **REQUIRES JUDGMENT: Adding <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> to an existing overload or changing its priority value**

The <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> affects overload resolution at the source level: callers that recompile might resolve to a different overload than before. The intended use is to add the attribute to a new, better overload so the compiler prefers it over existing ones. Adding it to an existing overload or changing the priority value on an already-attributed overload is a source breaking change because callers that recompile might change behavior.
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.

Suggested change
The <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> affects overload resolution at the source level: callers that recompile might resolve to a different overload than before. The intended use is to add the attribute to a new, better overload so the compiler prefers it over existing ones. Adding it to an existing overload or changing the priority value on an already-attributed overload is a source breaking change because callers that recompile might change behavior.
The <xref:System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute> affects overload resolution at the source level: callers that recompile might resolve to a different overload than before. The intended use is to add the attribute to a new, better overload so the compiler prefers it over existing ones. Adding it to an existing overload or changing the priority value on an already-attributed overload can be a source breaking change because callers that recompile might change behavior.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 14, 2026

There's one other new rule that should be considered:

In C# 13, ref struct types can implement interfaces. However, they can't be cast to those interfaces. So, as a result, adding an interface meth...

Done in commit 105c954. Updated the existing ❓ REQUIRES JUDGMENT "Adding a member to an interface" rule to explicitly address the C# 13 scenario. The body text now explains that ref struct types (C# 13+) must provide an explicit implementation for every instance interface member and can't fall back to default implementations. A [!NOTE] callout highlights this as a source breaking change for ref struct implementors specifically.

Copilot AI requested a review from gewarren April 14, 2026 15:34
Clarified the explanation regarding breaking changes when adding overloads that preclude existing ones.
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.

Update the compatibility article to reflect any new language features

4 participants