8000 Merge pull request #2121 from ahoppen/ahoppen/syntax-visitor-visitati… · swiftlang/swift-syntax@12e2eb8 · GitHub
[go: up one dir, main page]

Skip to content

Commit 12e2eb8

Browse files
authored
Merge pull request #2121 from ahoppen/ahoppen/syntax-visitor-visitation-function
Use two-step dispatch to `SyntaxVisitor.vistImpl`
2 parents c8faff7 + cfe2a11 commit 12e2eb8

File tree

6 files changed

+1529
-1679
lines changed
  • CodeGeneration/Sources
  • Sources/SwiftSyntax/generated
  • 6 files changed

    +1529
    -1679
    lines changed

    CodeGeneration/Sources/Utils/CodeGenerationFormat.swift

    Lines changed: 10 additions & 5 deletions
    Original file line numberDiff line numberDiff line change
    @@ -15,7 +15,12 @@ import SwiftSyntax
    1515

    1616
    /// A format style for files generated by CodeGeneration.
    1717
    public class CodeGenerationFormat: BasicFormat {
    18-
    public init() {
    18+
    /// The maximum number of array/dictionary/function parameters/tuple elements
    19+
    /// that should be put on the same line.
    20+
    private let maxElementsOnSameLine: Int
    21+
    22+
    public init(maxElementsOnSameLine: Int = 3) {
    23+
    self.maxElementsOnSameLine = maxElementsOnSameLine
    1924
    super.init(indentationWidth: .spaces(2))
    2025
    }
    2126

    @@ -26,7 +31,7 @@ public class CodeGenerationFormat: BasicFormat {
    2631
    public override func visit(_ node: ArrayElementListSyntax) -> ArrayElementListSyntax {
    2732
    let children = node.children(viewMode: .all)
    2833
    // Short array literals are presented on one line, list each element on a different line.
    29-
    if children.count > 3 {
    34+
    if children.count > maxElementsOnSameLine {
    3035
    return ArrayElementListSyntax(formatChildrenSeparatedByNewline(children: children, elementType: ArrayElementSyntax.self))
    3136
    } else {
    3237
    return super.visit(node)
    @@ -45,7 +50,7 @@ public class CodeGenerationFormat: BasicFormat {
    4550
    public override func visit(_ node: DictionaryElementListSyntax) -> DictionaryElementListSyntax {
    4651
    let children = node.children(viewMode: .all)
    4752
    // Short dictionary literals are presented on one line, list each element on a different line.
    48-
    if children.count > 3 {
    53+
    if children.count > maxElementsOnSameLine {
    4954
    return DictionaryElementListSyntax(formatChildrenSeparatedByNewline(children: children, elementType: DictionaryElementSyntax.self))
    5055
    } else {
    5156
    return super.visit(node)
    @@ -55,7 +60,7 @@ public class CodeGenerationFormat: BasicFormat {
    5560
    public override func visit(_ node: FunctionParameterListSyntax) -> FunctionParameterListSyntax {
    5661
    let children = node.children(viewMode: .all)
    5762
    // Short function parameter literals are presented on one line, list each element on a different line.
    58-
    if children.count > 3 {
    63+
    if children.count > maxElementsOnSameLine {
    5964
    return FunctionParameterListSyntax(formatChildrenSeparatedByNewline(children: children, elementType: FunctionParameterSyntax.self))
    6065
    } else {
    6166
    return super.visit(node)
    @@ -82,7 +87,7 @@ public class CodeGenerationFormat: BasicFormat {
    8287
    public override func visit(_ node: LabeledExprListSyntax) -> LabeledExprListSyntax {
    8388
    let children = node.children(viewMode: .all)
    8489
    // Short tuple element list literals are presented on one line, list each element on a different line.
    85-
    if children.count > 3 {
    90+
    if children.count > maxElementsOnSameLine {
    8691
    return LabeledExprListSyntax(formatChildrenSeparatedByNewline(children: children, elementType: LabeledExprSyntax.self))
    8792
    } else {
    8893
    return super.visit(node)

    CodeGeneration/Sources/generate-swift-syntax/GenerateSwiftSyntax.swift

    Lines changed: 3 additions & 3 deletions
    Original file line numberDiff line numberDiff line change
    @@ -47,8 +47,8 @@ struct GeneratedFileSpec {
    4747
    self.contentsGenerator = contents
    4848
    }
    4949

    50-
    init(_ pathComponents: [String], _ contents: @escaping @autoclosure () -> SourceFileSyntax) {
    51-
    self.init(pathComponents, "\(contents().formatted(using: CodeGenerationFormat()))\n")
    50+
    init(_ pathComponents: [String], _ contents: @escaping @autoclosure () -> SourceFileSyntax, format: CodeGenerationFormat = CodeGenerationFormat()) {
    51+
    self.init(pathComponents, "\(contents().formatted(using: format))\n")
    5252
    }
    5353
    }
    5454

    @@ -112,7 +112,7 @@ struct GenerateSwiftSyntax: ParsableCommand {
    112112
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxRewriter.swift"], syntaxRewriterFile),
    113113
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTraits.swift"], syntaxTraitsFile),
    114114
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxTransform.swift"], syntaxTransformFile),
    115-
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxVisitor.swift"], syntaxVisitorFile),
    115+
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["SyntaxVisitor.swift"], syntaxVisitorFile, format: CodeGenerationFormat(maxElementsOnSameLine: 4)),
    116116
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["TokenKind.swift"], tokenKindFile),
    117117
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["Tokens.swift"], tokensFile),
    118118
    GeneratedFileSpec(swiftSyntaxGeneratedDir + ["TriviaPieces.swift"], triviaPiecesFile),

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

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -210,7 +210,7 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
    210210
    /// we need to switch through a huge switch statement that covers all syntax
    211211
    /// types. In debug builds, the cases of this switch statement do not share
    212212
    /// stack space (rdar://55929175). Because of this, the switch statement
    213-
    /// requires allocates about 15KB of stack space. In scenarios with reduced
    213+
    /// requires about 15KB of stack space. In scenarios with reduced
    214214
    /// stack size (in particular dispatch queues), this often results in a stack
    215215
    /// overflow during syntax tree rewriting.
    216216
    ///

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

    Lines changed: 99 additions & 18 deletions
    Original file line numberDiff line numberDiff line change
    @@ -113,27 +113,108 @@ let syntaxVisitorFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
    113113
    """
    114114
    )
    115115

    116-
    try FunctionDeclSyntax("private func visit(_ data: SyntaxData)") {
    117-
    try SwitchExprSyntax("switch data.raw.kind") {
    118-
    SwitchCaseSyntax("case .token:") {
    119-
    DeclSyntax("let node = TokenSyntax(data)")
    120-
    121-
    ExprSyntax("_ = visit(node)")
    122-
    ExprSyntax(
    123-
    """
    124-
    // No children to visit.
    125-
    visitPost(node)
    126-
    """
    116+
    try IfConfigDeclSyntax(
    117+
    leadingTrivia:
    118+
    """
    119+
    // SwiftSyntax requires a lot of stack space in debug builds for syntax tree
    120+
    // visitation. In scenarios with reduced stack space (in particular dispatch
    121+
    // queues), this easily results in a stack overflow. To work around this issue,
    122+
    // use a less performant but also less stack-hungry version of SwiftSyntax's
    123+
    // SyntaxVisitor in debug builds.
    124+
    125+
    """,
    126+
    clauses: IfConfigClauseListSyntax {
    127+
    IfConfigClauseSyntax(
    128+
    poundKeyword: .poundIfToken(),
    129+
    condition: ExprSyntax("DEBUG"),
    130+
    elements: .statements(
    131+
    try CodeBlockItemListSyntax {
    132+
    try FunctionDeclSyntax(
    133+
    """
    134+
    /// Implementation detail of visit(_:). Do not call directly.
    135+
    ///
    136+
    /// Returns the function that shall be called to visit a specific syntax node.
    137+
    ///
    138+
    /// To determine the correct specific visitation function for a syntax node,
    139+
    /// we need to switch through a huge switch statement that covers all syntax
    140+
    /// types. In debug builds, the cases of this switch statement do not share
    141+
    /// stack space (rdar://55929175). Because of this, the switch statement
    142+
    /// requires about 15KB of stack space. In scenarios with reduced
    143+
    /// stack size (in particular dispatch queues), this often results in a stack
    144+
    /// overflow during syntax tree rewriting.
    145+
    ///
    146+
    /// To circumvent this problem, make calling the specific visitation function
    147+
    /// a two-step process: First determine the function to call in this function
    148+
    /// and return a reference to it, then call it. This way, the stack frame
    149+
    /// that determines the correct visitation function will be popped of the
    150+
    /// stack before the function is being called, making the switch's stack
    151+
    /// space transient instead of having it linger in the call stack.
    152+
    private func visitationFunc(for data: SyntaxData) -> ((SyntaxData) -> Void)
    153+
    """
    154+
    ) {
    155+
    try SwitchExprSyntax("switch data.raw.kind") {
    156+
    SwitchCaseSyntax("case .token:") {
    157+
    StmtSyntax(
    158+
    """
    159+
    return {
    160+
    let node = TokenSyntax($0)
    161+
    _ = self.visit(node)
    162+
    // No children to visit.
    163+
    self.visitPost(node)
    164+
    }
    165+
    """
    166+
    )
    167+
    }
    168+
    169+
    for node in NON_BASE_SYNTAX_NODES {
    170+
    SwitchCaseSyntax("case .\(node.varOrCaseName):") {
    171+
    StmtSyntax("return { self.visitImpl($0, \(node.kind.syntaxType).self, self.visit, self.visitPost) }")
    172+
    }
    173+
    }
    174+
    }
    175+
    }
    176+
    177+
    DeclSyntax(
    178+
    """
    179+
    private func visit(_ data: SyntaxData) {
    180+
    return visitationFunc(for: data)(data)
    181+
    }
    182+
    """
    183+
    )
    184+
    }
    127185
    )
    128-
    }
    186+
    )
    187+
    IfConfigClauseSyntax(
    188+
    poundKeyword: .poundElseToken(),
    189+
    elements: .statements(
    190+
    CodeBlockItemListSyntax {
    191+
    try! FunctionDeclSyntax("private func visit(_ data: SyntaxData)") {
    192+
    try SwitchExprSyntax("switch data.raw.kind") {
    193+
    SwitchCaseSyntax("case .token:") {
    194+
    DeclSyntax("let node = TokenSyntax(data)")
    129195

    130-
    for node in NON_BASE_SYNTAX_NODES {
    131-
    SwitchCaseSyntax("case .\(node.varOrCaseName):") {
    132-
    ExprSyntax("visitImpl(data, \(node.kind.syntaxType).self, visit, visitPost)")
    133-
    }
    134-
    }
    196+
    ExprSyntax("_ = visit(node)")
    197+
    ExprSyntax(
    198+
    """
    199+
    // No children to visit.
    200+
    visitPost(node)
    201+
    """
    202+
    )
    203+
    }
    204+
    205+
    for node in NON_BASE_SYNTAX_NODES {
    206+
    SwitchCaseSyntax("case .\(node.varOrCaseName):") {
    207+
    ExprSyntax("visitImpl(data, \(node.kind.syntaxType).self, visit, visitPost)")
    208+
    }
    209+
    }
    210+
    }
    211+
    }
    212+
    213+
    }
    214+
    )
    215+
    )
    135216
    }
    136-
    }
    217+
    )
    137218

    138219
    DeclSyntax(
    139220
    """

    Sources/SwiftSyntax/generated/SyntaxRewriter.swift

    Lines changed: 1 addition & 1 deletion
    Original file line numberDiff line numberDiff line change
    @@ -2073,7 +2073,7 @@ open class SyntaxRewriter {
    20732073
    /// we need to switch through a huge switch statement that covers all syntax
    20742074
    /// types. In debug builds, the cases of this switch statement do not share
    20752075
    /// stack space (rdar://55929175). Because of this, the switch statement
    2076-
    /// requires allocates about 15KB of stack space. In scenarios with reduced
    2076+
    /// requires about 15KB of stack space. In scenarios with reduced
    20772077
    /// stack size (in particular dispatch queues), this often results in a stack
    20782078
    /// overflow during syntax tree rewriting.
    20792079
    ///

    0 commit comments

    Comments
     (0)
    0