Skip to content

Emit map_entry as a typed option on synthetic entry messages - #113

Merged
trendvidia merged 1 commit into
trendvidiafrom
map-entry-typed-option
Jul 24, 2026
Merged

Emit map_entry as a typed option on synthetic entry messages#113
trendvidia merged 1 commit into
trendvidiafrom
map-entry-typed-option

Conversation

@trendvidia

Copy link
Copy Markdown
Owner

Fixes #110.

Root cause

Not an omission in message() alone — the generator serializes all options into unknown-field bytes (generator.optionsSetUnknown) for byte-exact protoc parity. The IR does model the option (generateMapEntries builds the MessageOptions value with the map_entry bit), and it was emitted — as unknown field 7: 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.NewFileIsMap() == false, protoreflect map accessors panicking, dynamicpb) read the typed field and see nothing.

Change

map_entry is 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). So message() now emits Options: {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_entry to synthetic entries) once it bumps past this.

Testing

New TestMapEntryOptionsTyped: typed field set, no residual unknown bytes, and protodesc.NewFile classifies the field IsMap(). Full make (race + protolegacy, vet, golangci-lint) and the dual-compiler protoc sweep pass clean.

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.
@trendvidia
trendvidia merged commit b97c1cf into trendvidia Jul 24, 2026
1 check passed
@trendvidia
trendvidia deleted the map-entry-typed-option branch July 24, 2026 04:20
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.
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.

fdp: synthetic map-entry messages are emitted without options.map_entry

1 participant