Skip to content

Commit aa74b1b

Browse files
authored
Merge pull request #36313 from ahoppen/pr/custom-opt-storage
[libSyntax] Hide null AbsoluteRawSyntax behind a custom OptionalStorage implementation
2 parents aea32c2 + da83571 commit aa74b1b

File tree

4 files changed

+544
-179
lines changed

4 files changed

+544
-179
lines changed

include/swift/Syntax/AbsoluteRawSyntax.h

Lines changed: 206 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ class SyntaxIndexInTree {
2828
explicit SyntaxIndexInTree(size_t IndexInTree) : IndexInTree(IndexInTree) {}
2929

3030
public:
31+
/// Create an *uninitialized* \c SyntaxIndexInTree.
32+
SyntaxIndexInTree() {}
3133
static SyntaxIndexInTree zero() { return SyntaxIndexInTree(0); }
3234

3335
/// Assuming that this index points to the start of \p Raw, advance it so that
@@ -83,6 +85,9 @@ class SyntaxIdentifier {
8385
SyntaxIndexInTree IndexInTree;
8486

8587
public:
88+
/// Create an *uninitialized* \c SyntaxIdentifier.
89+
SyntaxIdentifier() {}
90+
8691
SyntaxIdentifier(RootIdType RootId, SyntaxIndexInTree IndexInTree)
8792
: RootId(RootId), IndexInTree(IndexInTree) {
8893
assert(RootId < NextUnusedRootId && "New RootIds should only be created "
@@ -139,6 +144,9 @@ class AbsoluteSyntaxPosition {
139144
IndexInParentType IndexInParent;
140145

141146
public:
147+
/// Create an *uninitialized* \c AbsoluteSyntaxPosition.
148+
AbsoluteSyntaxPosition() {}
149+
142150
AbsoluteSyntaxPosition(OffsetType Offset, IndexInParentType IndexInParent)
143151
: Offset(Offset), IndexInParent(IndexInParent) {}
144152

@@ -185,6 +193,8 @@ class AbsoluteOffsetPosition {
185193
AbsoluteSyntaxPosition::OffsetType Offset;
186194

187195
public:
196+
/// Create an *uninitialized* \c AbsoluteOffsetPosition.
197+
AbsoluteOffsetPosition() {}
188198
explicit AbsoluteOffsetPosition(AbsoluteSyntaxPosition::OffsetType Offset)
189199
: Offset(Offset) {}
190200
AbsoluteOffsetPosition(AbsoluteSyntaxPosition Position)
@@ -205,6 +215,8 @@ class AbsoluteSyntaxInfo {
205215
SyntaxIdentifier NodeId;
206216

207217
public:
218+
/// Create an *uninitialized* \c AbsoluteSyntaxInfo.
219+
AbsoluteSyntaxInfo() {}
208220
AbsoluteSyntaxInfo(AbsoluteSyntaxPosition Position, SyntaxIdentifier NodeId)
209221
: Position(Position), NodeId(NodeId) {}
210222

@@ -242,42 +254,111 @@ class AbsoluteSyntaxInfo {
242254

243255
/// A \c RawSyntax node that is enrichted with information of its position
244256
/// within the syntax tree it lives in.
245-
struct AbsoluteRawSyntax {
257+
class AbsoluteRawSyntax {
258+
/// OptionalStorage is a friend so it can access the \c nullptr initializer
259+
/// and \c isNull.
260+
template <typename, bool>
261+
friend class llvm::optional_detail::OptionalStorage;
262+
246263
const RawSyntax *Raw;
247-
const AbsoluteSyntaxInfo Info;
264+
AbsoluteSyntaxInfo Info;
265+
266+
/// Whether this is a null \c AbsoluteRawSyntax.
267+
bool isNull() const { return Raw == nullptr; }
268+
269+
/// Create a null \c AbsoluteRawSyntax. This should only be used in \c
270+
/// AbsoluteRawSyntax's \c OptionalStorage.
271+
explicit AbsoluteRawSyntax(std::nullptr_t) : Raw(nullptr) {}
248272

249273
public:
274+
/// Create an *uninitialized* \c AbsoluteRawSyntax.
275+
explicit AbsoluteRawSyntax() {}
276+
277+
/// Create a new \c AbsoluteRawData backed by \p Raw and with additional \p
278+
/// Info. The caller of this constructor is responsible to ensure that the
279+
/// Arena of \p Raw (and thus \p Raw itself) outlives this \c
280+
/// AbsoluteRawSyntax.
250281
AbsoluteRawSyntax(const RawSyntax *Raw, AbsoluteSyntaxInfo Info)
251-
: Raw(Raw), Info(Info) {}
282+
: Raw(Raw), Info(Info) {
283+
assert(Raw != nullptr &&
284+
"A AbsoluteRawSyntax created through the memberwise constructor "
285+
"should always have a RawSyntax");
286+
}
252287

253288
/// Construct a \c AbsoluteRawSyntax for a \c RawSyntax node that represents
254289
/// the syntax tree's root.
255290
static AbsoluteRawSyntax forRoot(const RawSyntax *Raw) {
256291
return AbsoluteRawSyntax(Raw, AbsoluteSyntaxInfo::forRoot());
257292
}
258293

259-
const RawSyntax *getRaw() const { return Raw; }
294+
const RawSyntax *getRaw() const {
295+
assert(!isNull() && "Cannot get Raw of a null AbsoluteRawSyntax");
296+
return Raw;
297+
}
260298

261-
AbsoluteSyntaxInfo getInfo() const { return Info; }
299+
AbsoluteSyntaxInfo getInfo() const {
300+
assert(!isNull() && "Cannot get Raw of a null AbsoluteRawSyntax");
301+
return Info;
302+
}
262303

263304
/// Get the position at which the leading triva of this node starts.
264-
AbsoluteSyntaxPosition getPosition() const { return Info.getPosition(); };
305+
AbsoluteSyntaxPosition getPosition() const {
306+
return getInfo().getPosition();
307+
};
265308

266-
SyntaxIdentifier getNodeId() const { return Info.getNodeId(); };
309+
SyntaxIdentifier getNodeId() const { return getInfo().getNodeId(); };
267310

268311
AbsoluteSyntaxPosition::IndexInParentType getIndexInParent() const {
269312
return getPosition().getIndexInParent();
270313
}
271314

315+
size_t getNumChildren() const { return getRaw()->getLayout().size(); }
316+
317+
/// Get the child at \p Index if it exists. If the node does not have a child
318+
/// at \p Index, return \c None. Asserts that \p Index < \c NumChildren
319+
inline Optional<AbsoluteRawSyntax>
320+
getChild(AbsoluteSyntaxPosition::IndexInParentType Index) const;
321+
322+
/// Get the child at \p Index, asserting that it exists. This is slightly
323+
/// more performant than \c getChild in these cases since the \c
324+
/// AbsoluteRawSyntax node does not have to be wrapped in an \c Optional.
325+
AbsoluteRawSyntax
326+
getPresentChild(AbsoluteSyntaxPosition::IndexInParentType Index) const {
327+
assert(Index < getNumChildren() && "Index out of bounds");
328+
auto RawChild = getRaw()->getChild(Index);
329+
assert(RawChild &&
330+
"Child retrieved using getPresentChild must always exist");
331+
332+
AbsoluteSyntaxPosition Position = getPosition().advancedToFirstChild();
333+
SyntaxIdentifier NodeId = getNodeId().advancedToFirstChild();
334+
335+
for (size_t I = 0; I < Index; ++I) {
336+
Position = Position.advancedBy(getRaw()->getChild(I));
337+
NodeId = NodeId.advancedBy(getRaw()->getChild(I));
338+
}
339+
340+
AbsoluteSyntaxInfo Info(Position, NodeId);
341+
return AbsoluteRawSyntax(RawChild, Info);
342+
}
343+
344+
/// Get the first non-missing token node in this tree. Return \c None if
345+
/// this node does not contain non-missing tokens.
346+
inline Optional<AbsoluteRawSyntax> getFirstToken() const;
347+
348+
/// Get the last non-missing token node in this tree. Return \c None if
349+
/// this node does not contain non-missing tokens.
350+
inline Optional<AbsoluteRawSyntax> getLastToken() const;
351+
272352
/// Construct a new \c AbsoluteRawSyntax node that has the same info as the
273353
/// current one, but
274354
/// - the \p NewRaw as the backing storage
275355
/// - the \p NewRootId as the RootId
276356
AbsoluteRawSyntax
277357
replacingSelf(const RawSyntax *NewRaw,
278358
SyntaxIdentifier::RootIdType NewRootId) const {
279-
SyntaxIdentifier NewNodeId(NewRootId, Info.getNodeId().getIndexInTree());
280-
AbsoluteSyntaxInfo NewInfo(Info.getPosition(), NewNodeId);
359+
SyntaxIdentifier NewNodeId(NewRootId,
360+
getInfo().getNodeId().getIndexInTree());
361+
AbsoluteSyntaxInfo NewInfo(getInfo().getPosition(), NewNodeId);
281362
return AbsoluteRawSyntax(NewRaw, NewInfo);
282363
}
283364
};
@@ -288,6 +369,122 @@ struct AbsoluteRawSyntax {
288369
namespace llvm {
289370
raw_ostream &operator<<(raw_ostream &OS,
290371
swift::syntax::AbsoluteOffsetPosition Pos);
372+
373+
namespace optional_detail {
374+
375+
using swift::syntax::AbsoluteRawSyntax;
376+
377+
/// A custom \c OptionalStorage implementation for \c AbsoluteRawSyntax that
378+
/// makes \c Optional<AbsoluteRawSyntax> a zero-cost wrapper around \c
379+
/// AbsoluteRawSyntax by using a special (externally not accessible) null \c
380+
/// AbsoluteRawSyntax to represent a missing value.
381+
template <>
382+
class OptionalStorage<AbsoluteRawSyntax> {
383+
AbsoluteRawSyntax Storage;
384+
385+
public:
386+
OptionalStorage() : Storage(nullptr) {}
387+
OptionalStorage(OptionalStorage const &other) = default;
388+
OptionalStorage(OptionalStorage &&other) = default;
389+
390+
template <class... ArgTypes>
391+
explicit OptionalStorage(llvm::optional_detail::in_place_t,
392+
ArgTypes &&...Args)
393+
: Storage(std::forward<ArgTypes>(Args)...) {}
394+
395+
void reset() { Storage = AbsoluteRawSyntax(nullptr); }
396+
397+
bool hasValue() const { return !Storage.isNull(); }
398+
399+
AbsoluteRawSyntax &getValue() LLVM_LVALUE_FUNCTION {
400+
assert(hasValue());
401+
return Storage;
402+
}
403+
AbsoluteRawSyntax const &getValue() const LLVM_LVALUE_FUNCTION {
404+
assert(hasValue());
405+
return Storage;
406+
}
407+
#if LLVM_HAS_RVALUE_REFERENCE_THIS
408+
AbsoluteRawSyntax &&getValue() &&noexcept {
409+
assert(hasValue());
410+
return std::move(Storage);
411+
}
412+
#endif
413+
414+
template <class... Args>
415+
void emplace(Args &&...args) {
416+
Storage = AbsoluteRawSyntax(std::forward<Args>(args)...);
417+
}
418+
419+
OptionalStorage &operator=(const AbsoluteRawSyntax &AbsoluteRaw) {
420+
Storage = AbsoluteRaw;
421+
return *this;
422+
}
423+
424+
OptionalStorage &operator=(AbsoluteRawSyntax &&AbsoluteRaw) {
425+
Storage = std::move(AbsoluteRaw);
426+
return *this;
427+
}
428+
429+
OptionalStorage &operator=(OptionalStorage const &other) = default;
430+
OptionalStorage &operator=(OptionalStorage &&other) = default;
431+
};
432+
} // namespace optional_detail
291433
} // end namespace llvm
292434

435+
namespace swift {
436+
namespace syntax {
437+
438+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getChild(
439+
AbsoluteSyntaxPosition::IndexInParentType Index) const {
440+
assert(Index < getNumChildren() && "Index out of bounds");
441+
if (getRaw()->getChild(Index)) {
442+
return getPresentChild(Index);
443+
} else {
444+
return None;
445+
}
446+
}
447+
448+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getFirstToken() const {
449+
if (getRaw()->isToken() && !getRaw()->isMissing()) {
450+
return *this;
451+
}
452+
453+
size_t NumChildren = getNumChildren();
454+
for (size_t I = 0; I < NumChildren; ++I) {
455+
if (auto Child = getChild(I)) {
456+
if (Child->getRaw()->isMissing()) {
457+
continue;
458+
}
459+
460+
if (auto Token = Child->getFirstToken()) {
461+
return Token;
462+
}
463+
}
464+
}
465+
return None;
466+
}
467+
468+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getLastToken() const {
469+
if (getRaw()->isToken() && !getRaw()->isMissing()) {
470+
return *this;
471+
}
472+
473+
for (int I = getNumChildren() - 1; I >= 0; --I) {
474+
if (auto Child = getChild(I)) {
475+
if (Child->getRaw()->isMissing()) {
476+
continue;
477+
}
478+
479+
if (auto Token = Child->getLastToken()) {
480+
return Token;
481+
}
482+
}
483+
}
484+
return None;
485+
}
486+
487+
} // end namespace syntax
488+
} // end namespace swift
489+
293490
#endif // SWIFT_SYNTAX_ABSOLUTERAWSYNTAX_H

include/swift/Syntax/RawSyntax.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ class RawSyntax final
526526
/// Get a child based on a particular node's "Cursor", indicating
527527
/// the position of the terms in the production of the Swift grammar.
528528
const RawSyntax *getChild(CursorIndex Index) const {
529+
assert(Index < getNumChildren() && "Index out of bounds");
529530
return getLayout()[Index];
530531
}
531532

0 commit comments

Comments
 (0)