Skip to content

Revert "[Syntax] Use BumpPtrAllocator for Syntax node internals" #2933

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
"""
)

DeclSyntax(
"""
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
"""
)

DeclSyntax(
"""
public init(viewMode: SyntaxTreeViewMode = .sourceAccurate) {
Expand Down Expand Up @@ -323,11 +330,11 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
// with 'Syntax'
var rewrittens: ContiguousArray<RetainedSyntaxArena> = []

for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {

// Build the Syntax node to rewrite
let childNode = visitImpl(Syntax(arena: node.arena, dataRef: childDataRef))
if childNode.raw.id != childDataRef.pointee.raw.id {
var childNode = visitImpl(nodeFactory.create(parent: node, raw: child, absoluteInfo: info))
if childNode.raw.id != child.id {
// The node was rewritten, let's handle it

if newLayout.baseAddress == nil {
Expand All @@ -338,10 +345,13 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}

// Update the rewritten child.
newLayout[Int(childDataRef.pointee.absoluteInfo.layoutIndexInParent)] = childNode.raw
newLayout[Int(info.indexInParent)] = childNode.raw
// Retain the syntax arena of the new node until it's wrapped with Syntax node.
rewrittens.append(childNode.raw.arenaReference.retained)
}

// Recycle 'childNode.info'
nodeFactory.dispose(&childNode)
}

if newLayout.baseAddress != nil {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
try! ClassDeclSyntax("open class SyntaxVisitor") {
DeclSyntax("public let viewMode: SyntaxTreeViewMode")

DeclSyntax(
"""
/// 'Syntax' object factory recycling 'Syntax.Info' instances.
private let nodeFactory: SyntaxNodeFactory = SyntaxNodeFactory()
"""
)

DeclSyntax(
"""
public init(viewMode: SyntaxTreeViewMode) {
Expand Down Expand Up @@ -214,8 +221,10 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
DeclSyntax(
"""
private func visitChildren(_ node: Syntax) {
for case let childDataRef? in node.layoutBuffer where viewMode.shouldTraverse(node: childDataRef.pointee.raw) {
dispatchVisit(Syntax(arena: node.arena, dataRef: childDataRef))
for case let (child?, info) in RawSyntaxChildren(node) where viewMode.shouldTraverse(node: child) {
var childNode = nodeFactory.create(parent: node, raw: child, absoluteInfo: info)
dispatchVisit(childNode)
nodeFactory.dispose(&childNode)
}
}
"""
Expand Down
6 changes: 0 additions & 6 deletions Release Notes/602.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,6 @@
- Migration steps: Do not use `SyntaxArena` or `ParsingSyntaxArena` directly.
- 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.

- `SyntaxChildrenIndex` is no longer `ExpressibleByNilLiteral`
- 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.
- Pull Request: https://github.com/swiftlang/swift-syntax/pull/2925
- Migration steps: Use `SyntaxChildren.endIndex` instead.
- 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.

## Template

- *Affected API or two word description*
Expand Down
47 changes: 47 additions & 0 deletions Sources/SwiftSyntax/AbsoluteRawSyntax.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

struct AbsoluteRawSyntax: Sendable {
let raw: RawSyntax
let info: AbsoluteSyntaxInfo

/// Returns first `present` child.
func firstChild(viewMode: SyntaxTreeViewMode) -> AbsoluteRawSyntax? {
guard let layoutView = raw.layoutView else { return nil }
var curInfo = info.advancedToFirstChild()
for childOpt in layoutView.children {
if let child = childOpt, viewMode.shouldTraverse(node: child) {
return AbsoluteRawSyntax(raw: child, info: curInfo)
}
curInfo = curInfo.advancedBySibling(childOpt)
}
return nil
}

/// Returns next `present` sibling.
func nextSibling(parent: AbsoluteRawSyntax, viewMode: SyntaxTreeViewMode) -> AbsoluteRawSyntax? {
var curInfo = info.advancedBySibling(raw)
for siblingOpt in parent.raw.layoutView!.children.dropFirst(Int(info.indexInParent + 1)) {
if let sibling = siblingOpt, viewMode.shouldTraverse(node: sibling) {
return AbsoluteRawSyntax(raw: sibling, info: curInfo)
}
curInfo = curInfo.advancedBySibling(siblingOpt)
}
return nil
}

func replacingSelf(_ newRaw: RawSyntax, newRootId: UInt) -> AbsoluteRawSyntax {
let nodeId = SyntaxIdentifier(rootId: newRootId, indexInTree: info.nodeId.indexInTree)
let newInfo = AbsoluteSyntaxInfo(position: info.position, nodeId: nodeId)
return .init(raw: newRaw, info: newInfo)
}
}
69 changes: 33 additions & 36 deletions Sources/SwiftSyntax/AbsoluteSyntaxInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,57 @@
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

struct AbsoluteSyntaxPosition: Sendable {
/// The UTF-8 offset of the syntax node in the source file
let offset: UInt32
let indexInParent: UInt32

func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxPosition {
let newOffset = self.offset + UInt32(truncatingIfNeeded: raw?.totalLength.utf8Length ?? 0)
let newIndexInParent = self.indexInParent + 1
return .init(offset: newOffset, indexInParent: newIndexInParent)
}

func advancedToFirstChild() -> AbsoluteSyntaxPosition {
return .init(offset: self.offset, indexInParent: 0)
}

static var forRoot: AbsoluteSyntaxPosition {
return .init(offset: 0, indexInParent: 0)
}
}

/// `AbsoluteSyntaxInfo` represents the information that relates a `RawSyntax`
/// to a source file tree, like its absolute source offset.
struct AbsoluteSyntaxInfo: Sendable {
/// The UTF-8 offset at which the syntax node’s leading trivia start in the source file.
let offset: UInt32

/// Index in parent's layout. Note that this counts `nil` children.
let layoutIndexInParent: UInt32
let position: AbsoluteSyntaxPosition
let nodeId: SyntaxIdentifier

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

func advancedBySibling(_ raw: RawSyntax?) -> AbsoluteSyntaxInfo {
if let raw {
// '&+' operations are safe because we have the preconditions in 'forRoot(_:)'.
return AbsoluteSyntaxInfo(
offset: offset &+ UInt32(truncatingIfNeeded: raw.totalLength.utf8Length),
layoutIndexInParent: layoutIndexInParent &+ 1,
indexInTree: indexInTree &+ UInt32(truncatingIfNeeded: raw.totalNodes)
)
} else {
return AbsoluteSyntaxInfo(
offset: offset,
layoutIndexInParent: layoutIndexInParent &+ 1,
indexInTree: indexInTree
)
}
let newPosition = position.advancedBySibling(raw)
let newNodeId = nodeId.advancedBySibling(raw)
return .init(position: newPosition, nodeId: newNodeId)
}

func advancedToFirstChild() -> AbsoluteSyntaxInfo {
return AbsoluteSyntaxInfo(
offset: offset,
layoutIndexInParent: 0,
indexInTree: indexInTree &+ 1
)
let newPosition = position.advancedToFirstChild()
let newNodeId = nodeId.advancedToFirstChild()
return .init(position: newPosition, nodeId: newNodeId)
}

static func forRoot(_ raw: RawSyntax) -> AbsoluteSyntaxInfo {
// These checks ensure the safety of the unchecked arithmetic operations in 'advancedBySibling(_:)'.
precondition(raw.totalLength.utf8Length <= UInt32.max, "too long")
precondition(raw.totalNodes <= UInt32.max, "too many nodes")
return AbsoluteSyntaxInfo(
offset: 0,
layoutIndexInParent: 0,
indexInTree: 0
)
return .init(position: .forRoot, nodeId: .forRoot(raw))
}
}
2 changes: 2 additions & 0 deletions Sources/SwiftSyntax/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

add_swift_syntax_library(SwiftSyntax
AbsolutePosition.swift
AbsoluteRawSyntax.swift
AbsoluteSyntaxInfo.swift
Assert.swift
BumpPtrAllocator.swift
Expand All @@ -30,6 +31,7 @@ add_swift_syntax_library(SwiftSyntax
SyntaxCollection.swift
SyntaxHashable.swift
SyntaxIdentifier.swift
SyntaxNodeFactory.swift
SyntaxNodeStructure.swift
SyntaxProtocol.swift
SyntaxText.swift
Expand Down
6 changes: 3 additions & 3 deletions Sources/SwiftSyntax/MemoryLayout.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
// See `MemoryLayoutTest.swift`.
@_spi(Testing) public enum SyntaxMemoryLayout: Sendable {
public struct Value: Equatable, Sendable {
public let size: Int
public let stride: Int
public let alignment: Int
var size: Int
var stride: Int
var alignment: Int

public init(size: Int, stride: Int, alignment: Int) {
self.size = size
Expand Down
13 changes: 3 additions & 10 deletions Sources/SwiftSyntax/Raw/RawSyntax.swift
Original file line number Diff line number Diff line change
Expand Up @@ -186,10 +186,10 @@ extension RawSyntaxData.ParsedToken {

extension RawSyntaxData.MaterializedToken {
var leadingTrivia: RawTriviaPieceBuffer {
RawTriviaPieceBuffer(rebasing: triviaPieces[..<Int(numLeadingTrivia)])
triviaPieces[..<Int(numLeadingTrivia)]
}
var trailingTrivia: RawTriviaPieceBuffer {
RawTriviaPieceBuffer(rebasing: triviaPieces[Int(numLeadingTrivia)...])
triviaPieces[Int(numLeadingTrivia)...]
}
}

Expand Down Expand Up @@ -954,7 +954,7 @@ extension RawSyntax {
extension RawSyntax: Identifiable {
public struct ID: Hashable, @unchecked Sendable {
/// The pointer to the start of the `RawSyntax` node.
fileprivate var pointer: UnsafeRawPointer
private var pointer: UnsafeRawPointer
fileprivate init(_ raw: RawSyntax) {
self.pointer = raw.pointer.unsafeRawPointer
}
Expand All @@ -965,13 +965,6 @@ extension RawSyntax: Identifiable {
}
}

extension UInt {
/// Convert `RawSymtax.ID` to `UInt`. Lossless.
init(rawID: RawSyntax.ID) {
self.init(bitPattern: rawID.pointer)
}
}

/// See `SyntaxMemoryLayout`.
let RawSyntaxDataMemoryLayouts: [String: SyntaxMemoryLayout.Value] = [
"RawSyntaxData": .init(RawSyntaxData.self),
Expand Down
Loading