Skip to content

Commit bc667c0

Browse files
committed
Add Sendable conformances to the SwiftSyntax module
1 parent c4529c1 commit bc667c0

39 files changed

+203
-94
lines changed

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/KeywordFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ let lookupTable = ArrayExprSyntax(leftSquare: .leftSquareToken(trailingTrivia: .
2424
let keywordFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2525
try! EnumDeclSyntax(
2626
"""
27-
public enum Keyword: UInt8, Hashable
27+
public enum Keyword: UInt8, Hashable, Sendable
2828
"""
2929
) {
3030
for keyword in Keyword.allCases {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxEnumFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ let syntaxEnumFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
try! EnumDeclSyntax(
2020
"""
2121
/// Enum to exhaustively switch over all different syntax nodes.
22-
public enum SyntaxEnum
22+
public enum SyntaxEnum: Sendable
2323
"""
2424
) {
2525
DeclSyntax("case token(TokenSyntax)")

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/SyntaxKindFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ let syntaxKindFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
try! EnumDeclSyntax(
2020
"""
2121
/// Enumerates the known kinds of Syntax represented in the Syntax tree.
22-
public enum SyntaxKind
22+
public enum SyntaxKind: Sendable
2323
"""
2424
) {
2525
DeclSyntax("case token")

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TokenKindFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ let tokenKindFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
try! EnumDeclSyntax(
2020
"""
2121
/// Enumerates the kinds of tokens in the Swift language.
22-
public enum TokenKind: Hashable
22+
public enum TokenKind: Hashable, Sendable
2323
"""
2424
) {
2525
for tokenSpec in Token.allCases.map(\.spec) {

CodeGeneration/Sources/generate-swift-syntax/templates/swiftsyntax/TriviaPiecesFile.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ let triviaPiecesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
2626
///
2727
/// In general, you should deal with the actual Trivia collection instead
2828
/// of individual pieces whenever possible.
29-
public enum TriviaPiece
29+
public enum TriviaPiece: Sendable
3030
"""
3131
) {
3232
for trivia in TRIVIAS {
@@ -176,7 +176,7 @@ let triviaPiecesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
176176
/// In contrast to ``TriviaPiece``, a ``RawTriviaPiece`` does not own the source
177177
/// text of the trivia.
178178
@_spi(RawSyntax)
179-
public enum RawTriviaPiece: Equatable
179+
public enum RawTriviaPiece: Equatable, Sendable
180180
"""
181181
) {
182182
for trivia in TRIVIAS {

Sources/SwiftParser/CollectionNodes+Parsable.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ fileprivate extension SyntaxCollection {
4545
} else {
4646
// First unwrap: We know that children.last exists because children is not empty
4747
// Second unwrap: This is a collection and collections never have optional children. Thus the last child can’t be nil.
48-
let lastWithRemainder = parser.parseRemainder(into: layoutView.children.last!!)
48+
let lastWithRemainder = parser.parseRemainder(into: layoutView.children[layoutView.children.count - 1]!)
4949
let raw = layoutView.replacingChild(at: layoutView.children.count - 1, with: lastWithRemainder, arena: parser.arena)
5050
return Syntax(raw: raw, rawNodeArena: parser.arena).cast(Self.self)
5151
}

Sources/SwiftSyntax/AbsolutePosition.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/// An absolute position in a source file as text - the absolute utf8Offset from
1414
/// the start of the file.
15-
public struct AbsolutePosition: Comparable, Hashable {
15+
public struct AbsolutePosition: Comparable, Hashable, Sendable {
1616
public let utf8Offset: Int
1717

1818
static let startOfFile = AbsolutePosition(utf8Offset: 0)

Sources/SwiftSyntax/AbsoluteRawSyntax.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
struct AbsoluteRawSyntax {
13+
struct AbsoluteRawSyntax: Sendable {
1414
let raw: RawSyntax
1515
let info: AbsoluteSyntaxInfo
1616

Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
struct AbsoluteSyntaxPosition {
13+
struct AbsoluteSyntaxPosition: Sendable {
1414
/// The UTF-8 offset of the syntax node in the source file
1515
let offset: UInt32
1616
let indexInParent: UInt32
@@ -32,7 +32,7 @@ struct AbsoluteSyntaxPosition {
3232

3333
/// `AbsoluteSyntaxInfo` represents the information that relates a `RawSyntax`
3434
/// to a source file tree, like its absolute source offset.
35-
struct AbsoluteSyntaxInfo {
35+
struct AbsoluteSyntaxInfo: Sendable {
3636
let position: AbsoluteSyntaxPosition
3737
let nodeId: SyntaxIdentifier
3838

Sources/SwiftSyntax/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ add_swift_syntax_library(SwiftSyntax
2323
SwiftSyntaxCompatibility.swift
2424
Syntax.swift
2525
SyntaxArena.swift
26+
SyntaxArenaAllocatedBuffer.swift
2627
SyntaxChildren.swift
2728
SyntaxCollection.swift
2829
SyntaxHashable.swift

Sources/SwiftSyntax/MemoryLayout.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
// See `MemoryLayoutTest.swift`.
14-
@_spi(Testing) public enum SyntaxMemoryLayout {
14+
@_spi(Testing) public enum SyntaxMemoryLayout: Sendable {
1515
public struct Value: Equatable, Sendable {
1616
var size: Int
1717
var stride: Int

Sources/SwiftSyntax/Raw/RawSyntax.swift

Lines changed: 26 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
@_spi(RawSyntax) public typealias RawSyntaxBuffer = UnsafeBufferPointer<RawSyntax?>
14-
typealias RawTriviaPieceBuffer = UnsafeBufferPointer<RawTriviaPiece>
13+
@_spi(RawSyntax) public typealias RawSyntaxBuffer = SyntaxArenaAllocatedBufferPointer<RawSyntax?>
14+
15+
typealias RawTriviaPieceBuffer = SyntaxArenaAllocatedBufferPointer<RawTriviaPiece>
1516

1617
fileprivate extension SyntaxKind {
1718
/// Whether this node kind should be considered as `hasError` for purposes of `RecursiveRawSyntaxFlags`.
@@ -20,7 +21,7 @@ fileprivate extension SyntaxKind {
2021
}
2122
}
2223

23-
struct RecursiveRawSyntaxFlags: OptionSet {
24+
struct RecursiveRawSyntaxFlags: OptionSet, Sendable {
2425
let rawValue: UInt8
2526

2627
/// Whether the tree contained by this layout has any
@@ -36,8 +37,8 @@ struct RecursiveRawSyntaxFlags: OptionSet {
3637
}
3738

3839
/// Node data for RawSyntax tree. Tagged union plus common data.
39-
internal struct RawSyntaxData {
40-
internal enum Payload {
40+
internal struct RawSyntaxData: Sendable {
41+
internal enum Payload: Sendable {
4142
case parsedToken(ParsedToken)
4243
case materializedToken(MaterializedToken)
4344
case layout(Layout)
@@ -47,7 +48,7 @@ internal struct RawSyntaxData {
4748
///
4849
/// The RawSyntax's `arena` must have a valid trivia parsing function to
4950
/// lazily materialize the leading/trailing trivia pieces.
50-
struct ParsedToken {
51+
struct ParsedToken: Sendable {
5152
var tokenKind: RawTokenKind
5253

5354
/// Whole text of this token including leading/trailing trivia.
@@ -97,7 +98,7 @@ internal struct RawSyntaxData {
9798
}
9899

99100
/// Token typically created with `TokenSyntax.<someToken>`.
100-
struct MaterializedToken {
101+
struct MaterializedToken: Sendable {
101102
var tokenKind: RawTokenKind
102103
var tokenText: SyntaxText
103104
var triviaPieces: RawTriviaPieceBuffer
@@ -150,7 +151,7 @@ internal struct RawSyntaxData {
150151
}
151152

152153
/// Layout node including collections.
153-
struct Layout {
154+
struct Layout: Sendable {
154155
var kind: SyntaxKind
155156
var layout: RawSyntaxBuffer
156157
var byteLength: Int
@@ -177,32 +178,36 @@ extension RawSyntaxData.ParsedToken {
177178

178179
extension RawSyntaxData.MaterializedToken {
179180
var leadingTrivia: RawTriviaPieceBuffer {
180-
RawTriviaPieceBuffer(rebasing: triviaPieces[..<Int(numLeadingTrivia)])
181+
triviaPieces[..<Int(numLeadingTrivia)]
181182
}
182183
var trailingTrivia: RawTriviaPieceBuffer {
183-
RawTriviaPieceBuffer(rebasing: triviaPieces[Int(numLeadingTrivia)...])
184+
triviaPieces[Int(numLeadingTrivia)...]
184185
}
185186
}
186187

187188
/// Represents the raw tree structure underlying the syntax tree. These nodes
188189
/// have no notion of identity and only provide structure to the tree. They
189190
/// are immutable and can be freely shared between syntax nodes.
190191
@_spi(RawSyntax)
191-
public struct RawSyntax {
192+
public struct RawSyntax: Sendable {
192193

193194
/// Pointer to the actual data which resides in a SyntaxArena.
194-
var pointer: UnsafePointer<RawSyntaxData>
195-
init(pointer: UnsafePointer<RawSyntaxData>) {
195+
var pointer: SyntaxArenaAllocatedPointer<RawSyntaxData>
196+
init(pointer: SyntaxArenaAllocatedPointer<RawSyntaxData>) {
196197
self.pointer = pointer
197198
}
198199

199200
init(arena: __shared SyntaxArena, payload: RawSyntaxData.Payload) {
200201
let arenaRef = SyntaxArenaRef(arena)
201-
self.init(pointer: arena.intern(RawSyntaxData(payload: payload, arenaReference: arenaRef)))
202+
let data = RawSyntaxData(
203+
payload: payload,
204+
arenaReference: arenaRef
205+
)
206+
self.init(pointer: SyntaxArenaAllocatedPointer(arena.intern(data)))
202207
}
203208

204209
var rawData: RawSyntaxData {
205-
unsafeAddress { pointer }
210+
pointer.pointee
206211
}
207212

208213
internal var arenaReference: SyntaxArenaRef {
@@ -350,18 +355,6 @@ extension RawSyntax {
350355
}
351356
}
352357

353-
extension RawSyntax {
354-
@_spi(RawSyntax)
355-
public func toOpaque() -> UnsafeRawPointer {
356-
UnsafeRawPointer(pointer)
357-
}
358-
359-
@_spi(RawSyntax)
360-
public static func fromOpaque(_ pointer: UnsafeRawPointer) -> RawSyntax {
361-
Self(pointer: pointer.assumingMemoryBound(to: RawSyntaxData.self))
362-
}
363-
}
364-
365358
extension RawTriviaPiece {
366359
func withSyntaxText(body: (SyntaxText) throws -> Void) rethrows {
367360
if let syntaxText = storedText {
@@ -649,7 +642,7 @@ extension RawSyntax {
649642
return .materializedToken(
650643
kind: kind,
651644
text: text,
652-
triviaPieces: RawTriviaPieceBuffer(triviaBuffer),
645+
triviaPieces: RawTriviaPieceBuffer(UnsafeBufferPointer(triviaBuffer)),
653646
numLeadingTrivia: numericCast(leadingTriviaPieceCount),
654647
byteLength: numericCast(byteLength),
655648
presence: presence,
@@ -711,7 +704,7 @@ extension RawSyntax {
711704
return .materializedToken(
712705
kind: rawKind,
713706
text: rawKind.defaultText ?? "",
714-
triviaPieces: .init(start: nil, count: 0),
707+
triviaPieces: RawTriviaPieceBuffer(),
715708
numLeadingTrivia: 0,
716709
byteLength: 0,
717710
presence: .missing,
@@ -792,7 +785,7 @@ extension RawSyntax {
792785
}
793786
return .layout(
794787
kind: kind,
795-
layout: RawSyntaxBuffer(layoutBuffer),
788+
layout: RawSyntaxBuffer(UnsafeBufferPointer(layoutBuffer)),
796789
byteLength: byteLength,
797790
descendantCount: descendantCount,
798791
recursiveFlags: recursiveFlags,
@@ -810,7 +803,7 @@ extension RawSyntax {
810803
}
811804
return .layout(
812805
kind: kind,
813-
layout: .init(start: nil, count: 0),
806+
layout: RawSyntaxBuffer(),
814807
byteLength: 0,
815808
descendantCount: 0,
816809
recursiveFlags: recursiveFlags,
@@ -931,11 +924,11 @@ extension RawSyntax {
931924
}
932925

933926
extension RawSyntax: Identifiable {
934-
public struct ID: Hashable {
927+
public struct ID: Hashable, @unchecked Sendable {
935928
/// The pointer to the start of the `RawSyntax` node.
936929
private var pointer: UnsafeRawPointer
937930
fileprivate init(_ raw: RawSyntax) {
938-
self.pointer = UnsafeRawPointer(raw.pointer)
931+
self.pointer = raw.pointer.unsafeRawPointer
939932
}
940933
}
941934

Sources/SwiftSyntax/Raw/RawSyntaxNodeProtocol.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
/// All typed raw syntax nodes conform to this protocol.
1414
/// `RawXXXSyntax` is a typed wrappeer of ``RawSyntax``.
1515
@_spi(RawSyntax)
16-
public protocol RawSyntaxNodeProtocol: CustomStringConvertible, TextOutputStreamable {
16+
public protocol RawSyntaxNodeProtocol: CustomStringConvertible, TextOutputStreamable, Sendable {
1717
/// Returns `true` if `raw` can be cast to this concrete raw syntax type.
1818
static func isKindOf(_ raw: RawSyntax) -> Bool
1919

Sources/SwiftSyntax/Raw/RawSyntaxTokenView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extension RawSyntax {
2626

2727
/// A view into ``RawSyntax`` that exposes functionality that only applies to tokens.
2828
@_spi(RawSyntax)
29-
public struct RawSyntaxTokenView {
29+
public struct RawSyntaxTokenView: Sendable {
3030
let raw: RawSyntax
3131

3232
fileprivate init(raw: RawSyntax) {

Sources/SwiftSyntax/SourceEdit.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/// A textual edit to the original source represented by a range and a
1414
/// replacement.
15-
public struct SourceEdit: Equatable {
15+
public struct SourceEdit: Equatable, Sendable {
1616
/// The half-open range that this edit applies to.
1717
public let range: Range<AbsolutePosition>
1818
/// The text to replace the original range with. Empty for a deletion.

Sources/SwiftSyntax/SourceLength.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
/// The length a syntax node spans in the source code. From any AbsolutePosition
1414
/// you reach a node's end location by adding its UTF-8 length.
15-
public struct SourceLength: Comparable {
15+
public struct SourceLength: Comparable, Sendable {
1616
/// The length in bytes when the text is represented as UTF-8.
1717
public let utf8Length: Int
1818

Sources/SwiftSyntax/SourceLocation.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
//===----------------------------------------------------------------------===//
1212

1313
/// Represents a source location in a Swift file.
14-
public struct SourceLocation: Hashable, Codable {
14+
public struct SourceLocation: Hashable, Codable, Sendable {
1515

1616
/// The line in the file where this location resides. 1-based.
1717
///
@@ -82,7 +82,7 @@ public struct SourceLocation: Hashable, Codable {
8282
}
8383

8484
/// Represents a half-open range in a Swift file.
85-
public struct SourceRange: Hashable, Codable {
85+
public struct SourceRange: Hashable, Codable, Sendable {
8686

8787
/// The beginning location of the source range.
8888
///
@@ -235,7 +235,11 @@ public final class SourceLocationConverter {
235235
self.fileName = file
236236
self.source = Array(source.utf8)
237237
(self.lines, endOfFile) = self.source.withUnsafeBufferPointer { buf in
238-
return computeLines(SyntaxText(buffer: buf))
238+
// Technically, `buf` is not allocated in a `SyntaxArena` but it satisfies
239+
// all the required properties: `buf` will always outlive any references
240+
// to it.
241+
let syntaxArenaBuf = SyntaxArenaAllocatedBufferPointer(buf)
242+
return computeLines(SyntaxText(buffer: syntaxArenaBuf))
239243
}
240244
precondition(source.utf8.count == endOfFile.utf8Offset)
241245
}

Sources/SwiftSyntax/SourcePresence.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
///
1515
/// A `missing` node does not mean, necessarily, that the source item is
1616
/// considered "implicit", but rather that it was not found in the source.
17-
public enum SourcePresence {
17+
public enum SourcePresence: Sendable {
1818
/// The syntax was authored by a human and found, or was generated.
1919
case present
2020

Sources/SwiftSyntax/Syntax.swift

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,23 @@
1414
/// Each node has accessors for its known children, and allows efficient
1515
/// iteration over the children through its `children` property.
1616
public struct Syntax: SyntaxProtocol, SyntaxHashable {
17-
fileprivate enum Info {
17+
fileprivate enum Info: Sendable {
1818
case root(Root)
1919
indirect case nonRoot(NonRoot)
2020

2121
// For root node.
22-
struct Root {
23-
var arena: SyntaxArena
22+
struct Root: @unchecked Sendable {
23+
// Unchecked conformance to sendable is fine because `arena` is not
24+
// accessible. It is just used to keep the arena alive.
25+
private var arena: SyntaxArena
26+
27+
init(arena: SyntaxArena) {
28+
self.arena = arena
29+
}
2430
}
2531

2632
// For non-root nodes.
27-
struct NonRoot {
33+
struct NonRoot: Sendable {
2834
var parent: Syntax
2935
var absoluteInfo: AbsoluteSyntaxInfo
3036
}

0 commit comments

Comments
 (0)