Fix STJ source gen missing type parameter constraints on generic accessor wrappers#126507
Fix STJ source gen missing type parameter constraints on generic accessor wrappers#126507
Conversation
…ssor wrappers When the source generator emits UnsafeAccessor wrapper classes for generic types (e.g., __GenericAccessors_MyType<T>), it was not propagating type parameter constraints from the original type. This caused CS0314 compilation errors when the type parameter had constraints like 'where T : notnull, BaseClass'. The fix extracts constraint clauses from ITypeParameterSymbol in the parser and appends them to the emitted generic wrapper class declaration. Fixes the aspnetcore build failure for PublicKeyCredential<TResponse> where TResponse : notnull, AuthenticatorResponse. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Tagging subscribers to this area: @dotnet/area-system-text-json |
There was a problem hiding this comment.
Pull request overview
Fixes System.Text.Json source generator output for generic UnsafeAccessor wrapper classes by propagating generic type parameter constraints, preventing downstream compilation failures (e.g., CS0314) when the declaring type has constrained type parameters.
Changes:
- Capture generic declaring-type constraint clauses during parsing and store them on
PropertyGenerationSpec. - Emit captured constraint clauses on generated
__GenericAccessors_...<T...>wrapper classes. - Add unit + runtime test coverage for constrained generic types with init-only properties.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/System.Text.Json/gen/Model/PropertyGenerationSpec.cs | Adds a new spec property to carry combined declaring-type constraint clauses into emission. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs | Extracts and formats generic type parameter constraint clauses from ITypeParameterSymbol. |
| src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs | Appends extracted constraint clauses to the generated generic accessor wrapper class declaration. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorTests.cs | Adds a unit test ensuring generated code compiles for constrained generic types with init-only properties. |
| src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs | Adds constrained-generic test types and a roundtrip test validating runtime serialization/deserialization behavior. |
| src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs | Registers the new constrained generic type in source-gen contexts so it participates in source generation tests. |
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/gen/Model/PropertyGenerationSpec.cs
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
- Handle AllowsRefLikeType via reflection (Roslyn 4.9+) for the 'allows ref struct' anti-constraint - Add Theory test covering notnull, class, class+new(), struct, unmanaged, and interface constraints - Add tests for multi-parameter constraints and base class+interface Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs
Outdated
Show resolved
Hide resolved
This comment has been minimized.
This comment has been minimized.
|
Do we want to put this behind some sort of opt-in? The feature is useful, but generally the use of unsafe code should come with some sort of user acknowledgment |
It should be already. Only members explicit opted in via annotation (or setters of init-only properties) will prompt unsafe accessor generation. |
…aints Replace manual constraint clause reconstruction with Roslyn's built-in SymbolDisplayFormat.IncludeTypeConstraints, which renders all constraint clauses automatically. This is future-proof against new C# constraint kinds (like 'allows ref struct') without requiring manual updates or reflection-based accessors. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
🤖 Copilot Code Review — PR #126507Note This review was generated by GitHub Copilot. Holistic AssessmentMotivation: Justified — generic UnsafeAccessor wrapper classes without their type parameter constraints produce a C# compile error ( Approach: The final approach (using Roslyn's Summary: Detailed Findings✅ String parsing —
|
|
/ba-g wasm coreclr timeout is non-blocking |
…ssor wrappers (dotnet#126507) > [!NOTE] > This PR was generated with the assistance of GitHub Copilot. ## Description Fixes the aspnetcore build failure reported in dotnet/dotnet#5880 (comment), caused by dotnet#124650. When the STJ source generator emits `UnsafeAccessor` wrapper classes for generic types (e.g., `__GenericAccessors_MyType<T>`), it was not propagating type parameter constraints from the original type definition. This caused **CS0314** compilation errors in downstream consumers (aspnetcore) when the type parameter had constraints like `where TResponse : notnull, AuthenticatorResponse`. ### Before (broken) ```csharp private static class __GenericAccessors_...<TResponse> // missing constraint! { [UnsafeAccessor(...)] public static extern ... __get_...(PublicKeyCredential<TResponse> obj); // CS0314 } ``` ### After (fixed) ```csharp private static class __GenericAccessors_...<TResponse> where TResponse : notnull, global::...AuthenticatorResponse { [UnsafeAccessor(...)] public static extern ... __get_...(PublicKeyCredential<TResponse> obj); // OK } ``` ## Changes - **`PropertyGenerationSpec.cs`** — Added `DeclaringTypeParameterConstraintClauses` property to store constraint clauses - **`JsonSourceGenerator.Parser.cs`** — Added `GetTypeParameterConstraintClause()` and `GetTypeParameterConstraintsCombined()` to extract all constraint kinds (`struct`, `class`, `notnull`, `unmanaged`, base types, interfaces, `new()`) from `ITypeParameterSymbol` - **`JsonSourceGenerator.Emitter.cs`** — Appends constraint clauses to the generic wrapper class declaration ## Tests - **Unit test**: `GenericTypeWithConstrainedTypeParameters_InitOnlyProperties` — verifies source gen compiles for constrained generic types - **Runtime test**: `InitOnlyProperties_GenericTypeWithConstraints_CanRoundtrip` — verifies serialization/deserialization end-to-end for a generic type with `where T : notnull, BaseClass` constraints - All 194 source gen unit tests pass - All 378 PropertyVisibility runtime tests pass --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
This PR was generated with the assistance of GitHub Copilot.
Description
Fixes the aspnetcore build failure reported in dotnet/dotnet#5880 (comment), caused by #124650.
When the STJ source generator emits
UnsafeAccessorwrapper classes for generic types (e.g.,__GenericAccessors_MyType<T>), it was not propagating type parameter constraints from the original type definition. This caused CS0314 compilation errors in downstream consumers (aspnetcore) when the type parameter had constraints likewhere TResponse : notnull, AuthenticatorResponse.Before (broken)
After (fixed)
Changes
PropertyGenerationSpec.cs— AddedDeclaringTypeParameterConstraintClausesproperty to store constraint clausesJsonSourceGenerator.Parser.cs— AddedGetTypeParameterConstraintClause()andGetTypeParameterConstraintsCombined()to extract all constraint kinds (struct,class,notnull,unmanaged, base types, interfaces,new()) fromITypeParameterSymbolJsonSourceGenerator.Emitter.cs— Appends constraint clauses to the generic wrapper class declarationTests
GenericTypeWithConstrainedTypeParameters_InitOnlyProperties— verifies source gen compiles for constrained generic typesInitOnlyProperties_GenericTypeWithConstraints_CanRoundtrip— verifies serialization/deserialization end-to-end for a generic type withwhere T : notnull, BaseClassconstraints