Skip to content

feat(codegen): generate AOT-safe C# private field accessors - #11372

Merged
ReubenBond merged 7 commits into
dotnet:mainfrom
ReubenBond:rb-nativeaot-field-access
Oct 2, 2026
Merged

ReubenBond merged 7 commits into
dotnet:mainfrom
ReubenBond:rb-nativeaot-field-access

Conversation

@ReubenBond

@ReubenBond ReubenBond commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Generated serializers and copiers for C# payloads currently initialize inaccessible-field delegates through FieldAccessor and Reflection.Emit.DynamicMethod. Private fields, readonly fields, and compiler-generated auto-property backing fields therefore require runtime code generation, even when the generator knows the exact field at compile time.

Generate ref-returning UnsafeAccessorKind.Field methods for C# fields on supported target runtimes. Each generated codec or copier emits one accessField_<id> method per field and uses its returned reference for both reads and writes, reducing method and attribute metadata compared with separate getter/setter accessors. Non-generic C# payloads use this path on .NET 8+, and generic C# payloads use it on .NET 9+, preserving the declaring type's generic parameter positions and constraints. Volatile fields use this path on .NET 10+; earlier targets restore them through existing delegates so their libraries remain compatible with runtimes affected by dotnet/runtime#109665. Struct receivers are passed by reference, and deserializers/copiers assign through the returned field reference to restore readonly, get-only, init-only, and private-setter state. Serializer and copier emission share accessor declaration and deduplication helpers.

Select runtime capabilities from the SDK's TargetFrameworkIdentifier and TargetFrameworkVersion compiler-visible properties, and separately require UnsafeAccessorAttribute in the compilation references. Parsed capability flags participate in incremental option equality and hashing: changing targets across an accessor-support boundary updates generated output, while targets with equivalent capabilities reuse cached output. Compilation-only library type caching remains independent of the target options. Synthetic compilation tests supply target metadata explicitly, including platform-specific TFMs, supported version boundaries, and missing or invalid metadata.

For C# payloads, legacy targets, .NET 8 generic payloads, pre-.NET 10 volatile fields, and hot-reload field access retain the existing separate getter/setter delegate behavior. Normal accessible member access and wire IDs remain unchanged. Focused generator/runtime tests and reviewed snapshots cover both strategies, including actual .NET 8 and .NET 10 execution, exact accessor declaration counts, and shared read/write calls. Volatile-field runtime cases also reproduce and verify compatibility with unpatched CoreCLR 9.0.0. F# record and union field restoration retains its existing generated-delegate strategy on JIT-enabled runtimes.

The reusable Orleans.NativeAotSmoke harness contains a Fields scenario with project-local PublishAot. Its default component path statically constructs generated codecs/copiers for C# payloads and verifies native private/backing-field round trips, readonly and volatile fields, constrained and nested generics, generic structs, and array deep-copy isolation. Fields.smoke.json registers this case for the centralized Windows/Linux native-smoke matrix runner introduced by #11367. Its diagnostic policy keeps the existing broader serializer backlog visible while rejecting generated-code, FieldAccessor, and UnsafeAccessor diagnostics. This PR contributes the case and manifest and uses the shared runner and central CI pipeline already on main.

Ordinary AddSerializer/Serializer<T>/DeepCopier behavior is covered under JIT. Full NativeAOT serializer startup remains gated by the independently tracked manifest metadata failure in #11368; the optional --full-pipeline scenario preserves that reproduction. No runtime activation changes or public API changes are included.

Related to #8254 and the parent NativeAOT series.

Microsoft Reviewers: Open in CodeFlow

Copilot AI balanced review requested due to automatic review settings October 1, 2026 16:16

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

F# record and union serializers still use dynamic FieldAccessor delegates despite the documented AOT-safe behavior.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Adds AOT-safe generated access to inaccessible serialized fields using ref-returning UnsafeAccessor methods.

Changes:

  • Generates runtime-appropriate field accessors for serializers and copiers.
  • Adds JIT, snapshot, and NativeAOT coverage.
  • Documents target-framework and hot-reload behavior.
File Description
.github/​workflows/​analyzer-audit.yml Adds Windows/Linux NativeAOT validation.
src/​Orleans.CodeGenerator/​CopierGenerator.cs Shares unsafe-accessor declaration generation.
src/​Orleans.CodeGenerator/​LibraryTypes.cs Detects runtime accessor support.
src/​Orleans.CodeGenerator/​SerializerGenerator.cs Generates and uses ref-returning field accessors.
src/​Orleans.Serialization/​README.md Documents generated field-access behavior.
test/​Orleans.CodeGenerator.Tests/​FieldAccessCodegenTests.cs Tests generated accessor strategies.
test/​Orleans.CodeGenerator.Tests/​FieldAccessRuntimeTests.cs Tests runtime serialization and copying.
test/​Orleans.CodeGenerator.Tests/​HotReloadCodegenTests.cs Verifies hot-reload fallback behavior.
test/​Orleans.CodeGenerator.Tests/​Orleans.CodeGenerator.Tests.csproj Links shared field-access fixtures.
test/​Orleans.CodeGenerator.Tests/​OrleansSourceGeneratorTests.cs Selects framework-specific snapshots.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestBasicClassWithAnnotatedFields.verified.cs Updates private-field output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestClassReferenceProperties.verified.cs Updates backing-field output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestClassWithInterfaceConstructorParameter.verified.cs Updates interface-field output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestClassWithOptionalConstructorParameters.verified.cs Updates constructor-field output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestGenericClassWithConstructorParameters.FieldAccessor.verified.cs Captures generic fallback output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestGenericClassWithConstructorParameters.UnsafeAccessor.verified.cs Captures generic unsafe-accessor output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestRecords.verified.cs Updates record backing-field output.
test/​Orleans.CodeGenerator.Tests/​snapshots/​OrleansSourceGeneratorTests.TestRecordsWithParameterIdAttributes.verified.cs Updates attributed-record output.
test/​Orleans.NativeAotSmoke/​Fields.cs Adds the field-access smoke entry point.
test/​Orleans.NativeAotSmoke/​FieldsChecks.cs Provides shared round-trip assertions.
test/​Orleans.NativeAotSmoke/​FieldsNativeChecks.cs Exercises generated components under NativeAOT.
test/​Orleans.NativeAotSmoke/​FieldsPayloads.cs Defines private and generic payload fixtures.
test/​Orleans.NativeAotSmoke/​NativeAotEnvironment.cs Ensures dynamic code is unavailable.
test/​Orleans.NativeAotSmoke/​Orleans.NativeAotSmoke.csproj Configures reusable NativeAOT scenarios.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Orleans.CodeGenerator/SerializerGenerator.cs
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage

Metric Pull request Current main Variance
Lines 82.95% (115,560 / 139,315) 82.88% (115,459 / 139,303) +0.0654 pp
Branches 72.25% (33,577 / 46,471) 72.19% (33,510 / 46,419) +0.0634 pp

Report-only conclusion: improved.

The current-main baseline is commit cc1a9ff176 and uses the same reviewed coverage matrix.

Coverage combines every CI test matrix job, including providers, CodeGen, .NET 8/10, Linux, Windows, and macOS, using canonical physical source and branch identities.

The comparison remains report-only while normal line and branch variance is calibrated.

Coverage details

@ReubenBond ReubenBond changed the title feat(codegen): generate AOT-safe private field accessors feat(codegen): generate AOT-safe C# private field accessors Oct 1, 2026
Copilot AI balanced review requested due to automatic review settings October 1, 2026 16:50

Copilot AI 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.

Copilot review overview

🟢 Approval recommended

The runtime-gated implementation, documented fallbacks, snapshots, cross-target runtime tests, and NativeAOT workflow provide comprehensive coverage.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Runtime-dependent source generation and NativeAOT field mutation warrant final human validation despite comprehensive focused coverage.

Review effort: Balanced
Findings: None

Copilot AI balanced review requested due to automatic review settings October 1, 2026 22:23
@ReubenBond
ReubenBond force-pushed the rb-nativeaot-field-access branch from 9cb3d51 to 7985c70 Compare October 1, 2026 22:23

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Cross-runtime code generation and NativeAOT behavior warrant final human validation despite comprehensive focused coverage.

Review effort: Balanced
Findings: None

Copilot AI balanced review requested due to automatic review settings October 1, 2026 22:37

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

The .NET 8 path can generate accessors for volatile fields that fail with MissingFieldException on CoreCLR.

Review effort: Balanced
Findings: 1 High severity

Open (1)

Comment thread src/Orleans.CodeGenerator/SerializerGenerator.cs
Copilot AI balanced review requested due to automatic review settings October 1, 2026 23:15

Copilot AI 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.

Copilot review overview

🔵 Needs a closer look

Runtime-version-specific code generation and NativeAOT behavior warrant final human validation despite broad test coverage.

Review effort: Balanced
Findings: None

Resolved since last review (1)

@ReubenBond
ReubenBond merged commit de94cf0 into dotnet:main Oct 2, 2026
80 checks passed
@ReubenBond
ReubenBond deleted the rb-nativeaot-field-access branch October 2, 2026 02:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants