Explorar o código

[table-driven-branch] Implement support for enums. (#1907)

## Enum generators

While I was in here, I refactored the enum generators to create a common
base class but separate "open" and "closed" generators. This cleaned up
some of the generator code a bit, and gives us a foothold in the future
if we want to change the representation of one of these, like turning
open enums into `RawRepresentable` structs.

## Storage

As singular fields, we store the underlying `Int32` value of the enum in
storage instead of the Swift enum value because it's
simpler. For repeated enums, since we don't want to penalize the
performance of accessors that return Swift arrays, we store the `Array`
of Swift enum values.

## Binary encoding/decoding

The above means that we need trampoline functions for some specific
cases:

* Deinitializing and copying a repeated enum field.
* Checking whether two Swift enum arrays are equal.
* When encoding a repeated enum, we need to get the raw values.
* When decoding a singular or repeated enum, we need to validate the
`Int32`s on the wire to make sure they're valid cases.

So this change is larger than I had hoped, because we needed to
introduce a new trampoline (`performOnRawEnumValues`), and then it made
sense to rename some of the existing trampoline infrastructure that no
longer just serves submessages.
Tony Allevato hai 4 meses
pai
achega
f40cf4696e

+ 4 - 2
Makefile

@@ -371,7 +371,7 @@ regenerate-test-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufTest
 		`find Protos/Tests/SwiftProtobufPluginLibraryTests -type f -name "*.proto"`
 
 # TODO: Remove this once all the existing protos just work as table-driven.
-regenerate-table-driven-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufTests/unittest.proto Protos/Tests/SwiftProtobufTests/unittest_import.proto Protos/Tests/SwiftProtobufTests/unittest_import_public.proto Protos/Tests/SwiftProtobufTests/unittest_swift_required_fields.proto Protos/Tests/SwiftProtobufTests/unittest_swift_all_required_types.proto
+regenerate-table-driven-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProtobufTests/unittest.proto Protos/Tests/SwiftProtobufTests/unittest_import.proto Protos/Tests/SwiftProtobufTests/unittest_import_public.proto Protos/Tests/SwiftProtobufTests/unittest_swift_required_fields.proto Protos/Tests/SwiftProtobufTests/unittest_swift_all_required_types.proto Protos/Tests/SwiftProtobufTests/unittest_swift_enum_proto2.proto Protos/Tests/SwiftProtobufTests/unittest_swift_enum_proto3.proto
 	find Tests/ExperimentalTableDrivenSwiftProtobufTests -name "*.pb.swift" -exec rm -f {} \;
 	${GENERATE_SRCS} \
 	    -I Protos/Tests/SwiftProtobufTests \
@@ -381,7 +381,9 @@ regenerate-table-driven-protos: build ${PROTOC_GEN_SWIFT} Protos/Tests/SwiftProt
 		Protos/Tests/SwiftProtobufTests/unittest_import.proto \
 		Protos/Tests/SwiftProtobufTests/unittest_import_public.proto \
 		Protos/Tests/SwiftProtobufTests/unittest_swift_all_required_types.proto \
-		Protos/Tests/SwiftProtobufTests/unittest_swift_required_fields.proto
+		Protos/Tests/SwiftProtobufTests/unittest_swift_required_fields.proto \
+		Protos/Tests/SwiftProtobufTests/unittest_swift_enum_proto2.proto \
+		Protos/Tests/SwiftProtobufTests/unittest_swift_enum_proto3.proto
 
 # Rebuild the protos for FuzzTesting/Sources/FuzzCommon, the file lives in the
 # Protos/Tests/SwiftProtobufTests to have just one copy.

+ 72 - 48
Sources/SwiftProtobuf/_MessageLayout.swift

@@ -73,7 +73,7 @@ import Foundation
 /// ```
 /// +---------------------+-----------+-----------+------------------+------------+
 /// | Bytes 0-4           | Bytes 5-7 | Bytes 8-9 | Bytes 10-11      | Byte 12    |
-/// | Field number & mode | Offset    | Presence  | Submessage index | Field type |
+/// | Field number & mode | Offset    | Presence  | Trampoline index | Field type |
 /// +---------------------+-----------+-----------+------------------+------------+
 /// ```
 ///
@@ -87,8 +87,8 @@ import Foundation
 ///         field is stored.
 ///     *   Otherwise, the value is the index of the has-bit used to store the presence of the
 ///         field.
-/// *   Bytes 10-11: For message/group fields, an opaque index as a base-128 integer used to
-///     request the metatype of the submessage from the containing message's submessage accessor.
+/// *   Bytes 10-11: For message/group/enum fields, an opaque index as a base-128 integer used to
+///     perform operations on submessage or enum fields that require the concrete type hint.
 /// *   Byte 12: The type of the field.
 @_spi(ForGeneratedCodeOnly) public struct _MessageLayout: @unchecked Sendable {
     // Using `UnsafeRawBufferPointer` requires that we declare the `Sendable` conformance as
@@ -98,82 +98,102 @@ import Foundation
     /// The encoded layout of the fields of the message.
     private let layout: UnsafeRawBufferPointer
 
-    /// The function type for the generated function that is called to deinitialize a field
-    /// of a complex type.
-    public typealias SubmessageDeinitializer = (
-        _ token: SubmessageToken,
+    /// The function type for the generated function that is called to deinitialize a field whose
+    /// type is a message, array of messages, or array of enums.
+    public typealias TrampolineDeinitializer = (
+        _ token: TrampolineToken,
         _ field: FieldLayout,
         _ storage: _MessageStorage
     ) -> Void
 
-    /// The function type for the generated function that is called to copy a field of a
-    /// complex type.
-    public typealias SubmessageCopier = (
-        _ token: SubmessageToken,
+    /// The function type for the generated function that is called to copy a field whose type is a
+    /// message, array of messages, or array of enums.
+    public typealias TrampolineCopier = (
+        _ token: TrampolineToken,
         _ field: FieldLayout,
         _ source: _MessageStorage,
         _ destination: _MessageStorage
     ) -> Void
 
-    /// The function type for the generated function that is called to test the values of a complex
-    /// field type from two different messages for equality.
-    public typealias SubmessageEquater = (
-        _ token: SubmessageToken,
+    /// The function type for the generated function that is called to test for equality two fields
+    /// whose types are a message, array of messages, or array of enums.
+    public typealias TrampolineEquater = (
+        _ token: TrampolineToken,
         _ field: FieldLayout,
         _ lhs: _MessageStorage,
         _ rhs: _MessageStorage
     ) -> Bool
 
     /// The function type for the generated function that is called to test if a field whose type
-    /// is a submessage is initialized.
+    /// is a message or array of messages is initialized.
     public typealias SubmessageInitializedChecker = (
-        _ token: SubmessageToken,
+        _ token: TrampolineToken,
         _ field: FieldLayout,
         _ storage: _MessageStorage
     ) -> Bool
 
     /// The function type for the generated function that is called to perform an arbitrary
-    /// operation on the storage of a submessage field.
+    /// operation on the storage of a field whose type is a message or array of messages.
     public typealias SubmessageStoragePerformer = (
-        _ token: SwiftProtobuf._MessageLayout.SubmessageToken,
+        _ token: TrampolineToken,
         _ field: FieldLayout,
-        _ storage: SwiftProtobuf._MessageStorage,
-        _ operation: SubmessageStorageOperation,
-        _ perform: (SwiftProtobuf._MessageStorage) throws -> Bool
+        _ storage: _MessageStorage,
+        _ operation: TrampolineFieldOperation,
+        _ perform: (_MessageStorage) throws -> Bool
     ) throws -> Bool
 
-    /// The function that is called to deinitialize a field whose type is a message.
-    let deinitializeSubmessage: SubmessageDeinitializer
+    /// The function type for the generated function that is called to perform an arbitrary
+    /// operation on the raw values of a singular or repeated enum field.
+    public typealias RawEnumValuesPerformer = (
+        _ token: TrampolineToken,
+        _ field: FieldLayout,
+        _ storage: _MessageStorage,
+        _ operation: TrampolineFieldOperation,
+        _ perform: (inout Int32) throws -> Bool,
+        _ onInvalidValue: (Int32) -> Void
+    ) throws -> Void
 
-    /// The function that is called to copy a field whose type is a submessage.
-    let copySubmessage: SubmessageCopier
+    /// The function that is called to deinitialize a field whose type is a message (singular or
+    /// repeated) or a repeated enum field.
+    let deinitializeField: TrampolineDeinitializer
 
-    /// The function that is called to test a field whose type is a submessage for equality.
-    let areSubmessagesEqual: SubmessageEquater
+    /// The function that is called to copy a field whose type is a message (singular or repeated)
+    /// or a repeated enum field.
+    let copyField: TrampolineCopier
+
+    /// The function that is called to test a field whose type is a message, array of messages, or
+    /// array of enums for equality.
+    let areFieldsEqual: TrampolineEquater
 
     /// The function that is called to perform an arbitrary operation on the storage of a submessage
     /// field.
     let performOnSubmessageStorage: SubmessageStoragePerformer
 
+    /// The function that is called to perform an arbitrary operation on the raw values of an enum
+    /// field.
+    let performOnRawEnumValues: RawEnumValuesPerformer
+
     /// Creates a new message layout and submessage operations from the given values.
     ///
     /// This initializer is public because generated messages need to call it.
     public init(
         layout: StaticString,
-        deinitializeSubmessage: @escaping SubmessageDeinitializer,
-        copySubmessage: @escaping SubmessageCopier,
-        areSubmessagesEqual: @escaping SubmessageEquater,
-        performOnSubmessageStorage: @escaping SubmessageStoragePerformer
+        deinitializeField: @escaping TrampolineDeinitializer,
+        copyField: @escaping TrampolineCopier,
+        areFieldsEqual: @escaping TrampolineEquater,
+        performOnSubmessageStorage: @escaping SubmessageStoragePerformer,
+        performOnRawEnumValues: @escaping RawEnumValuesPerformer
     ) {
         precondition(
             layout.hasPointerRepresentation,
             "The layout string should have a pointer-based representation; this is a generator bug"
         )
         self.layout = UnsafeRawBufferPointer(start: layout.utf8Start, count: layout.utf8CodeUnitCount)
-        self.deinitializeSubmessage = deinitializeSubmessage
-        self.copySubmessage = copySubmessage
-        self.areSubmessagesEqual = areSubmessagesEqual
+        self.deinitializeField = deinitializeField
+        self.copyField = copyField
+        self.areFieldsEqual = areFieldsEqual
         self.performOnSubmessageStorage = performOnSubmessageStorage
+        self.performOnRawEnumValues = performOnRawEnumValues
         precondition(version == 0, "This runtime only supports version 0 message layouts")
         precondition(
             self.layout.count == messageLayoutHeaderSize + self.fieldCount * fieldLayoutSize,
@@ -194,17 +214,20 @@ import Foundation
     public init(layout: StaticString) {
         self.init(
             layout: layout,
-            deinitializeSubmessage: { _, _, _ in
+            deinitializeField: { _, _, _ in
                 preconditionFailure("This should have been unreachable; this is a generator bug")
             },
-            copySubmessage: { _, _, _, _ in
+            copyField: { _, _, _, _ in
                 preconditionFailure("This should have been unreachable; this is a generator bug")
             },
-            areSubmessagesEqual: { _, _, _, _ in
+            areFieldsEqual: { _, _, _, _ in
                 preconditionFailure("This should have been unreachable; this is a generator bug")
             },
             performOnSubmessageStorage: { _, _, _, _, _ in
                 preconditionFailure("This should have been unreachable; this is a generator bug")
+            },
+            performOnRawEnumValues: { _, _, _, _, _, _ in
+                preconditionFailure("This should have been unreachable; this is a generator bug")
             }
         )
     }
@@ -323,25 +346,26 @@ extension _MessageLayout {
 
 extension _MessageLayout {
     /// An opaque token that is used to ask a message for the metatype of one of its submessage
-    /// fields.
-    public struct SubmessageToken: Sendable, Equatable {
-        /// The index that identifies the submessage type being requested.
+    /// or enum fields.
+    public struct TrampolineToken: Sendable, Equatable {
+        /// The index that identifies the submessage or enum type being requested.
         public let index: Int
     }
 }
 
-/// The nature of the operation that is being performed by `performOnSubmessageStorage`.
-@_spi(ForGeneratedCodeOnly) public enum SubmessageStorageOperation {
-    /// The submessage's storage is being read.
+/// The nature of the operation that is being performed by `performOnSubmessageStorage` or
+/// `performOnEnumRawValues`.
+@_spi(ForGeneratedCodeOnly) public enum TrampolineFieldOperation {
+    /// The submessage's storage or enum's raw value is being read.
     case read
 
-    /// The submessage's storage is being mutated.
+    /// The submessage's storage or enum's raw value is being mutated.
     ///
-    /// The submessage should be created if it is not already present. If already present, the
-    /// storage should be made unique before the mutation.
+    /// For submessages, the value should be created if it is not already present. If already
+    /// present, the storage should be made unique before the mutation.
     case mutate
 
-    /// The submessage's array storage is having a new value appended to it.
+    /// The submessage's array storage or enum's array value is having a new value appended to it.
     ///
     /// The array should be created if it is not already present.
     case append

+ 122 - 8
Sources/SwiftProtobuf/_MessageStorage+BinaryDecoding.swift

@@ -124,8 +124,14 @@ extension _MessageStorage {
                 }
 
             case .enum:
-                // TODO: Support enums.
-                break
+                switch tag.wireFormat {
+                case .varint:
+                    try updateEnumValue(of: field, from: &reader, fieldNumber: tag.fieldNumber, isRepeated: true)
+                case .lengthDelimited:
+                    try appendPackedEnumValues(from: &reader, to: field, fieldNumber: tag.fieldNumber)
+                default:
+                    return false
+                }
 
             case .fixed32:
                 return try appendMaybePackedValues(from: &reader, to: field, tag: tag, unpackedWireFormat: .fixed32) {
@@ -145,7 +151,7 @@ extension _MessageStorage {
             case .group:
                 guard tag.wireFormat == .startGroup else { return false }
                 _ = try layout.performOnSubmessageStorage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self,
                     .append
@@ -175,7 +181,7 @@ extension _MessageStorage {
             case .message:
                 guard tag.wireFormat == .lengthDelimited else { return false }
                 _ = try layout.performOnSubmessageStorage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self,
                     .append
@@ -253,8 +259,8 @@ extension _MessageStorage {
                 updateValue(of: field, to: Double(bitPattern: try reader.nextLittleEndianUInt64()))
 
             case .enum:
-                // TODO: Support enums.
-                break
+                guard tag.wireFormat == .varint else { return false }
+                try updateEnumValue(of: field, from: &reader, fieldNumber: tag.fieldNumber, isRepeated: false)
 
             case .fixed32:
                 guard tag.wireFormat == .fixed32 else { return false }
@@ -271,7 +277,7 @@ extension _MessageStorage {
             case .group:
                 guard tag.wireFormat == .startGroup else { return false }
                 _ = try layout.performOnSubmessageStorage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self,
                     .mutate
@@ -299,7 +305,7 @@ extension _MessageStorage {
             case .message:
                 guard tag.wireFormat == .lengthDelimited else { return false }
                 _ = try layout.performOnSubmessageStorage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self,
                     .mutate
@@ -398,7 +404,115 @@ extension _MessageStorage {
         default:
             return false
         }
+    }
+
+    /// Updates the value of the given field by reading the next varint from the reader and treating
+    /// it as the raw value of that enum field.
+    ///
+    /// This method handles both the singular case (by setting the field) and the repeated unpacked
+    /// case (by appending to it).
+    private func updateEnumValue(
+        of field: FieldLayout,
+        from reader: inout WireFormatReader,
+        fieldNumber: Int,
+        isRepeated: Bool
+    ) throws {
+        var alreadyReadValue = false
+        try layout.performOnRawEnumValues(
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
+            field,
+            self,
+            isRepeated ? .append : .mutate
+        ) { outRawValue in
+            // In the singular case, this doesn't matter. In the repeated case, we need to return
+            // true *exactly once* and then return false the next time this is called. This is
+            // because the same trampoline function is used to handle the packed case, where it
+            // calls the closure over and over until it returns that there is no data left.
+            guard !alreadyReadValue else { return false }
+            outRawValue = Int32(bitPattern: UInt32(truncatingIfNeeded: try reader.nextVarint()))
+            alreadyReadValue = true
+            return true
+        } /*onInvalidValue*/ _: { rawValue in
+            // Serialize the invalid values into a binary blob that will be passed as a single
+            // varint field into unknown fields.
+            //
+            // Note that because we've already read the value, we have to put it into unknown fields
+            // ourselves. The `decodeUnknownField` flow assumes that we've only read the tag and
+            // are positioned at the beginning of the value.
+            let fieldTag = FieldTag(fieldNumber: fieldNumber, wireFormat: .varint)
+            let fieldSize = fieldTag.encodedSize + Varint.encodedSize(of: Int64(rawValue))
+            var field = Data(count: fieldSize)
+            field.withUnsafeMutableBytes { body in
+                var encoder = BinaryEncoder(forWritingInto: body)
+                encoder.startField(tag: fieldTag)
+                encoder.putVarInt(value: Int64(rawValue))
+                unknownFields.append(protobufBytes: UnsafeRawBufferPointer(body))
+            }
+        }
+    }
+
+    /// Appends either a single enum value or multiple packed values to the array value of the
+    /// given field, depending on the wire format of the corresponding tag.
+    ///
+    /// - Parameters:
+    ///   - reader: The reader from which the values to append should be read.
+    ///   - field: The field being decoded.
+    ///   - tag: The tag that was read from the wire.
+    private func appendPackedEnumValues(
+        from reader: inout WireFormatReader,
+        to field: FieldLayout,
+        fieldNumber: Int
+    ) throws {
+        assert(field.rawFieldType == .enum, "Internal error: should only be called for enum fields")
+
+        let elementsBuffer = try reader.nextLengthDelimitedSlice()
+        guard elementsBuffer.baseAddress != nil, elementsBuffer.count > 0 else {
+            return
+        }
+
+        // If we see any invalid values during decode them, save them here so we can write them
+        // into unknown fields at the end.
+        var invalidValues: [Int32] = []
+
+        // Recursion budget is irrelevant here because we're only reading enums.
+        var elementsReader = WireFormatReader(buffer: elementsBuffer, recursionBudget: 0)
+
+        try layout.performOnRawEnumValues(
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
+            field,
+            self,
+            .append
+        ) { outRawValue in
+            guard elementsReader.hasAvailableData else { return false }
+            outRawValue = Int32(bitPattern: UInt32(truncatingIfNeeded: try elementsReader.nextVarint()))
+            return true
+        } /*onInvalidValue*/ _: {
+            invalidValues.append($0)
+        }
 
+        if invalidValues.isEmpty {
+            return
+        }
+
+        // Serialize all of the invalid values into a binary blob that will be passed as a
+        // single length-delimited field into unknown fields.
+        //
+        // Note that because we've already read the values, we have to put them into unknown fields
+        // ourselves. The `decodeUnknownField` flow assumes that we've only read the tag and
+        // are positioned at the beginning of the value.
+        let fieldTag = FieldTag(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+        let bodySize = invalidValues.reduce(0) { $0 + Varint.encodedSize(of: Int64($1)) }
+        let fieldSize = fieldTag.encodedSize + Varint.encodedSize(of: Int64(bodySize)) + bodySize
+        var field = Data(count: fieldSize)
+        field.withUnsafeMutableBytes { body in
+            var encoder = BinaryEncoder(forWritingInto: body)
+            encoder.startField(tag: fieldTag)
+            encoder.putVarInt(value: Int64(bodySize))
+            for value in invalidValues {
+                encoder.putVarInt(value: Int64(value))
+            }
+            unknownFields.append(protobufBytes: UnsafeRawBufferPointer(body))
+        }
     }
 
     /// Reads the next length-delimited slice from the reader and calls the given function to

+ 58 - 6
Sources/SwiftProtobuf/_MessageStorage+BinaryEncoding.swift

@@ -109,8 +109,7 @@ extension _MessageStorage {
                 }
 
             case .enum:
-                // TODO: Support enums.
-                break
+                try serializeRepeatedEnumField(for: fieldNumber, field: field, into: &encoder, isPacked: isPacked)
 
             case .fixed32:
                 let values = assumedPresentValue(at: offset, as: [UInt32].self)
@@ -274,8 +273,7 @@ extension _MessageStorage {
                 serializeDoubleField(assumedPresentValue(at: offset), for: fieldNumber, into: &encoder)
 
             case .enum:
-                // TODO: Support enums.
-                break
+                serializeInt32Field(assumedPresentValue(at: offset), for: fieldNumber, into: &encoder)
 
             case .fixed32:
                 serializeFixed32Field(assumedPresentValue(at: offset), for: fieldNumber, into: &encoder)
@@ -381,7 +379,7 @@ extension _MessageStorage {
         options: BinaryEncodingOptions
     ) throws {
         _ = try layout.performOnSubmessageStorage(
-            _MessageLayout.SubmessageToken(index: field.submessageIndex),
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
             field,
             self,
             .read
@@ -419,7 +417,7 @@ extension _MessageStorage {
         options: BinaryEncodingOptions
     ) throws {
         _ = try layout.performOnSubmessageStorage(
-            _MessageLayout.SubmessageToken(index: field.submessageIndex),
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
             field,
             self,
             .read
@@ -494,4 +492,58 @@ extension _MessageStorage {
             encode(value, &encoder)
         }
     }
+
+    /// Serializes the field tag and values for a repeated (packed or unpacked) `enum` field.
+    private func serializeRepeatedEnumField(
+        for fieldNumber: Int,
+        field: FieldLayout,
+        into encoder: inout BinaryEncoder,
+        isPacked: Bool
+    ) throws {
+        if isPacked {
+            // First, iterate over the values to compute the packed length.
+            var length = 0
+            _ = try layout.performOnRawEnumValues(
+                _MessageLayout.TrampolineToken(index: field.submessageIndex),
+                field,
+                self,
+                .read
+            ) {
+                length += Varint.encodedSize(of: $0)
+                return true
+            } /*onInvalidValue*/ _: { _ in
+                assertionFailure("invalid value handler should never be called for .read")
+            }
+
+            encoder.startField(fieldNumber: fieldNumber, wireFormat: .lengthDelimited)
+            encoder.putVarInt(value: length)
+
+            // Then, iterate over them again to encode the actual varints.
+            _ = try layout.performOnRawEnumValues(
+                _MessageLayout.TrampolineToken(index: field.submessageIndex),
+                field,
+                self,
+                .read
+            ) {
+                encoder.putVarInt(value: Int64($0))
+                return true
+            } /*onInvalidValue*/ _: { _ in
+                assertionFailure("invalid value handler should never be called for .read")
+            }
+        } else {
+            // Iterate over the raw values and encode each as its own tag and varint.
+            _ = try layout.performOnRawEnumValues(
+                _MessageLayout.TrampolineToken(index: field.submessageIndex),
+                field,
+                self,
+                .read
+            ) {
+                encoder.startField(fieldNumber: fieldNumber, wireFormat: .varint)
+                encoder.putVarInt(value: Int64($0))
+                return true
+            } /*onInvalidValue*/ _: { _ in
+                assertionFailure("invalid value handler should never be called for .read")
+            }
+        }
+    }
 }

+ 34 - 7
Sources/SwiftProtobuf/_MessageStorage+BinarySize.swift

@@ -23,7 +23,7 @@ extension _MessageStorage {
             serializedSize += serializedByteSize(of: field)
         }
         serializedSize += unknownFields.data.count
-         // TODO: Support extensions.
+        // TODO: Support extensions.
         return serializedSize
     }
 
@@ -59,8 +59,7 @@ extension _MessageStorage {
                 return fixedWidthRepeatedFieldSize(for: fieldNumber, at: offset, isPacked: isPacked, as: Double.self)
 
             case .enum:
-                // TODO: Support enums.
-                return 0
+                return serializedByteSize(ofRepeatedEnumField: field, fieldNumber: fieldNumber)
 
             case .fixed32:
                 return fixedWidthRepeatedFieldSize(for: fieldNumber, at: offset, isPacked: isPacked, as: UInt32.self)
@@ -161,8 +160,8 @@ extension _MessageStorage {
                 return fixedWidthSingularFieldSize(for: fieldNumber, as: Double.self)
 
             case .enum:
-                // TODO: Support enums.
-                return 0
+                return FieldTag.encodedSize(ofTagWithFieldNumber: fieldNumber)
+                    + Varint.encodedSize(of: assumedPresentValue(at: offset, as: Int32.self))
 
             case .fixed32:
                 return fixedWidthSingularFieldSize(for: fieldNumber, as: UInt32.self)
@@ -247,6 +246,34 @@ extension _MessageStorage {
         return (tagSize + MemoryLayout<T>.size) * count
     }
 
+    /// Returns the serialized byte size of the given repeated enum field.
+    ///
+    /// This function takes the field number as a separate argument even though it can be computed
+    /// from the `FieldLayout` to avoid the (minor but non-zero) cost of decoding it again from the
+    /// layout, since that has already been done by the caller.
+    private func serializedByteSize(ofRepeatedEnumField field: FieldLayout, fieldNumber: Int) -> Int {
+        var totalEnumsSize = 0
+        var count = 0
+        _ = try! layout.performOnRawEnumValues(
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
+            field,
+            self,
+            .read
+        ) {
+            count += 1
+            totalEnumsSize += Varint.encodedSize(of: $0)
+            return true
+        } /*onInvalidValue*/ _: { _ in
+        }
+        if field.fieldMode.isPacked {
+            // Packed: we need to add a single (length-delimited) tag and a varint for the length.
+            return totalEnumsSize + FieldTag.encodedSize(ofTagWithFieldNumber: fieldNumber)
+                + Varint.encodedSize(of: UInt64(totalEnumsSize))
+        }
+        // Unpacked: there will be a separate tag for each value.
+        return totalEnumsSize + FieldTag.encodedSize(ofTagWithFieldNumber: fieldNumber) * count
+    }
+
     /// Returns the serialized byte size of the given submessage field.
     ///
     /// Since this function recurses via `performOnSubmessageStorage`, it supports both the singular
@@ -259,7 +286,7 @@ extension _MessageStorage {
     private func serializedByteSize(ofMessageField field: FieldLayout, fieldNumber: Int) -> Int {
         var totalMessagesSize = 0
         _ = try! layout.performOnSubmessageStorage(
-            _MessageLayout.SubmessageToken(index: field.submessageIndex),
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
             field,
             self,
             .read
@@ -288,7 +315,7 @@ extension _MessageStorage {
     private func serializedByteSize(ofGroupField field: FieldLayout, fieldNumber: Int) -> Int {
         var totalMessagesSize = 0
         _ = try! layout.performOnSubmessageStorage(
-            _MessageLayout.SubmessageToken(index: field.submessageIndex),
+            _MessageLayout.TrampolineToken(index: field.submessageIndex),
             field,
             self,
             .read

+ 214 - 42
Sources/SwiftProtobuf/_MessageStorage.swift

@@ -81,18 +81,15 @@ import Foundation
             case .bool: deinitializeField(field, type: [Bool].self)
             case .bytes: deinitializeField(field, type: [Data].self)
             case .double: deinitializeField(field, type: [Double].self)
-            case .enum:
-                // TODO: Figure out how we represent enums (open vs. closed).
-                break
-            case .fixed32, .uint32: deinitializeField(field, type: [UInt32].self)
-            case .fixed64, .uint64: deinitializeField(field, type: [UInt64].self)
-            case .float: deinitializeField(field, type: [Float].self)
-            case .group, .message:
-                layout.deinitializeSubmessage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+            case .enum, .group, .message:
+                layout.deinitializeField(
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self
                 )
+            case .fixed32, .uint32: deinitializeField(field, type: [UInt32].self)
+            case .fixed64, .uint64: deinitializeField(field, type: [UInt64].self)
+            case .float: deinitializeField(field, type: [Float].self)
             case .int32, .sfixed32, .sint32: deinitializeField(field, type: [Int32].self)
             case .int64, .sfixed64, .sint64: deinitializeField(field, type: [Int64].self)
             case .string: deinitializeField(field, type: [String].self)
@@ -104,8 +101,8 @@ import Foundation
             case .bytes: deinitializeField(field, type: Data.self)
             case .string: deinitializeField(field, type: String.self)
             case .group, .message:
-                layout.deinitializeSubmessage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                layout.deinitializeField(
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self
                 )
@@ -175,19 +172,16 @@ extension _MessageStorage {
                 case .bool: copyField(field, to: destination, type: [Bool].self)
                 case .bytes: copyField(field, to: destination, type: [Data].self)
                 case .double: copyField(field, to: destination, type: [Double].self)
-                case .enum:
-                    // TODO: Figure out how we represent enums (open vs. closed).
-                    break
-                case .fixed32, .uint32: copyField(field, to: destination, type: [UInt32].self)
-                case .fixed64, .uint64: copyField(field, to: destination, type: [UInt64].self)
-                case .float: copyField(field, to: destination, type: [Float].self)
-                case .group, .message:
-                    layout.copySubmessage(
-                        _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                case .enum, .group, .message:
+                    layout.copyField(
+                        _MessageLayout.TrampolineToken(index: field.submessageIndex),
                         field,
                         self,
                         destination
                     )
+                case .fixed32, .uint32: copyField(field, to: destination, type: [UInt32].self)
+                case .fixed64, .uint64: copyField(field, to: destination, type: [UInt64].self)
+                case .float: copyField(field, to: destination, type: [Float].self)
                 case .int32, .sfixed32, .sint32: copyField(field, to: destination, type: [Int32].self)
                 case .int64, .sfixed64, .sint64: copyField(field, to: destination, type: [Int64].self)
                 case .string: copyField(field, to: destination, type: [String].self)
@@ -206,8 +200,8 @@ extension _MessageStorage {
                     if field.offset < firstNontrivialStorageOffset {
                         firstNontrivialStorageOffset = field.offset
                     }
-                    layout.copySubmessage(
-                        _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    layout.copyField(
+                        _MessageLayout.TrampolineToken(index: field.submessageIndex),
                         field,
                         self,
                         destination
@@ -264,7 +258,7 @@ extension _MessageStorage {
     /// - Returns: The value returned from the closure.
     public func performOnSubmessageStorage<T: _MessageImplementationBase>(
         of field: FieldLayout,
-        operation: SubmessageStorageOperation,
+        operation: TrampolineFieldOperation,
         type: T.Type,
         perform: (_MessageStorage) throws -> Bool
     ) rethrows -> Bool {
@@ -307,7 +301,7 @@ extension _MessageStorage {
     /// - Returns: The value returned from the last invocation of the closure.
     public func performOnSubmessageStorage<T: _MessageImplementationBase>(
         of field: FieldLayout,
-        operation: SubmessageStorageOperation,
+        operation: TrampolineFieldOperation,
         type: [T].Type,
         perform: (_MessageStorage) throws -> Bool
     ) rethrows -> Bool {
@@ -341,6 +335,112 @@ extension _MessageStorage {
         }
     }
 
+    /// Called by generated trampoline functions to invoke the given closure on the raw value of a
+    /// singular enum field, providing the type hint of the concrete enum type.
+    ///
+    /// - Precondition: For read operations, the field is already known to be present.
+    ///
+    /// - Parameters:
+    ///   - field: The enum field being operated on.
+    ///   - operation: The specific operation to perform on the field.
+    ///   - type: The concrete type of the enum.
+    ///   - perform: A closure called with the (possibly mutable) value of the field. For `.read`
+    ///     operations, the incoming value will be the actual value of the field, and mutating it
+    ///     will be ignored. For `.mutate`, the incoming value is not specified and the closure
+    ///     must mutate it to supply the desired value.
+    ///   - onInvalidValue: A closure that is called during `.mutate` operations if the raw value
+    ///     returned by the `perform` closure is not a valid enum case.
+    public func performOnRawEnumValues<T: Enum>(
+        of field: FieldLayout,
+        operation: TrampolineFieldOperation,
+        type: T.Type,
+        perform: (inout Int32) throws -> Bool,
+        onInvalidValue: (Int32) -> Void
+    ) rethrows {
+        switch operation {
+        case .read:
+            // When reading, we can get the raw value directly from storage, and we don't need to
+            // verify it against the defined values in the actual enum.
+            var rawValue = assumedPresentValue(at: field.offset, as: Int32.self)
+            _ = try perform(&rawValue)
+
+        case .mutate:
+            // When updating a singular enum field, verify that it is a defined enum case. If not,
+            // call the invalid value handler.
+            var rawValue: Int32 = 0
+            _ = try perform(&rawValue)
+            if T(rawValue: Int(rawValue)) != nil {
+                updateValue(of: field, to: rawValue)
+            } else {
+                onInvalidValue(rawValue)
+            }
+
+        case .append:
+            preconditionFailure("Internal error: singular performOnRawEnumValues should not be called to append")
+        }
+    }
+
+    /// Called by generated trampoline functions to invoke the given closure on the raw value of
+    /// each element in a repeated enum field, providing the type hint of the concrete enum type.
+    ///
+    /// The closure can return false to stop iteration over the values early. Furthermore, when the
+    /// operation is `.append`, the closure will be called repeatedly **until** it returns false.
+    /// Likewise, if the closure throws an error, that error will be propagated all the way to the
+    /// caller.
+    ///
+    /// - Precondition: For the read and mutate operations, the field is already known to be
+    ///   present.
+    ///
+    /// - Parameters:
+    ///   - field: The enum field being operated on.
+    ///   - operation: The specific operation to perform on the field.
+    ///   - type: The concrete type of the enum.
+    ///   - perform: A closure called with the (possibly mutable) value of the field. For `.read`
+    ///     operations, the incoming value will be the actual value of the field, and mutating it
+    ///     will be ignored. For `.mutate` and `.append`, the incoming value is not specified, and
+    ///     the closure must mutate it to supply the desired value.
+    ///   - onInvalidValue: A closure that is called during `.mutate` and `.append` operations if
+    ///     the raw value returned by the `perform` closure is not a valid enum case.
+    public func performOnRawEnumValues<T: Enum>(
+        of field: FieldLayout,
+        operation: TrampolineFieldOperation,
+        type: [T].Type,
+        perform: (inout Int32) throws -> Bool,
+        onInvalidValue: (Int32) -> Void
+    ) rethrows {
+        switch operation {
+        case .read:
+            for value in assumedPresentValue(at: field.offset, as: [T].self) {
+                var rawValue = Int32(value.rawValue)
+                guard try perform(&rawValue) else { break }
+            }
+
+        case .mutate:
+            preconditionFailure("Internal error: repeated performOnRawEnumValues should not be called to mutate")
+
+        case .append:
+            let pointer = (buffer.baseAddress! + field.offset).bindMemory(to: [T].self, capacity: 1)
+            var rawValue: Int32 = 0
+            var isFieldPresent = isPresent(field)
+            while try perform(&rawValue) {
+                if let newValue = T(rawValue: Int(rawValue)) {
+                    if !isFieldPresent {
+                        pointer.initialize(to: [])
+                        switch field.presence {
+                        case .hasBit(let hasByteOffset, let hasMask):
+                            _ = updatePresence(hasBit: (hasByteOffset, hasMask), willBeSet: true)
+                        case .oneOfMember(let oneofOffset):
+                            _ = updatePopulatedOneofMember((oneofOffset, field.fieldNumber))
+                        }
+                        isFieldPresent = true
+                    }
+                    pointer.pointee.append(newValue)
+                } else {
+                    onInvalidValue(rawValue)
+                }
+            }
+        }
+    }
 }
 
 // MARK: - Presence helpers
@@ -375,6 +475,20 @@ extension _MessageStorage {
     func assumedPresentValue<Value>(at offset: Int, as type: Value.Type = Value.self) -> Value {
         (buffer.baseAddress! + offset).bindMemory(to: Value.self, capacity: 1).pointee
     }
+
+    /// Returns the value at the given offset in the storage.
+    ///
+    /// - Precondition: The value must already be known to be present.
+    @_alwaysEmitIntoClient @inline(__always)
+    func assumedPresentValue<Value: Enum>(at offset: Int, as type: Value.Type = Value.self) -> Value {
+        // It is always safe to force-unwrap this. For open enums, the raw value initializer never
+        // fails. For closed enums, it fails if the raw value is not a valid case, but such a value
+        // should never cause presence to be set. For example, during decoding such a value would be
+        // placed in unknown fields.
+        //
+        // TODO: Change this to `Int32` when we're using that as the raw value type.
+        Value(rawValue: Int((buffer.baseAddress! + offset).bindMemory(to: Int32.self, capacity: 1).pointee))!
+    }
 }
 
 // MARK: - Field readers used by generated accessors
@@ -481,6 +595,20 @@ extension _MessageStorage {
         return (buffer.baseAddress! + offset).bindMemory(to: [Key: Value].self, capacity: 1).pointee
     }
 
+    /// Returns the protobuf enum value at the given offset in the storage, or the default value if
+    /// the value is not present.
+    @_alwaysEmitIntoClient @inline(__always)
+    public func value<T: Enum>(at offset: Int, default defaultValue: T, hasBit: HasBit) -> T {
+        guard isPresent(hasBit: hasBit) else { return defaultValue }
+        // It is always safe to force-unwrap this. For open enums, the raw value initializer never
+        // fails. For closed enums, it fails if the raw value is not a valid case, but such a value
+        // should never cause presence to be set. For example, during decoding such a value would be
+        // placed in unknown fields.
+        //
+        // TODO: Change this to `Int32` when we're using that as the raw value type.
+        return T(rawValue: Int((buffer.baseAddress! + offset).bindMemory(to: Int32.self, capacity: 1).pointee))!
+    }
+
     /// Returns the value at the given offset in the storage, or the default value if the value is
     /// not present.
     @_alwaysEmitIntoClient @inline(__always)
@@ -560,6 +688,14 @@ extension _MessageStorage {
         pointer.pointee = newValue
     }
 
+    /// Updates the protobuf enum value at the given offset in the storage, along with its presence.
+    @_alwaysEmitIntoClient @inline(__always)
+    public func updateValue<T: Enum>(at offset: Int, to newValue: T, willBeSet: Bool, hasBit: HasBit) {
+        let pointer = (buffer.baseAddress! + offset).bindMemory(to: Int32.self, capacity: 1)
+        _ = updatePresence(hasBit: hasBit, willBeSet: willBeSet)
+        pointer.pointee = Int32(newValue.rawValue)
+    }
+
     /// Updates the value at the given offset in the storage, along with its presence.
     @_alwaysEmitIntoClient @inline(__always)
     public func updateValue<T>(at offset: Int, to newValue: T, willBeSet: Bool, hasBit: HasBit) {
@@ -591,9 +727,6 @@ extension _MessageStorage {
             bytes.initialize(repeating: 0, count: MemoryLayout<T>.stride)
         }
     }
-
-    // TODO: Implement accessors/mutators for remaining types:
-    // - Enums
 }
 
 // MARK: - Field mutators used for parsing and reflection APIs
@@ -747,6 +880,22 @@ extension _MessageStorage {
         }
     }
 
+    /// Updates the protobuf enum value of the given field, tracking its presence accordingly.
+    func updateValue<T: Enum>(of field: FieldLayout, to newValue: T) {
+        let offset = field.offset
+        switch field.presence {
+        case .hasBit(let hasByteOffset, let hasMask):
+            updateValue(
+                at: offset,
+                to: newValue,
+                willBeSet: layout.fieldHasPresence(field) ? true : newValue != T(),
+                hasBit: (hasByteOffset, hasMask)
+            )
+        case .oneOfMember(let oneofOffset):
+            updateValue(at: offset, to: newValue, oneofPresence: (oneofOffset, field.fieldNumber))
+        }
+    }
+
     /// Appends the given value to the values already present in the field, initializing the field
     /// if necessary.
     func appendValue<T>(_ value: T, to field: FieldLayout) {
@@ -764,7 +913,6 @@ extension _MessageStorage {
             pointer.pointee.append(value)
         }
     }
-
 }
 
 // MARK: - Oneof support
@@ -881,6 +1029,22 @@ extension _MessageStorage {
         return (buffer.baseAddress! + offset).bindMemory(to: Data.self, capacity: 1).pointee
     }
 
+    /// Returns the protobuf enum value at the given offset in the storage if it is the currently
+    /// populated member of its containing oneof, or the default value otherwise.
+    @_alwaysEmitIntoClient @inline(__always)
+    public func value<T: Enum>(at offset: Int, default defaultValue: T, oneofPresence: OneofPresence) -> T {
+        guard populatedOneofMember(at: oneofPresence.offset) == oneofPresence.fieldNumber else {
+            return defaultValue
+        }
+        // It is always safe to force-unwrap this. For open enums, the raw value initializer never
+        // fails. For closed enums, it fails if the raw value is not a valid case, but such a value
+        // should never cause presence to be set. For example, during decoding such a value would be
+        // placed in unknown fields.
+        //
+        // TODO: Change this to `Int32` when we're using that as the raw value type.
+        return T(rawValue: Int((buffer.baseAddress! + offset).bindMemory(to: Int32.self, capacity: 1).pointee))!
+    }
+
     /// Returns the value at the given offset in the storage if it is the currently populated
     /// member of its containing oneof, or the default value otherwise.
     @_alwaysEmitIntoClient @inline(__always)
@@ -969,6 +1133,17 @@ extension _MessageStorage {
         (buffer.baseAddress! + offset).bindMemory(to: Double.self, capacity: 1).pointee = newValue
     }
 
+    /// Updates the protobuf enum value at the given offset in the storage, along with its presence.
+    @_alwaysEmitIntoClient @inline(__always)
+    public func updateValue<T: Enum>(at offset: Int, to newValue: T, oneofPresence: OneofPresence) {
+        let oldFieldNumber = updatePopulatedOneofMember(oneofPresence)
+        if oldFieldNumber != 0 {
+            // We can force-unwrap this because the field must exist or it would be a generator bug.
+            deinitializeOneofMember(layout[fieldNumber: oldFieldNumber]!)
+        }
+        (buffer.baseAddress! + offset).bindMemory(to: Int32.self, capacity: 1).initialize(to: Int32(newValue.rawValue))
+    }
+
     /// Updates the value at the given offset in the storage, along with its presence.
     @_alwaysEmitIntoClient @inline(__always)
     public func updateValue<T>(at offset: Int, to newValue: T, oneofPresence: OneofPresence) {
@@ -1054,22 +1229,19 @@ extension _MessageStorage {
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [Data].self)
                 case .double:
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [Double].self)
-                case .enum:
-                    // TODO: Figure out how we represent enums (open vs. closed).
-                    break
+                case .enum, .group, .message:
+                    equalSoFar = layout.areFieldsEqual(
+                        _MessageLayout.TrampolineToken(index: field.submessageIndex),
+                        field,
+                        self,
+                        other
+                    )
                 case .fixed32, .uint32:
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [UInt32].self)
                 case .fixed64, .uint64:
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [UInt64].self)
                 case .float:
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [Float].self)
-                case .group, .message:
-                    equalSoFar = layout.areSubmessagesEqual(
-                        _MessageLayout.SubmessageToken(index: field.submessageIndex),
-                        field,
-                        self,
-                        other
-                    )
                 case .int32, .sfixed32, .sint32:
                     equalSoFar = isField(field, equalToSameFieldIn: other, type: [Int32].self)
                 case .int64, .sfixed64, .sint64:
@@ -1092,8 +1264,8 @@ extension _MessageStorage {
                     if field.offset < firstNontrivialStorageOffset {
                         firstNontrivialStorageOffset = field.offset
                     }
-                    equalSoFar = layout.areSubmessagesEqual(
-                        _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    equalSoFar = layout.areFieldsEqual(
+                        _MessageLayout.TrampolineToken(index: field.submessageIndex),
                         field,
                         self,
                         other
@@ -1205,9 +1377,9 @@ extension _MessageStorage {
                 }
 
                 // This never actually throws because the closure cannot throw, but closures cannot
-                // be declared rethrows..
+                // be declared rethrows.
                 let isSubmessageInitialized = try! layout.performOnSubmessageStorage(
-                    _MessageLayout.SubmessageToken(index: field.submessageIndex),
+                    _MessageLayout.TrampolineToken(index: field.submessageIndex),
                     field,
                     self,
                     .read

+ 175 - 80
Sources/protoc-gen-swift/EnumGenerator.swift

@@ -23,21 +23,41 @@ private let unrecognizedCaseName = "UNRECOGNIZED"
 /// Generates a Swift enum from a protobuf enum descriptor.
 class EnumGenerator {
     // TODO: Move these conformances back onto the `Enum` protocol when we do a major release.
-    private static let requiredProtocolConformancesForEnums = ["Swift.CaseIterable"].joined(separator: ", ")
+    fileprivate static let requiredProtocolConformancesForEnums = ["Swift.CaseIterable"].joined(separator: ", ")
 
-    private let enumDescriptor: EnumDescriptor
-    private let generatorOptions: GeneratorOptions
-    private let namer: SwiftProtobufNamer
+    fileprivate let enumDescriptor: EnumDescriptor
+    fileprivate let generatorOptions: GeneratorOptions
+    fileprivate let namer: SwiftProtobufNamer
 
     /// The aliasInfo for the values.
     private let aliasInfo: EnumDescriptor.ValueAliasInfo
+
     /// The values that aren't aliases, sorted by number.
-    private let mainEnumValueDescriptorsSorted: [EnumValueDescriptor]
+    fileprivate let mainEnumValueDescriptorsSorted: [EnumValueDescriptor]
+
+    fileprivate let swiftRelativeName: String
+    fileprivate let swiftFullName: String
+
+    /// The Swift expression that is equivalent to the default value of the enum (its first case).
+    fileprivate var swiftDefaultValue: String
+
+    /// The defined values in the enum, ignoring aliases.
+    fileprivate var valuesIgnoringAliases: [EnumValueDescriptor] {
+        aliasInfo.mainValues
+    }
 
-    private let swiftRelativeName: String
-    private let swiftFullName: String
+    /// Returns an instance of the appropriate generator subclass for the given enum descriptor.
+    static func makeEnumGenerator(
+        descriptor: EnumDescriptor,
+        generatorOptions: GeneratorOptions,
+        namer: SwiftProtobufNamer
+    ) -> EnumGenerator {
+        descriptor.isClosed
+            ? ClosedEnumGenerator(descriptor: descriptor, generatorOptions: generatorOptions, namer: namer)
+            : OpenEnumGenerator(descriptor: descriptor, generatorOptions: generatorOptions, namer: namer)
+    }
 
-    init(
+    fileprivate init(
         descriptor: EnumDescriptor,
         generatorOptions: GeneratorOptions,
         namer: SwiftProtobufNamer
@@ -53,64 +73,21 @@ class EnumGenerator {
 
         swiftRelativeName = namer.relativeName(enum: descriptor)
         swiftFullName = namer.fullName(enum: descriptor)
+        swiftDefaultValue = namer.dottedRelativeName(enumValue: enumDescriptor.values.first!)
     }
 
-    func generateMainEnum(printer p: inout CodePrinter) {
-        let visibility = generatorOptions.visibilitySourceSnippet
-
-        p.print(
-            "",
-            "\(enumDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)enum \(swiftRelativeName): \(enumDescriptor.isClosed ? "Int, " : "")\(namer.swiftProtobufModulePrefix)Enum, \(Self.requiredProtocolConformancesForEnums) {"
-        )
-        p.withIndentation { p in
-            if !enumDescriptor.isClosed {
-                p.print("\(visibility)typealias RawValue = Int")
-            }
-
-            // Cases/aliases
-            generateCasesOrAliases(printer: &p)
-
-            // Generate the default initializer.
-            p.print(
-                "",
-                "\(visibility)init() {"
-            )
-            p.printIndented("self = \(namer.dottedRelativeName(enumValue: enumDescriptor.values.first!))")
-            p.print("}")
-
-            if !enumDescriptor.isClosed {
-                p.print()
-                generateInitRawValue(printer: &p)
-
-                p.print()
-                generateRawValueProperty(printer: &p)
-            }
-
-            maybeGenerateCaseIterable(printer: &p)
-
-        }
-        p.print(
-            "",
-            "}"
-        )
+    /// Prints the main Swift type declaration for the protobuf enum.
+    ///
+    /// This method must be implemented by subclasses.
+    func generateTypeDeclaration(to printer: inout CodePrinter) {
+        fatalError("Must be implemented by subclass")
     }
 
-    func maybeGenerateCaseIterable(printer p: inout CodePrinter) {
-        guard !enumDescriptor.isClosed else { return }
-
-        let visibility = generatorOptions.visibilitySourceSnippet
-        p.print(
-            "",
-            "// The compiler won't synthesize support with the \(unrecognizedCaseName) case.",
-            "\(visibility)static let allCases: [\(swiftFullName)] = ["
-        )
-        p.withIndentation { p in
-            for v in aliasInfo.mainValues {
-                let dottedName = namer.dottedRelativeName(enumValue: v)
-                p.print("\(dottedName),")
-            }
-        }
-        p.print("]")
+    /// Prints the Swift declaration that corresponds to the given protobuf enum case.
+    ///
+    /// This method must be implemented by subclasses.
+    func generateCaseDeclaration(for valueDescriptor: EnumValueDescriptor, to printer: inout CodePrinter) {
+        fatalError("Must be implemented by subclass")
     }
 
     func generateRuntimeSupport(printer p: inout CodePrinter) {
@@ -124,29 +101,42 @@ class EnumGenerator {
         p.print("}")
     }
 
-    /// Generates the cases or statics (for alias) for the values.
+    /// Iterates over the cases in the protobuf enum and generates the appropriate cases or static
+    /// properties.
     ///
-    /// - Parameter p: The code printer.
-    private func generateCasesOrAliases(printer p: inout CodePrinter) {
-        let visibility = generatorOptions.visibilitySourceSnippet
+    /// This default implementation simply calls either `generateCaseDeclaration` or
+    /// `generateAliasDeclaration` as appropriate for each case in the enum. Subclasses can override
+    /// this if they wish to print additional code immediately before or after those cases.
+    func generateCaseDeclarations(to p: inout CodePrinter) {
         for enumValueDescriptor in namer.uniquelyNamedValues(valueAliasInfo: aliasInfo) {
-            let comments = enumValueDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions)
-            if !comments.isEmpty {
-                p.print()
-            }
-            let relativeName = namer.relativeName(enumValue: enumValueDescriptor)
+            printComments(of: enumValueDescriptor, to: &p)
             if let aliasOf = aliasInfo.original(of: enumValueDescriptor) {
-                let aliasOfName = namer.relativeName(enumValue: aliasOf)
-                p.print("\(comments)\(visibility)static let \(relativeName) = \(aliasOfName)")
-            } else if enumDescriptor.isClosed {
-                p.print("\(comments)case \(relativeName) = \(enumValueDescriptor.number)")
+                generateAliasDeclaration(alias: enumValueDescriptor, original: aliasOf, to: &p)
             } else {
-                p.print("\(comments)case \(relativeName) // = \(enumValueDescriptor.number)")
+                generateCaseDeclaration(for: enumValueDescriptor, to: &p)
             }
         }
-        if !enumDescriptor.isClosed {
-            p.print("case \(unrecognizedCaseName)(Int)")
+    }
+
+    /// Prints the comments for the given enum value descriptor, if it has any.
+    private func printComments(of valueDescriptor: EnumValueDescriptor, to p: inout CodePrinter) {
+        let comments = valueDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions)
+        if !comments.isEmpty {
+            p.print()
         }
+        // Suppress the final newline because the comment itself will have one.
+        p.print(comments, newlines: false)
+    }
+
+    /// Prints the static property corresponding to an enum value alias.
+    private func generateAliasDeclaration(
+        alias aliasDescriptor: EnumValueDescriptor,
+        original originalDescriptor: EnumValueDescriptor,
+        to p: inout CodePrinter
+    ) {
+        let aliasName = namer.relativeName(enumValue: aliasDescriptor)
+        let originalName = namer.relativeName(enumValue: originalDescriptor)
+        p.print("\(generatorOptions.visibilitySourceSnippet)static let \(aliasName) = \(originalName)")
     }
 
     /// Generates the mapping from case numbers to their text/JSON names.
@@ -167,11 +157,63 @@ class EnumGenerator {
             "\(visibility)static let _protobuf_nameMap = \(namer.swiftProtobufModulePrefix)_NameMap(bytecode: \(writer.bytecode.stringLiteral))"
         )
     }
+}
+
+/// Generates an open protobuf enum as a Swift enum.
+private final class OpenEnumGenerator: EnumGenerator {
+    override func generateTypeDeclaration(to p: inout CodePrinter) {
+        let visibility = generatorOptions.visibilitySourceSnippet
+
+        p.print(
+            "",
+            "\(enumDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)enum \(swiftRelativeName): \(namer.swiftProtobufModulePrefix)Enum, \(Self.requiredProtocolConformancesForEnums) {"
+        )
+        p.withIndentation { p in
+            p.print("\(visibility)typealias RawValue = Int")
+
+            // Cases/aliases
+            generateCaseDeclarations(to: &p)
+
+            // Generate the default initializer.
+            p.print(
+                "",
+                "\(visibility)init() {"
+            )
+            p.printIndented("self = \(swiftDefaultValue)")
+            p.print(
+                "}",
+                ""
+            )
+
+            // Since open enums can't be declared with the raw value in their inheritance clause,
+            // we have to generate the `RawRepresentable` initializer and property requirements
+            // ourselves.
+            generateInitRawValue(to: &p)
+            p.print()
+            generateRawValueProperty(to: &p)
+            generateCaseIterableConformance(to: &p)
+
+        }
+        p.print(
+            "",
+            "}"
+        )
+    }
+
+    override func generateCaseDeclarations(to p: inout CodePrinter) {
+        super.generateCaseDeclarations(to: &p)
+        p.print("case \(unrecognizedCaseName)(Int)")
+    }
+
+    override func generateCaseDeclaration(for enumValueDescriptor: EnumValueDescriptor, to p: inout CodePrinter) {
+        let relativeName = namer.relativeName(enumValue: enumValueDescriptor)
+        p.print("case \(relativeName) // = \(enumValueDescriptor.number)")
+    }
 
     /// Generates `init?(rawValue:)` for the enum.
     ///
     /// - Parameter p: The code printer.
-    private func generateInitRawValue(printer p: inout CodePrinter) {
+    private func generateInitRawValue(to p: inout CodePrinter) {
         let visibility = generatorOptions.visibilitySourceSnippet
 
         p.print("\(visibility)init?(rawValue: Int) {")
@@ -194,7 +236,7 @@ class EnumGenerator {
     /// Generates the `rawValue` property of the enum.
     ///
     /// - Parameter p: The code printer.
-    private func generateRawValueProperty(printer p: inout CodePrinter) {
+    private func generateRawValueProperty(to p: inout CodePrinter) {
         let visibility = generatorOptions.visibilitySourceSnippet
 
         // See https://github.com/apple/swift-protobuf/issues/904 for the full
@@ -254,4 +296,57 @@ class EnumGenerator {
         }
         p.print("}")
     }
+
+    private func generateCaseIterableConformance(to p: inout CodePrinter) {
+        guard !enumDescriptor.isClosed else { return }
+
+        let visibility = generatorOptions.visibilitySourceSnippet
+        p.print(
+            "",
+            "// The compiler won't synthesize support with the \(unrecognizedCaseName) case.",
+            "\(visibility)static let allCases: [\(swiftFullName)] = ["
+        )
+        p.withIndentation { p in
+            for v in valuesIgnoringAliases {
+                let dottedName = namer.dottedRelativeName(enumValue: v)
+                p.print("\(dottedName),")
+            }
+        }
+        p.print("]")
+    }
+
+}
+
+/// Generates a closed protobuf enum as a Swift enum.
+private final class ClosedEnumGenerator: EnumGenerator {
+    override func generateTypeDeclaration(to p: inout CodePrinter) {
+        let visibility = generatorOptions.visibilitySourceSnippet
+
+        p.print(
+            "",
+            "\(enumDescriptor.protoSourceCommentsWithDeprecation(generatorOptions: generatorOptions))\(visibility)enum \(swiftRelativeName): Int, \(namer.swiftProtobufModulePrefix)Enum, \(Self.requiredProtocolConformancesForEnums) {"
+        )
+        p.withIndentation { p in
+            // Cases/aliases
+            generateCaseDeclarations(to: &p)
+
+            // Generate the default initializer.
+            p.print(
+                "",
+                "\(visibility)init() {"
+            )
+            p.printIndented("self = \(swiftDefaultValue)")
+            p.print("}")
+        }
+        p.print(
+            "",
+            "}"
+        )
+
+    }
+
+    override func generateCaseDeclaration(for enumValueDescriptor: EnumValueDescriptor, to p: inout CodePrinter) {
+        let relativeName = namer.relativeName(enumValue: enumValueDescriptor)
+        p.print("case \(relativeName) = \(enumValueDescriptor.number)")
+    }
 }

+ 23 - 3
Sources/protoc-gen-swift/FieldGenerator.swift

@@ -72,6 +72,27 @@ enum FieldPresence {
     }
 }
 
+/// Information about a field that needs to be part of trampoline function generation.
+enum TrampolineFieldKind {
+    /// The field is a message type or an array of a message type.
+    ///
+    /// The associated value is the full Swift name of that (possibly array) type.
+    case message(String)
+
+    /// The field is an enum type or an array of an enum type.
+    ///
+    /// The associated value is the full Swift name of that (possibly array) type.
+    case `enum`(String)
+
+    /// The full Swift name of the (possibly array) type of the field.
+    var name: String {
+        switch self {
+        case .message(let name): return name
+        case .enum(let name): return name
+        }
+    }
+}
+
 /// Interface for field generators.
 protocol FieldGenerator: AnyObject {
     /// The field number of the field.
@@ -86,9 +107,8 @@ protocol FieldGenerator: AnyObject {
     /// The raw type of the field.
     var rawFieldType: RawFieldType { get }
 
-    /// The fully-qualified name of the Swift message type for this field, if it is a message or
-    /// group field.
-    var submessageTypeName: String? { get }
+    /// The kind and name of a message, group, or enum field that needs trampoline generation.
+    var trampolineFieldKind: TrampolineFieldKind? { get }
 
     /// Additional properties that describe the layout and behavior of the field.
     var fieldMode: FieldMode { get }

+ 2 - 2
Sources/protoc-gen-swift/FileGenerator.swift

@@ -167,7 +167,7 @@ class FileGenerator {
         extensionSet.add(extensionFields: fileDescriptor.extensions)
 
         let enums = fileDescriptor.enums.map {
-            EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
+            EnumGenerator.makeEnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
         }
 
         let messages = fileDescriptor.messages.map {
@@ -180,7 +180,7 @@ class FileGenerator {
         }
 
         for e in enums {
-            e.generateMainEnum(printer: &p)
+            e.generateTypeDeclaration(to: &p)
         }
 
         for m in messages {

+ 5 - 3
Sources/protoc-gen-swift/MessageFieldGenerator.swift

@@ -63,7 +63,7 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator {
         }
     }
 
-    let submessageTypeName: String?
+    let trampolineFieldKind: TrampolineFieldKind?
 
     init(
         descriptor: FieldDescriptor,
@@ -95,9 +95,11 @@ class MessageFieldGenerator: FieldGeneratorBase, FieldGenerator {
 
         switch descriptor.type {
         case .group, .message:
-            submessageTypeName = swiftType
+            trampolineFieldKind = .message(swiftType)
+        case .enum:
+            trampolineFieldKind = .enum(swiftType)
         default:
-            submessageTypeName = nil
+            trampolineFieldKind = nil
         }
 
         super.init(descriptor: descriptor)

+ 59 - 28
Sources/protoc-gen-swift/MessageGenerator.swift

@@ -69,7 +69,7 @@ class MessageGenerator {
         extensionSet.add(extensionFields: descriptor.extensions)
 
         enums = descriptor.enums.map {
-            EnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
+            EnumGenerator.makeEnumGenerator(descriptor: $0, generatorOptions: generatorOptions, namer: namer)
         }
 
         messages = descriptor.messages.filter { !$0.options.mapEntry }.map {
@@ -165,7 +165,7 @@ class MessageGenerator {
 
             // Nested enums
             for e in enums {
-                e.generateMainEnum(printer: &p)
+                e.generateTypeDeclaration(to: &p)
             }
 
             // Nested messages
@@ -213,7 +213,9 @@ class MessageGenerator {
                 )
             }
             p.print("}")
-            p.print("\(visibility)mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {")
+            p.print(
+                "\(visibility)mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {"
+            )
             p.printIndented("_ = _uniqueStorage()")
             p.print("}")
         }
@@ -254,12 +256,16 @@ class MessageGenerator {
             // TODO: These only need to exist while we are in the transitional state of using
             // table-driven messages for testing but needing to support the old generated WKTs and
             // plugin protos. Remove them when everything is generated table-driven.
-            p.print("\(visibility)func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {")
+            p.print(
+                "\(visibility)func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {"
+            )
             p.withIndentation { p in
                 p.print("return try _storage.serializedBytes(partial: partial, options: options)")
             }
             p.print("}")
-            p.print("\(visibility)mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {")
+            p.print(
+                "\(visibility)mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {"
+            )
             p.withIndentation { p in
                 p.print("try _uniqueStorage().merge(byReadingFrom: body, partial: partial, options: options)")
             }
@@ -287,41 +293,62 @@ class MessageGenerator {
             )
         }
 
-        let submessages = messageLayoutCalculator.submessages
+        let trampolineFields = messageLayoutCalculator.trampolineFields
         p.print()
         p.print(
             "private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString",
             newlines: false
         )
 
-        if submessages.isEmpty {
-            // If there are no submessages, we can use the layout initializer that defaults the
-            // trampoline functions to trapping placeholders.
+        if trampolineFields.isEmpty {
+            // If there are no trampoline fields, we can use the layout initializer that defaults
+            // the trampoline functions to trapping placeholders.
             p.print(")")
         } else {
             // Otherwise, pass the static member functions that will be generated below.
             p.print(
                 """
-                , deinitializeSubmessage: _protobuf_deinitializeSubmessage\
-                , copySubmessage: _protobuf_copySubmessage\
-                , areSubmessagesEqual: _protobuf_areSubmessagesEqual\
+                , deinitializeField: _protobuf_deinitializeField\
+                , copyField: _protobuf_copyField\
+                , areFieldsEqual: _protobuf_areFieldsEqual\
                 , performOnSubmessageStorage: _protobuf_performOnSubmessageStorage\
+                , performOnRawEnumValues: _protobuf_performOnRawEnumValues\
                 )
                 """
             )
             p.print(
                 "",
-                "private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {"
+                "private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {"
+            )
+            p.withIndentation { p in
+                p.print("switch token.index {")
+                for field in trampolineFields {
+                    p.print(
+                        "case \(field.index): storage.deinitializeField(field, type: \(field.kind.name).self)"
+                    )
+                }
+                p.print(
+                    "default: preconditionFailure(\"invalid trampoline token; this is a generator bug\")",
+                    "}"
+                )
+            }
+            p.print(
+                "}"
+            )
+
+            p.print(
+                "",
+                "private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {"
             )
             p.withIndentation { p in
                 p.print("switch token.index {")
-                for (index, submessage) in submessages.enumerated() {
+                for field in trampolineFields {
                     p.print(
-                        "case \(index + 1): storage.deinitializeField(field, type: \(submessage.typeName).self)"
+                        "case \(field.index): source.copyField(field, to: destination, type: \(field.kind.name).self)"
                     )
                 }
                 p.print(
-                    "default: preconditionFailure(\"invalid submessage token; this is a generator bug\")",
+                    "default: preconditionFailure(\"invalid trampoline token; this is a generator bug\")",
                     "}"
                 )
             }
@@ -331,17 +358,17 @@ class MessageGenerator {
 
             p.print(
                 "",
-                "private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {"
+                "private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {"
             )
             p.withIndentation { p in
                 p.print("switch token.index {")
-                for (index, submessage) in submessages.enumerated() {
+                for field in trampolineFields {
                     p.print(
-                        "case \(index + 1): source.copyField(field, to: destination, type: \(submessage.typeName).self)"
+                        "case \(field.index): return lhs.isField(field, equalToSameFieldIn: rhs, type: \(field.kind.name).self)"
                     )
                 }
                 p.print(
-                    "default: preconditionFailure(\"invalid submessage token; this is a generator bug\")",
+                    "default: preconditionFailure(\"invalid trampoline token; this is a generator bug\")",
                     "}"
                 )
             }
@@ -351,17 +378,19 @@ class MessageGenerator {
 
             p.print(
                 "",
-                "private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {"
+                "private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {"
             )
             p.withIndentation { p in
                 p.print("switch token.index {")
-                for (index, submessage) in submessages.enumerated() {
+                for field in trampolineFields {
+                    // Only submessage fields need this storage trampoline.
+                    guard case .message(let name) = field.kind else { continue }
                     p.print(
-                        "case \(index + 1): return lhs.isField(field, equalToSameFieldIn: rhs, type: \(submessage.typeName).self)"
+                        "case \(field.index): return try storage.performOnSubmessageStorage(of: field, operation: operation, type: \(name).self, perform: perform)"
                     )
                 }
                 p.print(
-                    "default: preconditionFailure(\"invalid submessage token; this is a generator bug\")",
+                    "default: preconditionFailure(\"invalid trampoline token; this is a generator bug\")",
                     "}"
                 )
             }
@@ -371,17 +400,19 @@ class MessageGenerator {
 
             p.print(
                 "",
-                "private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {"
+                "private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {"
             )
             p.withIndentation { p in
                 p.print("switch token.index {")
-                for (index, submessage) in submessages.enumerated() {
+                for field in trampolineFields {
+                    // Only enum fields need this raw value trampoline.
+                    guard case .enum(let name) = field.kind else { continue }
                     p.print(
-                        "case \(index + 1): return try storage.performOnSubmessageStorage(of: field, operation: operation, type: \(submessage.typeName).self, perform: perform)"
+                        "case \(field.index): return try storage.performOnRawEnumValues(of: field, operation: operation, type: \(name).self, perform: perform, onInvalidValue: onInvalidValue)"
                     )
                 }
                 p.print(
-                    "default: preconditionFailure(\"invalid submessage token; this is a generator bug\")",
+                    "default: preconditionFailure(\"invalid trampoline token; this is a generator bug\")",
                     "}"
                 )
             }

+ 22 - 25
Sources/protoc-gen-swift/MessageLayoutCalculator.swift

@@ -24,7 +24,7 @@ struct MessageLayoutCalculator {
 
     /// Collects submessage information as it is encountered while iterating over the fields of the
     /// message.
-    private var submessageCollector = SubmessageCollector()
+    private var trampolineFieldCollector = TrampolineFieldCollector()
 
     /// The Swift string literals (without surrounding quotes) that encode the message layout in
     /// the generated source.
@@ -37,8 +37,8 @@ struct MessageLayoutCalculator {
     ///
     /// The first element in this array corresponds to the submessage with index 1, and the rest
     /// increase accordingly.
-    var submessages: [SubmessageInfo] {
-        submessageCollector.usedSubmessages.sorted { $0.value.index < $1.value.index }.map { $0.value }
+    var trampolineFields: [TrampolineField] {
+        trampolineFieldCollector.usedFields.sorted { $0.value.index < $1.value.index }.map { $0.value }
     }
 
     /// Creates a new message layout calculator for a message containing the given fields and for
@@ -128,7 +128,7 @@ struct MessageLayoutCalculator {
             field.storageOffsets = byteOffsets
             byteOffsets.add(fieldSizes)
 
-            submessageCollector.collect(field)
+            trampolineFieldCollector.collect(field)
         }
 
         // Now we have all the information we need to generate the layout string. First we write
@@ -145,7 +145,7 @@ struct MessageLayoutCalculator {
                 writer.writeBase128Int(UInt64(field.storageOffsets[which]), byteWidth: 3)
                 writer.writeBase128Int(UInt64(field.presence.rawPresence), byteWidth: 2)
                 writer.writeBase128Int(
-                    UInt64(submessageCollector.fieldNumberToSubmessageIndexMap[field.number, default: 0]),
+                    UInt64(trampolineFieldCollector.fieldNumberToTrampolineIndexMap[field.number, default: 0]),
                     byteWidth: 2
                 )
                 writer.writeBase128Int(UInt64(field.rawFieldType.rawValue), byteWidth: 1)
@@ -195,45 +195,42 @@ private struct MessageLayoutWriter {
 
 }
 
-/// Collects the submessages referenced by a message whose layout is being generated, assigning
-/// each one a unique index that will be used when looking them up by the runtime.
-private struct SubmessageCollector {
+/// Collects the message and enum types referenced by a message whose layout is being generated,
+/// assigning each one a unique index that will be used when looking them up by the runtime.
+private struct TrampolineFieldCollector {
     /// Tracks the field numbers of any submessage fields and the corresponding index of that
     /// submessage.
-    var fieldNumberToSubmessageIndexMap: [Int: Int] = [:]
+    var fieldNumberToTrampolineIndexMap: [Int: Int] = [:]
 
     /// Tracks which submessage types have already been encountered, along with their field
     /// generator and index.
-    var usedSubmessages: [String: SubmessageInfo] = [:]
+    var usedFields: [String: TrampolineField] = [:]
 
     /// Tracks the index that will be assigned to the next newly encountered submessage.
     private var nextIndex = 1
 
     /// Tracks the submessage with the given type name and field number.
     mutating func collect(_ field: any FieldGenerator) {
-        guard let name = field.submessageTypeName else { return }
-        let submessageIndex: Int
-        if let foundIndex = usedSubmessages[name]?.index {
-            submessageIndex = foundIndex
+        guard let kind = field.trampolineFieldKind else { return }
+        let trampolineIndex: Int
+        if let foundIndex = usedFields[kind.name]?.index {
+            trampolineIndex = foundIndex
         } else {
-            submessageIndex = nextIndex
-            usedSubmessages[name] = SubmessageInfo(
-                typeName: name,
-                index: submessageIndex,
+            trampolineIndex = nextIndex
+            usedFields[kind.name] = TrampolineField(
+                kind: kind,
+                index: trampolineIndex,
                 needsIsInitializedCheck: field.needsIsInitializedGeneration
             )
             nextIndex += 1
         }
-        fieldNumberToSubmessageIndexMap[field.number] = submessageIndex
+        fieldNumberToTrampolineIndexMap[field.number] = trampolineIndex
     }
 }
 
-struct SubmessageInfo {
-    /// The Swift type name of the submessage.
-    ///
-    /// Note that for repeated fields, this is the spelling of an array of the message type (e.g.,
-    /// `[Foo]`).
-    var typeName: String
+struct TrampolineField {
+    /// The kind and name of the field that needs trampoline generation.
+    var kind: TrampolineFieldKind
 
     /// The index of the submessage, which will be used to generate submessage tokens.
     var index: Int

+ 8 - 4
Sources/protoc-gen-swift/OneofGenerator.swift

@@ -61,7 +61,7 @@ class OneofGenerator {
         // Only valid on message fields.
         var messageType: Descriptor? { fieldDescriptor.messageType }
 
-        let submessageTypeName: String?
+        let trampolineFieldKind: TrampolineFieldKind?
 
         init(descriptor: FieldDescriptor, generatorOptions: GeneratorOptions, namer: SwiftProtobufNamer) {
             precondition(descriptor.oneofIndex != nil)
@@ -85,9 +85,11 @@ class OneofGenerator {
 
             switch descriptor.type {
             case .group, .message:
-                submessageTypeName = swiftType
+                trampolineFieldKind = .message(swiftType)
+            case .enum:
+                trampolineFieldKind = .enum(swiftType)
             default:
-                submessageTypeName = nil
+                trampolineFieldKind = nil
             }
 
             super.init(descriptor: descriptor)
@@ -229,7 +231,9 @@ class OneofGenerator {
                 for f in fields {
                     p.print("case \(f.number): return .\(f.swiftName)(\(f.swiftName))")
                 }
-                p.print(#"default: preconditionFailure("Internal logic error; populated oneof field \(populatedField) is not a member of this oneof")"#)
+                p.print(
+                    #"default: preconditionFailure("Internal logic error; populated oneof field \(populatedField) is not a member of this oneof")"#
+                )
                 p.print("}")
             }
             p.print("}")

+ 80 - 0
Tests/ExperimentalTableDrivenSwiftProtobufTests/Test_TableDriven.swift

@@ -26,6 +26,7 @@ final class Test_TableDriven: XCTestCase {
         msg.optionalInt32 = 50
         msg.optionalString = "some string"
         msg.optionalImportMessage = .with { $0.d = 20 }
+        msg.optionalImportEnum = .importBaz
         msg.repeatedInt32 = [1, 10, 100]
         msg.repeatedString = ["a", "b", "c"]
         msg.repeatedImportMessage = [
@@ -33,17 +34,22 @@ final class Test_TableDriven: XCTestCase {
             .with { $0.d = 20 },
             .with { $0.d = 30 },
         ]
+        msg.repeatedImportEnum = [.importBaz, .importBar]
 
         XCTAssertEqual(msg.optionalBool, true)
         XCTAssertEqual(msg.optionalInt32, 50)
         XCTAssertEqual(msg.optionalString, "some string")
         XCTAssertEqual(msg.optionalImportMessage.d, 20)
+        XCTAssertEqual(msg.optionalImportEnum, .importBaz)
         XCTAssertEqual(msg.repeatedInt32, [1, 10, 100])
         XCTAssertEqual(msg.repeatedString, ["a", "b", "c"])
         XCTAssertEqual(msg.repeatedImportMessage.count, 3)
         XCTAssertEqual(msg.repeatedImportMessage[0].d, 10)
         XCTAssertEqual(msg.repeatedImportMessage[1].d, 20)
         XCTAssertEqual(msg.repeatedImportMessage[2].d, 30)
+        XCTAssertEqual(msg.repeatedImportEnum.count, 2)
+        XCTAssertEqual(msg.repeatedImportEnum[0], .importBaz)
+        XCTAssertEqual(msg.repeatedImportEnum[1], .importBar)
     }
 
     func testCopyAndModifyCopy() {
@@ -1078,6 +1084,41 @@ final class Test_TableDriven: XCTestCase {
         }
     }
 
+    func testEncoding_optionalNestedEnum() throws {
+        assertEncode([168, 1, 2]) { (o: inout MessageTestType) in
+            o.optionalNestedEnum = .bar
+        }
+        assertDecodeSucceeds([168, 1, 2]) {
+            if $0.hasOptionalNestedEnum {
+                return $0.optionalNestedEnum == .bar
+            } else {
+                XCTFail("Nonexistent value")
+                return false
+            }
+        }
+        assertDecodeFails([168, 1])
+        assertDecodeSucceeds([168, 1, 128, 1]) { !$0.hasOptionalNestedEnum }
+        assertDecodeSucceeds([168, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 1]) { $0.optionalNestedEnum == .neg }
+
+        // The out-of-range enum value should be preserved as an unknown field
+        let decoded = try SwiftProtoTesting_TestAllTypes(serializedBytes: [168, 1, 128, 1])
+        XCTAssertFalse(decoded.hasOptionalNestedEnum)
+        let recoded: [UInt8] = try decoded.serializedBytes()
+        XCTAssertEqual(recoded, [168, 1, 128, 1])
+    }
+
+    func testEncoding_optionalForeignEnum() {
+        assertEncode([176, 1, 5]) { (o: inout MessageTestType) in
+            o.optionalForeignEnum = .foreignBar
+        }
+    }
+
+    func testEncoding_optionalImportEnum() {
+        assertEncode([184, 1, 8]) { (o: inout MessageTestType) in
+            o.optionalImportEnum = .importBar
+        }
+    }
+
     //
     // Repeated types
     //
@@ -1621,6 +1662,45 @@ final class Test_TableDriven: XCTestCase {
         }
     }
 
+    func testEncoding_repeatedNestedEnum() throws {
+        assertEncode([152, 3, 2, 152, 3, 3]) { (o: inout MessageTestType) in
+            o.repeatedNestedEnum = [.bar, .baz]
+        }
+        assertDecodeSucceeds([152, 3, 2, 152, 3, 128, 1]) {
+            $0.repeatedNestedEnum == [.bar]
+        }
+
+        // The out-of-range enum value should be preserved as an unknown field
+        do {
+            let decoded1 = try SwiftProtoTesting_TestAllTypes(serializedBytes: [152, 3, 1, 152, 3, 128, 1])
+            XCTAssertEqual(decoded1.repeatedNestedEnum, [.foo])
+            let recoded1: [UInt8] = try decoded1.serializedBytes()
+            XCTAssertEqual(recoded1, [152, 3, 1, 152, 3, 128, 1])
+        } catch let e {
+            XCTFail("Decode failed: \(e)")
+        }
+
+        // Unknown fields always get reserialized last, which trashes order here:
+        do {
+            let decoded2 = try SwiftProtoTesting_TestAllTypes(serializedBytes: [152, 3, 128, 1, 152, 3, 2])
+            XCTAssertEqual(decoded2.repeatedNestedEnum, [.bar])
+            let recoded2: [UInt8] = try decoded2.serializedBytes()
+            XCTAssertEqual(recoded2, [152, 3, 2, 152, 3, 128, 1])
+        } catch let e {
+            XCTFail("Decode failed: \(e)")
+        }
+
+        // Unknown enums within packed behave as if it were plain repeated
+        do {
+            let decoded3 = try SwiftProtoTesting_TestAllTypes(serializedBytes: [154, 3, 3, 128, 1, 2])
+            XCTAssertEqual(decoded3.repeatedNestedEnum, [.bar])
+            let recoded3: [UInt8] = try decoded3.serializedBytes()
+            XCTAssertEqual(recoded3, [152, 3, 2, 154, 3, 2, 128, 1])
+        } catch let e {
+            XCTFail("Decode failed: \(e)")
+        }
+    }
+
     //
     // Singular with Defaults
     //

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 0 - 0
Tests/ExperimentalTableDrivenSwiftProtobufTests/unittest.pb.swift


+ 86 - 34
Tests/ExperimentalTableDrivenSwiftProtobufTests/unittest_swift_all_required_types.pb.swift

@@ -689,57 +689,75 @@ extension SwiftProtoTesting_TestAllRequiredTypes: SwiftProtobuf.Message, SwiftPr
   static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}required_int32\0\u{3}required_int64\0\u{3}required_uint32\0\u{3}required_uint64\0\u{3}required_sint32\0\u{3}required_sint64\0\u{3}required_fixed32\0\u{3}required_fixed64\0\u{3}required_sfixed32\0\u{3}required_sfixed64\0\u{3}required_float\0\u{3}required_double\0\u{3}required_bool\0\u{3}required_string\0\u{3}required_bytes\0\u{7}RequiredGroup\0\u{4}\u{2}required_nested_message\0\u{3}required_foreign_message\0\u{3}required_import_message\0\u{3}required_nested_enum\0\u{3}required_foreign_enum\0\u{3}required_import_enum\0\u{4}\u{3}required_public_import_message\0\u{4}#default_int32\0\u{3}default_int64\0\u{3}default_uint32\0\u{3}default_uint64\0\u{3}default_sint32\0\u{3}default_sint64\0\u{3}default_fixed32\0\u{3}default_fixed64\0\u{3}default_sfixed32\0\u{3}default_sfixed64\0\u{3}default_float\0\u{3}default_double\0\u{3}default_bool\0\u{3}default_string\0\u{3}default_bytes\0\u{4}\u{6}default_nested_enum\0\u{3}default_foreign_enum\0\u{3}default_import_enum\0\u{4}\u{1c}oneof_uint32\0\u{3}oneof_nested_message\0\u{3}oneof_string\0\u{3}oneof_bytes\0")
   #if _pointerBitWidth(_64)
     @_alwaysEmitIntoClient @inline(__always)
-    private static var _protobuf_messageLayoutString: StaticString { "\0`\u{2}\0-\0\0)\0\0-\0\0\u{11}\0\0\u{1}\0\0\0\0\u{1c}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0h\0\0\u{1}\0\0\0\u{3}\u{3}\0\0\0\0 \0\0\u{2}\0\0\0\u{d}\u{4}\0\0\0\0p\0\0\u{3}\0\0\0\u{4}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{11}\u{6}\0\0\0\0x\0\0\u{5}\0\0\0\u{12}\u{7}\0\0\0\0(\0\0\u{6}\0\0\0\u{7}\u{8}\0\0\0\0\0\u{1}\0\u{7}\0\0\0\u{6}\u{9}\0\0\0\0,\0\0\u{8}\0\0\0\u{f}\u{a}\0\0\0\0\u{8}\u{1}\0\u{9}\0\0\0\u{10}\u{b}\0\0\0\00\0\0\u{a}\0\0\0\u{2}\u{c}\0\0\0\0\u{10}\u{1}\0\u{b}\0\0\0\u{1}\u{d}\0\0\0\0\u{18}\0\0\u{c}\0\0\0\u{8}\u{e}\0\0\0\0\0\u{2}\0\u{d}\0\0\0\u{9}\u{f}\0\0\0\0\u{10}\u{2}\0\u{e}\0\0\0\u{c}\u{10}\0\0\0\0H\u{1}\0\u{f}\0\u{1}\0\u{a}\u{12}\0\0\0\0P\u{1}\0\u{10}\0\u{2}\0\u{b}\u{13}\0\0\0\0X\u{1}\0\u{11}\0\u{3}\0\u{b}\u{14}\0\0\0\0`\u{1}\0\u{12}\0\u{4}\0\u{b}\u{15}\0\0\0\04\0\0\u{13}\0\0\0\u{e}\u{16}\0\0\0\08\0\0\u{14}\0\0\0\u{e}\u{17}\0\0\0\0<\0\0\u{15}\0\0\0\u{e}\u{1a}\0\0\0\0h\u{1}\0\u{16}\0\u{5}\0\u{b}=\0\0\0\0@\0\0\u{17}\0\0\0\u{5}>\0\0\0\0\u{18}\u{1}\0\u{18}\0\0\0\u{3}?\0\0\0\0D\0\0\u{19}\0\0\0\u{d}@\0\0\0\0 \u{1}\0\u{1a}\0\0\0\u{4}A\0\0\0\0H\0\0\u{1b}\0\0\0\u{11}B\0\0\0\0(\u{1}\0\u{1c}\0\0\0\u{12}C\0\0\0\0L\0\0\u{1d}\0\0\0\u{7}D\0\0\0\00\u{1}\0\u{1e}\0\0\0\u{6}E\0\0\0\0P\0\0\u{1f}\0\0\0\u{f}F\0\0\0\08\u{1}\0 \0\0\0\u{10}G\0\0\0\0T\0\0!\0\0\0\u{2}H\0\0\0\0@\u{1}\0\"\0\0\0\u{1}I\0\0\0\0\u{19}\0\0#\0\0\0\u{8}J\0\0\0\0 \u{2}\0$\0\0\0\u{9}K\0\0\0\00\u{2}\0%\0\0\0\u{c}Q\0\0\0\0X\0\0&\0\0\0\u{e}R\0\0\0\0\\\0\0'\0\0\0\u{e}S\0\0\0\0`\0\0(\0\0\0\u{e}o\0\0\0\0d\0\0w\u{7f}\0\0\u{d}p\0\0\0\0p\u{1}\0w\u{7f}\u{2}\0\u{b}q\0\0\0\0@\u{2}\0w\u{7f}\0\0\u{9}r\0\0\0\0P\u{2}\0w\u{7f}\0\0\u{c}" }
+    private static var _protobuf_messageLayoutString: StaticString { "\0`\u{2}\0-\0\0)\0\0-\0\0\u{11}\0\0\u{1}\0\0\0\0\u{1c}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0h\0\0\u{1}\0\0\0\u{3}\u{3}\0\0\0\0 \0\0\u{2}\0\0\0\u{d}\u{4}\0\0\0\0p\0\0\u{3}\0\0\0\u{4}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{11}\u{6}\0\0\0\0x\0\0\u{5}\0\0\0\u{12}\u{7}\0\0\0\0(\0\0\u{6}\0\0\0\u{7}\u{8}\0\0\0\0\0\u{1}\0\u{7}\0\0\0\u{6}\u{9}\0\0\0\0,\0\0\u{8}\0\0\0\u{f}\u{a}\0\0\0\0\u{8}\u{1}\0\u{9}\0\0\0\u{10}\u{b}\0\0\0\00\0\0\u{a}\0\0\0\u{2}\u{c}\0\0\0\0\u{10}\u{1}\0\u{b}\0\0\0\u{1}\u{d}\0\0\0\0\u{18}\0\0\u{c}\0\0\0\u{8}\u{e}\0\0\0\0\0\u{2}\0\u{d}\0\0\0\u{9}\u{f}\0\0\0\0\u{10}\u{2}\0\u{e}\0\0\0\u{c}\u{10}\0\0\0\0H\u{1}\0\u{f}\0\u{4}\0\u{a}\u{12}\0\0\0\0P\u{1}\0\u{10}\0\u{5}\0\u{b}\u{13}\0\0\0\0X\u{1}\0\u{11}\0\u{6}\0\u{b}\u{14}\0\0\0\0`\u{1}\0\u{12}\0\u{7}\0\u{b}\u{15}\0\0\0\04\0\0\u{13}\0\u{1}\0\u{e}\u{16}\0\0\0\08\0\0\u{14}\0\u{2}\0\u{e}\u{17}\0\0\0\0<\0\0\u{15}\0\u{3}\0\u{e}\u{1a}\0\0\0\0h\u{1}\0\u{16}\0\u{8}\0\u{b}=\0\0\0\0@\0\0\u{17}\0\0\0\u{5}>\0\0\0\0\u{18}\u{1}\0\u{18}\0\0\0\u{3}?\0\0\0\0D\0\0\u{19}\0\0\0\u{d}@\0\0\0\0 \u{1}\0\u{1a}\0\0\0\u{4}A\0\0\0\0H\0\0\u{1b}\0\0\0\u{11}B\0\0\0\0(\u{1}\0\u{1c}\0\0\0\u{12}C\0\0\0\0L\0\0\u{1d}\0\0\0\u{7}D\0\0\0\00\u{1}\0\u{1e}\0\0\0\u{6}E\0\0\0\0P\0\0\u{1f}\0\0\0\u{f}F\0\0\0\08\u{1}\0 \0\0\0\u{10}G\0\0\0\0T\0\0!\0\0\0\u{2}H\0\0\0\0@\u{1}\0\"\0\0\0\u{1}I\0\0\0\0\u{19}\0\0#\0\0\0\u{8}J\0\0\0\0 \u{2}\0$\0\0\0\u{9}K\0\0\0\00\u{2}\0%\0\0\0\u{c}Q\0\0\0\0X\0\0&\0\u{1}\0\u{e}R\0\0\0\0\\\0\0'\0\u{2}\0\u{e}S\0\0\0\0`\0\0(\0\u{3}\0\u{e}o\0\0\0\0d\0\0w\u{7f}\0\0\u{d}p\0\0\0\0p\u{1}\0w\u{7f}\u{5}\0\u{b}q\0\0\0\0@\u{2}\0w\u{7f}\0\0\u{9}r\0\0\0\0P\u{2}\0w\u{7f}\0\0\u{c}" }
   #elseif _pointerBitWidth(_32)
     @_alwaysEmitIntoClient @inline(__always)
-    private static var _protobuf_messageLayoutString: StaticString { "\0,\u{2}\0-\0\0)\0\0-\0\0\u{11}\0\0\u{1}\0\0\0\0\u{1c}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0h\0\0\u{1}\0\0\0\u{3}\u{3}\0\0\0\0 \0\0\u{2}\0\0\0\u{d}\u{4}\0\0\0\0p\0\0\u{3}\0\0\0\u{4}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{11}\u{6}\0\0\0\0x\0\0\u{5}\0\0\0\u{12}\u{7}\0\0\0\0(\0\0\u{6}\0\0\0\u{7}\u{8}\0\0\0\0\0\u{1}\0\u{7}\0\0\0\u{6}\u{9}\0\0\0\0,\0\0\u{8}\0\0\0\u{f}\u{a}\0\0\0\0\u{8}\u{1}\0\u{9}\0\0\0\u{10}\u{b}\0\0\0\00\0\0\u{a}\0\0\0\u{2}\u{c}\0\0\0\0\u{10}\u{1}\0\u{b}\0\0\0\u{1}\u{d}\0\0\0\0\u{18}\0\0\u{c}\0\0\0\u{8}\u{e}\0\0\0\0d\u{1}\0\u{d}\0\0\0\u{9}\u{f}\0\0\0\0p\u{1}\0\u{e}\0\0\0\u{c}\u{10}\0\0\0\0H\u{1}\0\u{f}\0\u{1}\0\u{a}\u{12}\0\0\0\0L\u{1}\0\u{10}\0\u{2}\0\u{b}\u{13}\0\0\0\0P\u{1}\0\u{11}\0\u{3}\0\u{b}\u{14}\0\0\0\0T\u{1}\0\u{12}\0\u{4}\0\u{b}\u{15}\0\0\0\04\0\0\u{13}\0\0\0\u{e}\u{16}\0\0\0\08\0\0\u{14}\0\0\0\u{e}\u{17}\0\0\0\0<\0\0\u{15}\0\0\0\u{e}\u{1a}\0\0\0\0X\u{1}\0\u{16}\0\u{5}\0\u{b}=\0\0\0\0@\0\0\u{17}\0\0\0\u{5}>\0\0\0\0\u{18}\u{1}\0\u{18}\0\0\0\u{3}?\0\0\0\0D\0\0\u{19}\0\0\0\u{d}@\0\0\0\0 \u{1}\0\u{1a}\0\0\0\u{4}A\0\0\0\0H\0\0\u{1b}\0\0\0\u{11}B\0\0\0\0(\u{1}\0\u{1c}\0\0\0\u{12}C\0\0\0\0L\0\0\u{1d}\0\0\0\u{7}D\0\0\0\00\u{1}\0\u{1e}\0\0\0\u{6}E\0\0\0\0P\0\0\u{1f}\0\0\0\u{f}F\0\0\0\08\u{1}\0 \0\0\0\u{10}G\0\0\0\0T\0\0!\0\0\0\u{2}H\0\0\0\0@\u{1}\0\"\0\0\0\u{1}I\0\0\0\0\u{19}\0\0#\0\0\0\u{8}J\0\0\0\0|\u{1}\0$\0\0\0\u{9}K\0\0\0\0\u{8}\u{2}\0%\0\0\0\u{c}Q\0\0\0\0X\0\0&\0\0\0\u{e}R\0\0\0\0\\\0\0'\0\0\0\u{e}S\0\0\0\0`\0\0(\0\0\0\u{e}o\0\0\0\0d\0\0w\u{7f}\0\0\u{d}p\0\0\0\0\\\u{1}\0w\u{7f}\u{2}\0\u{b}q\0\0\0\0\u{14}\u{2}\0w\u{7f}\0\0\u{9}r\0\0\0\0 \u{2}\0w\u{7f}\0\0\u{c}" }
+    private static var _protobuf_messageLayoutString: StaticString { "\0,\u{2}\0-\0\0)\0\0-\0\0\u{11}\0\0\u{1}\0\0\0\0\u{1c}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0h\0\0\u{1}\0\0\0\u{3}\u{3}\0\0\0\0 \0\0\u{2}\0\0\0\u{d}\u{4}\0\0\0\0p\0\0\u{3}\0\0\0\u{4}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{11}\u{6}\0\0\0\0x\0\0\u{5}\0\0\0\u{12}\u{7}\0\0\0\0(\0\0\u{6}\0\0\0\u{7}\u{8}\0\0\0\0\0\u{1}\0\u{7}\0\0\0\u{6}\u{9}\0\0\0\0,\0\0\u{8}\0\0\0\u{f}\u{a}\0\0\0\0\u{8}\u{1}\0\u{9}\0\0\0\u{10}\u{b}\0\0\0\00\0\0\u{a}\0\0\0\u{2}\u{c}\0\0\0\0\u{10}\u{1}\0\u{b}\0\0\0\u{1}\u{d}\0\0\0\0\u{18}\0\0\u{c}\0\0\0\u{8}\u{e}\0\0\0\0d\u{1}\0\u{d}\0\0\0\u{9}\u{f}\0\0\0\0p\u{1}\0\u{e}\0\0\0\u{c}\u{10}\0\0\0\0H\u{1}\0\u{f}\0\u{4}\0\u{a}\u{12}\0\0\0\0L\u{1}\0\u{10}\0\u{5}\0\u{b}\u{13}\0\0\0\0P\u{1}\0\u{11}\0\u{6}\0\u{b}\u{14}\0\0\0\0T\u{1}\0\u{12}\0\u{7}\0\u{b}\u{15}\0\0\0\04\0\0\u{13}\0\u{1}\0\u{e}\u{16}\0\0\0\08\0\0\u{14}\0\u{2}\0\u{e}\u{17}\0\0\0\0<\0\0\u{15}\0\u{3}\0\u{e}\u{1a}\0\0\0\0X\u{1}\0\u{16}\0\u{8}\0\u{b}=\0\0\0\0@\0\0\u{17}\0\0\0\u{5}>\0\0\0\0\u{18}\u{1}\0\u{18}\0\0\0\u{3}?\0\0\0\0D\0\0\u{19}\0\0\0\u{d}@\0\0\0\0 \u{1}\0\u{1a}\0\0\0\u{4}A\0\0\0\0H\0\0\u{1b}\0\0\0\u{11}B\0\0\0\0(\u{1}\0\u{1c}\0\0\0\u{12}C\0\0\0\0L\0\0\u{1d}\0\0\0\u{7}D\0\0\0\00\u{1}\0\u{1e}\0\0\0\u{6}E\0\0\0\0P\0\0\u{1f}\0\0\0\u{f}F\0\0\0\08\u{1}\0 \0\0\0\u{10}G\0\0\0\0T\0\0!\0\0\0\u{2}H\0\0\0\0@\u{1}\0\"\0\0\0\u{1}I\0\0\0\0\u{19}\0\0#\0\0\0\u{8}J\0\0\0\0|\u{1}\0$\0\0\0\u{9}K\0\0\0\0\u{8}\u{2}\0%\0\0\0\u{c}Q\0\0\0\0X\0\0&\0\u{1}\0\u{e}R\0\0\0\0\\\0\0'\0\u{2}\0\u{e}S\0\0\0\0`\0\0(\0\u{3}\0\u{e}o\0\0\0\0d\0\0w\u{7f}\0\0\u{d}p\0\0\0\0\\\u{1}\0w\u{7f}\u{5}\0\u{b}q\0\0\0\0\u{14}\u{2}\0w\u{7f}\0\0\u{9}r\0\0\0\0 \u{2}\0w\u{7f}\0\0\u{c}" }
   #else
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeSubmessage: _protobuf_deinitializeSubmessage, copySubmessage: _protobuf_copySubmessage, areSubmessagesEqual: _protobuf_areSubmessagesEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
 
-  private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
     switch token.index {
-    case 1: storage.deinitializeField(field, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
-    case 2: storage.deinitializeField(field, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
-    case 3: storage.deinitializeField(field, type: SwiftProtoTesting_ForeignMessage.self)
-    case 4: storage.deinitializeField(field, type: SwiftProtoTesting_Import_ImportMessage.self)
-    case 5: storage.deinitializeField(field, type: SwiftProtoTesting_Import_PublicImportMessage.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    case 1: storage.deinitializeField(field, type: SwiftProtoTesting_TestAllRequiredTypes.NestedEnum.self)
+    case 2: storage.deinitializeField(field, type: SwiftProtoTesting_ForeignEnum.self)
+    case 3: storage.deinitializeField(field, type: SwiftProtoTesting_Import_ImportEnum.self)
+    case 4: storage.deinitializeField(field, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
+    case 5: storage.deinitializeField(field, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
+    case 6: storage.deinitializeField(field, type: SwiftProtoTesting_ForeignMessage.self)
+    case 7: storage.deinitializeField(field, type: SwiftProtoTesting_Import_ImportMessage.self)
+    case 8: storage.deinitializeField(field, type: SwiftProtoTesting_Import_PublicImportMessage.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
     switch token.index {
-    case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
-    case 2: source.copyField(field, to: destination, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
-    case 3: source.copyField(field, to: destination, type: SwiftProtoTesting_ForeignMessage.self)
-    case 4: source.copyField(field, to: destination, type: SwiftProtoTesting_Import_ImportMessage.self)
-    case 5: source.copyField(field, to: destination, type: SwiftProtoTesting_Import_PublicImportMessage.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_TestAllRequiredTypes.NestedEnum.self)
+    case 2: source.copyField(field, to: destination, type: SwiftProtoTesting_ForeignEnum.self)
+    case 3: source.copyField(field, to: destination, type: SwiftProtoTesting_Import_ImportEnum.self)
+    case 4: source.copyField(field, to: destination, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
+    case 5: source.copyField(field, to: destination, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
+    case 6: source.copyField(field, to: destination, type: SwiftProtoTesting_ForeignMessage.self)
+    case 7: source.copyField(field, to: destination, type: SwiftProtoTesting_Import_ImportMessage.self)
+    case 8: source.copyField(field, to: destination, type: SwiftProtoTesting_Import_PublicImportMessage.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
     switch token.index {
-    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
-    case 2: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
-    case 3: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_ForeignMessage.self)
-    case 4: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_Import_ImportMessage.self)
-    case 5: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_Import_PublicImportMessage.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestAllRequiredTypes.NestedEnum.self)
+    case 2: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_ForeignEnum.self)
+    case 3: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_Import_ImportEnum.self)
+    case 4: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self)
+    case 5: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self)
+    case 6: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_ForeignMessage.self)
+    case 7: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_Import_ImportMessage.self)
+    case 8: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_Import_PublicImportMessage.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
     switch token.index {
-    case 1: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self, perform: perform)
-    case 2: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self, perform: perform)
-    case 3: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_ForeignMessage.self, perform: perform)
-    case 4: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_Import_ImportMessage.self, perform: perform)
-    case 5: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_Import_PublicImportMessage.self, perform: perform)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    case 4: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_TestAllRequiredTypes.RequiredGroup.self, perform: perform)
+    case 5: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_TestAllRequiredTypes.NestedMessage.self, perform: perform)
+    case 6: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_ForeignMessage.self, perform: perform)
+    case 7: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_Import_ImportMessage.self, perform: perform)
+    case 8: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_Import_PublicImportMessage.self, perform: perform)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: SwiftProtoTesting_TestAllRequiredTypes.NestedEnum.self, perform: perform, onInvalidValue: onInvalidValue)
+    case 2: return try storage.performOnRawEnumValues(of: field, operation: operation, type: SwiftProtoTesting_ForeignEnum.self, perform: perform, onInvalidValue: onInvalidValue)
+    case 3: return try storage.performOnRawEnumValues(of: field, operation: operation, type: SwiftProtoTesting_Import_ImportEnum.self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
@@ -860,15 +878,49 @@ extension SwiftProtoTesting_TestSomeRequiredTypes: SwiftProtobuf.Message, SwiftP
   static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{3}required_int32\0\u{3}required_float\0\u{3}required_bool\0\u{3}required_string\0\u{3}required_bytes\0\u{3}required_nested_enum\0")
   #if _pointerBitWidth(_64)
     @_alwaysEmitIntoClient @inline(__always)
-    private static var _protobuf_messageLayoutString: StaticString { "\00\0\0\u{6}\0\0\u{6}\0\0\u{6}\0\0\u{7}\0\0\u{1}\0\0\0\0\u{4}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0\u{8}\0\0\u{1}\0\0\0\u{2}\u{3}\0\0\0\0\u{1}\0\0\u{2}\0\0\0\u{8}\u{4}\0\0\0\0\u{10}\0\0\u{3}\0\0\0\u{9}\u{5}\0\0\0\0 \0\0\u{4}\0\0\0\u{c}\u{6}\0\0\0\0\u{c}\0\0\u{5}\0\0\0\u{e}" }
+    private static var _protobuf_messageLayoutString: StaticString { "\00\0\0\u{6}\0\0\u{6}\0\0\u{6}\0\0\u{7}\0\0\u{1}\0\0\0\0\u{4}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0\u{8}\0\0\u{1}\0\0\0\u{2}\u{3}\0\0\0\0\u{1}\0\0\u{2}\0\0\0\u{8}\u{4}\0\0\0\0\u{10}\0\0\u{3}\0\0\0\u{9}\u{5}\0\0\0\0 \0\0\u{4}\0\0\0\u{c}\u{6}\0\0\0\0\u{c}\0\0\u{5}\0\u{1}\0\u{e}" }
   #elseif _pointerBitWidth(_32)
     @_alwaysEmitIntoClient @inline(__always)
-    private static var _protobuf_messageLayoutString: StaticString { "\00\0\0\u{6}\0\0\u{6}\0\0\u{6}\0\0\u{7}\0\0\u{1}\0\0\0\0\u{4}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0\u{8}\0\0\u{1}\0\0\0\u{2}\u{3}\0\0\0\0\u{1}\0\0\u{2}\0\0\0\u{8}\u{4}\0\0\0\0\u{18}\0\0\u{3}\0\0\0\u{9}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{c}\u{6}\0\0\0\0\u{c}\0\0\u{5}\0\0\0\u{e}" }
+    private static var _protobuf_messageLayoutString: StaticString { "\00\0\0\u{6}\0\0\u{6}\0\0\u{6}\0\0\u{7}\0\0\u{1}\0\0\0\0\u{4}\0\0\0\0\0\0\u{5}\u{2}\0\0\0\0\u{8}\0\0\u{1}\0\0\0\u{2}\u{3}\0\0\0\0\u{1}\0\0\u{2}\0\0\0\u{8}\u{4}\0\0\0\0\u{18}\0\0\u{3}\0\0\0\u{9}\u{5}\0\0\0\0$\0\0\u{4}\0\0\0\u{c}\u{6}\0\0\0\0\u{c}\0\0\u{5}\0\u{1}\0\u{e}" }
   #else
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
+
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: storage.deinitializeField(field, type: SwiftProtoTesting_TestSomeRequiredTypes.NestedEnum.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_TestSomeRequiredTypes.NestedEnum.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+    switch token.index {
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_TestSomeRequiredTypes.NestedEnum.self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: SwiftProtoTesting_TestSomeRequiredTypes.NestedEnum.self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
 
   func _protobuf_messageStorage(accessToken: SwiftProtobuf._MessageStorageToken) -> AnyObject { _storage }
 

+ 361 - 0
Tests/ExperimentalTableDrivenSwiftProtobufTests/unittest_swift_enum_proto2.pb.swift

@@ -0,0 +1,361 @@
+// DO NOT EDIT.
+// swift-format-ignore-file
+// swiftlint:disable all
+//
+// Generated by the Swift generator plugin for the protocol buffer compiler.
+// Source: unittest_swift_enum_proto2.proto
+//
+// For information on using the generated types, please see the documentation:
+//   https://github.com/apple/swift-protobuf/
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Apple, Inc.  All Rights Reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+@_spi(ForGeneratedCodeOnly) import SwiftProtobuf
+
+// If the compiler emits an error on this type, it is because this file
+// was generated by a version of the `protoc` Swift plug-in that is
+// incompatible with the version of SwiftProtobuf to which you are linking.
+// Please ensure that you are building against the same version of the API
+// that was used to generate this file.
+fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
+  struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
+  typealias Version = _2
+}
+
+struct SwiftProtoTesting_Enum2_SwiftEnumTest: @unchecked Sendable {
+  // SwiftProtobuf.Message conformance is added in an extension below. See the
+  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
+  // methods supported on all messages.
+
+  var values1: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(8, 4), hasBit: (0, 1)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(8, 4), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 1)) }
+  }
+
+  var values2: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(16, 8), hasBit: (0, 2)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(16, 8), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 2)) }
+  }
+
+  var values3: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(24, 12), hasBit: (0, 4)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(24, 12), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 4)) }
+  }
+
+  var values4: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(32, 16), hasBit: (0, 8)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(32, 16), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 8)) }
+  }
+
+  var unknownFields: SwiftProtobuf.UnknownStorage {
+    get { _storage.unknownFields }
+    _modify {
+      _ = _uniqueStorage()
+      yield &_storage.unknownFields
+    }
+  }
+
+  enum EnumTest1: Int, SwiftProtobuf.Enum, Swift.CaseIterable {
+    case firstValue = 1
+    case secondValue = 2
+
+    init() {
+      self = .firstValue
+    }
+
+  }
+
+  enum EnumTest2: Int, SwiftProtobuf.Enum, Swift.CaseIterable {
+    case firstValue = 1
+    case secondValue = 2
+
+    init() {
+      self = .firstValue
+    }
+
+  }
+
+  enum EnumTestNoStem: Int, SwiftProtobuf.Enum, Swift.CaseIterable {
+    case enumTestNoStem1 = 1
+    case enumTestNoStem2 = 2
+
+    init() {
+      self = .enumTestNoStem1
+    }
+
+  }
+
+  enum EnumTestReservedWord: Int, SwiftProtobuf.Enum, Swift.CaseIterable {
+    case `var` = 1
+    case notReserved = 2
+
+    init() {
+      self = .var
+    }
+
+  }
+
+  init() {}
+
+  private var _storage = SwiftProtobuf._MessageStorage(layout: Self._protobuf_messageLayout)
+
+  private mutating func _uniqueStorage() -> SwiftProtobuf._MessageStorage {
+    if !isKnownUniquelyReferenced(&_storage) { _storage = _storage.copy() }
+    return _storage
+  }
+  mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {
+    _ = _uniqueStorage()
+  }
+}
+
+struct SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest: @unchecked Sendable {
+  // SwiftProtobuf.Message conformance is added in an extension below. See the
+  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
+  // methods supported on all messages.
+
+  var values: [SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(8, 4), hasBit: (0, 1)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(8, 4), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 1)) }
+  }
+
+  var unknownFields: SwiftProtobuf.UnknownStorage {
+    get { _storage.unknownFields }
+    _modify {
+      _ = _uniqueStorage()
+      yield &_storage.unknownFields
+    }
+  }
+
+  enum EnumWithAlias: Int, SwiftProtobuf.Enum, Swift.CaseIterable {
+    case foo1 = 1
+    static let foo2 = foo1
+
+    /// out of value order to test allCases
+    case baz1 = 3
+    case bar1 = 2
+    static let bar2 = bar1
+
+    init() {
+      self = .foo1
+    }
+
+  }
+
+  init() {}
+
+  private var _storage = SwiftProtobuf._MessageStorage(layout: Self._protobuf_messageLayout)
+
+  private mutating func _uniqueStorage() -> SwiftProtobuf._MessageStorage {
+    if !isKnownUniquelyReferenced(&_storage) { _storage = _storage.copy() }
+    return _storage
+  }
+  mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {
+    _ = _uniqueStorage()
+  }
+}
+
+// MARK: - Code below here is support for the SwiftProtobuf runtime.
+
+fileprivate let _protobuf_package = "swift_proto_testing.enum2"
+
+extension SwiftProtoTesting_Enum2_SwiftEnumTest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = _protobuf_package + ".SwiftEnumTest"
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}values1\0\u{1}values2\0\u{1}values3\0\u{1}values4\0")
+  #if _pointerBitWidth(_64)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0(\0\0\u{4}\0\0\0\0\0\0\0\0\u{5}\0\0\u{1}\0\0\0\u{2}\u{8}\0\0\0\0\u{1}\0\u{e}\u{2}\0\0\0\u{2}\u{10}\0\0\u{1}\0\u{2}\0\u{e}\u{3}\0\0\0\u{2}\u{18}\0\0\u{2}\0\u{3}\0\u{e}\u{4}\0\0\0\u{2} \0\0\u{3}\0\u{4}\0\u{e}" }
+  #elseif _pointerBitWidth(_32)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{14}\0\0\u{4}\0\0\0\0\0\0\0\0\u{5}\0\0\u{1}\0\0\0\u{2}\u{4}\0\0\0\0\u{1}\0\u{e}\u{2}\0\0\0\u{2}\u{8}\0\0\u{1}\0\u{2}\0\u{e}\u{3}\0\0\0\u{2}\u{c}\0\0\u{2}\0\u{3}\0\u{e}\u{4}\0\0\0\u{2}\u{10}\0\0\u{3}\0\u{4}\0\u{e}" }
+  #else
+    #error("Unsupported platform")
+  #endif
+
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
+
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1].self)
+    case 2: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2].self)
+    case 3: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1].self)
+    case 2: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2].self)
+    case 3: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+    switch token.index {
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1].self)
+    case 2: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2].self)
+    case 3: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 2: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 3: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 4: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord].self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  func _protobuf_messageStorage(accessToken: SwiftProtobuf._MessageStorageToken) -> AnyObject { _storage }
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
+    fatalError("table-driven decodeMessage not yet implemented")
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    fatalError("table-driven traverse not yet implemented")
+  }
+
+  func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {
+    return try _storage.serializedBytes(partial: partial, options: options)
+  }
+  mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {
+    try _uniqueStorage().merge(byReadingFrom: body, partial: partial, options: options)
+  }
+
+  static func ==(lhs: SwiftProtoTesting_Enum2_SwiftEnumTest, rhs: SwiftProtoTesting_Enum2_SwiftEnumTest) -> Bool {
+    return lhs._storage.isEqual(to: rhs._storage)
+  }
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest1: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ENUM_TEST_1_FIRST_VALUE\0\u{1}ENUM_TEST_1_SECOND_VALUE\0")
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTest2: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ENUM_TEST_2_FIRST_VALUE\0\u{1}SECOND_VALUE\0")
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestNoStem: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ENUM_TEST_NO_STEM_1\0\u{1}ENUM_TEST_NO_STEM_2\0")
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumTest.EnumTestReservedWord: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}ENUM_TEST_RESERVED_WORD_VAR\0\u{1}ENUM_TEST_RESERVED_WORD_NOT_RESERVED\0")
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = _protobuf_package + ".SwiftEnumWithAliasTest"
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}values\0")
+  #if _pointerBitWidth(_64)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{10}\0\0\u{1}\0\0\0\0\0\0\0\0\u{2}\0\0\u{1}\0\0\0\u{a}\u{8}\0\0\0\0\u{1}\0\u{e}" }
+  #elseif _pointerBitWidth(_32)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{8}\0\0\u{1}\0\0\0\0\0\0\0\0\u{2}\0\0\u{1}\0\0\0\u{a}\u{4}\0\0\0\0\u{1}\0\u{e}" }
+  #else
+    #error("Unsupported platform")
+  #endif
+
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
+
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+    switch token.index {
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias].self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  func _protobuf_messageStorage(accessToken: SwiftProtobuf._MessageStorageToken) -> AnyObject { _storage }
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
+    fatalError("table-driven decodeMessage not yet implemented")
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    fatalError("table-driven traverse not yet implemented")
+  }
+
+  func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {
+    return try _storage.serializedBytes(partial: partial, options: options)
+  }
+  mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {
+    try _uniqueStorage().merge(byReadingFrom: body, partial: partial, options: options)
+  }
+
+  static func ==(lhs: SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest, rhs: SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest) -> Bool {
+    return lhs._storage.isEqual(to: rhs._storage)
+  }
+}
+
+extension SwiftProtoTesting_Enum2_SwiftEnumWithAliasTest.EnumWithAlias: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{9}FOO1\0\u{1}FOO2\0\u{9}BAR1\0\u{1}BAR2\0\u{1}BAZ1\0")
+}

+ 484 - 0
Tests/ExperimentalTableDrivenSwiftProtobufTests/unittest_swift_enum_proto3.pb.swift

@@ -0,0 +1,484 @@
+// DO NOT EDIT.
+// swift-format-ignore-file
+// swiftlint:disable all
+//
+// Generated by the Swift generator plugin for the protocol buffer compiler.
+// Source: unittest_swift_enum_proto3.proto
+//
+// For information on using the generated types, please see the documentation:
+//   https://github.com/apple/swift-protobuf/
+
+// Protocol Buffers - Google's data interchange format
+// Copyright 2015 Apple, Inc.  All Rights Reserved.
+// https://developers.google.com/protocol-buffers/
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+//     * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+@_spi(ForGeneratedCodeOnly) import SwiftProtobuf
+
+// If the compiler emits an error on this type, it is because this file
+// was generated by a version of the `protoc` Swift plug-in that is
+// incompatible with the version of SwiftProtobuf to which you are linking.
+// Please ensure that you are building against the same version of the API
+// that was used to generate this file.
+fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
+  struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
+  typealias Version = _2
+}
+
+struct SwiftProtoTesting_Enum3_SwiftEnumTest: @unchecked Sendable {
+  // SwiftProtobuf.Message conformance is added in an extension below. See the
+  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
+  // methods supported on all messages.
+
+  var values1: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(8, 4), hasBit: (0, 1)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(8, 4), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 1)) }
+  }
+
+  var values2: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(16, 8), hasBit: (0, 2)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(16, 8), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 2)) }
+  }
+
+  var values3: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(24, 12), hasBit: (0, 4)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(24, 12), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 4)) }
+  }
+
+  var values4: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(32, 16), hasBit: (0, 8)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(32, 16), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 8)) }
+  }
+
+  var unknownFields: SwiftProtobuf.UnknownStorage {
+    get { _storage.unknownFields }
+    _modify {
+      _ = _uniqueStorage()
+      yield &_storage.unknownFields
+    }
+  }
+
+  enum EnumTest1: SwiftProtobuf.Enum, Swift.CaseIterable {
+    typealias RawValue = Int
+    case firstValue // = 0
+    case secondValue // = 2
+    case UNRECOGNIZED(Int)
+
+    init() {
+      self = .firstValue
+    }
+
+    init?(rawValue: Int) {
+      switch rawValue {
+      case 0: self = .firstValue
+      case 2: self = .secondValue
+      default: self = .UNRECOGNIZED(rawValue)
+      }
+    }
+
+    var rawValue: Int {
+      switch self {
+      case .firstValue: return 0
+      case .secondValue: return 2
+      case .UNRECOGNIZED(let i): return i
+      }
+    }
+
+    // The compiler won't synthesize support with the UNRECOGNIZED case.
+    static let allCases: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1] = [
+      .firstValue,
+      .secondValue,
+    ]
+
+  }
+
+  enum EnumTest2: SwiftProtobuf.Enum, Swift.CaseIterable {
+    typealias RawValue = Int
+    case firstValue // = 0
+    case secondValue // = 2
+    case UNRECOGNIZED(Int)
+
+    init() {
+      self = .firstValue
+    }
+
+    init?(rawValue: Int) {
+      switch rawValue {
+      case 0: self = .firstValue
+      case 2: self = .secondValue
+      default: self = .UNRECOGNIZED(rawValue)
+      }
+    }
+
+    var rawValue: Int {
+      switch self {
+      case .firstValue: return 0
+      case .secondValue: return 2
+      case .UNRECOGNIZED(let i): return i
+      }
+    }
+
+    // The compiler won't synthesize support with the UNRECOGNIZED case.
+    static let allCases: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2] = [
+      .firstValue,
+      .secondValue,
+    ]
+
+  }
+
+  enum EnumTestNoStem: SwiftProtobuf.Enum, Swift.CaseIterable {
+    typealias RawValue = Int
+    case enumTestNoStem1 // = 0
+    case enumTestNoStem2 // = 2
+    case UNRECOGNIZED(Int)
+
+    init() {
+      self = .enumTestNoStem1
+    }
+
+    init?(rawValue: Int) {
+      switch rawValue {
+      case 0: self = .enumTestNoStem1
+      case 2: self = .enumTestNoStem2
+      default: self = .UNRECOGNIZED(rawValue)
+      }
+    }
+
+    var rawValue: Int {
+      switch self {
+      case .enumTestNoStem1: return 0
+      case .enumTestNoStem2: return 2
+      case .UNRECOGNIZED(let i): return i
+      }
+    }
+
+    // The compiler won't synthesize support with the UNRECOGNIZED case.
+    static let allCases: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem] = [
+      .enumTestNoStem1,
+      .enumTestNoStem2,
+    ]
+
+  }
+
+  enum EnumTestReservedWord: SwiftProtobuf.Enum, Swift.CaseIterable {
+    typealias RawValue = Int
+    case `var` // = 0
+    case notReserved // = 2
+    case UNRECOGNIZED(Int)
+
+    init() {
+      self = .var
+    }
+
+    init?(rawValue: Int) {
+      switch rawValue {
+      case 0: self = .var
+      case 2: self = .notReserved
+      default: self = .UNRECOGNIZED(rawValue)
+      }
+    }
+
+    var rawValue: Int {
+      switch self {
+      case .var: return 0
+      case .notReserved: return 2
+      case .UNRECOGNIZED(let i): return i
+      }
+    }
+
+    // The compiler won't synthesize support with the UNRECOGNIZED case.
+    static let allCases: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord] = [
+      .var,
+      .notReserved,
+    ]
+
+  }
+
+  init() {}
+
+  private var _storage = SwiftProtobuf._MessageStorage(layout: Self._protobuf_messageLayout)
+
+  private mutating func _uniqueStorage() -> SwiftProtobuf._MessageStorage {
+    if !isKnownUniquelyReferenced(&_storage) { _storage = _storage.copy() }
+    return _storage
+  }
+  mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {
+    _ = _uniqueStorage()
+  }
+}
+
+struct SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest: @unchecked Sendable {
+  // SwiftProtobuf.Message conformance is added in an extension below. See the
+  // `Message` and `Message+*Additions` files in the SwiftProtobuf library for
+  // methods supported on all messages.
+
+  var values: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias] {
+    get { return _storage.value(at: SwiftProtobuf._fieldOffset(8, 4), hasBit: (0, 1)) }
+    set { _uniqueStorage().updateValue(at: SwiftProtobuf._fieldOffset(8, 4), to: newValue, willBeSet: !newValue.isEmpty, hasBit: (0, 1)) }
+  }
+
+  var unknownFields: SwiftProtobuf.UnknownStorage {
+    get { _storage.unknownFields }
+    _modify {
+      _ = _uniqueStorage()
+      yield &_storage.unknownFields
+    }
+  }
+
+  enum EnumWithAlias: SwiftProtobuf.Enum, Swift.CaseIterable {
+    typealias RawValue = Int
+    case foo1 // = 0
+    static let foo2 = foo1
+
+    /// out of value order to test allCases
+    case baz1 // = 3
+    case bar1 // = 2
+    static let bar2 = bar1
+    case UNRECOGNIZED(Int)
+
+    init() {
+      self = .foo1
+    }
+
+    init?(rawValue: Int) {
+      switch rawValue {
+      case 0: self = .foo1
+      case 2: self = .bar1
+      case 3: self = .baz1
+      default: self = .UNRECOGNIZED(rawValue)
+      }
+    }
+
+    var rawValue: Int {
+      switch self {
+      case .foo1: return 0
+      case .bar1: return 2
+      case .baz1: return 3
+      case .UNRECOGNIZED(let i): return i
+      }
+    }
+
+    // The compiler won't synthesize support with the UNRECOGNIZED case.
+    static let allCases: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias] = [
+      .foo1,
+      .baz1,
+      .bar1,
+    ]
+
+  }
+
+  init() {}
+
+  private var _storage = SwiftProtobuf._MessageStorage(layout: Self._protobuf_messageLayout)
+
+  private mutating func _uniqueStorage() -> SwiftProtobuf._MessageStorage {
+    if !isKnownUniquelyReferenced(&_storage) { _storage = _storage.copy() }
+    return _storage
+  }
+  mutating func _protobuf_ensureUniqueStorage(accessToken: SwiftProtobuf._MessageStorageToken) {
+    _ = _uniqueStorage()
+  }
+}
+
+// MARK: - Code below here is support for the SwiftProtobuf runtime.
+
+fileprivate let _protobuf_package = "swift_proto_testing.enum3"
+
+extension SwiftProtoTesting_Enum3_SwiftEnumTest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = _protobuf_package + ".SwiftEnumTest"
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}values1\0\u{1}values2\0\u{1}values3\0\u{1}values4\0")
+  #if _pointerBitWidth(_64)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0(\0\0\u{4}\0\0\0\0\0\0\0\0\u{5}\0\0\u{1}\0\0\0\u{a}\u{8}\0\0\0\0\u{1}\0\u{5}\u{2}\0\0\0\u{a}\u{10}\0\0\u{1}\0\u{2}\0\u{5}\u{3}\0\0\0\u{a}\u{18}\0\0\u{2}\0\u{3}\0\u{5}\u{4}\0\0\0\u{a} \0\0\u{3}\0\u{4}\0\u{5}" }
+  #elseif _pointerBitWidth(_32)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{14}\0\0\u{4}\0\0\0\0\0\0\0\0\u{5}\0\0\u{1}\0\0\0\u{a}\u{4}\0\0\0\0\u{1}\0\u{5}\u{2}\0\0\0\u{a}\u{8}\0\0\u{1}\0\u{2}\0\u{5}\u{3}\0\0\0\u{a}\u{c}\0\0\u{2}\0\u{3}\0\u{5}\u{4}\0\0\0\u{a}\u{10}\0\0\u{3}\0\u{4}\0\u{5}" }
+  #else
+    #error("Unsupported platform")
+  #endif
+
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
+
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1].self)
+    case 2: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2].self)
+    case 3: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1].self)
+    case 2: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2].self)
+    case 3: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+    switch token.index {
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1].self)
+    case 2: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2].self)
+    case 3: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem].self)
+    case 4: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 2: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 3: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem].self, perform: perform, onInvalidValue: onInvalidValue)
+    case 4: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord].self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  func _protobuf_messageStorage(accessToken: SwiftProtobuf._MessageStorageToken) -> AnyObject { _storage }
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
+    fatalError("table-driven decodeMessage not yet implemented")
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    fatalError("table-driven traverse not yet implemented")
+  }
+
+  func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {
+    return try _storage.serializedBytes(partial: partial, options: options)
+  }
+  mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {
+    try _uniqueStorage().merge(byReadingFrom: body, partial: partial, options: options)
+  }
+
+  static func ==(lhs: SwiftProtoTesting_Enum3_SwiftEnumTest, rhs: SwiftProtoTesting_Enum3_SwiftEnumTest) -> Bool {
+    return lhs._storage.isEqual(to: rhs._storage)
+  }
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest1: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ENUM_TEST_1_FIRST_VALUE\0\u{2}\u{2}ENUM_TEST_1_SECOND_VALUE\0")
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTest2: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ENUM_TEST_2_FIRST_VALUE\0\u{2}\u{2}SECOND_VALUE\0")
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestNoStem: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ENUM_TEST_NO_STEM_1\0\u{2}\u{2}ENUM_TEST_NO_STEM_2\0")
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumTest.EnumTestReservedWord: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{2}\0ENUM_TEST_RESERVED_WORD_VAR\0\u{2}\u{2}ENUM_TEST_RESERVED_WORD_NOT_RESERVED\0")
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = _protobuf_package + ".SwiftEnumWithAliasTest"
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{1}values\0")
+  #if _pointerBitWidth(_64)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{10}\0\0\u{1}\0\0\0\0\0\0\0\0\u{2}\0\0\u{1}\0\0\0\u{a}\u{8}\0\0\0\0\u{1}\0\u{5}" }
+  #elseif _pointerBitWidth(_32)
+    @_alwaysEmitIntoClient @inline(__always)
+    private static var _protobuf_messageLayoutString: StaticString { "\0\u{8}\0\0\u{1}\0\0\0\0\0\0\0\0\u{2}\0\0\u{1}\0\0\0\u{a}\u{4}\0\0\0\0\u{1}\0\u{5}" }
+  #else
+    #error("Unsupported platform")
+  #endif
+
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
+
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: storage.deinitializeField(field, type: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+    switch token.index {
+    case 1: source.copyField(field, to: destination, type: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+    switch token.index {
+    case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias].self)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    case 1: return try storage.performOnRawEnumValues(of: field, operation: operation, type: [SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias].self, perform: perform, onInvalidValue: onInvalidValue)
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  func _protobuf_messageStorage(accessToken: SwiftProtobuf._MessageStorageToken) -> AnyObject { _storage }
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
+    fatalError("table-driven decodeMessage not yet implemented")
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    fatalError("table-driven traverse not yet implemented")
+  }
+
+  func serializedBytes<Bytes: SwiftProtobufContiguousBytes>(partial: Bool = false, options: BinaryEncodingOptions = BinaryEncodingOptions()) throws -> Bytes {
+    return try _storage.serializedBytes(partial: partial, options: options)
+  }
+  mutating func _merge(rawBuffer body: UnsafeRawBufferPointer, extensions: (any ExtensionMap)?, partial: Bool, options: BinaryDecodingOptions) throws {
+    try _uniqueStorage().merge(byReadingFrom: body, partial: partial, options: options)
+  }
+
+  static func ==(lhs: SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest, rhs: SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest) -> Bool {
+    return lhs._storage.isEqual(to: rhs._storage)
+  }
+}
+
+extension SwiftProtoTesting_Enum3_SwiftEnumWithAliasTest.EnumWithAlias: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap = SwiftProtobuf._NameMap(bytecode: "\0\u{a}\0FOO1\0\u{1}FOO2\0\u{a}\u{2}BAR1\0\u{1}BAR2\0\u{1}BAZ1\0")
+}

+ 60 - 36
Tests/ExperimentalTableDrivenSwiftProtobufTests/unittest_swift_required_fields.pb.swift

@@ -848,33 +848,39 @@ extension SwiftProtoTesting_RequiredWithNested: SwiftProtobuf.Message, SwiftProt
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeSubmessage: _protobuf_deinitializeSubmessage, copySubmessage: _protobuf_copySubmessage, areSubmessagesEqual: _protobuf_areSubmessagesEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
 
-  private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: storage.deinitializeField(field, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
     switch token.index {
     case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
     switch token.index {
     case 1: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_NestedRequired.self, perform: perform)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
@@ -917,33 +923,39 @@ extension SwiftProtoTesting_RequiredWithRepeatedNested: SwiftProtobuf.Message, S
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeSubmessage: _protobuf_deinitializeSubmessage, copySubmessage: _protobuf_copySubmessage, areSubmessagesEqual: _protobuf_areSubmessagesEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
 
-  private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: storage.deinitializeField(field, type: [SwiftProtoTesting_NestedRequired].self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: source.copyField(field, to: destination, type: [SwiftProtoTesting_NestedRequired].self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
     switch token.index {
     case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: [SwiftProtoTesting_NestedRequired].self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
     switch token.index {
     case 1: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: [SwiftProtoTesting_NestedRequired].self, perform: perform)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
@@ -1027,33 +1039,39 @@ extension SwiftProtoTesting_NoneRequired: SwiftProtobuf.Message, SwiftProtobuf._
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeSubmessage: _protobuf_deinitializeSubmessage, copySubmessage: _protobuf_copySubmessage, areSubmessagesEqual: _protobuf_areSubmessagesEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
 
-  private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: storage.deinitializeField(field, type: SwiftProtoTesting_NestedNoneRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_NestedNoneRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
     switch token.index {
     case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_NestedNoneRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
     switch token.index {
     case 1: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_NestedNoneRequired.self, perform: perform)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
@@ -1129,33 +1147,39 @@ extension SwiftProtoTesting_NoneRequiredButNestedRequired: SwiftProtobuf.Message
     #error("Unsupported platform")
   #endif
 
-  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeSubmessage: _protobuf_deinitializeSubmessage, copySubmessage: _protobuf_copySubmessage, areSubmessagesEqual: _protobuf_areSubmessagesEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage)
+  private static let _protobuf_messageLayout = SwiftProtobuf._MessageLayout(layout: _protobuf_messageLayoutString, deinitializeField: _protobuf_deinitializeField, copyField: _protobuf_copyField, areFieldsEqual: _protobuf_areFieldsEqual, performOnSubmessageStorage: _protobuf_performOnSubmessageStorage, performOnRawEnumValues: _protobuf_performOnRawEnumValues)
 
-  private static func _protobuf_deinitializeSubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_deinitializeField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: storage.deinitializeField(field, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_copySubmessage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
+  private static func _protobuf_copyField(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, from source: SwiftProtobuf._MessageStorage, to destination: SwiftProtobuf._MessageStorage) {
     switch token.index {
     case 1: source.copyField(field, to: destination, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_areSubmessagesEqual(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
+  private static func _protobuf_areFieldsEqual(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, lhs: SwiftProtobuf._MessageStorage, rhs: SwiftProtobuf._MessageStorage) -> Bool {
     switch token.index {
     case 1: return lhs.isField(field, equalToSameFieldIn: rhs, type: SwiftProtoTesting_NestedRequired.self)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 
-  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.SubmessageToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.SubmessageStorageOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
+  private static func _protobuf_performOnSubmessageStorage(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (SwiftProtobuf._MessageStorage) throws -> Bool) throws -> Bool {
     switch token.index {
     case 1: return try storage.performOnSubmessageStorage(of: field, operation: operation, type: SwiftProtoTesting_NestedRequired.self, perform: perform)
-    default: preconditionFailure("invalid submessage token; this is a generator bug")
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
+    }
+  }
+
+  private static func _protobuf_performOnRawEnumValues(for token: SwiftProtobuf._MessageLayout.TrampolineToken, field: SwiftProtobuf.FieldLayout, storage: SwiftProtobuf._MessageStorage, operation: SwiftProtobuf.TrampolineFieldOperation, perform: (inout Int32) throws -> Bool, onInvalidValue: (Int32) -> Void) throws {
+    switch token.index {
+    default: preconditionFailure("invalid trampoline token; this is a generator bug")
     }
   }
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio