Skip to content

Commit

Permalink
Revise how modules imports are done to better support import public
Browse files Browse the repository at this point in the history
Historically Swift didn't have a good way to model `import public` in .proto
files. But by using `@_exported import`, we can get the majority of the way
there. This makes restructuring of proto files less likely to break Swift source
code; meaning Swift is less of a "special case" compared to other languages.

If generating with `public` or `package` visibility, then instead of importing
the whole module of for an `import public`, explicitly re-export the imports of
the types from that file. That will make them usable for the given source file
but will also let the types continue to be used by someone just importing this
module.

This also means a file with no `import public` usages becomes must simpler as it
can just rely on its direct dependencies to provide all the types that could be
used.
  • Loading branch information
thomasvl committed Jan 8, 2024
1 parent 5c215b2 commit e4f5491
Show file tree
Hide file tree
Showing 19 changed files with 618 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import Foundation
import SwiftProtobuf

import ModuleA
// Use of 'import public' causes re-exports:
@_exported import enum ModuleA.E
@_exported import let ModuleA.Extensions_ext_str
@_exported import struct ModuleA.A

// 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
Expand Down Expand Up @@ -62,6 +65,11 @@ extension ImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
12: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ModuleA
// Use of 'import public' causes re-exports:
@_exported import enum ModuleA.E
@_exported import let ModuleA.Extensions_ext_str
@_exported import struct ImportsAPublicly.ImportsAPublicly
@_exported import struct ModuleA.A

// 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
Expand Down Expand Up @@ -63,6 +66,11 @@ extension ImportsImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._Message
22: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
59 changes: 58 additions & 1 deletion CompileTests/MultiModule/Sources/ModuleA/a.pb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public enum E: SwiftProtobuf.Enum {

}

public struct A: Sendable {
public struct A: SwiftProtobuf.ExtensibleMessage, 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.
Expand All @@ -67,9 +67,57 @@ public struct A: Sendable {

public init() {}

public var _protobuf_extensionFieldValues = SwiftProtobuf.ExtensionFieldValueSet()
fileprivate var _e: E? = nil
}

// MARK: - Extension support defined in a.proto.

// MARK: - Extension Properties

// Swift Extensions on the exteneded Messages to add easy access to the declared
// extension fields. The names are based on the extension field name from the proto
// declaration. To avoid naming collisions, the names are prefixed with the name of
// the scope where the extend directive occurs.

extension A {

public var extStr: String {
get {return getExtensionValue(ext: Extensions_ext_str) ?? String()}
set {setExtensionValue(ext: Extensions_ext_str, value: newValue)}
}
/// Returns true if extension `Extensions_ext_str`
/// has been explicitly set.
public var hasExtStr: Bool {
return hasExtensionValue(ext: Extensions_ext_str)
}
/// Clears the value of extension `Extensions_ext_str`.
/// Subsequent reads from it will return its default value.
public mutating func clearExtStr() {
clearExtensionValue(ext: Extensions_ext_str)
}

}

// MARK: - File's ExtensionMap: A_Extensions

/// A `SwiftProtobuf.SimpleExtensionMap` that includes all of the extensions defined by
/// this .proto file. It can be used any place an `SwiftProtobuf.ExtensionMap` is needed
/// in parsing, or it can be combined with other `SwiftProtobuf.SimpleExtensionMap`s to create
/// a larger `SwiftProtobuf.SimpleExtensionMap`.
public let A_Extensions: SwiftProtobuf.SimpleExtensionMap = [
Extensions_ext_str
]

// Extension Objects - The only reason these might be needed is when manually
// constructing a `SimpleExtensionMap`, otherwise, use the above _Extension Properties_
// accessors for the extension fields on the messages directly.

public let Extensions_ext_str = SwiftProtobuf.MessageExtension<SwiftProtobuf.OptionalExtensionField<SwiftProtobuf.ProtobufString>, A>(
_protobuf_fieldNumber: 100,
fieldName: "ext_str"
)

// MARK: - Code below here is support for the SwiftProtobuf runtime.

extension E: SwiftProtobuf._ProtoNameProviding {
Expand All @@ -86,13 +134,20 @@ extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, Sw
1: .same(proto: "e"),
]

public var isInitialized: Bool {
if !_protobuf_extensionFieldValues.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self._e) }()
case 100..<1001:
try { try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: A.self, fieldNumber: fieldNumber) }()
default: break
}
}
Expand All @@ -106,12 +161,14 @@ extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, Sw
try { if let v = self._e {
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
} }()
try visitor.visitExtensionFields(fields: _protobuf_extensionFieldValues, start: 100, end: 1001)
try unknownFields.traverse(visitor: &visitor)
}

public static func ==(lhs: A, rhs: A) -> Bool {
if lhs._e != rhs._e {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
if lhs._protobuf_extensionFieldValues != rhs._protobuf_extensionFieldValues {return false}
return true
}
}
2 changes: 1 addition & 1 deletion CompileTests/MultiModule/Tests/Test1/test1.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ImportsAPublicly
import ModuleA // Needed because `import public` doesn't help Swift
// Don't need to import ModuleA because of the file being a `import public`

import XCTest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ModuleA

// 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
Expand Down Expand Up @@ -63,6 +62,11 @@ extension UsesATransitively: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
102: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
2 changes: 1 addition & 1 deletion CompileTests/MultiModule/Tests/Test2/test2.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import ImportsImportsAPublicly
import ModuleA // Needed because `import public` doesn't help Swift
// Don't need to import ModuleA because of the file being a `import public`

import XCTest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ImportsImportsAPublicly
import ModuleA

// 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
Expand Down Expand Up @@ -64,6 +62,11 @@ extension UsesATransitively2: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
122: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
5 changes: 5 additions & 0 deletions Protos/CompileTests/MultiModule/Sources/ModuleA/a.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ enum E {

message A {
optional E e = 1;
extensions 100 to 1000;
}

extend A {
optional string ext_str = 100;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
import Foundation
import SwiftProtobuf

import ModuleA
// Use of 'import public' causes re-exports:
@_exported import enum ModuleA.E
@_exported import let ModuleA.Extensions_ext_str
@_exported import struct ModuleA.A

// 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
Expand Down Expand Up @@ -62,6 +65,11 @@ extension ImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
12: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ModuleA
// Use of 'import public' causes re-exports:
@_exported import enum ModuleA.E
@_exported import let ModuleA.Extensions_ext_str
@_exported import struct ImportsAPublicly.ImportsAPublicly
@_exported import struct ModuleA.A

// 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
Expand Down Expand Up @@ -63,6 +66,11 @@ extension ImportsImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._Message
22: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
59 changes: 58 additions & 1 deletion Reference/CompileTests/MultiModule/Sources/ModuleA/a.pb.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public enum E: SwiftProtobuf.Enum {

}

public struct A: Sendable {
public struct A: SwiftProtobuf.ExtensibleMessage, 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.
Expand All @@ -67,9 +67,57 @@ public struct A: Sendable {

public init() {}

public var _protobuf_extensionFieldValues = SwiftProtobuf.ExtensionFieldValueSet()
fileprivate var _e: E? = nil
}

// MARK: - Extension support defined in a.proto.

// MARK: - Extension Properties

// Swift Extensions on the exteneded Messages to add easy access to the declared
// extension fields. The names are based on the extension field name from the proto
// declaration. To avoid naming collisions, the names are prefixed with the name of
// the scope where the extend directive occurs.

extension A {

public var extStr: String {
get {return getExtensionValue(ext: Extensions_ext_str) ?? String()}
set {setExtensionValue(ext: Extensions_ext_str, value: newValue)}
}
/// Returns true if extension `Extensions_ext_str`
/// has been explicitly set.
public var hasExtStr: Bool {
return hasExtensionValue(ext: Extensions_ext_str)
}
/// Clears the value of extension `Extensions_ext_str`.
/// Subsequent reads from it will return its default value.
public mutating func clearExtStr() {
clearExtensionValue(ext: Extensions_ext_str)
}

}

// MARK: - File's ExtensionMap: A_Extensions

/// A `SwiftProtobuf.SimpleExtensionMap` that includes all of the extensions defined by
/// this .proto file. It can be used any place an `SwiftProtobuf.ExtensionMap` is needed
/// in parsing, or it can be combined with other `SwiftProtobuf.SimpleExtensionMap`s to create
/// a larger `SwiftProtobuf.SimpleExtensionMap`.
public let A_Extensions: SwiftProtobuf.SimpleExtensionMap = [
Extensions_ext_str
]

// Extension Objects - The only reason these might be needed is when manually
// constructing a `SimpleExtensionMap`, otherwise, use the above _Extension Properties_
// accessors for the extension fields on the messages directly.

public let Extensions_ext_str = SwiftProtobuf.MessageExtension<SwiftProtobuf.OptionalExtensionField<SwiftProtobuf.ProtobufString>, A>(
_protobuf_fieldNumber: 100,
fieldName: "ext_str"
)

// MARK: - Code below here is support for the SwiftProtobuf runtime.

extension E: SwiftProtobuf._ProtoNameProviding {
Expand All @@ -86,13 +134,20 @@ extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, Sw
1: .same(proto: "e"),
]

public var isInitialized: Bool {
if !_protobuf_extensionFieldValues.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularEnumField(value: &self._e) }()
case 100..<1001:
try { try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: A.self, fieldNumber: fieldNumber) }()
default: break
}
}
Expand All @@ -106,12 +161,14 @@ extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, Sw
try { if let v = self._e {
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
} }()
try visitor.visitExtensionFields(fields: _protobuf_extensionFieldValues, start: 100, end: 1001)
try unknownFields.traverse(visitor: &visitor)
}

public static func ==(lhs: A, rhs: A) -> Bool {
if lhs._e != rhs._e {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
if lhs._protobuf_extensionFieldValues != rhs._protobuf_extensionFieldValues {return false}
return true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import Foundation
import SwiftProtobuf

import ImportsAPublicly
import ModuleA

// 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
Expand Down Expand Up @@ -63,6 +62,11 @@ extension UsesATransitively: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
102: .same(proto: "e"),
]

public var isInitialized: Bool {
if let v = self._a, !v.isInitialized {return false}
return true
}

public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
Expand Down
Loading

0 comments on commit e4f5491

Please sign in to comment.