Skip to content

Commit 6a8b21a

Browse files
authored
Merge pull request #2925 from rintaro/syntax-data-arena
[Syntax] Use BumpPtrAllocator for Syntax node internals
2 parents 0082a7f + 8fff0de commit 6a8b21a

28 files changed

+608
-902
lines changed

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

+4-14
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,6 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
4242
"""
4343
)
4444

45-
DeclSyntax(
46-
"""
47-
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
48-
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
49-
"""
50-
)
51-
5245
DeclSyntax(
5346
"""
5447
public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) {
@@ -330,11 +323,11 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
330323
// with 'Syntax'
331324
var rewrittens: ContiguousArray<RetainedSyntaxArena> = []
332325
333-
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
326+
for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
334327
335328
// Build the Syntax node to rewrite
336-
var childNode = visitImpl(nodeFactory.create(parent: node, raw: child, absoluteInfo: info))
337-
if childNode.raw.id != child.id {
329+
let childNode = visitImpl(Syntax(arena: node.arena, dataRef: childDataRef))
330+
if childNode.raw.id != childDataRef.pointee.raw.id {
338331
// The node was rewritten, let's handle it
339332
340333
if newLayout.baseAddress == nil {
@@ -345,13 +338,10 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
345338
}
346339
347340
// Update the rewritten child.
348-
newLayout[Int(info.indexInParent)] = childNode.raw
341+
newLayout[Int(childDataRef.pointee.absoluteInfo.layoutIndexInParent)] = childNode.raw
349342
// Retain the syntax arena of the new node until it's wrapped with Syntax node.
350343
rewrittens.append(childNode.raw.arenaReference.retained)
351344
}
352-
353-
// Recycle 'childNode.info'
354-
nodeFactory.dispose(&childNode)
355345
}
356346
357347
if newLayout.baseAddress != nil {

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

+2-11
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,6 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
3232
try! ClassDeclSyntax("open class SyntaxVisitor") {
3333
DeclSyntax("public let viewMode: SyntaxTreeViewMode")
3434

35-
DeclSyntax(
36-
"""
37-
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
38-
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
39-
"""
40-
)
41-
4235
DeclSyntax(
4336
"""
4437
public init(viewMode: SyntaxTreeViewMode) {
@@ -221,10 +214,8 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
221214
DeclSyntax(
222215
"""
223216
private func visitChildren(_ node: Syntax) {
224-
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
225-
var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
226-
dispatchVisit(childNode)
227-
nodeFactory.dispose(&childNode)
217+
for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
218+
dispatchVisit(Syntax(arena: node.arena, dataRef: childDataRef))
228219
}
229220
}
230221
"""

Release Notes/602.md

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@
2020
- Migration steps: Do not use `SyntaxArena` or `ParsingSyntaxArena` directly.
2121
- Notes: Although the type itself was `public`, most initializers were already SPI and there was no way to retrive them from existing types via public API.
2222

23+
- `SyntaxChildrenIndex` is no longer `ExpressibleByNilLiteral`
24+
- Description: `nil` used to represent the end index. However, due to a change in the internal structure, the end index must now be retrieved from the collection.
25+
- Pull Request: https://github.com/swiftlang/swift-syntax/pull/2925
26+
- Migration steps: Use `SyntaxChildren.endIndex` instead.
27+
- Notes: `ExpressibleByNilLiteral` was a mistake. In general, `Collection.Index` should only be created and managed by the collection itself. For example, `Collection.index(after:)` exists, but `Index.advanced(by:)` does not.
28+
2329
## Template
2430

2531
- *Affected API or two word description*

Sources/SwiftSyntax/AbsoluteRawSyntax.swift

-47
This file was deleted.

Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift

+36-33
Original file line numberDiff line numberDiff line change
@@ -2,57 +2,60 @@
22
//
33
// This source file is part of the Swift.org open source project
44
//
5-
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
5+
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
66
// Licensed under Apache License v2.0 with Runtime Library Exception
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
1010
//
1111
//===----------------------------------------------------------------------===//
1212

13-
struct AbsoluteSyntaxPosition: Sendable {
14-
/// The UTF-8 offset of the syntax node in the source file
15-
let offset: UInt32
16-
let indexInParent: UInt32
17-
18-
func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxPosition {
19-
let newOffset = self.offset + UInt32(truncatingIfNeeded: raw?.totalLength.utf8Length ?? 0)
20-
let newIndexInParent = self.indexInParent + 1
21-
return .init(offset: newOffset, indexInParent: newIndexInParent)
22-
}
23-
24-
func advancedToFirstChild() -> AbsoluteSyntaxPosition {
25-
return .init(offset: self.offset, indexInParent: 0)
26-
}
27-
28-
static var forRoot: AbsoluteSyntaxPosition {
29-
return .init(offset: 0, indexInParent: 0)
30-
}
31-
}
32-
3313
/// `AbsoluteSyntaxInfo` represents the information that relates a `RawSyntax`
3414
/// to a source file tree, like its absolute source offset.
3515
struct AbsoluteSyntaxInfo: Sendable {
36-
let position: AbsoluteSyntaxPosition
37-
let nodeId: SyntaxIdentifier
16+
/// The UTF-8 offset at which the syntax node’s leading trivia start in the source file.
17+
let offset: UInt32
18+
19+
/// Index in parent's layout. Note that this counts `nil` children.
20+
let layoutIndexInParent: UInt32
3821

39-
/// The UTF-8 offset of the syntax node in the source file
40-
var offset: UInt32 { return position.offset }
41-
var indexInParent: UInt32 { return position.indexInParent }
22+
/// Index of the node when traversing the syntax tree using a depth-first traversal.
23+
/// This skips `nil` children in the parent's layout.
24+
let indexInTree: UInt32
4225

4326
func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxInfo {
44-
let newPosition = position.advancedBySibling(raw)
45-
let newNodeId = nodeId.advancedBySibling(raw)
46-
return .init(position: newPosition, nodeId: newNodeId)
27+
if let raw {
28+
// '&+' operations are safe because we have the preconditions in 'forRoot(_:)'.
29+
return AbsoluteSyntaxInfo(
30+
offset: offset &+ UInt32(truncatingIfNeeded: raw.totalLength.utf8Length),
31+
layoutIndexInParent: layoutIndexInParent &+ 1,
32+
indexInTree: indexInTree &+ UInt32(truncatingIfNeeded: raw.totalNodes)
33+
)
34+
} else {
35+
return AbsoluteSyntaxInfo(
36+
offset: offset,
37+
layoutIndexInParent: layoutIndexInParent &+ 1,
38+
indexInTree: indexInTree
39+
)
40+
}
4741
}
4842

4943
func advancedToFirstChild() -> AbsoluteSyntaxInfo {
50-
let newPosition = position.advancedToFirstChild()
51-
let newNodeId = nodeId.advancedToFirstChild()
52-
return .init(position: newPosition, nodeId: newNodeId)
44+
return AbsoluteSyntaxInfo(
45+
offset: offset,
46+
layoutIndexInParent: 0,
47+
indexInTree: indexInTree &+ 1
48+
)
5349
}
5450

5551
static func forRoot(_ raw: RawSyntax) -> AbsoluteSyntaxInfo {
56-
return .init(position: .forRoot, nodeId: .forRoot(raw))
52+
// These checks ensure the safety of the unchecked arithmetic operations in 'advancedBySibling(_:)'.
53+
precondition(raw.totalLength.utf8Length <= UInt32.max, "too long")
54+
precondition(raw.totalNodes <= UInt32.max, "too many nodes")
55+
return AbsoluteSyntaxInfo(
56+
offset: 0,
57+
layoutIndexInParent: 0,
58+
indexInTree: 0
59+
)
5760
}
5861
}

Sources/SwiftSyntax/CMakeLists.txt

-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
add_swift_syntax_library(SwiftSyntax
1010
AbsolutePosition.swift
11-
AbsoluteRawSyntax.swift
1211
AbsoluteSyntaxInfo.swift
1312
Assert.swift
1413
BumpPtrAllocator.swift
@@ -31,7 +30,6 @@ add_swift_syntax_library(SwiftSyntax
3130
SyntaxCollection.swift
3231
SyntaxHashable.swift
3332
SyntaxIdentifier.swift
34-
SyntaxNodeFactory.swift
3533
SyntaxNodeStructure.swift
3634
SyntaxProtocol.swift
3735
SyntaxText.swift

Sources/SwiftSyntax/MemoryLayout.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
// See `MemoryLayoutTest.swift`.
1414
@_spi(Testing) public enum SyntaxMemoryLayout: Sendable {
1515
public struct Value: Equatable, Sendable {
16-
var size: Int
17-
var stride: Int
18-
var alignment: Int
16+
public let size: Int
17+
public let stride: Int
18+
public let alignment: Int
1919

2020
public init(size: Int, stride: Int, alignment: Int) {
2121
self.size = size

Sources/SwiftSyntax/Raw/RawSyntax.swift

+10-3
Original file line numberDiff line numberDiff line change
@@ -186,10 +186,10 @@ extension RawSyntaxData.ParsedToken {
186186

187187
extension RawSyntaxData.MaterializedToken {
188188
var leadingTrivia: RawTriviaPieceBuffer {
189-
triviaPieces[..<Int(numLeadingTrivia)]
189+
RawTriviaPieceBuffer(rebasing: triviaPieces[..<Int(numLeadingTrivia)])
190190
}
191191
var trailingTrivia: RawTriviaPieceBuffer {
192-
triviaPieces[Int(numLeadingTrivia)...]
192+
RawTriviaPieceBuffer(rebasing: triviaPieces[Int(numLeadingTrivia)...])
193193
}
194194
}
195195

@@ -954,7 +954,7 @@ extension RawSyntax {
954954
extension RawSyntax: Identifiable {
955955
public struct ID: Hashable, @unchecked Sendable {
956956
/// The pointer to the start of the `RawSyntax` node.
957-
private var pointer: UnsafeRawPointer
957+
fileprivate var pointer: UnsafeRawPointer
958958
fileprivate init(_ raw: RawSyntax) {
959959
self.pointer = raw.pointer.unsafeRawPointer
960960
}
@@ -965,6 +965,13 @@ extension RawSyntax: Identifiable {
965965
}
966966
}
967967

968+
extension UInt {
969+
/// Convert `RawSymtax.ID` to `UInt`. Lossless.
970+
init(rawID: RawSyntax.ID) {
971+
self.init(bitPattern: rawID.pointer)
972+
}
973+
}
974+
968975
/// See `SyntaxMemoryLayout`.
969976
let RawSyntaxDataMemoryLayouts: [String: SyntaxMemoryLayout.Value] = [
970977
"RawSyntaxData": .init(RawSyntaxData.self),

0 commit comments

Comments
 (0)