Skip to content

fix(compilers/openapi): keep a conflicting redeclaration's losing type - #436

Open
fuad-daoud wants to merge 1 commit into
stack/1-detectionfrom
stack/2-redeclaration
Open

fix(compilers/openapi): keep a conflicting redeclaration's losing type#436
fuad-daoud wants to merge 1 commit into
stack/1-detectionfrom
stack/2-redeclaration

Conversation

@fuad-daoud

Copy link
Copy Markdown
Collaborator

Closes #424. Not breaking.

When allOf branches declare the same field with incompatible types, the first declaration wins, a warning names both pointers, and the losing type was simply dropped — not written to Unmodeled.

Every other degradation in this compiler keeps what it could not model. That is the discipline: an Unmodeled entry with a Reason, so nothing reaches the IR in no form. This path was the exception, and it was silent to any consumer reading the document rather than the diagnostic stream.

102 occurrences in GitHub's published spec — e.g. webhook-fork/properties/forkee/allOf/1/properties/clone_url, where t/prim/url and t/prim/string disagree and the first wins. A consumer diffing two versions of that document sees "no change" across a release where the losing branch's type moved.

The whole ir.TypeRef is kept (so Nullable survives, not just the target ID), keyed by the redeclaration's own pointer so three conflicting branches keep three entries rather than the last overwriting the rest.

Scope note: the constraint half of the same diagnostic still discards its loser. That is left alone deliberately — #10 records the direction for it (intersect the bounds), and preserving the loser instead would settle a decision that already has one.


Stack 2 of 8. Base stack/1-detection — review and merge bottom-up. Every commit here passed make gate when it landed, and the full gate was re-run on the top of the stack. Run it as GOTOOLCHAIN=go1.26.3 make gate; this machine's Go 1.27 fails it for reasons unrelated to any change (#431).

🤖 Generated with Claude Code

https://claude.ai/code/session_01SYeBgDsskwnyitLgPGCPn1

When allOf branches declare one field with incompatible types the merge
keeps the first declaration and warns, naming both pointers. The losing
declaration was then dropped: it reached the IR in no form at all, so a
consumer reading the document rather than the diagnostic stream saw no
trace of it — and a diff across two revisions in which only the losing
branch's type moved reported no change. GitHub's published spec writes
this shape 102 times.

Every other degradation in this compiler keeps what it could not model.
This one now does too: the discarded ir.TypeRef is written to the merged
property's Unmodeled under ReasonDegradedLowering, keyed by the
redeclaration's own pointer so sibling branches never overwrite one
another, and stamped with the losing declaration's provenance.

The constraint half of the same diagnostic is deliberately left alone.
It also discards the redeclaration's keyword, but the recorded direction
there is to intersect the bounds so the merged field satisfies both
branches (#10), and preserving the loser instead would settle a decision
that already has one. The code comment on keepLosingType says so.

Fixes #424

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYeBgDsskwnyitLgPGCPn1
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