Emit map_entry as a typed option on synthetic entry messages - #113
Merged
Conversation
The generator serializes all options into unknown-field bytes for byte-exact protoc parity, so the synthetic map entry's map_entry marker never reached the typed MessageOptions field. Wire round-trips repair it, but reflection consumers of the in-memory FDP see a plain repeated message field: protodesc.NewFile classifies IsMap() false, protoreflect map accessors panic, and dynamicpb builds the wrong shape. map_entry is semantic, not metadata, and a synthetic entry can carry no other options — emit it as the typed field and skip the generic unknown-bytes path for map entries. The wire encoding is unchanged (protoc sweep stays byte-identical). Fixes #110.
This was referenced Jul 24, 2026
trendvidia
added a commit
that referenced
this pull request
Jul 24, 2026
User-written options serialize into unknown-field bytes for protoc byte parity, so typed accessors on in-memory descriptors read as unset until a marshal/unmarshal round-trip; the schema carriers and map_entry are the typed exemptions. This was previously recorded only in #113's commit message. Fixes #114.
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.
Fixes #110.
Root cause
Not an omission in
message()alone — the generator serializes all options into unknown-field bytes (generator.options→SetUnknown) for byte-exact protoc parity. The IR does model the option (generateMapEntriesbuilds theMessageOptionsvalue with themap_entrybit), and it was emitted — as unknown field7: 1. A wire round-trip parses that into the typed field, which is why the protoc-comparison sweep never caught it; but reflection consumers of the in-memory FDP (protodesc.NewFile→IsMap() == false, protoreflect map accessors panicking, dynamicpb) read the typed field and see nothing.Change
map_entryis semantic — it changes how every reflection consumer classifies the field — and a synthetic entry can carry no other options (it has no AST for users to attach any). Somessage()now emitsOptions: {MapEntry: true}as the typed field for map entries and skips the generic unknown-bytes path for them. Wire encoding is unchanged (typed field 7 and the previous unknown bytes encode identically; the protoc sweep stays byte-exact).Other options keep the unknown-bytes design; if an in-memory consumer ever needs typed
deprecated/features, that's a separate decision about the generic mechanism, not this marker.protocheck can drop its post-processing workaround (re-adding
map_entryto synthetic entries) once it bumps past this.Testing
New
TestMapEntryOptionsTyped: typed field set, no residual unknown bytes, andprotodesc.NewFileclassifies the fieldIsMap(). Fullmake(race + protolegacy, vet, golangci-lint) and the dual-compiler protoc sweep pass clean.