// 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 #include #include #include "common/check.h" #include "explorer/ast/ast_rtti.h" #include "explorer/common/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) {} CloneContext(const CloneContext&) = delete; auto operator=(const CloneContext&) -> const CloneContext& = delete; // Clone an AST element. template auto Clone(Nonnull node) -> Nonnull { if constexpr (std::is_convertible_v) { const AstNode* base_node = node; // Note, we can't use `llvm::cast` 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(CloneBase(base_node)); } else if constexpr (std::is_convertible_v) { const Value* base_value = node; return static_cast(CloneBase(base_value)); } else { static_assert(std::is_convertible_v, "unknown pointer type to clone"); const Element* base_elem = node; return static_cast(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 auto Clone(const T& other) -> std::enable_if_t, T> { return T(*this, other); } template auto Clone(std::optional node) -> std::optional { if (node) { return Clone(*node); } return std::nullopt; } template auto Clone(const std::vector& nodes) -> std::vector { std::vector 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 auto Remap(Nonnull node) -> Nonnull { // Note, we can't use `llvm::cast` 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(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 auto Remap(Nonnull node) -> Nonnull { return Remap(const_cast(node)); } template auto Remap(std::optional node) -> std::optional { if (node) { return Remap(*node); } return std::nullopt; } template auto Remap(const std::vector& nodes) -> std::vector { std::vector result; result.reserve(nodes.size()); for (const auto& node : nodes) { result.push_back(Remap(node)); } return result; } template auto GetExistingClone(Nonnull node) -> Nonnull { AstNode* cloned = nodes_.lookup(node); CARBON_CHECK(cloned) << "expected node to be cloned"; return llvm::cast(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 node) -> Nonnull; // Clone the given value, replacing references to cloned local declarations // with references to the copies. auto CloneBase(Nonnull value) -> Nonnull; // Clone the given element reference. auto CloneBase(Nonnull elem) -> Nonnull; // 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 node); // Arena to allocate new nodes within. Nonnull arena_; // Mapping from old nodes to new nodes. llvm::DenseMap nodes_; }; } // namespace Carbon #endif // CARBON_EXPLORER_AST_CLONE_CONTEXT_H_