@@ -106,33 +106,85 @@ open class SyntaxRewriter {
106
106
107
107
% end
108
108
109
+ /// Implementation detail of visit(_:). Do not call directly.
110
+ private func visitImplTokenSyntax ( _ data: SyntaxData) - > Syntax {
111
+ let node = TokenSyntax ( data)
112
+ // Accessing _syntaxNode directly is faster than calling Syntax(node)
113
+ visitPre ( node. _syntaxNode)
114
+ defer { visitPost ( node. _syntaxNode) }
115
+ if let newNode = visitAny ( node. _syntaxNode) { return newNode }
116
+ return visit ( node)
117
+ }
118
+
119
+ /// Implementation detail of visit(_:). Do not call directly.
120
+ private func visitImplUnknownSyntax ( _ data: SyntaxData) - > Syntax {
121
+ let node = UnknownSyntax ( data)
122
+ // Accessing _syntaxNode directly is faster than calling Syntax(node)
123
+ visitPre ( node. _syntaxNode)
124
+ defer { visitPost ( node. _syntaxNode) }
125
+ if let newNode = visitAny ( node. _syntaxNode) { return newNode }
126
+ return visit ( node)
127
+ }
128
+
129
+ // SwiftSyntax requires a lot of stack space in debug builds for syntax tree
130
+ // rewriting. In scenarios with reduced stack space (in particular dispatch
131
+ // queues), this easily results in a stack overflow. To work around this issue,
132
+ // use a less performant but also less stack-hungry version of SwiftSyntax's
133
+ // SyntaxRewriter in debug builds.
134
+ #if DEBUG
135
+
136
+ /// Implementation detail of visit(_:). Do not call directly.
137
+ ///
138
+ /// Returns the function that shall be called to visit a specific syntax node.
139
+ ///
140
+ /// To determine the correct specific visitation function for a syntax node,
141
+ /// we need to switch through a huge switch statement that covers all syntax
142
+ /// types. In debug builds, the cases of this switch statement do not share
143
+ /// stack space (rdar://55929175). Because of this, the switch statement
144
+ /// requires allocates about 15KB of stack space. In scenarios with reduced
145
+ /// stack size (in particular dispatch queues), this often results in a stack
146
+ /// overflow during syntax tree rewriting.
147
+ ///
148
+ /// To circumvent this problem, make calling the specific visitation function
149
+ /// a two-step process: First determine the function to call in this function
150
+ /// and return a reference to it, then call it. This way, the stack frame
151
+ /// that determines the correct visitiation function will be popped of the
152
+ /// stack before the function is being called, making the switch's stack
153
+ /// space transient instead of having it linger in the call stack.
154
+ private func visitationFunc( for data: SyntaxData ) -> ( ( SyntaxData ) -> Syntax ) {
155
+ switch data. raw. kind {
156
+ case . token:
157
+ return visitImplTokenSyntax
158
+ case . unknown:
159
+ return visitImplUnknownSyntax
160
+ % for node in SYNTAX_NODES:
161
+ case . ${ node. swift_syntax_kind} :
162
+ return visitImpl ${ node. name}
163
+ % end
164
+ }
165
+ }
166
+
167
+ private func visit( _ data: SyntaxData ) -> Syntax {
168
+ return visitationFunc ( for: data) ( data)
169
+ }
170
+
171
+ #else
172
+
109
173
private func visit( _ data: SyntaxData ) -> Syntax {
110
174
switch data. raw. kind {
111
175
case . token:
112
- let node = TokenSyntax ( data)
113
- // Accessing _syntaxNode directly is faster than calling Syntax(node)
114
- visitPre ( node. _syntaxNode)
115
- defer { visitPost ( node. _syntaxNode) }
116
- if let newNode = visitAny ( node. _syntaxNode) { return newNode }
117
- return visit ( node)
176
+ return visitImplTokenSyntax ( data)
118
177
case . unknown:
119
- let node = UnknownSyntax ( data)
120
- // Accessing _syntaxNode directly is faster than calling Syntax(node)
121
- visitPre ( node. _syntaxNode)
122
- defer { visitPost ( node. _syntaxNode) }
123
- if let newNode = visitAny ( node. _syntaxNode) { return newNode }
124
- return visit ( node)
125
- // The implementation of every generated case goes into its own function. This
126
- // circumvents an issue where the compiler allocates stack space for every
127
- // case statement next to each other in debug builds, causing it to allocate
128
- // ~50KB per call to this function. rdar://55929175
178
+ return visitImplUnknownSyntax ( data)
129
179
% for node in SYNTAX_NODES:
130
180
case . ${ node. swift_syntax_kind} :
131
181
return visitImpl ${ node. name} ( data)
132
182
% end
133
183
}
134
184
}
135
185
186
+ #endif
187
+
136
188
private func visitChildren< SyntaxType: SyntaxProtocol > (
137
189
_ node: SyntaxType
138
190
) -> SyntaxType {
0 commit comments