Skip to content

Infer System.Text.Json polymorphism from closed type hierarchies#130808

Merged
eiriktsarpalis merged 9 commits into
dotnet:mainfrom
eiriktsarpalis:eiriktsarpalis-stj-closed-hierarchies
Jul 16, 2026
Merged

Infer System.Text.Json polymorphism from closed type hierarchies#130808
eiriktsarpalis merged 9 commits into
dotnet:mainfrom
eiriktsarpalis:eiriktsarpalis-stj-closed-hierarchies

Conversation

@eiriktsarpalis

Copy link
Copy Markdown
Member

Summary

  • add opt-in closed-hierarchy polymorphism inference to JsonSerializerOptions and source-generation options
  • support reflection and source generation, including generic specialization, metadata-only hierarchies, deterministic discriminators, and explicit-registration precedence
  • report unsupported inferred registrations through source-generator diagnostics and add broad functional/unit coverage

Fixes #129041

Testing

  • rebuilt src/libraries/System.Text.Json/System.Text.Json.slnx with zero warnings and errors
  • ran all six System.Text.Json test projects: 130,435 tests, zero failures/errors, and 17 expected skips

Note

This pull request description was generated by GitHub Copilot.

eiriktsarpalis and others added 6 commits July 15, 2026 10:39
…erence

Implements feature dotnet#129041: a global InferClosedTypePolymorphism flag on
JsonSerializerOptions and JsonSourceGenerationOptionsAttribute that infers
polymorphic \ discriminators from compiler-emitted [IsClosedType]
metadata when no explicit [JsonDerivedType] set is present.

- Reflection: DefaultJsonTypeInfoResolver infers one derived type per closed
  hierarchy member; throws for inaccessible/un-unifiable derived types.
- Source-gen: forward-compat parser plumbing mirrors reflection parity
  (throwing factory for inaccessible/open-generic, kept entry for unassignable).
- Wire flag into options copy-ctor and cache equality comparer.
- New diagnostics SYSLIB1240-1242 for derived-type validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d derived types

Source-gen surfaces invalid polymorphic configurations through build-time SYSLIB
diagnostics (the established design), so the prior approach of baking a
non-localized `throw new InvalidOperationException(...)` into the generated
metadata was inconsistent and not localizable. Remove the UnresolvedDerivedTypeError
mechanism (the dispatch hook, the GenerateForTypeWithUnresolvedPolymorphism emitter
method, the PolymorphismOptionsSpec field, and the two non-localized message
constants) and restore the build-diagnostic + drop behavior:

- Explicit open-generic [JsonDerivedType] that cannot be unified: report
  SYSLIB1229 and drop the entry (matches the pre-existing base behavior).
- Inferred closed-type derived types that are open-generic-unresolvable or less
  accessible than the base: report SYSLIB1229 / SYSLIB1241 and drop the entry.

Track explicit [JsonDerivedType] presence with a dedicated flag so closed-type
inference is still suppressed when an explicit registration exists, even if every
explicit entry failed to resolve.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The closed-type inference tests forced an explicit DefaultJsonTypeInfoResolver,
which fails under the NativeAOT/source-gen test legs where reflection is
disabled. Closed-type polymorphism inference is reflection-only, so rely on the
ambient default reflection resolver instead and guard each test on
JsonSerializer.IsReflectionEnabledByDefault so they no-op when reflection is
unavailable. The forced resolver's stated rationale never applied: this file is
only compiled into the reflection test project, not the source-gen one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The header claimed the closed-type fixtures serve both the reflection-based and
source-generated serializers, but the fixture assembly is referenced only by
System.Text.Json.Tests and the source-gen path does not support closed-type
inference. Correct the comment to name the reflection-based tests as the sole
consumer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Exercise both derived types of the basic hierarchy via [Theory], and add tests for collection-element inference, nested closed-type properties alongside regular properties (with payload round-trip), and the unknown-discriminator deserialization failure path.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Align reflection and source-generation validation, generic specialization, diagnostics, metadata inference, and test coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 8967afcb-a170-497d-930a-a69e5ccf8773
Copilot AI review requested due to automatic review settings July 15, 2026 18:21
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 4 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-text-json
See info in area-owners.md if you want to be subscribed.

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.

Pull request overview

This PR adds opt-in “closed type hierarchy” polymorphism inference to System.Text.Json for both reflection-based metadata generation and source generation. When enabled, the resolver/source generator reads compiler-emitted closed-hierarchy metadata (via System.Runtime.CompilerServices.IsClosedTypeAttribute) and synthesizes JsonDerivedType registrations with deterministic string discriminators.

Changes:

  • Introduces JsonSerializerOptions.InferClosedTypePolymorphism and JsonSourceGenerationOptionsAttribute.InferClosedTypePolymorphism (public API) and wires the setting through reflection + source generator pipelines.
  • Implements closed-hierarchy derived-type discovery (runtime via CustomAttributeData; source-gen via Roslyn light-up/polyfills), plus accessibility validation and new SYSLIB diagnostics for unsupported inferred registrations.
  • Adds broad tests covering runtime behavior, source-gen behavior, and diagnostics; updates resources/diagnostic documentation.

Reviewed changes

Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj Includes new shared closed-type inference test file and IsClosedTypeAttribute source for tests.
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CacheTests.cs Ensures options caching equality/hash includes InferClosedTypePolymorphism.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Unit.Tests/JsonSourceGeneratorDiagnosticsTests.cs Adds diagnostic coverage for inferred-closed-type scenarios and extends open-generic derived-type diagnostics assertions.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets Adds shared closed-type inference tests and IsClosedTypeAttribute to source-gen test builds.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs Registers additional types used by closed-type inference/open-generic polymorphism scenarios for source-gen contexts.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.ClosedTypeInference.cs New source-gen runtime-behavior tests for closed-type inference + runtime guard behavior.
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.TestLibrary/TestClasses.cs Adds referenced closed-type hierarchy types and local compiler-attribute polyfills for test library assembly.
src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs Adjusts tests and warning suppressions to align with new diagnostics and source-gen vs reflection validation timing.
src/libraries/System.Text.Json/tests/Common/PolymorphicTests.ClosedTypeInference.cs New shared (reflection + source-gen) functional coverage for closed-type inference across many hierarchy shapes.
src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs Narrows warning suppression for intentionally invalid derived-type configurations.
src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs Adds a throw helper for inaccessible inferred derived types.
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonMetadataServices.Helpers.cs Formatting-only adjustment near polymorphism population callsite.
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/DefaultJsonTypeInfoResolver.Helpers.cs Implements runtime closed-hierarchy inference, discriminator synthesis, and accessibility validation.
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.cs Adds new public option InferClosedTypePolymorphism (and copies it in the copy ctor).
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerOptions.Caching.cs Updates options equality/hash to include _inferClosedTypePolymorphism.
src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs Adds Type.IsAtLeastAsVisibleAs ported accessibility comparison used to validate inferred registrations.
src/libraries/System.Text.Json/src/Resources/Strings.resx Adds runtime error string for inaccessible inferred derived types.
src/libraries/System.Text.Json/ref/System.Text.Json.cs Adds the new public API surface in the ref assembly (InferClosedTypePolymorphism properties).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hant.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.zh-Hans.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.tr.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ru.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pt-BR.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.pl.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ko.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.ja.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.it.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.fr.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.es.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.de.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/xlf/Strings.cs.xlf Adds new source-gen diagnostic resource entries (untranslated placeholders).
src/libraries/System.Text.Json/gen/Resources/Strings.resx Adds new source-gen diagnostic strings for derived-type support/accessibility/collision.
src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs Tracks closed-type-without-inferred-metadata marker for runtime guard emission.
src/libraries/System.Text.Json/gen/Model/SourceGenerationOptionsSpec.cs Adds InferClosedTypePolymorphism to parsed source-gen options model.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Parser.cs Parses new option, performs closed-type inference, and centralizes derived-type validation/diagnostics.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.ExceptionMessages.cs Adds runtime exception text for “runtime-only inference requested but metadata not generated”.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs Emits runtime guard for closed types lacking inferred metadata; writes the new option into generated default options.
src/libraries/System.Text.Json/gen/JsonSourceGenerator.DiagnosticDescriptors.cs Introduces SYSLIB1240–1242 source-gen diagnostics for polymorphism validation.
src/libraries/System.Text.Json/gen/Helpers/RoslynExtensions.cs Adds Roslyn light-up/polyfills for closed-type detection/derived-type enumeration and accessibility comparison.
src/libraries/System.Text.Json/Common/JsonSourceGenerationOptionsAttribute.cs Adds new public source-gen option property InferClosedTypePolymorphism.
docs/project/list-of-diagnostics.md Documents new SYSLIB1240–1249 reservation range and new diagnostic IDs.

Comment thread docs/project/list-of-diagnostics.md Outdated
Copilot AI review requested due to automatic review settings July 16, 2026 08:47

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.

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated 2 comments.

Comment thread docs/project/list-of-diagnostics.md Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 16, 2026 08:57

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.

Pull request overview

Copilot reviewed 42 out of 42 changed files in this pull request and generated 2 comments.

Comment thread src/libraries/System.Text.Json/src/System/ReflectionExtensions.cs
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8967afcb-a170-497d-930a-a69e5ccf8773
Copilot AI review requested due to automatic review settings July 16, 2026 11:05

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.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated 1 comment.

@tannergooding tannergooding left a comment

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.

LGTM. Only real feedback is that I didn't see any test validating reflection vs source-gen are equivalent, but I don't think we have existing tests for that either

@eiriktsarpalis

Copy link
Copy Markdown
Member Author

Only real feedback is that I didn't see any test validating reflection vs source-gen are equivalent

The test suite targets an abstraction implemented by both reflection and source gen, so this should ensure the two implementations are mostly observationally equivalent.

@eiriktsarpalis
eiriktsarpalis enabled auto-merge (squash) July 16, 2026 14:54
@eiriktsarpalis

Copy link
Copy Markdown
Member Author

/ba-g test failure is unrelated

@eiriktsarpalis
eiriktsarpalis merged commit 6bd973a into dotnet:main Jul 16, 2026
86 of 90 checks passed
@eiriktsarpalis
eiriktsarpalis deleted the eiriktsarpalis-stj-closed-hierarchies branch July 16, 2026 16:57
@dotnet-milestone-bot dotnet-milestone-bot Bot added this to the 11.0-preview7 milestone Jul 17, 2026
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.

[API Proposal]: Add STJ support for closed hierarchies

4 participants