Make Parse return value-canonical Subject Identifier form#23
Merged
Conversation
Registry constructors return pointers so the codec can populate them
via UnmarshalJSON, but Parse previously leaked that pointer form. A
caller constructs identifiers as value literals (IssSubID{}) yet read
them back as *IssSubID, so a direct type assertion silently took the
!ok branch on whichever form it did not expect.
Normalize Parse to the value form: a reflection-based deref dereferences
the populated pointer before returning, applied to the built-ins, the
UnknownFormat carrier, and every nested aliases element. deref falls
back to the original pointer for an extension type registered with
pointer-receiver methods rather than panicking. Parse and the
SubjectIdentifier interface godoc now document the canonical value form
and the extension-type value-receiver constraint.
Also make AliasesID.Validate detect nesting by the format discriminator
(Format() == "aliases") instead of a value type assertion. The old
check missed a nested aliases identifier arriving from the wire, which
Parse populates in pointer form, so ErrNestedAliases silently failed to
fire on real input; a hand-built value-form nested alias was still
caught. Switch the OpaqueID and UnknownFormat compile-time assertions
to value form to match the other built-ins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Record the initial 0.1.0 API surface and the 0.2.0 value-canonical Parse change, replacing the stale pre-release "no public API yet" note. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Parsenow returns the value form of a Subject Identifier (subjectid.IssSubID, never*IssSubID), so the dynamic type a caller reads back fromParseis identical to the struct literal they construct by hand.Why
Registry constructors return pointers (
&IssSubID{}) so the codec can populate them viaUnmarshalJSON, butParseleaked that pointer form. Since every behavior method has a value receiver, a caller naturally buildsIssSubID{}but read back*IssSubID— a direct type assertion silently took the!okbranch on whichever form it didn't expect. The library's API now commits to one canonical dynamic form.This is settled before the first
v0.1.0tag, so there is no released contract to break.Changes
unmarshal.go— new reflectionderefhelper;Parsereturns value form for built-ins, theUnknownFormatcarrier, and nested aliases elements.dereffalls back to the original pointer for a pointer-receiver extension type instead of panicking.aliases.go— nested-aliases detection now usesFormat() == "aliases"instead of a value type assertion. This fixes a latent bug: a nested aliases identifier arriving from the wire (pointer form) previously escapedErrNestedAliases, while a hand-built value-form one was caught. New testTestParseNestedAliasesFromWireRejectedcovers it.subjectid.go/registry.go— godoc documents the canonical value form and the extension-type value-receiver constraint.opaque.go/unknown.go— compile-time assertions switched to value form, matching the other six built-ins, so a future pointer-receiver regression fails to compile.canonical_form_test.go.Verification
gofmtclean ·go vetclean ·golangci-lint0 issues ·go test -race ./...green.🤖 Generated with Claude Code