Skip to content

Commit 4753414

Browse files
committed
Move implementation of each case in SyntaxRewriter.doVisit to separate function
In debug builds the compiler lays the stack space for each case statement out next to each other. This causes a call to doVisit(_:_:) to use ~50KB of stack space, quickly resulting in a stack overflow. Moving each implementation into its own function causes the each case to not use any stack space, circumventing the issue. <rdar://55929175> Fixes SR-11170
1 parent 5bfc40b commit 4753414

File tree

1 file changed

+32
-18
lines changed

1 file changed

+32
-18
lines changed

Sources/SwiftSyntax/SyntaxRewriter.swift.gyb

Lines changed: 32 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,32 @@ extension Syntax {
263263
}
264264
}
265265

266+
% for node in SYNTAX_NODES:
267+
/// Implementation detail of doVisit(_:_:). Do not call directly.
268+
private func _doVisitImpl${node.name}<Visitor>(
269+
_ data: SyntaxData, _ visitor: inout Visitor
270+
) where Visitor : SyntaxVisitor {
271+
% if node.is_base():
272+
let node = Unknown${node.name}(data)
273+
let needsChildren = (visitor.visit(node) == .visitChildren)
274+
// Avoid casting to `_SyntaxBase` if we don't need to visit children.
275+
if needsChildren && data.raw.numberOfChildren > 0 {
276+
visitChildren(data, parent: node, &visitor)
277+
}
278+
visitor.visitPost(node)
279+
% else:
280+
let node = ${node.name}(data)
281+
let needsChildren = (visitor.visit(node) == .visitChildren)
282+
// Avoid casting to `_SyntaxBase` if we don't need to visit children.
283+
if needsChildren && data.raw.numberOfChildren > 0 {
284+
visitChildren(data, parent: node, &visitor)
285+
}
286+
visitor.visitPost(node)
287+
% end
288+
}
289+
290+
% end
291+
266292
fileprivate func doVisit<Visitor>(
267293
_ data: SyntaxData, _ visitor: inout Visitor
268294
) where Visitor : SyntaxVisitor {
@@ -276,31 +302,19 @@ fileprivate func doVisit<Visitor>(
276302
visitor.visitPost(node)
277303
case .unknown:
278304
let node = UnknownSyntax(data)
279-
let needsChildren = visitor.visit(node) == .visitChildren
305+
let needsChildren = (visitor.visit(node) == .visitChildren)
280306
// Avoid casting to `_SyntaxBase` if we don't need to visit children.
281307
if needsChildren && data.raw.numberOfChildren > 0 {
282308
visitChildren(data, parent: node, &visitor)
283309
}
284310
visitor.visitPost(node)
311+
// The implementation of every generated case goes into its own function. This
312+
// circumvents an issue where the compiler allocates stack space for every
313+
// case statement next to each other in debug builds, causing it to allocate
314+
// ~50KB per call to this function. rdar://55929175
285315
% for node in SYNTAX_NODES:
286316
case .${node.swift_syntax_kind}:
287-
% if node.is_base():
288-
let node = Unknown${node.name}(data)
289-
let needsChildren = visitor.visit(node) == .visitChildren
290-
// Avoid casting to `_SyntaxBase` if we don't need to visit children.
291-
if needsChildren && data.raw.numberOfChildren > 0 {
292-
visitChildren(data, parent: node, &visitor)
293-
}
294-
visitor.visitPost(node)
295-
% else:
296-
let node = ${node.name}(data)
297-
let needsChildren = visitor.visit(node) == .visitChildren
298-
// Avoid casting to `_SyntaxBase` if we don't need to visit children.
299-
if needsChildren && data.raw.numberOfChildren > 0 {
300-
visitChildren(data, parent: node, &visitor)
301-
}
302-
visitor.visitPost(node)
303-
% end
317+
_doVisitImpl${node.name}(data, &visitor)
304318
% end
305319
}
306320
}

0 commit comments

Comments
 (0)