Skip to content

System.Text.Json: transitive [JsonDerivedType] resolution + cross-assembly boundary semantics#124506

Closed
Copilot wants to merge 4 commits intomainfrom
copilot/implement-json-polymorphism-feature
Closed

System.Text.Json: transitive [JsonDerivedType] resolution + cross-assembly boundary semantics#124506
Copilot wants to merge 4 commits intomainfrom
copilot/implement-json-polymorphism-feature

Conversation

Copy link
Contributor

Copilot AI commented Feb 17, 2026

  • Identify actionable new PR comments directed at @copilot.
  • Run required CI/workflow log checks for any mentioned build/CI context.
  • Execute a code review of the current PR changes using Opus 4.6 as requested.
  • Post the review analysis/results in a reply to the PR comment.
  • Verify no repository code changes are needed for this request.

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@dotnet-policy-service
Copy link
Contributor

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

Copilot AI and others added 3 commits February 17, 2026 11:54
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Add JSON polymorphism feature as discussed in #7245 System.Text.Json: transitive [JsonDerivedType] resolution + cross-assembly boundary semantics Feb 17, 2026
Copilot AI requested a review from stephentoub February 17, 2026 12:04
@stephentoub
Copy link
Member

@copilot invoke the code-review skill on opus 4.6 and post the analysis/results as a comment on this pr

Copy link
Contributor Author

Copilot AI commented Feb 17, 2026

@copilot invoke the code-review skill on opus 4.6 and post the analysis/results as a comment on this pr

I invoked the code-review skill and attempted to run it on Opus 4.6, but this environment rejected that model as unsupported. I then ran the same review with the available model and here are the results for commit f8b5adf2:

🤖 Copilot Code Review — PR #124506

Holistic Assessment

  • Motivation: The gap is real: direct-only [JsonDerivedType] discovery can miss valid leaf types in multi-hop/cross-assembly hierarchies.
  • Approach: Centralizing transitive discovery in JsonPolymorphismOptions.CreateFromAttributeDeclarations is the right place and keeps reflection/source-gen behavior aligned.
  • Summary: ⚠️ Needs Human Review (no clear blocker found, but one semantic edge case should be explicitly decided/tested).

Detailed Findings

  • ✅ Transitive resolution is implemented at the correct metadata boundary and shared by reflection/source-gen paths.
  • [JsonPolymorphic] boundary-stop behavior is explicit in code and covered by runtime + cross-assembly tests.
  • ⚠️ Potential semantic gap: when the same transitive type is discovered via different paths with different discriminators, current dedup is type-based and silently first-wins. Recommend explicit test and maintainer decision on expected behavior.
  • 💡 Coverage is strong overall; missing high-value test is the conflicting-transitive-discriminator case.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh gh pr view 124506 --repo dotnet/runtime --json number,title,body,author,labels,reviewRequests,reviews,comments,commits (http block)

If you need me to access, download, or install something from one of these locations, you can either:

/// Walks the DerivedTypes list (which may grow during iteration) and appends
/// any additional derived types found via transitive [JsonDerivedType] declarations
/// on already-known derived types. Types with [JsonPolymorphic] are treated as
/// boundary nodes and are not traversed. Uses a set to prevent duplicates and cycles.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Types with [JsonPolymorphic] are treated as boundary nodes and are not traversed

@copilot why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because [JsonPolymorphic] on the child means that child is declaring its own polymorphic contract (including its own discriminator space). If we kept traversing through it, the parent would implicitly absorb the child’s derived-type declarations, which can mix independent contracts and produce surprising parent-level behavior/discriminator coupling. Treating it as a boundary keeps transitive expansion to a single unbroken [JsonDerivedType] chain unless a new polymorphic root is explicitly declared.


// Do not walk through types that declare their own [JsonPolymorphic] configuration;
// those define an independent polymorphic scheme and their [JsonDerivedType] entries
// belong to that scheme, not to the root type's.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot what does it mean for those to be an independent scheme? Is that because [JsonPolymorphic] brings with it configuration that would then only apply to this type and its derivations (like discriminator)? Does that actually affect the semantics of the [JsonDerivedType] though?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes — that’s exactly the rationale. [JsonPolymorphic] marks a type as a polymorphic root with its own options (TypeDiscriminatorPropertyName, UnknownDerivedTypeHandling, etc.), so its [JsonDerivedType] declarations are interpreted in that root’s contract.

If we transitively hoist those declarations into an ancestor root, we effectively reinterpret them under the ancestor’s contract (possibly different discriminator property/handling), and we can also create discriminator-space coupling/conflicts between two roots that were authored independently.

So while [JsonDerivedType] still means “this base recognizes this derived type,” the question is which base’s scheme it belongs to. The boundary rule keeps it scoped to the nearest root that explicitly declares polymorphism.

Copilot AI requested a review from stephentoub February 28, 2026 10:01
@stephentoub stephentoub deleted the copilot/implement-json-polymorphism-feature branch February 28, 2026 10:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants