Skip to content

Commit 0e70157

Browse files
committed
Revise how modules imports are done to better support import public
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.
1 parent 2a383b6 commit 0e70157

File tree

19 files changed

+618
-36
lines changed

19 files changed

+618
-36
lines changed

CompileTests/MultiModule/Sources/ImportsAPublicly/imports_a_publicly.pb.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import Foundation
1111
import SwiftProtobuf
1212

13-
import ModuleA
13+
// Use of 'import public' causes re-exports:
14+
@_exported import enum ModuleA.E
15+
@_exported import let ModuleA.Extensions_ext_str
16+
@_exported import struct ModuleA.A
1417

1518
// If the compiler emits an error on this type, it is because this file
1619
// was generated by a version of the `protoc` Swift plug-in that is
@@ -62,6 +65,11 @@ extension ImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
6265
12: .same(proto: "e"),
6366
]
6467

68+
public var isInitialized: Bool {
69+
if let v = self._a, !v.isInitialized {return false}
70+
return true
71+
}
72+
6573
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6674
while let fieldNumber = try decoder.nextFieldNumber() {
6775
// The use of inline closures is to circumvent an issue where the compiler

CompileTests/MultiModule/Sources/ImportsImportsAPublicly/imports_imports_a_publicly.pb.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
import Foundation
1111
import SwiftProtobuf
1212

13-
import ImportsAPublicly
14-
import ModuleA
13+
// Use of 'import public' causes re-exports:
14+
@_exported import enum ModuleA.E
15+
@_exported import let ModuleA.Extensions_ext_str
16+
@_exported import struct ImportsAPublicly.ImportsAPublicly
17+
@_exported import struct ModuleA.A
1518

1619
// If the compiler emits an error on this type, it is because this file
1720
// was generated by a version of the `protoc` Swift plug-in that is
@@ -63,6 +66,11 @@ extension ImportsImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._Message
6366
22: .same(proto: "e"),
6467
]
6568

69+
public var isInitialized: Bool {
70+
if let v = self._a, !v.isInitialized {return false}
71+
return true
72+
}
73+
6674
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6775
while let fieldNumber = try decoder.nextFieldNumber() {
6876
// The use of inline closures is to circumvent an issue where the compiler

CompileTests/MultiModule/Sources/ModuleA/a.pb.swift

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public enum E: SwiftProtobuf.Enum {
4949

5050
}
5151

52-
public struct A: Sendable {
52+
public struct A: SwiftProtobuf.ExtensibleMessage, Sendable {
5353
// SwiftProtobuf.Message conformance is added in an extension below. See the
5454
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
5555
// methods supported on all messages.
@@ -67,9 +67,57 @@ public struct A: Sendable {
6767

6868
public init() {}
6969

70+
public var _protobuf_extensionFieldValues = SwiftProtobuf.ExtensionFieldValueSet()
7071
fileprivate var _e: E? = nil
7172
}
7273

74+
// MARK: - Extension support defined in a.proto.
75+
76+
// MARK: - Extension Properties
77+
78+
// Swift Extensions on the exteneded Messages to add easy access to the declared
79+
// extension fields. The names are based on the extension field name from the proto
80+
// declaration. To avoid naming collisions, the names are prefixed with the name of
81+
// the scope where the extend directive occurs.
82+
83+
extension A {
84+
85+
public var extStr: String {
86+
get {return getExtensionValue(ext: Extensions_ext_str) ?? String()}
87+
set {setExtensionValue(ext: Extensions_ext_str, value: newValue)}
88+
}
89+
/// Returns true if extension `Extensions_ext_str`
90+
/// has been explicitly set.
91+
public var hasExtStr: Bool {
92+
return hasExtensionValue(ext: Extensions_ext_str)
93+
}
94+
/// Clears the value of extension `Extensions_ext_str`.
95+
/// Subsequent reads from it will return its default value.
96+
public mutating func clearExtStr() {
97+
clearExtensionValue(ext: Extensions_ext_str)
98+
}
99+
100+
}
101+
102+
// MARK: - File's ExtensionMap: A_Extensions
103+
104+
/// A `SwiftProtobuf.SimpleExtensionMap` that includes all of the extensions defined by
105+
/// this .proto file. It can be used any place an `SwiftProtobuf.ExtensionMap` is needed
106+
/// in parsing, or it can be combined with other `SwiftProtobuf.SimpleExtensionMap`s to create
107+
/// a larger `SwiftProtobuf.SimpleExtensionMap`.
108+
public let A_Extensions: SwiftProtobuf.SimpleExtensionMap = [
109+
Extensions_ext_str
110+
]
111+
112+
// Extension Objects - The only reason these might be needed is when manually
113+
// constructing a `SimpleExtensionMap`, otherwise, use the above _Extension Properties_
114+
// accessors for the extension fields on the messages directly.
115+
116+
public let Extensions_ext_str = SwiftProtobuf.MessageExtension<SwiftProtobuf.OptionalExtensionField<SwiftProtobuf.ProtobufString>, A>(
117+
_protobuf_fieldNumber: 100,
118+
fieldName: "ext_str"
119+
)
120+
73121
// MARK: - Code below here is support for the SwiftProtobuf runtime.
74122

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

137+
public var isInitialized: Bool {
138+
if !_protobuf_extensionFieldValues.isInitialized {return false}
139+
return true
140+
}
141+
89142
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
90143
while let fieldNumber = try decoder.nextFieldNumber() {
91144
// The use of inline closures is to circumvent an issue where the compiler
92145
// allocates stack space for every case branch when no optimizations are
93146
// enabled. https://github.com/apple/swift-protobuf/issues/1034
94147
switch fieldNumber {
95148
case 1: try { try decoder.decodeSingularEnumField(value: &self._e) }()
149+
case 100..<1001:
150+
try { try decoder.decodeExtensionField(values: &_protobuf_extensionFieldValues, messageType: A.self, fieldNumber: fieldNumber) }()
96151
default: break
97152
}
98153
}
@@ -106,12 +161,14 @@ extension A: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, Sw
106161
try { if let v = self._e {
107162
try visitor.visitSingularEnumField(value: v, fieldNumber: 1)
108163
} }()
164+
try visitor.visitExtensionFields(fields: _protobuf_extensionFieldValues, start: 100, end: 1001)
109165
try unknownFields.traverse(visitor: &visitor)
110166
}
111167

112168
public static func ==(lhs: A, rhs: A) -> Bool {
113169
if lhs._e != rhs._e {return false}
114170
if lhs.unknownFields != rhs.unknownFields {return false}
171+
if lhs._protobuf_extensionFieldValues != rhs._protobuf_extensionFieldValues {return false}
115172
return true
116173
}
117174
}

CompileTests/MultiModule/Tests/Test1/test1.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ImportsAPublicly
2-
import ModuleA // Needed because `import public` doesn't help Swift
2+
// Don't need to import ModuleA because of the file being a `import public`
33

44
import XCTest
55

CompileTests/MultiModule/Tests/Test1/uses_a_transitively.pb.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import Foundation
1111
import SwiftProtobuf
1212

1313
import ImportsAPublicly
14-
import ModuleA
1514

1615
// If the compiler emits an error on this type, it is because this file
1716
// was generated by a version of the `protoc` Swift plug-in that is
@@ -63,6 +62,11 @@ extension UsesATransitively: SwiftProtobuf.Message, SwiftProtobuf._MessageImplem
6362
102: .same(proto: "e"),
6463
]
6564

65+
public var isInitialized: Bool {
66+
if let v = self._a, !v.isInitialized {return false}
67+
return true
68+
}
69+
6670
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6771
while let fieldNumber = try decoder.nextFieldNumber() {
6872
// The use of inline closures is to circumvent an issue where the compiler

CompileTests/MultiModule/Tests/Test2/test2.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import ImportsImportsAPublicly
2-
import ModuleA // Needed because `import public` doesn't help Swift
2+
// Don't need to import ModuleA because of the file being a `import public`
33

44
import XCTest
55

CompileTests/MultiModule/Tests/Test2/uses_a_transitively2.pb.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@
1010
import Foundation
1111
import SwiftProtobuf
1212

13-
import ImportsAPublicly
1413
import ImportsImportsAPublicly
15-
import ModuleA
1614

1715
// If the compiler emits an error on this type, it is because this file
1816
// was generated by a version of the `protoc` Swift plug-in that is
@@ -64,6 +62,11 @@ extension UsesATransitively2: SwiftProtobuf.Message, SwiftProtobuf._MessageImple
6462
122: .same(proto: "e"),
6563
]
6664

65+
public var isInitialized: Bool {
66+
if let v = self._a, !v.isInitialized {return false}
67+
return true
68+
}
69+
6770
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6871
while let fieldNumber = try decoder.nextFieldNumber() {
6972
// The use of inline closures is to circumvent an issue where the compiler

Protos/CompileTests/MultiModule/Sources/ModuleA/a.proto

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,9 @@ enum E {
66

77
message A {
88
optional E e = 1;
9+
extensions 100 to 1000;
10+
}
11+
12+
extend A {
13+
optional string ext_str = 100;
914
}

Reference/CompileTests/MultiModule/Sources/ImportsAPublicly/imports_a_publicly.pb.swift

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@
1010
import Foundation
1111
import SwiftProtobuf
1212

13-
import ModuleA
13+
// Use of 'import public' causes re-exports:
14+
@_exported import enum ModuleA.E
15+
@_exported import let ModuleA.Extensions_ext_str
16+
@_exported import struct ModuleA.A
1417

1518
// If the compiler emits an error on this type, it is because this file
1619
// was generated by a version of the `protoc` Swift plug-in that is
@@ -62,6 +65,11 @@ extension ImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._MessageImpleme
6265
12: .same(proto: "e"),
6366
]
6467

68+
public var isInitialized: Bool {
69+
if let v = self._a, !v.isInitialized {return false}
70+
return true
71+
}
72+
6573
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6674
while let fieldNumber = try decoder.nextFieldNumber() {
6775
// The use of inline closures is to circumvent an issue where the compiler

Reference/CompileTests/MultiModule/Sources/ImportsImportsAPublicly/imports_imports_a_publicly.pb.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@
1010
import Foundation
1111
import SwiftProtobuf
1212

13-
import ImportsAPublicly
14-
import ModuleA
13+
// Use of 'import public' causes re-exports:
14+
@_exported import enum ModuleA.E
15+
@_exported import let ModuleA.Extensions_ext_str
16+
@_exported import struct ImportsAPublicly.ImportsAPublicly
17+
@_exported import struct ModuleA.A
1518

1619
// If the compiler emits an error on this type, it is because this file
1720
// was generated by a version of the `protoc` Swift plug-in that is
@@ -63,6 +66,11 @@ extension ImportsImportsAPublicly: SwiftProtobuf.Message, SwiftProtobuf._Message
6366
22: .same(proto: "e"),
6467
]
6568

69+
public var isInitialized: Bool {
70+
if let v = self._a, !v.isInitialized {return false}
71+
return true
72+
}
73+
6674
public mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
6775
while let fieldNumber = try decoder.nextFieldNumber() {
6876
// The use of inline closures is to circumvent an issue where the compiler

0 commit comments

Comments
 (0)