Skip to content

Commit aeec682

Browse files
committed
[SyntaxParse] Prevent memory leak in Syntax parsing
Essentially re-applying b09f875
1 parent 984f262 commit aeec682

25 files changed

+375
-198
lines changed

include/swift/Parse/ParsedRawSyntaxNode.h

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class ParsedRawSyntaxNode {
5151
CharSourceRange Range;
5252
};
5353
struct DeferredLayoutNode {
54-
ArrayRef<ParsedRawSyntaxNode> Children;
54+
MutableArrayRef<ParsedRawSyntaxNode> Children;
5555
};
5656
struct DeferredTokenNode {
5757
const ParsedTriviaPiece *TriviaPieces;
@@ -73,8 +73,8 @@ class ParsedRawSyntaxNode {
7373
bool IsMissing = false;
7474

7575
ParsedRawSyntaxNode(syntax::SyntaxKind k,
76-
ArrayRef<ParsedRawSyntaxNode> deferredNodes)
77-
: DeferredLayout{deferredNodes},
76+
MutableArrayRef<ParsedRawSyntaxNode> deferredNodes)
77+
: DeferredLayout({deferredNodes}),
7878
SynKind(uint16_t(k)), TokKind(uint16_t(tok::unknown)),
7979
DK(DataKind::DeferredLayout) {
8080
assert(getKind() == k && "Syntax kind with too large value!");
@@ -97,6 +97,8 @@ class ParsedRawSyntaxNode {
9797
assert(DeferredToken.NumTrailingTrivia == numTrailingTrivia &&
9898
"numLeadingTrivia is too large value!");
9999
}
100+
ParsedRawSyntaxNode(ParsedRawSyntaxNode &other) = delete;
101+
ParsedRawSyntaxNode &operator=(ParsedRawSyntaxNode &other) = delete;
100102

101103
public:
102104
ParsedRawSyntaxNode()
@@ -115,6 +117,35 @@ class ParsedRawSyntaxNode {
115117
assert(getTokenKind() == tokKind && "Token kind with too large value!");
116118
}
117119

120+
ParsedRawSyntaxNode &operator=(ParsedRawSyntaxNode &&other) {
121+
assert(DK != DataKind::Recorded);
122+
switch (other.DK) {
123+
case DataKind::Null:
124+
break;
125+
case DataKind::Recorded:
126+
RecordedData = std::move(other.RecordedData);
127+
break;
128+
case DataKind::DeferredLayout:
129+
DeferredLayout = std::move(other.DeferredLayout);
130+
break;
131+
case DataKind::DeferredToken:
132+
DeferredToken = std::move(other.DeferredToken);
133+
break;
134+
}
135+
SynKind = std::move(other.SynKind);
136+
TokKind = std::move(other.TokKind);
137+
DK = std::move(other.DK);
138+
IsMissing = std::move(other.IsMissing);
139+
other.reset();
140+
return *this;
141+
}
142+
ParsedRawSyntaxNode(ParsedRawSyntaxNode &&other) : ParsedRawSyntaxNode() {
143+
*this = std::move(other);
144+
}
145+
~ParsedRawSyntaxNode() {
146+
assert(DK != DataKind::Recorded);
147+
}
148+
118149
syntax::SyntaxKind getKind() const { return syntax::SyntaxKind(SynKind); }
119150
tok getTokenKind() const { return tok(TokKind); }
120151

@@ -136,16 +167,52 @@ class ParsedRawSyntaxNode {
136167
/// Primary used for a deferred missing token.
137168
bool isMissing() const { return IsMissing; }
138169

170+
void reset() {
171+
RecordedData = {};
172+
SynKind = uint16_t(syntax::SyntaxKind::Unknown);
173+
TokKind = uint16_t(tok::unknown);
174+
DK = DataKind::Null;
175+
IsMissing = false;
176+
}
177+
178+
ParsedRawSyntaxNode unsafeCopy() const {
179+
ParsedRawSyntaxNode copy;
180+
switch (DK) {
181+
case DataKind::DeferredLayout:
182+
copy.DeferredLayout = DeferredLayout;
183+
break;
184+
case DataKind::DeferredToken:
185+
copy.DeferredToken = DeferredToken;
186+
break;
187+
case DataKind::Recorded:
188+
copy.RecordedData = RecordedData;
189+
break;
190+
case DataKind::Null:
191+
break;
192+
}
193+
copy.SynKind = SynKind;
194+
copy.TokKind = TokKind;
195+
copy.DK = DK;
196+
copy.IsMissing = IsMissing;
197+
return copy;
198+
}
199+
139200
// Recorded Data ===========================================================//
140201

141202
CharSourceRange getRange() const {
142203
assert(isRecorded());
143204
return RecordedData.Range;
144205
}
145-
OpaqueSyntaxNode getOpaqueNode() const {
206+
const OpaqueSyntaxNode &getOpaqueNode() const {
146207
assert(isRecorded());
147208
return RecordedData.OpaqueNode;
148209
}
210+
OpaqueSyntaxNode takeOpaqueNode() {
211+
assert(isRecorded());
212+
auto opaque = RecordedData.OpaqueNode;
213+
reset();
214+
return opaque;
215+
}
149216

150217
// Deferred Layout Data ====================================================//
151218

@@ -154,6 +221,30 @@ class ParsedRawSyntaxNode {
154221
return DeferredLayout.Children;
155222
}
156223

224+
MutableArrayRef<ParsedRawSyntaxNode> getDeferredChildren() {
225+
assert(DK == DataKind::DeferredLayout);
226+
return DeferredLayout.Children;
227+
}
228+
229+
ParsedRawSyntaxNode copyDeferred() const {
230+
ParsedRawSyntaxNode copy;
231+
switch (DK) {
232+
case DataKind::DeferredLayout:
233+
copy.DeferredLayout = DeferredLayout;
234+
break;
235+
case DataKind::DeferredToken:
236+
copy.DeferredToken = DeferredToken;
237+
break;
238+
default:
239+
llvm_unreachable("node not deferred");
240+
}
241+
copy.SynKind = SynKind;
242+
copy.TokKind = TokKind;
243+
copy.DK = DK;
244+
copy.IsMissing = IsMissing;
245+
return copy;
246+
}
247+
157248
// Deferred Token Data =====================================================//
158249

159250
CharSourceRange getDeferredTokenRange() const {
@@ -176,7 +267,7 @@ class ParsedRawSyntaxNode {
176267

177268
/// Form a deferred syntax layout node.
178269
static ParsedRawSyntaxNode makeDeferred(syntax::SyntaxKind k,
179-
ArrayRef<ParsedRawSyntaxNode> deferredNodes,
270+
MutableArrayRef<ParsedRawSyntaxNode> deferredNodes,
180271
SyntaxParsingContext &ctx);
181272

182273
/// Form a deferred token node.

include/swift/Parse/ParsedRawSyntaxRecorder.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,16 @@ class ParsedRawSyntaxRecorder {
6060
/// \p kind. Missing optional elements are represented with a null
6161
/// ParsedRawSyntaxNode object.
6262
ParsedRawSyntaxNode recordRawSyntax(syntax::SyntaxKind kind,
63-
ArrayRef<ParsedRawSyntaxNode> elements);
63+
MutableArrayRef<ParsedRawSyntaxNode> elements);
6464

6565
/// Record a raw syntax collecton without eny elements. \p loc can be invalid
6666
/// or an approximate location of where an element of the collection would be
6767
/// if not missing.
6868
ParsedRawSyntaxNode recordEmptyRawSyntaxCollection(syntax::SyntaxKind kind,
6969
SourceLoc loc);
7070

71+
void discardRecordedNode(ParsedRawSyntaxNode &node);
72+
7173
/// Used for incremental re-parsing.
7274
ParsedRawSyntaxNode lookupNode(size_t lexerOffset, SourceLoc loc,
7375
syntax::SyntaxKind kind);

include/swift/Parse/ParsedSyntax.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class ParsedSyntax {
2626
: RawNode(std::move(rawNode)) {}
2727

2828
const ParsedRawSyntaxNode &getRaw() const { return RawNode; }
29+
ParsedRawSyntaxNode takeRaw() { return std::move(RawNode); }
2930
syntax::SyntaxKind getKind() const { return RawNode.getKind(); }
3031

3132
/// Returns true if the syntax node is of the given type.
@@ -39,7 +40,7 @@ class ParsedSyntax {
3940
template <typename T>
4041
T castTo() const {
4142
assert(is<T>() && "castTo<T>() node of incompatible type!");
42-
return T { RawNode };
43+
return T { RawNode.copyDeferred() };
4344
}
4445

4546
/// If this Syntax node is of the right kind, cast and return it,
@@ -52,6 +53,10 @@ class ParsedSyntax {
5253
return llvm::None;
5354
}
5455

56+
ParsedSyntax copyDeferred() const {
57+
return ParsedSyntax { RawNode.copyDeferred() };
58+
}
59+
5560
static bool kindof(syntax::SyntaxKind Kind) {
5661
return true;
5762
}
@@ -65,7 +70,7 @@ class ParsedSyntax {
6570
class ParsedTokenSyntax final : public ParsedSyntax {
6671
public:
6772
explicit ParsedTokenSyntax(ParsedRawSyntaxNode rawNode)
68-
: ParsedSyntax(rawNode) {}
73+
: ParsedSyntax(std::move(rawNode)) {}
6974

7075
tok getTokenKind() const {
7176
return getRaw().getTokenKind();

include/swift/Parse/ParsedSyntaxRecorder.h.gyb

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,15 @@ public:
5353
% elif node.is_syntax_collection():
5454
private:
5555
static Parsed${node.name} record${node.syntax_kind}(
56-
ArrayRef<Parsed${node.collection_element_type}> elts,
56+
MutableArrayRef<Parsed${node.collection_element_type}> elts,
5757
ParsedRawSyntaxRecorder &rec);
5858

5959
public:
6060
static Parsed${node.name} defer${node.syntax_kind}(
61-
ArrayRef<Parsed${node.collection_element_type}> elts,
61+
MutableArrayRef<Parsed${node.collection_element_type}> elts,
6262
SyntaxParsingContext &SPCtx);
6363
static Parsed${node.name} make${node.syntax_kind}(
64-
ArrayRef<Parsed${node.collection_element_type}> elts,
64+
MutableArrayRef<Parsed${node.collection_element_type}> elts,
6565
SyntaxParsingContext &SPCtx);
6666

6767
static Parsed${node.name} makeBlank${node.syntax_kind}(SourceLoc loc,
@@ -75,18 +75,21 @@ public:
7575
/// optional trailing comma.
7676
static ParsedTupleTypeElementSyntax
7777
makeTupleTypeElement(ParsedTypeSyntax Type,
78-
Optional<ParsedTokenSyntax> TrailingComma,
79-
SyntaxParsingContext &SPCtx);
78+
Optional<ParsedTokenSyntax> TrailingComma,
79+
SyntaxParsingContext &SPCtx);
8080

8181
/// The provided \c elements are in the appropriate order for the syntax
8282
/// \c kind's layout but optional elements are not be included.
8383
/// This function will form the exact layout based on the provided elements,
8484
/// substituting missing parts with a null ParsedRawSyntaxNode object.
8585
///
8686
/// \returns true if the layout could be formed, false otherwise.
87-
static bool formExactLayoutFor(syntax::SyntaxKind kind,
88-
ArrayRef<ParsedRawSyntaxNode> elements,
89-
function_ref<void(syntax::SyntaxKind, ArrayRef<ParsedRawSyntaxNode>)> receiver);
87+
static bool
88+
formExactLayoutFor(syntax::SyntaxKind kind,
89+
MutableArrayRef<ParsedRawSyntaxNode> elements,
90+
function_ref<void(syntax::SyntaxKind,
91+
MutableArrayRef<ParsedRawSyntaxNode>)>
92+
receiver);
9093
};
9194
}
9295

include/swift/Parse/Parser.h

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,22 @@ class Parser {
358358
};
359359
friend class StructureMarkerRAII;
360360

361+
/// A RAII object that tells the SyntaxParsingContext to defer Syntax nodes.
362+
class DeferringContextRAII {
363+
SyntaxParsingContext &Ctx;
364+
bool WasDeferring;
365+
366+
public:
367+
explicit DeferringContextRAII(SyntaxParsingContext &SPCtx)
368+
: Ctx(SPCtx), WasDeferring(Ctx.shouldDefer()) {
369+
Ctx.setShouldDefer();
370+
}
371+
372+
~DeferringContextRAII() {
373+
Ctx.setShouldDefer(WasDeferring);
374+
}
375+
};
376+
361377
/// The stack of structure markers indicating the locations of
362378
/// structural elements actively being parsed, including the start
363379
/// of declarations, statements, and opening operators of various
@@ -390,7 +406,7 @@ class Parser {
390406

391407
/// Calling this function to finalize libSyntax tree creation without destroying
392408
/// the parser instance.
393-
ParsedRawSyntaxNode finalizeSyntaxTree() {
409+
OpaqueSyntaxNode finalizeSyntaxTree() {
394410
assert(Tok.is(tok::eof) && "not done parsing yet");
395411
return SyntaxContext->finalizeRoot();
396412
}

include/swift/Parse/SyntaxParseActions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ class SyntaxParseActions {
5555
ArrayRef<OpaqueSyntaxNode> elements,
5656
CharSourceRange range) = 0;
5757

58+
/// Discard raw syntax node.
59+
///
60+
/// FIXME: This breaks invariant that any recorded node will be a part of the
61+
/// result SourceFile syntax. This method is a temporary workaround, and
62+
/// should be removed when we fully migrate to libSyntax parsing.
63+
virtual void discardRecordedNode(OpaqueSyntaxNode node) = 0;
64+
5865
/// Used for incremental re-parsing.
5966
virtual std::pair<size_t, OpaqueSyntaxNode>
6067
lookupNode(size_t lexerOffset, syntax::SyntaxKind kind) {

include/swift/Parse/SyntaxParserResult.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,17 @@ template <typename Syntax, typename AST> class SyntaxParserResult {
2929
: SyntaxNode(None), ASTResult(nullptr) {}
3030
SyntaxParserResult(ParserStatus Status)
3131
: SyntaxNode(None), ASTResult(Status) {}
32-
SyntaxParserResult(llvm::Optional<Syntax> SyntaxNode, AST *ASTNode)
33-
: SyntaxNode(SyntaxNode), ASTResult(ASTNode) {}
34-
SyntaxParserResult(ParserStatus Status, llvm::Optional<Syntax> SyntaxNode,
32+
SyntaxParserResult(llvm::Optional<Syntax> &&SyntaxNode, AST *ASTNode)
33+
: SyntaxNode(std::move(SyntaxNode)), ASTResult(ASTNode) {}
34+
SyntaxParserResult(ParserStatus Status, llvm::Optional<Syntax> &&SyntaxNode,
3535
AST *ASTNode)
36-
: SyntaxNode(SyntaxNode), ASTResult(makeParserResult(Status, ASTNode)) {}
36+
: SyntaxNode(std::move(SyntaxNode)), ASTResult(makeParserResult(Status, ASTNode)) {}
3737

3838
/// Convert from a different but compatible parser result.
3939
template <typename U, typename Enabler = typename std::enable_if<
4040
std::is_base_of<AST, U>::value>::type>
41-
SyntaxParserResult(SyntaxParserResult<Syntax, U> Other)
42-
: SyntaxNode(Other.SyntaxNode), ASTResult(Other.ASTResult) {}
41+
SyntaxParserResult(SyntaxParserResult<Syntax, U> &&Other)
42+
: SyntaxNode(std::move(Other.SyntaxNode)), ASTResult(Other.ASTResult) {}
4343

4444

4545
bool isNull() const { return ASTResult.isNull(); }
@@ -58,9 +58,9 @@ template <typename Syntax, typename AST> class SyntaxParserResult {
5858
return SyntaxNode.hasValue();
5959
}
6060

61-
Syntax getSyntax() const {
61+
Syntax getSyntax() {
6262
assert(SyntaxNode.hasValue() && "getSyntax from None value");
63-
return *SyntaxNode;
63+
return std::move(*SyntaxNode);
6464
}
6565

6666
SyntaxParserResult<Syntax, AST> &
@@ -73,16 +73,16 @@ template <typename Syntax, typename AST> class SyntaxParserResult {
7373
/// Create a successful parser result.
7474
template <typename Syntax, typename AST>
7575
static inline SyntaxParserResult<Syntax, AST>
76-
makeSyntaxResult(llvm::Optional<Syntax> SyntaxNode, AST *ASTNode) {
77-
return SyntaxParserResult<Syntax, AST>(SyntaxNode, ASTNode);
76+
makeSyntaxResult(llvm::Optional<Syntax> &&SyntaxNode, AST *ASTNode) {
77+
return SyntaxParserResult<Syntax, AST>(std::move(SyntaxNode), ASTNode);
7878
}
7979

8080
/// Create a result with the specified status.
8181
template <typename Syntax, typename AST>
8282
static inline SyntaxParserResult<Syntax, AST>
83-
makeSyntaxResult(ParserStatus Status, llvm::Optional<Syntax> SyntaxNode,
83+
makeSyntaxResult(ParserStatus Status, llvm::Optional<Syntax> &&SyntaxNode,
8484
AST *ASTNode) {
85-
return SyntaxParserResult<Syntax, AST>(Status, SyntaxNode, ASTNode);
85+
return SyntaxParserResult<Syntax, AST>(Status, std::move(SyntaxNode), ASTNode);
8686
}
8787

8888
/// Create a result (null or non-null) with error and code completion bits set.

0 commit comments

Comments
 (0)