Conversation
Since square#3708, ProtoReader.decode(_:) under .returnNil retains an unrecognized singular enum value in unknownFields and returns nil. Generated Swift assigned that nil unconditionally, so when the same tag appeared twice as [recognized][unknown] the later occurrence erased the recognized value. For oneofs it also claimed the oneof's tag, dropping an already decoded sibling message case after the field loop. Generated Kotlin catches EnumConstantNotFoundException around the assignment and only records the unknown field, and protobuf runtimes only set recognized values. Emit `if let` around the enum decode so the assignment (and the oneof tag) happen only for a recognized value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
bek-kah
marked this pull request as ready for review
September 22, 2026 17:45
Member
|
@codex review |
dnkoutso
approved these changes
Sep 22, 2026
Member
|
@oldergod fyi |
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.
Follow-up to #3708. Fixes the generator edge disclosed there and tracked in #3710.
What goes wrong today
A protobuf message can carry the same field twice, and the last occurrence is meant to win. Since #3708, when generated Swift reads an enum value it does not recognize, the runtime keeps the raw bytes in
unknownFieldsand hands backnil. The generated decode loop then stored thatnilin the field.So if a message arrives as "field 2 = HOME, field 2 = some value this app doesn't know yet", Swift ends up with
type == nil(or the zero value in proto3) even though it just read HOME. Generated Kotlin keeps HOME, and so does Google's protobuf runtime.The same thing happens inside a
oneof. An unrecognized enum value also marked the oneof as "this enum case is set", which threw away a message case that had already been decoded a few bytes earlier.The fix
The generator now wraps each enum read in
if let, so the field (and the oneof marker) only change when the value was recognized. The unknown bytes still end up inunknownFieldsand are written back out unchanged.Before, for
Person.PhoneNumber(PhoneType type = 2) andOneOfs(oneof choice { NestedEnum enum_option = 1; … }):After:
If a message already has a field named
value, the binding is named_valueinstead (see theFooBar.swiftgolden). Repeated and map enum fields are untouched; they already skip unrecognized values inProtoReader.decode(into:).This is the shape generated Kotlin has always used: it wraps the read in
tryand only records the unknown field incatch (e: EnumConstantNotFoundException)(KotlinGenerator.kt L2099–L2110). Encoding is not affected and already matched Kotlin: known fields first, retained unknown bytes last.All golden files were regenerated with
./gradlew generateSwiftTests. Every changed line in them is one of the two rewrites above.How it is verified
New tests, all failing before the fix and passing after:
SwiftGeneratorTest.skipsAssignmentOfUnrecognizedEnumValueschecks the emitted Swift for a plain enum field, a field namedvalue, a repeated enum (unchanged), and a oneof enum with and without a message sibling.RoundTripTestsfeed "known value, then unknown value" bytes for a proto2 field, a oneof enum, a oneof next to a message case, and a proto3 field. Each checks that the known value survives, the unknown bytes land inunknownFields, and re-encoding gives back the exact input bytes. The oneof-next-to-a-message case needed a newmessage_optionfield in the test-onlyoneofs.proto.Two deliberate breakages to confirm the tests bite:
nilorMOBILEwhere the known value was expected).if let: only the "oneof next to a message" test fails, which is exactly what it exists to catch.Ran locally on a clean tree:
./gradlew :wire-runtime-swift:build -x xcTest generateSwiftTests :wire-swift-generator:test spotlessCheckandswift test(303 tests, 0 failures).