forked from carbon-language/carbon-lang
-
Notifications
You must be signed in to change notification settings - Fork 0
/
clone_context.h
167 lines (142 loc) · 5.56 KB
/
clone_context.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// 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_