Skip to content

Commit da83571

Browse files
committed
[libSyntax] Hide null AbsoluteRawSyntax behind a custom OptionalStorage implementation
This splits the previous null AbsoluteRawSyntax type into two categories and removes the public null initializer and isNull method. 1. The default initializer of AbsoluteRawSyntax now create uninitialized memory. This is exactly what we need since we just need to allocate the memory to initialise it using the SyntaxDataRef::getChild method wherever we use it. 2. Make Optional<AbsoluteRawSyntax> and Optional<SyntaxDataRef> zero-cost wrappers around their underlying type. These use the old null type to indicate a missing optional value. Overall, I believe this makes the code both safer (we now enforce null types properly in the type system) and potentially faster (although I haven't been able to measure an improvement)
1 parent c6a0439 commit da83571

File tree

2 files changed

+234
-71
lines changed

2 files changed

+234
-71
lines changed

include/swift/Syntax/AbsoluteRawSyntax.h

Lines changed: 148 additions & 58 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
@@ -68,6 +70,9 @@ class SyntaxIdentifier {
6870
SyntaxIndexInTree IndexInTree;
6971

7072
public:
73+
/// Create an *uninitialized* \c SyntaxIdentifier.
74+
SyntaxIdentifier() {}
75+
7176
SyntaxIdentifier(RootIdType RootId, SyntaxIndexInTree IndexInTree)
7277
: RootId(RootId), IndexInTree(IndexInTree) {
7378
assert(RootId < NextUnusedRootId && "New RootIds should only be created "
@@ -124,6 +129,9 @@ class AbsoluteSyntaxPosition {
124129
IndexInParentType IndexInParent;
125130

126131
public:
132+
/// Create an *uninitialized* \c AbsoluteSyntaxPosition.
133+
AbsoluteSyntaxPosition() {}
134+
127135
AbsoluteSyntaxPosition(OffsetType Offset, IndexInParentType IndexInParent)
128136
: Offset(Offset), IndexInParent(IndexInParent) {}
129137

@@ -156,6 +164,8 @@ class AbsoluteOffsetPosition {
156164
AbsoluteSyntaxPosition::OffsetType Offset;
157165

158166
public:
167+
/// Create an *uninitialized* \c AbsoluteOffsetPosition.
168+
AbsoluteOffsetPosition() {}
159169
explicit AbsoluteOffsetPosition(AbsoluteSyntaxPosition::OffsetType Offset)
160170
: Offset(Offset) {}
161171
AbsoluteOffsetPosition(AbsoluteSyntaxPosition Position)
@@ -176,6 +186,8 @@ class AbsoluteSyntaxInfo {
176186
SyntaxIdentifier NodeId;
177187

178188
public:
189+
/// Create an *uninitialized* \c AbsoluteSyntaxInfo.
190+
AbsoluteSyntaxInfo() {}
179191
AbsoluteSyntaxInfo(AbsoluteSyntaxPosition Position, SyntaxIdentifier NodeId)
180192
: Position(Position), NodeId(NodeId) {}
181193

@@ -213,16 +225,25 @@ class AbsoluteSyntaxInfo {
213225

214226
/// A \c RawSyntax node that is enrichted with information of its position
215227
/// within the syntax tree it lives in.
216-
struct AbsoluteRawSyntax {
228+
class AbsoluteRawSyntax {
229+
/// OptionalStorage is a friend so it can access the \c nullptr initializer
230+
/// and \c isNull.
231+
template <typename, bool>
232+
friend class llvm::optional_detail::OptionalStorage;
233+
217234
const RawSyntax *Raw;
218-
const AbsoluteSyntaxInfo Info;
235+
AbsoluteSyntaxInfo Info;
236+
237+
/// Whether this is a null \c AbsoluteRawSyntax.
238+
bool isNull() const { return Raw == nullptr; }
239+
240+
/// Create a null \c AbsoluteRawSyntax. This should only be used in \c
241+
/// AbsoluteRawSyntax's \c OptionalStorage.
242+
explicit AbsoluteRawSyntax(std::nullptr_t) : Raw(nullptr) {}
219243

220244
public:
221-
/// Create a null \c AbsoluteRawSyntax to which a real \c AbsoluteRawSyntax
222-
/// can be stored later.
223-
explicit AbsoluteRawSyntax()
224-
: Raw(nullptr), Info(AbsoluteSyntaxPosition(0, 0),
225-
SyntaxIdentifier(0, SyntaxIndexInTree::zero())) {}
245+
/// Create an *uninitialized* \c AbsoluteRawSyntax.
246+
explicit AbsoluteRawSyntax() {}
226247

227248
/// Create a new \c AbsoluteRawData backed by \p Raw and with additional \p
228249
/// Info. The caller of this constructor is responsible to ensure that the
@@ -235,10 +256,6 @@ struct AbsoluteRawSyntax {
235256
"should always have a RawSyntax");
236257
}
237258

238-
/// Whether this is a null \c AbsoluteRawSyntax created through the default
239-
/// constructor.
240-
bool isNull() const { return Raw == nullptr; }
241-
242259
/// Construct a \c AbsoluteRawSyntax for a \c RawSyntax node that represents
243260
/// the syntax tree's root.
244261
static AbsoluteRawSyntax forRoot(const RawSyntax *Raw) {
@@ -270,16 +287,8 @@ struct AbsoluteRawSyntax {
270287

271288
/// Get the child at \p Index if it exists. If the node does not have a child
272289
/// at \p Index, return \c None. Asserts that \p Index < \c NumChildren
273-
Optional<AbsoluteRawSyntax>
274-
getChild(AbsoluteSyntaxPosition::IndexInParentType Index) const {
275-
assert(Index < getNumChildren() && "Index out of bounds");
276-
auto RawChild = getRaw()->getChild(Index);
277-
if (RawChild) {
278-
return getPresentChild(Index);
279-
} else {
280-
return None;
281-
}
282-
}
290+
inline Optional<AbsoluteRawSyntax>
291+
getChild(AbsoluteSyntaxPosition::IndexInParentType Index) const;
283292

284293
/// Get the child at \p Index, asserting that it exists. This is slightly
285294
/// more performant than \c getChild in these cases since the \c
@@ -305,46 +314,11 @@ struct AbsoluteRawSyntax {
305314

306315
/// Get the first non-missing token node in this tree. Return \c None if
307316
/// this node does not contain non-missing tokens.
308-
Optional<AbsoluteRawSyntax> getFirstToken() const {
309-
if (getRaw()->isToken() && !getRaw()->isMissing()) {
310-
return *this;
311-
}
312-
313-
size_t NumChildren = getNumChildren();
314-
for (size_t I = 0; I < NumChildren; ++I) {
315-
if (auto Child = getChild(I)) {
316-
if (Child->getRaw()->isMissing()) {
317-
continue;
318-
}
319-
320-
if (auto Token = Child->getFirstToken()) {
321-
return Token;
322-
}
323-
}
324-
}
325-
return None;
326-
}
317+
inline Optional<AbsoluteRawSyntax> getFirstToken() const;
327318

328319
/// Get the last non-missing token node in this tree. Return \c None if
329320
/// this node does not contain non-missing tokens.
330-
Optional<AbsoluteRawSyntax> getLastToken() const {
331-
if (getRaw()->isToken() && !getRaw()->isMissing()) {
332-
return *this;
333-
}
334-
335-
for (int I = getNumChildren() - 1; I >= 0; --I) {
336-
if (auto Child = getChild(I)) {
337-
if (Child->getRaw()->isMissing()) {
338-
continue;
339-
}
340-
341-
if (auto Token = Child->getLastToken()) {
342-
return Token;
343-
}
344-
}
345-
}
346-
return None;
347-
}
321+
inline Optional<AbsoluteRawSyntax> getLastToken() const;
348322

349323
/// Construct a new \c AbsoluteRawSyntax node that has the same info as the
350324
/// current one, but
@@ -366,6 +340,122 @@ struct AbsoluteRawSyntax {
366340
namespace llvm {
367341
raw_ostream &operator<<(raw_ostream &OS,
368342
swift::syntax::AbsoluteOffsetPosition Pos);
343+
344+
namespace optional_detail {
345+
346+
using swift::syntax::AbsoluteRawSyntax;
347+
348+
/// A custom \c OptionalStorage implementation for \c AbsoluteRawSyntax that
349+
/// makes \c Optional<AbsoluteRawSyntax> a zero-cost wrapper around \c
350+
/// AbsoluteRawSyntax by using a special (externally not accessible) null \c
351+
/// AbsoluteRawSyntax to represent a missing value.
352+
template <>
353+
class OptionalStorage<AbsoluteRawSyntax> {
354+
AbsoluteRawSyntax Storage;
355+
356+
public:
357+
OptionalStorage() : Storage(nullptr) {}
358+
OptionalStorage(OptionalStorage const &other) = default;
359+
OptionalStorage(OptionalStorage &&other) = default;
360+
361+
template <class... ArgTypes>
362+
explicit OptionalStorage(llvm::optional_detail::in_place_t,
363+
ArgTypes &&...Args)
364+
: Storage(std::forward<ArgTypes>(Args)...) {}
365+
366+
void reset() { Storage = AbsoluteRawSyntax(nullptr); }
367+
368+
bool hasValue() const { return !Storage.isNull(); }
369+
370+
AbsoluteRawSyntax &getValue() LLVM_LVALUE_FUNCTION {
371+
assert(hasValue());
372+
return Storage;
373+
}
374+
AbsoluteRawSyntax const &getValue() const LLVM_LVALUE_FUNCTION {
375+
assert(hasValue());
376+
return Storage;
377+
}
378+
#if LLVM_HAS_RVALUE_REFERENCE_THIS
379+
AbsoluteRawSyntax &&getValue() &&noexcept {
380+
assert(hasValue());
381+
return std::move(Storage);
382+
}
383+
#endif
384+
385+
template <class... Args>
386+
void emplace(Args &&...args) {
387+
Storage = AbsoluteRawSyntax(std::forward<Args>(args)...);
388+
}
389+
390+
OptionalStorage &operator=(const AbsoluteRawSyntax &AbsoluteRaw) {
391+
Storage = AbsoluteRaw;
392+
return *this;
393+
}
394+
395+
OptionalStorage &operator=(AbsoluteRawSyntax &&AbsoluteRaw) {
396+
Storage = std::move(AbsoluteRaw);
397+
return *this;
398+
}
399+
400+
OptionalStorage &operator=(OptionalStorage const &other) = default;
401+
OptionalStorage &operator=(OptionalStorage &&other) = default;
402+
};
403+
} // namespace optional_detail
369404
} // end namespace llvm
370405

406+
namespace swift {
407+
namespace syntax {
408+
409+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getChild(
410+
AbsoluteSyntaxPosition::IndexInParentType Index) const {
411+
assert(Index < getNumChildren() && "Index out of bounds");
412+
if (getRaw()->getChild(Index)) {
413+
return getPresentChild(Index);
414+
} else {
415+
return None;
416+
}
417+
}
418+
419+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getFirstToken() const {
420+
if (getRaw()->isToken() && !getRaw()->isMissing()) {
421+
return *this;
422+
}
423+
424+
size_t NumChildren = getNumChildren();
425+
for (size_t I = 0; I < NumChildren; ++I) {
426+
if (auto Child = getChild(I)) {
427+
if (Child->getRaw()->isMissing()) {
428+
continue;
429+
}
430+
431+
if (auto Token = Child->getFirstToken()) {
432+
return Token;
433+
}
434+
}
435+
}
436+
return None;
437+
}
438+
439+
Optional<AbsoluteRawSyntax> AbsoluteRawSyntax::getLastToken() const {
440+
if (getRaw()->isToken() && !getRaw()->isMissing()) {
441+
return *this;
442+
}
443+
444+
for (int I = getNumChildren() - 1; I >= 0; --I) {
445+
if (auto Child = getChild(I)) {
446+
if (Child->getRaw()->isMissing()) {
447+
continue;
448+
}
449+
450+
if (auto Token = Child->getLastToken()) {
451+
return Token;
452+
}
453+
}
454+
}
455+
return None;
456+
}
457+
458+
} // end namespace syntax
459+
} // end namespace swift
460+
371461
#endif // SWIFT_SYNTAX_ABSOLUTERAWSYNTAX_H

0 commit comments

Comments
 (0)