Fix System.Text.Json source-generated unsafe accessors - #133599
Conversation
Preserve open generic constructor signatures and share indexed generic helpers with member accessors. Use reflection for generic layouts that the existing helper scheme cannot support, preserving user exceptions and updates to boxed struct fields. Emit feature-gated safety annotations and enable the updated memory safety rules across System.Text.Json test projects. Add public-API coverage for constructors, inherited members, and struct accessors. Fix dotnet#133369 Fix dotnet#133423 Fix dotnet#133592 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2af8f83f-b01f-4ae3-b171-750c75bfde9e
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
1 similar comment
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
🟡 Changes recommended
It appears to introduce/expand public API surface in the source-generator model types (including new required members) without any linked api-approved issue, which is a merge-blocking compatibility/process concern.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Updates the System.Text.Json source generator and its tests to improve handling of inaccessible members/constructors on generic types, reduce generated helper collisions, and make generated/accessor code compatible with the updated-memory-safety-rules compiler feature.
Changes:
- Extend generator model/spec metadata to track open generic signatures, declaring-type hierarchy index, target framework, and memory-safety feature usage.
- Update generator emission for generic unsafe accessor wrappers (including constructors) and adjust reflection fallback invocation to preserve original exceptions.
- Enable
updated-memory-safety-rulesacross System.Text.Json test projects and update/expand tests + baselines accordingly.
File summaries
| File | Description |
|---|---|
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.Values.StringSegment.cs | Wraps reinterpret-cast spans in unsafe(...) for updated memory-safety rules. |
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Utf8JsonWriterTests.cs | Adjusts unsafe pointer dereference usage to comply with updated memory-safety rules. |
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj | Enables updated-memory-safety-rules feature for the main STJ test project. |
| src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonEncodedTextTests.cs | Updates overridden unsafe methods to use unsafe(...) expressions under new rules. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/System.Text.Json.SourceGeneration.Unit.Tests.targets | Enables updated-memory-safety-rules for source-gen unit test compilation. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/Baselines/UnsafeAccessors_InaccessibleConstructor/net462/MyContext.InaccessibleCtor.g.cs.txt | Updates baseline to use unwrapped reflection invocation for ctor exceptions. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/Baselines/UnsafeAccessors_InaccessibleConstructor/net462/MyContext.g.cs.txt | Updates baseline to include new InvokeUnwrapped helper. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/Baselines/UnsafeAccessors_GenericTypeInitOnlyProperties/netcoreapp/MyContext.GenericPropsInt32.g.cs.txt | Updates baseline for renamed/disambiguated generic accessor helper naming. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets | Enables updated-memory-safety-rules and unsafe blocks for source-gen functional tests. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs | Adds coverage for generic contexts/nesting and additional inaccessible-member scenarios. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/ConstructorTests.cs | Adds coverage for generic types with inaccessible [JsonConstructor] constructors. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/NETStandardContextTests.cs | Adds NETStandard coverage for exception preservation with reflection accessors. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs | Narrows unsafe usage around Unsafe.Unbox to satisfy updated rules. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs | Adds new test types and includes them in the NETStandard serializer context. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/System.Text.Json.TestLibrary.targets | Enables updated-memory-safety-rules for the netstandard test library build. |
| src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.Constructor.cs | Adds new generic/non-public constructor test types and imports needed attributes. |
| src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs | Expands common non-public accessor test matrix (generic structs, fields, inheritance). |
| src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs | Adds init-only roundtrip coverage for generic color type (NET-only). |
| src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.Exceptions.cs | Adds regression coverage for preserving original exceptions from non-public ctors. |
| src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.AttributePresence.cs | Adds generic non-public-ctor roundtrip coverage and required/init-only scenarios. |
| src/libraries/System.Text.Json/gen/System.Text.Json.SourceGeneration.targets | Includes the new generator model file in compilation. |
| src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs | Adds open-generic/type-parameter metadata for constructor accessor emission on generic types. |
| src/libraries/System.Text.Json/gen/Model/TargetFramework.cs | Introduces a target-framework discriminator used by emission logic. |
| src/libraries/System.Text.Json/gen/Model/PropertyGenerationSpec.cs | Adds declaring-type hierarchy index and refines open-type metadata for generic member accessors. |
| src/libraries/System.Text.Json/gen/Model/ParameterGenerationSpec.cs | Adds open-parameter-type metadata for generic constructor accessor signatures. |
| src/libraries/System.Text.Json/gen/Model/ContextGenerationSpec.cs | Adds target framework and updated memory-safety feature flags to the context spec. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs | Computes open-generic metadata, declaring-type indices, and feature flags during parsing. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs | Updates emission for safe extern accessors, generic ctor wrappers, and exception-unwrapping reflection invokes. |
| src/libraries/System.Text.Json/gen/Helpers/KnownTypeSymbols.cs | Detects consumer target framework via core library identity to guide emission. |
Review details
- Files reviewed: 29/29 changed files
- Comments generated: 3
- Review effort level: Lite
|
@eiriktsarpalis with the new rules, the fact that |
@EgorBo I don't think that it is. The problem fundamentally is the shape of STJ's setter delegate which uses |
|
@eiriktsarpalis Separately, does the generator have a way to disable unsafe code emit? Unsafe code without AllowUnsafeBlocks is not allowed in the v2 safety rules and there will certainly be some users who are not allowed to use unsafe code via policy restriction. Do we have a way to make sure that those people can still use the source generator in supported configurations that don't require unsafe? |
Not really, but doing so would necessitate having a fallback available. Unsafe accessors (only used in scenaria where a customer has explicitly opted into an inaccessible field via the |
There was a problem hiding this comment.
🟡 Changes recommended
The generated generic unsafe-constructor wrappers must preserve ref-kind modifiers before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
- Files reviewed: 27/27 changed files
- Comments generated: 1
- Review effort level: Lite
I think this is basically the answer -- you have to opt-in to a specific feature to get unsafe code emitted. I think that's OK for now. |
@eiriktsarpalis @agocke just to make sure I understand. This minimal totally safely looking code: using System.Text.Json.Serialization;
public struct Point
{
public int X { get; set; }
}
[JsonSerializable(typeof(Point))]
internal partial class MyContext : JsonSerializerContext { }currently complains with: While SG should wrap it with the unsafe {} what this PR does, it also means the user has to enable |
There was a problem hiding this comment.
🔵 Needs a closer look
RoslynExtensions binds an enum-returning getter to Func<IModuleSymbol, int>, which can fail generator initialization.
Review details
Suppressed comments (1)
src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs:43
IModuleSymbol.MemorySafetyRulesVersionreturns theMemorySafetyRulesVersionenum, notint.MethodInfo.CreateDelegaterequires a compatible return type and does not reinterpret an enum as its underlying integer, so this cast throws duringRoslynExtensionstype initialization on compilers that expose the new API; the generator then fails before emitting code. Adapt the reflected result (or use a compatible adapter) instead of binding the getter directly toFunc<IModuleSymbol, int>.
: (Func<IModuleSymbol, int>)getter.CreateDelegate(typeof(Func<IModuleSymbol, int>));
- Files reviewed: 23/23 changed files
- Comments generated: 0 new
- Review effort level: Lite
|
/ba-g test failure is unrelated to STJ. |
|
/backport to release/11.0 |
|
Started backporting to |
Yeah this is something we'll need to fix for 12. |
…#133886) Backport of #133599 to release/11.0 /cc @eiriktsarpalis ## Customer Impact - [x] Customer reported - [ ] Found internally Address two closely related customer reported issues: 1. Corner cases not accounted for in the newly introduced unsafe accessor support in the STJ source generator. 2. STJ source generator codegen is incompatible with unsafe v2 semantics. Enabling unsafe v2 in projects using the source generator will frequently result in uncompilable code without possibility for workarounds. ## Regression - [x] Yes - [ ] No #124650 introduced unsafe accessor suppor in .NET 11. ## Testing Added comprehensive testing covering the impacted corner cases. Enabled unsafe v2 in the STJ test projects to ensure it is being appropriately dogfed. ## Risk Medium. The fix involves nontrivial changes to the source generator emitter. Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2af8f83f-b01f-4ae3-b171-750c75bfde9e
Reconciles the shared UnsafeAccessorEmitter extraction with #133599 (fix STJ source-generated unsafe accessors). The only conflict was JsonSourceGenerator.Emitter.cs; it was resolved by taking main's post-#133599 accessor code and re-applying the extraction on top. The #133599 improvements are folded into the shared helper so STJ output stays byte-identical: - safe/unsafe extern modifier, threaded as a useUpdatedMemorySafetyRules bool parameter on EmitMemberAccessors/EmitConstructorAccessor (STJ computes the flag at parse time and stores it on ContextGenerationSpec); - DeclaringTypeIndex disambiguator in the generic wrapper class name; - generic constructor accessor emitted inside a partial __GenericAccessors_<name>_0 wrapper shared with the member accessors; - ref/out/in constructor parameters (including the reflection fallback that writes back ref/out args); - field reflection-fallback and open-type fallbacks. STJ's GenerateMemberAccessors/GenerateConstructorAccessor remain thin adapters that build the neutral specs and delegate to the helper; the moved name/dup helpers and the GenericAccessorEntry alias are removed. Both Roslyn versions build clean and the SourceGeneratedOutputTests baselines are byte-identical.
Now that the shared UnsafeAccessorEmitter emits the constructor extern inside a generic wrapper class (reconciled with dotnet#133599), the config binder can construct generic required-member types through an [UnsafeAccessor(Constructor)] accessor on .NET 9+ instead of falling back to reflection. - Drop the !IsGenericType gate on ConstructorCanUseUnsafeAccessor; a generic type now qualifies when generic UnsafeAccessor is supported and it is not nested in a generic type, matching the STJ parser logic. - Thread the type's open-generic definition (type-parameter names, open FQN, constraint clauses) and each constructor parameter's open type FQN to the helper so the wrapper and its extern are emitted with open types. - Thread DeclaringTypeIndex through init-only setter accessors so the generic wrapper class name matches the shared helper's __GenericAccessors_<name>_<index> scheme, and adopt the safe/unsafe extern modifier by computing the compilation's updated-memory-safety-rules flag at parse time (stored on SourceGenerationSpec). Adds a functional test binding a generic type with a required init-only member, exercising the generic constructor + setter accessor wrappers; it passes under both the source-generated and reflection binders. Only the netcoreapp UnsupportedTypes baseline changes, adopting the _<index> wrapper name. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1a0d7ac2-f2a3-4526-93ab-36bf1a23933f
Fixes a number of codegen issues related to the source generator's handling of newly introduced unsafe accessors. To be backported to .NET 11. Fixes dotnet#133369. Fixes dotnet#133423. Fixes dotnet#133592. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 2af8f83f-b01f-4ae3-b171-750c75bfde9e
Fixes a number of codegen issues related to the source generator's handling of newly introduced unsafe accessors. To be backported to .NET 11.
Fixes #133369. Fixes #133423. Fixes #133592.