mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-09-25 18:00:10 +01:00
168 lines
5.6 KiB
C++
168 lines
5.6 KiB
C++
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
|
|
// Exceptions. See /LICENSE for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
|
|
#ifndef CARBON_EXPLORER_AST_CLONE_CONTEXT_H_
|
|
#define CARBON_EXPLORER_AST_CLONE_CONTEXT_H_
|
|
|
|
#include <optional>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include "common/check.h"
|
|
#include "explorer/ast/ast_rtti.h"
|
|
#include "explorer/base/arena.h"
|
|
#include "llvm/ADT/DenseMap.h"
|
|
#include "llvm/Support/Casting.h"
|
|
|
|
namespace Carbon {
|
|
|
|
class AstNode;
|
|
class Element;
|
|
class Value;
|
|
|
|
// A context for performing a deep copy of some fragment of the AST.
|
|
//
|
|
// This class carries the state necessary to make the copy, including ensuring
|
|
// that each node is cloned only once and mapping from old nodes to new ones.
|
|
class CloneContext {
|
|
public:
|
|
explicit CloneContext(Nonnull<Arena*> arena) : arena_(arena) {}
|
|
|
|
CloneContext(const CloneContext&) = delete;
|
|
auto operator=(const CloneContext&) -> const CloneContext& = delete;
|
|
|
|
// Clone an AST element.
|
|
template <typename T>
|
|
auto Clone(Nonnull<const T*> node) -> Nonnull<T*> {
|
|
if constexpr (std::is_convertible_v<const T*, const AstNode*>) {
|
|
const AstNode* base_node = node;
|
|
// Note, we can't use `llvm::cast<T>` here because we might not have
|
|
// finished cloning `base_node` yet and its kind might not be set. This
|
|
// happens when there is a pointer cycle in the AST.
|
|
return static_cast<T*>(CloneBase(base_node));
|
|
} else if constexpr (std::is_convertible_v<const T*, const Value*>) {
|
|
const Value* base_value = node;
|
|
return static_cast<T*>(CloneBase(base_value));
|
|
} else {
|
|
static_assert(std::is_convertible_v<const T*, const Element*>,
|
|
"unknown pointer type to clone");
|
|
const Element* base_elem = node;
|
|
return static_cast<T*>(CloneBase(base_elem));
|
|
}
|
|
}
|
|
|
|
// Clone anything with a clone constructor, that is, a constructor of the
|
|
// form:
|
|
//
|
|
// explicit MyType(CloneContext&, const MyType&)
|
|
//
|
|
// Clone constructors should call Clone on their owned elements to form a new
|
|
// value. Pointers returned by Clone should not be inspected by the clone
|
|
// constructor, as the pointee is not necessarily fully initialized until the
|
|
// overall cloning process completes.
|
|
//
|
|
// Clone constructors should call Remap on values that they do not own, such
|
|
// as for the declaration named by an IdentifierExpression.
|
|
template <typename T>
|
|
auto Clone(const T& other)
|
|
-> std::enable_if_t<std::is_constructible_v<T, CloneContext&, const T&>,
|
|
T> {
|
|
return T(*this, other);
|
|
}
|
|
|
|
template <typename T>
|
|
auto Clone(std::optional<T> node) -> std::optional<T> {
|
|
if (node) {
|
|
return Clone(*node);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
template <typename T>
|
|
auto Clone(const std::vector<T>& nodes) -> std::vector<T> {
|
|
std::vector<T> result;
|
|
result.reserve(nodes.size());
|
|
for (const auto& node : nodes) {
|
|
result.push_back(Clone(node));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Find the new or existing node corresponding to the given node. This should
|
|
// be used when a cloned node has a non-owning reference to another node,
|
|
// that might refer to something being cloned or might refer to the original
|
|
// object. The returned node might not be fully constructed and should not be
|
|
// inspected.
|
|
template <typename T>
|
|
auto Remap(Nonnull<T*> node) -> Nonnull<T*> {
|
|
// Note, we can't use `llvm::cast<T>` here because we might not have
|
|
// finished cloning `base_node` yet and its kind might not be set. This
|
|
// happens when there is a pointer cycle in the AST.
|
|
T* cloned = static_cast<T*>(nodes_[node]);
|
|
return cloned ? cloned : node;
|
|
}
|
|
|
|
// It's safe to remap a `const` object by remapping the non-const version and
|
|
// adding back the `const`.
|
|
template <typename T>
|
|
auto Remap(Nonnull<const T*> node) -> Nonnull<const T*> {
|
|
return Remap(const_cast<T*>(node));
|
|
}
|
|
|
|
template <typename T>
|
|
auto Remap(std::optional<T> node) -> std::optional<T> {
|
|
if (node) {
|
|
return Remap(*node);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
template <typename T>
|
|
auto Remap(const std::vector<T>& nodes) -> std::vector<T> {
|
|
std::vector<T> result;
|
|
result.reserve(nodes.size());
|
|
for (const auto& node : nodes) {
|
|
result.push_back(Remap(node));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <typename T>
|
|
auto GetExistingClone(Nonnull<const T*> node) -> Nonnull<T*> {
|
|
AstNode* cloned = nodes_.lookup(node);
|
|
CARBON_CHECK(cloned) << "expected node to be cloned";
|
|
return llvm::cast<T>(cloned);
|
|
}
|
|
|
|
private:
|
|
// A value transform that remaps or clones AST elements referred to by the
|
|
// value being transformed.
|
|
class CloneValueTransform;
|
|
|
|
// Clone the given node, and remember the mapping from the original to the
|
|
// new node for remapping.
|
|
auto CloneBase(Nonnull<const AstNode*> node) -> Nonnull<AstNode*>;
|
|
|
|
// Clone the given value, replacing references to cloned local declarations
|
|
// with references to the copies.
|
|
auto CloneBase(Nonnull<const Value*> value) -> Nonnull<Value*>;
|
|
|
|
// Clone the given element reference.
|
|
auto CloneBase(Nonnull<const Element*> elem) -> Nonnull<Element*>;
|
|
|
|
// Clone the given node if it's not already been cloned. This should be used
|
|
// very sparingly, in cases where ownership is unclear.
|
|
void MaybeCloneBase(Nonnull<const AstNode*> node);
|
|
|
|
// Arena to allocate new nodes within.
|
|
Nonnull<Arena*> arena_;
|
|
|
|
// Mapping from old nodes to new nodes.
|
|
llvm::DenseMap<const AstNode*, AstNode*> nodes_;
|
|
};
|
|
|
|
} // namespace Carbon
|
|
|
|
#endif // CARBON_EXPLORER_AST_CLONE_CONTEXT_H_
|