Files
swift-mirror/include/swift/Syntax/SyntaxData.h
Alex Hoppen 3fb5dd1c68 [libSyntax] Create SyntaxRef, which uses SyntaxDataRef internally
Now that we have a fast SyntaxDataRef, create a corresponding SyntaxRef hierarchy. In contrast to the Syntax, SyntaxRef does *not* own the backing SyntaxDataRef, but merely has a pointer to the SyntaxDataRef.
In addition to the requirements imposed by SyntaxDataRef, the user of the SyntaxRef hierarchy needs to make sure that the backing SyntaxDataRef of a SyntaxRef node stays alive. While this sounds like a lot of requirements, it has performance advantages:
 - Passing a SyntaxRef node around is just passing a pointer around.
 - When casting a SyntaxRef node, we only need to create a new SyntaxRef (aka. pointer) that points to the same underlying SyntaxDataRef - there's no need to duplicate the SyntaxDataRef.
 - As SyntaxDataRef is not ref-counted, there's no ref-counting overhead involved.

Furthermore, the requirements are typically fulfilled. The getChild methods on SyntaxRef return an OwnedSyntaxRef, which stores the SyntaxDataRef. As long as this variable is stored somewhere on the stack, the corresponding SyntaxRef can safely be used for the duration of the stack frame. Even calls like the following are possible, because OwnedSyntaxRef returned by getChild stays alive for the duration of the entire statement.
```
useSyntaxRef(mySyntaxRef.getChild(0).getRef())
```
2021-03-11 19:34:49 +01:00

457 lines
16 KiB
C++

//===--- SyntaxData.h - Swift Syntax Data Interface -------------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the SyntaxData interface, the type for the instance
// data for Syntax nodes.
//
// Effectively, these provide two main things to a Syntax node - parental
// relationships and caching for its children.
//
// A SyntaxData contains at least a strong reference to the RawSyntax,
// from which most information comes, and additionally a weak reference to
// its parent and the "index" at which it occurs in its parent. These were
// originally intended to have the important public APIs for structured
// editing but now contain no significant or public API; for those, see the
// Syntax type. These are purely to contain data, hence the name.
//
// Conceptually, SyntaxData add the characteristic of specific identity in a
// piece of Swift source code. While the RawSyntax for the integer literal
// token '1' can be reused anywhere a '1' occurs and has identical formatting,
// a SyntaxData represents *a* specific '1' at a particular location in
// Swift source.
//
// These are effectively internal implementation. For all public APIs, look
// for the type without "Data" in its name. For example, a StructDeclSyntaxData
// expresses its API through the wrapping StructDeclSyntax type.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_SYNTAX_SYNTAXDATA_H
#define SWIFT_SYNTAX_SYNTAXDATA_H
#include "swift/Basic/Debug.h"
#include "swift/Syntax/AbsoluteRawSyntax.h"
#include "swift/Syntax/AtomicCache.h"
#include "swift/Syntax/RawSyntax.h"
#include "swift/Syntax/References.h"
#include "llvm/ADT/DenseMap.h"
#include <atomic>
namespace swift {
namespace syntax {
/// The class for holding parented syntax.
///
/// \c SyntaxDataRef is designed for efficiency, not memory safety.
/// It *does not* retain the \c RawSyntax that backs the syntax node, the \c
/// SyntaxArena in which the \c RawSyntax node lives nor the parent \c
/// SyntaxDataRef node. It is the user's responsibility to ensure these values
/// outlive the \c SyntaxDataRef.
///
/// Always use \c SyntaxData if performance is not a concern and memory safety
/// should be guaranteed.
class SyntaxDataRef {
friend class SyntaxData;
/// OptionalStorage is a friend so it can call the memberwise initialiser
/// to construct a null \c SyntaxDataRef.
template <typename, bool>
friend class llvm::optional_detail::OptionalStorage;
protected:
/// The \c AbsoluteRawSyntaxNode that provides the majority of this node's
/// data. The underlying \c RawSyntax node is *not* retained by the \c
/// SyntaxDataRef. It is the user's responsibility to ensure that the \c
/// RawSyntax's \c SyntaxArena outlives this \c SyntaxDataRef.
///
/// In \c SyntaxData, the backing \c SyntaxArena is retained via the \c Arena
/// property, lifiting the responsibility to guarantee the \c RawSyntax node
/// stays alive from the user.
AbsoluteRawSyntax AbsoluteRaw;
/// The parent of this node. The parent is *not* retained by the \c
/// SyntaxDataRef. It is the user's responsibility to ensure that the parent
/// always outlives the child.
///
/// If this is a \c SyntaxData object, \c Parent is guaranteed to also be a
/// \c SyntaxData object, which is manually retained when \c this is created
/// and released when \c this is destroyed.
const SyntaxDataRef *Parent;
/// Creates a \c SyntaxDataRef. \p AbsoluteRaw must not be null. If \p Parent
/// is a \c nullptr, this node represents the root of a tree.
SyntaxDataRef(const AbsoluteRawSyntax &AbsoluteRaw,
const SyntaxDataRef *Parent)
: AbsoluteRaw(AbsoluteRaw), Parent(Parent) {
}
#ifndef NDEBUG
virtual bool isRef() const { return true; }
#endif
public:
/// Creates an *uninitialized* \c SyntaxDataRef.
SyntaxDataRef() {}
SyntaxDataRef(const SyntaxDataRef &DataRef) = default;
SyntaxDataRef(SyntaxDataRef &&DataRef) = default;
SyntaxDataRef &operator=(SyntaxDataRef const &other) = default;
SyntaxDataRef &operator=(SyntaxDataRef &&other) = default;
#ifndef NDEBUG
virtual ~SyntaxDataRef() {}
#endif
// MARK: - Retrieving underlying data
const AbsoluteRawSyntax &getAbsoluteRaw() const {
return AbsoluteRaw;
}
/// Returns the raw syntax node for this syntax node.
const RawSyntax *getRaw() const { return getAbsoluteRaw().getRaw(); }
// MARK: - Retrieving related nodes
/// Return the parent \c SyntaxDataRef if it exists, otherwise \c nullptr.
const SyntaxDataRef *getParentRef() const {
return Parent;
}
/// Returns true if this syntax node has a parent.
bool hasParent() const { return getParentRef() != nullptr; }
/// Returns the number of children this SyntaxData has.
size_t getNumChildren() const { return getRaw()->getLayout().size(); }
/// Returns the child index of this node in its parent, if it has a parent,
/// otherwise 0.
AbsoluteSyntaxPosition::IndexInParentType getIndexInParent() const {
return getAbsoluteRaw().getPosition().getIndexInParent();
}
/// If \c this node has a child at \p Cursor, write the child's \c
/// SyntaxDataRef to \p DataMem and return \c true.
/// If no child exists at \p Cursor, leave \p DataMem untouched and return \c
/// false.
template <typename CursorType>
bool getChildRef(CursorType Cursor, SyntaxDataRef *DataMem) const {
return getChildRef(
(AbsoluteSyntaxPosition::IndexInParentType)cursorIndex(Cursor),
DataMem);
}
/// If \c this node has a child at \p Index, write the child's \c
/// SyntaxDataRef to \p DataMem and return \c true. If no child exists at \p
/// Index, leave \p DataMem untouched and return \c false.
bool getChildRef(AbsoluteSyntaxPosition::IndexInParentType Index,
SyntaxDataRef *DataMem) const {
auto AbsoluteRaw = getAbsoluteRaw().getChild(Index);
if (AbsoluteRaw) {
new (DataMem) SyntaxDataRef(*AbsoluteRaw,
/*Parent=*/const_cast<SyntaxDataRef *>(this));
return true;
} else {
return false;
}
}
/// Assuming that the child at \p Cursor exists, write its \c SyntaxData to \p
/// DataMem.
template <typename CursorType>
void getPresentChildRef(CursorType Cursor, SyntaxDataRef *DataMem) const {
return getPresentChildRef(
(AbsoluteSyntaxPosition::IndexInParentType)cursorIndex(Cursor),
DataMem);
}
/// Assuming that the child at \p Index exists, write its \c SyntaxData to \p
/// DataMem.
void getPresentChildRef(AbsoluteSyntaxPosition::IndexInParentType Index,
SyntaxDataRef *DataMem) const {
auto AbsoluteRaw = getAbsoluteRaw().getPresentChild(Index);
new (DataMem) SyntaxDataRef(AbsoluteRaw,
/*Parent=*/const_cast<SyntaxDataRef *>(this));
}
// MARK: - Retrieving source locations
/// Get the offset at which the leading trivia of this node starts.
AbsoluteOffsetPosition getAbsolutePositionBeforeLeadingTrivia() const {
return getAbsoluteRaw().getPosition();
}
/// Get the offset at which the content of this node (excluding leading
/// trivia) starts.
AbsoluteOffsetPosition getAbsolutePositionAfterLeadingTrivia() const {
if (auto FirstToken = getAbsoluteRaw().getFirstToken()) {
return getAbsolutePositionBeforeLeadingTrivia().advancedBy(
FirstToken->getRaw()->getLeadingTriviaLength());
} else {
return getAbsolutePositionBeforeLeadingTrivia();
}
}
/// Get the offset at which the content (excluding trailing trivia) of this
/// node ends.
AbsoluteOffsetPosition getAbsoluteEndPositionBeforeTrailingTrivia() const {
if (auto LastToken = getAbsoluteRaw().getLastToken()) {
return getAbsoluteEndPositionAfterTrailingTrivia().advancedBy(
-LastToken->getRaw()->getTrailingTriviaLength());
} else {
return getAbsoluteEndPositionAfterTrailingTrivia();
}
}
/// Get the offset at chiwh the trailing trivia of this node ends.
AbsoluteOffsetPosition getAbsoluteEndPositionAfterTrailingTrivia() const {
return getAbsolutePositionBeforeLeadingTrivia().advancedBy(
getRaw()->getTextLength());
}
// MARK: - Getting the node's kind
/// Returns which kind of syntax node this is.
SyntaxKind getKind() const { return getRaw()->getKind(); }
/// Returns true if the data node represents type syntax.
bool isType() const { return getRaw()->isType(); }
/// Returns true if the data node represents statement syntax.
bool isStmt() const { return getRaw()->isStmt(); }
/// Returns true if the data node represents declaration syntax.
bool isDecl() const { return getRaw()->isDecl(); }
/// Returns true if the data node represents expression syntax.
bool isExpr() const { return getRaw()->isExpr(); }
/// Returns true if the data node represents pattern syntax.
bool isPattern() const { return getRaw()->isPattern(); }
/// Returns true if this syntax is of some "unknown" kind.
bool isUnknown() const { return getRaw()->isUnknown(); }
// MARK: - Miscellaneous
/// Dump a debug description of the syntax data for debugging to
/// standard error.
void dump(llvm::raw_ostream &OS) const;
SWIFT_DEBUG_DUMP;
};
class SyntaxData final : public SyntaxDataRef {
/// If this node is the root of a Syntax tree (i.e. \c Parent is \c nullptr ),
/// the arena in which this node's \c RawSyntax node has been allocated.
/// This keeps this \c RawSyntax nodes referenced by this tree alive.
/// If \c Parent has a value, this is always \c nullptr.
RC<SyntaxArena> Arena;
mutable std::atomic<int> RefCount{0};
/// Create a non-root node with a \p Parent.
SyntaxData(const AbsoluteRawSyntax &AbsoluteRaw,
const RC<const SyntaxData> &Parent)
: SyntaxDataRef(AbsoluteRaw, Parent.get()), Arena(nullptr) {
assert(
Parent != nullptr &&
"Use SyntaxData(AbsoluteRawSyntax) or makeRoot to create a root node.");
assert(!Parent->isRef() &&
"Cannot create a SyntaxData as a child of a SyntaxDataRef");
Parent->Retain();
}
/// Create a new root node.
SyntaxData(const AbsoluteRawSyntax &AbsoluteRaw)
: SyntaxDataRef(AbsoluteRaw, nullptr),
Arena(AbsoluteRaw.getRaw()->getArena()) {}
#ifndef NDEBUG
virtual bool isRef() const override { return false; }
#endif
public:
SyntaxData(const SyntaxData &DataRef)
: SyntaxDataRef(DataRef.AbsoluteRaw, DataRef.Parent),
Arena(DataRef.Arena) {
if (auto Parent = getParent()) {
assert(!Parent->isRef() &&
"Parent of a SyntaxData node should always be a SyntaxData node");
Parent->Retain();
}
}
~SyntaxData() {
assert(RefCount == 0 &&
"Destruction occured when there are still references to this.");
if (auto Parent = getParent()) {
Parent->Release();
}
}
/// Make a new \c SyntaxData node for the tree's root.
static RC<const SyntaxData> makeRoot(const AbsoluteRawSyntax &AbsoluteRaw) {
return RC<const SyntaxData>(new SyntaxData(AbsoluteRaw));
}
void Retain() const { RefCount.fetch_add(1, std::memory_order_relaxed); }
void Release() const {
int NewRefCount = RefCount.fetch_sub(1, std::memory_order_acq_rel) - 1;
assert(NewRefCount >= 0 && "Reference count was already zero.");
if (NewRefCount == 0) {
delete this;
}
}
// MARK: - Retrieving related nodes
/// Return the parent syntax if there is one, otherwise return \c nullptr.
RC<const SyntaxData> getParent() const {
if (auto ParentRef = getParentRef()) {
assert(!ParentRef->isRef() &&
"Parent of a SyntaxData node should always be a SyntaxData node");
return RC<const SyntaxData>(static_cast<const SyntaxData *>(ParentRef));
} else {
return nullptr;
}
}
/// Gets the child at the index specified by the provided cursor if there is
/// one, otherwise returns \c nullptr.
template <typename CursorType>
RC<const SyntaxData> getChild(CursorType Cursor) const {
return getChild(
(AbsoluteSyntaxPosition::IndexInParentType)cursorIndex(Cursor));
}
/// Gets the child at the specified \p Index if there is one, otherwise
/// returns \c nullptr.
RC<const SyntaxData>
getChild(AbsoluteSyntaxPosition::IndexInParentType Index) const;
/// Get the node immediately before this current node that does contain a
/// non-missing token. Return \c nullptr if we cannot find such node.
RC<const SyntaxData> getPreviousNode() const;
/// Get the node immediately after this current node that does contain a
/// non-missing token. Return \c nullptr if we cannot find such node.
RC<const SyntaxData> getNextNode() const;
/// Get the first non-missing token node in this tree. Return \c nullptr if
/// this node does not contain non-missing tokens.
RC<const SyntaxData> getFirstToken() const;
/// Get the last non-missing token node in this tree. Return \c nullptr if
/// this node does not contain non-missing tokens.
RC<const SyntaxData> getLastToken() const;
// MARK: - Modifying node
/// With a new \c RawSyntax node, create a new node from this one and
/// recursively rebuild the parental chain up to the root.
RC<const SyntaxData> replacingSelf(const RawSyntax *NewRaw) const;
/// Replace a child in the raw syntax and recursively rebuild the
/// parental chain up to the root.
template <typename CursorType>
RC<const SyntaxData> replacingChild(const RawSyntax *RawChild,
CursorType ChildCursor) const {
auto NewRaw = AbsoluteRaw.getRaw()->replacingChild(ChildCursor, RawChild);
return replacingSelf(NewRaw);
}
};
} // end namespace syntax
} // end namespace swift
namespace llvm {
namespace optional_detail {
using swift::syntax::SyntaxDataRef;
/// A custom \c OptionalStorage implementation for \c SyntaxDataRef.
/// This makes \c Optional<SyntaxDataRef> a zero-cost wrapper around \c
/// SyntaxDataRef that uses an internal null type to denote a missing value.
/// This way, we can guarantee that \c SyntaxDataRef values are always non-null.
/// Additionally, it allows writing a value into the \c Optional's storage
/// pointer, which automatically flips the \c Optional's \c hasValue property
/// to \c true. E.g.
/// \code
/// Optional<SyntaxDataRef> optRef;
/// optRef.hasValue(); // false
/// new (optRef.getPointer()) SyntaxDataRef(...);
/// optRef.hasValue(); // true
/// \endcode
/// This behaviour is important so we can stack-allocate an optional \c
/// SyntaxDataRef and later populate its value using the \c SyntaxDataRef's
/// \c getChild method.
template <>
class OptionalStorage<SyntaxDataRef> {
SyntaxDataRef Storage;
public:
OptionalStorage() : Storage(AbsoluteRawSyntax(nullptr), nullptr) {}
OptionalStorage(OptionalStorage const &other) = default;
OptionalStorage(OptionalStorage &&other) = default;
template <class... ArgTypes>
explicit OptionalStorage(llvm::optional_detail::in_place_t,
ArgTypes &&...Args)
: Storage(std::forward<ArgTypes>(Args)...) {}
void reset() { Storage = SyntaxDataRef(AbsoluteRawSyntax(nullptr), nullptr); }
bool hasValue() const { return !Storage.getAbsoluteRaw().isNull(); }
SyntaxDataRef &getValue() LLVM_LVALUE_FUNCTION {
assert(hasValue());
return Storage;
}
SyntaxDataRef const &getValue() const LLVM_LVALUE_FUNCTION {
assert(hasValue());
return Storage;
}
#if LLVM_HAS_RVALUE_REFERENCE_THIS
SyntaxDataRef &&getValue() &&noexcept {
assert(hasValue());
return std::move(Storage);
}
#endif
template <class... Args>
void emplace(Args &&...args) {
Storage = SyntaxDataRef(std::forward<Args>(args)...);
}
OptionalStorage &operator=(const SyntaxDataRef &AbsoluteRaw) {
Storage = AbsoluteRaw;
return *this;
}
OptionalStorage &operator=(SyntaxDataRef &&AbsoluteRaw) {
Storage = std::move(AbsoluteRaw);
return *this;
}
OptionalStorage &operator=(OptionalStorage const &other) = default;
OptionalStorage &operator=(OptionalStorage &&other) = default;
};
} // namespace optional_detail
} // end namespace llvm
#endif // SWIFT_SYNTAX_SYNTAXDATA_H