1 | //===--- Context.h - Mechanism for passing implicit data --------*- C++-*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // Context for storing and retrieving implicit data. Useful for passing implicit |
10 | // parameters on a per-request basis. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_CONTEXT_H |
15 | #define |
16 | |
17 | #include "llvm/ADT/STLExtras.h" |
18 | #include "llvm/Support/Compiler.h" |
19 | #include <memory> |
20 | #include <type_traits> |
21 | |
22 | namespace clang { |
23 | namespace clangd { |
24 | |
25 | /// Values in a Context are indexed by typed keys. |
26 | /// Key<T> serves two purposes: |
27 | /// - it provides a lookup key for the context (each Key is unique), |
28 | /// - it makes lookup type-safe: a Key<T> can only map to a T (or nothing). |
29 | /// |
30 | /// Example: |
31 | /// Key<int> RequestID; |
32 | /// Key<int> Version; |
33 | /// |
34 | /// Context Ctx = Context::empty().derive(RequestID, 10).derive(Version, 3); |
35 | /// assert(*Ctx.get(RequestID) == 10); |
36 | /// assert(*Ctx.get(Version) == 3); |
37 | /// |
38 | /// Keys are typically used across multiple functions, so most of the time you |
39 | /// would want to make them static class members or global variables. |
40 | template <class Type> class Key { |
41 | public: |
42 | static_assert(!std::is_reference<Type>::value, |
43 | "Reference arguments to Key<> are not allowed" ); |
44 | |
45 | constexpr Key() = default; |
46 | |
47 | Key(Key const &) = delete; |
48 | Key &operator=(Key const &) = delete; |
49 | Key(Key &&) = delete; |
50 | Key &operator=(Key &&) = delete; |
51 | }; |
52 | |
53 | /// A context is an immutable container for per-request data that must be |
54 | /// propagated through layers that don't care about it. An example is a request |
55 | /// ID that we may want to use when logging. |
56 | /// |
57 | /// Conceptually, a context is a heterogeneous map<Key<T>, T>. Each key has |
58 | /// an associated value type, which allows the map to be typesafe. |
59 | /// |
60 | /// There is an "ambient" context for each thread, Context::current(). |
61 | /// Most functions should read from this, and use WithContextValue or |
62 | /// WithContext to extend or replace the context within a block scope. |
63 | /// Only code dealing with threads and extension points should need to use |
64 | /// other Context objects. |
65 | /// |
66 | /// You can't add data to an existing context, instead you create a new |
67 | /// immutable context derived from it with extra data added. When you retrieve |
68 | /// data, the context will walk up the parent chain until the key is found. |
69 | class Context { |
70 | public: |
71 | /// Returns an empty root context that contains no data. |
72 | static Context empty(); |
73 | /// Returns the context for the current thread, creating it if needed. |
74 | static const Context ¤t(); |
75 | // Sets the current() context to Replacement, and returns the old context. |
76 | // Prefer to use WithContext or WithContextValue to do this safely. |
77 | static Context swapCurrent(Context Replacement); |
78 | |
79 | private: |
80 | struct Data; |
81 | Context(std::shared_ptr<const Data> DataPtr); |
82 | |
83 | public: |
84 | /// Same as Context::empty(), please use Context::empty() instead. |
85 | Context() = default; |
86 | |
87 | /// Copy operations for this class are deleted, use an explicit clone() method |
88 | /// when you need a copy of the context instead. |
89 | Context(Context const &) = delete; |
90 | Context &operator=(const Context &) = delete; |
91 | |
92 | Context(Context &&) = default; |
93 | Context &operator=(Context &&) = default; |
94 | |
95 | /// Get data stored for a typed \p Key. If values are not found |
96 | /// \returns Pointer to the data associated with \p Key. If no data is |
97 | /// specified for \p Key, return null. |
98 | template <class Type> const Type *get(const Key<Type> &Key) const { |
99 | for (const Data *DataPtr = this->DataPtr.get(); DataPtr != nullptr; |
100 | DataPtr = DataPtr->Parent.get()) { |
101 | if (DataPtr->KeyPtr == &Key) |
102 | return static_cast<const Type *>(DataPtr->Value->getValuePtr()); |
103 | } |
104 | return nullptr; |
105 | } |
106 | |
107 | /// A helper to get a reference to a \p Key that must exist in the map. |
108 | /// Must not be called for keys that are not in the map. |
109 | template <class Type> const Type &getExisting(const Key<Type> &Key) const { |
110 | auto Val = get(Key); |
111 | assert(Val && "Key does not exist" ); |
112 | return *Val; |
113 | } |
114 | |
115 | /// Derives a child context |
116 | /// It is safe to move or destroy a parent context after calling derive(). |
117 | /// The child will keep its parent alive, and its data remains accessible. |
118 | template <class Type> |
119 | Context derive(const Key<Type> &Key, std::decay_t<Type> Value) const & { |
120 | return Context(std::make_shared<Data>( |
121 | args: Data{/*Parent=*/DataPtr, &Key, |
122 | std::make_unique<TypedAnyStorage<std::decay_t<Type>>>( |
123 | std::move(Value))})); |
124 | } |
125 | |
126 | template <class Type> |
127 | Context derive(const Key<Type> &Key, |
128 | std::decay_t<Type> Value) && /* takes ownership */ { |
129 | return Context(std::make_shared<Data>( |
130 | args: Data{/*Parent=*/std::move(DataPtr), &Key, |
131 | std::make_unique<TypedAnyStorage<std::decay_t<Type>>>( |
132 | std::move(Value))})); |
133 | } |
134 | |
135 | /// Derives a child context, using an anonymous key. |
136 | /// Intended for objects stored only for their destructor's side-effect. |
137 | template <class Type> Context derive(Type &&Value) const & { |
138 | static Key<std::decay_t<Type>> Private; |
139 | return derive(Private, std::forward<Type>(Value)); |
140 | } |
141 | |
142 | template <class Type> Context derive(Type &&Value) && { |
143 | static Key<std::decay_t<Type>> Private; |
144 | return std::move(*this).derive(Private, std::forward<Type>(Value)); |
145 | } |
146 | |
147 | /// Clone this context object. |
148 | Context clone() const; |
149 | |
150 | private: |
151 | class AnyStorage { |
152 | public: |
153 | virtual ~AnyStorage() = default; |
154 | virtual void *getValuePtr() = 0; |
155 | }; |
156 | |
157 | template <class T> class TypedAnyStorage : public Context::AnyStorage { |
158 | static_assert(std::is_same<std::decay_t<T>, T>::value, |
159 | "Argument to TypedAnyStorage must be decayed" ); |
160 | |
161 | public: |
162 | TypedAnyStorage(T &&Value) : Value(std::move(Value)) {} |
163 | |
164 | void *getValuePtr() override { return &Value; } |
165 | |
166 | private: |
167 | T Value; |
168 | }; |
169 | |
170 | struct Data { |
171 | // We need to make sure Parent outlives the Value, so the order of members |
172 | // is important. We do that to allow classes stored in Context's child |
173 | // layers to store references to the data in the parent layers. |
174 | std::shared_ptr<const Data> Parent; |
175 | const void *KeyPtr; |
176 | std::unique_ptr<AnyStorage> Value; |
177 | }; |
178 | |
179 | std::shared_ptr<const Data> DataPtr; |
180 | }; |
181 | |
182 | /// WithContext replaces Context::current() with a provided scope. |
183 | /// When the WithContext is destroyed, the original scope is restored. |
184 | /// For extending the current context with new value, prefer WithContextValue. |
185 | class [[nodiscard]] WithContext { |
186 | public: |
187 | WithContext(Context C) : Restore(Context::swapCurrent(Replacement: std::move(C))) {} |
188 | ~WithContext() { Context::swapCurrent(Replacement: std::move(Restore)); } |
189 | WithContext(const WithContext &) = delete; |
190 | WithContext &operator=(const WithContext &) = delete; |
191 | WithContext(WithContext &&) = delete; |
192 | WithContext &operator=(WithContext &&) = delete; |
193 | |
194 | private: |
195 | Context Restore; |
196 | }; |
197 | |
198 | /// WithContextValue extends Context::current() with a single value. |
199 | /// When the WithContextValue is destroyed, the original scope is restored. |
200 | class [[nodiscard]] WithContextValue { |
201 | public: |
202 | template <typename T> |
203 | WithContextValue(const Key<T> &K, std::decay_t<T> V) |
204 | : Restore(Context::current().derive(K, std::move(V))) {} |
205 | |
206 | // Anonymous values can be used for the destructor side-effect. |
207 | template <typename T> |
208 | WithContextValue(T &&V) |
209 | : Restore(Context::current().derive(std::forward<T>(V))) {} |
210 | |
211 | private: |
212 | WithContext Restore; |
213 | }; |
214 | |
215 | } // namespace clangd |
216 | } // namespace clang |
217 | |
218 | #endif |
219 | |