Skip to content

Write newer C# operators and readonly struct members as the source declares them - #110

Merged
Mpdreamz merged 1 commit into
mainfrom
modern-csharp
Nov 15, 2026
Merged

Mpdreamz merged 1 commit into
mainfrom
modern-csharp

Conversation

@Mpdreamz

Copy link
Copy Markdown
Contributor

A survey of C# 12–14 features, read through the reader, the writer, the parser and the Roslyn oracle, found three things written wrong:

  • Operators. C# 14 compound assignment operators (void operator +=(Money other), %=, >>>= and the rest), the instance void operator ++() and --, their checked forms, and explicit operator checked int came out under their metadata names, such as op_CheckedAdditionAssignment. The parser reads them all back; a ++ without a parameter is the instance one.
  • Readonly struct members. Every auto-property getter in a struct is readonly, so int X { get; set; } was written readonly int X { readonly get; set; }. Property-level readonly now means what C# means by it: every accessor is readonly, which C# writes on the property. A readonly set was not read at all.
  • Params spans. params ReadOnlySpan<string> parts was written scoped params ReadOnlySpan<string>, which C# rejects (CS9350). A params span is scoped without saying so; the reader leaves that implied scoped out, and the writer puts params first if a model has both.

The rest of the survey reads right, matches Roslyn and round-trips: params collections, allows ref struct, [Experimental], [OverloadResolutionPriority], InlineArray, ref structs implementing interfaces, partial properties and events, field-backed properties, primary constructors, ref readonly parameters, record structs and static abstract/virtual interface members. They are now all in the new modern writer sample.

Verified:

  • The oracle now checks readonly on methods and accessors, for structs that are not readonly.
  • All properties pass, and mise run ci is green locally.
  • Mutations caught:
    • property readonly taken from the getter alone fails the framework round trips and the generated libraries;
    • parsing ++ as always static fails the model round trip;
    • keeping the implied scoped fails the oracle on CoreLib, System.Text.Json and System.Collections.Immutable.

🤖 Generated with Claude Code

…clares them

- C# 14 compound assignment operators, `void operator +=(Money other)`, the instance `++` and `--`, their
  checked forms, and `explicit operator checked int` were written by their metadata names (`op_…`).
- A struct property whose getter alone is readonly, as every auto-property getter is, was written
  `readonly int X { readonly get; set; }`: readonly on the property is for when every accessor is.
  A readonly setter was not read at all.
- A params span was written `scoped params`, which C# rejects; params spans are scoped without saying so.

The Roslyn oracle now also checks readonly on methods and accessors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@Mpdreamz Mpdreamz added the bug A defect fixed (patch) label Nov 15, 2026
@Mpdreamz
Mpdreamz merged commit 7c4f286 into main Nov 15, 2026
8 checks passed
@Mpdreamz
Mpdreamz deleted the modern-csharp branch November 15, 2026 07:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug A defect fixed (patch)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant