1 //===- clang/Basic/DirectoryEntry.h - Directory references ------*- 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 /// \file
10 /// Defines interfaces for clang::DirectoryEntry and clang::DirectoryEntryRef.
11 ///
12 //===----------------------------------------------------------------------===//
13 
14 #ifndef LLVM_CLANG_BASIC_DIRECTORYENTRY_H
15 #define LLVM_CLANG_BASIC_DIRECTORYENTRY_H
16 
17 #include "clang/Basic/LLVM.h"
18 #include "llvm/ADT/DenseMapInfo.h"
19 #include "llvm/ADT/Hashing.h"
20 #include "llvm/ADT/StringMap.h"
21 #include "llvm/ADT/StringRef.h"
22 #include "llvm/ADT/STLExtras.h"
23 #include "llvm/Support/ErrorOr.h"
24 
25 namespace clang {
26 namespace FileMgr {
27 
28 template <class RefTy> class MapEntryOptionalStorage;
29 
30 } // end namespace FileMgr
31 
32 /// Cached information about one directory (either on disk or in
33 /// the virtual file system).
34 class DirectoryEntry {
35   DirectoryEntry() = default;
36   DirectoryEntry(const DirectoryEntry &) = delete;
37   DirectoryEntry &operator=(const DirectoryEntry &) = delete;
38   friend class FileManager;
39   friend class FileEntryTestHelper;
40 
41   // FIXME: We should not be storing a directory entry name here.
42   StringRef Name; // Name of the directory.
43 
44 public:
45   StringRef getName() const { return Name; }
46 };
47 
48 /// A reference to a \c DirectoryEntry  that includes the name of the directory
49 /// as it was accessed by the FileManager's client.
50 class DirectoryEntryRef {
51 public:
52   const DirectoryEntry &getDirEntry() const { return *ME->getValue(); }
53 
54   StringRef getName() const { return ME->getKey(); }
55 
56   /// Hash code is based on the DirectoryEntry, not the specific named
57   /// reference.
58   friend llvm::hash_code hash_value(DirectoryEntryRef Ref) {
59     return llvm::hash_value(&Ref.getDirEntry());
60   }
61 
62   using MapEntry = llvm::StringMapEntry<llvm::ErrorOr<DirectoryEntry &>>;
63 
64   const MapEntry &getMapEntry() const { return *ME; }
65 
66   /// Check if RHS referenced the file in exactly the same way.
67   bool isSameRef(DirectoryEntryRef RHS) const { return ME == RHS.ME; }
68 
69   DirectoryEntryRef() = delete;
70   DirectoryEntryRef(const MapEntry &ME) : ME(&ME) {}
71 
72   /// Allow DirectoryEntryRef to degrade into 'const DirectoryEntry*' to
73   /// facilitate incremental adoption.
74   ///
75   /// The goal is to avoid code churn due to dances like the following:
76   /// \code
77   /// // Old code.
78   /// lvalue = rvalue;
79   ///
80   /// // Temporary code from an incremental patch.
81   /// lvalue = &rvalue.getDirectoryEntry();
82   ///
83   /// // Final code.
84   /// lvalue = rvalue;
85   /// \endcode
86   ///
87   /// FIXME: Once DirectoryEntryRef is "everywhere" and DirectoryEntry::getName
88   /// has been deleted, delete this implicit conversion.
89   operator const DirectoryEntry *() const { return &getDirEntry(); }
90 
91 private:
92   friend class FileMgr::MapEntryOptionalStorage<DirectoryEntryRef>;
93   struct optional_none_tag {};
94 
95   // Private constructor for use by OptionalStorage.
96   DirectoryEntryRef(optional_none_tag) : ME(nullptr) {}
97   bool hasOptionalValue() const { return ME; }
98 
99   friend struct llvm::DenseMapInfo<DirectoryEntryRef>;
100   struct dense_map_empty_tag {};
101   struct dense_map_tombstone_tag {};
102 
103   // Private constructors for use by DenseMapInfo.
104   DirectoryEntryRef(dense_map_empty_tag)
105       : ME(llvm::DenseMapInfo<const MapEntry *>::getEmptyKey()) {}
106   DirectoryEntryRef(dense_map_tombstone_tag)
107       : ME(llvm::DenseMapInfo<const MapEntry *>::getTombstoneKey()) {}
108   bool isSpecialDenseMapKey() const {
109     return isSameRef(DirectoryEntryRef(dense_map_empty_tag())) ||
110            isSameRef(DirectoryEntryRef(dense_map_tombstone_tag()));
111   }
112 
113   const MapEntry *ME;
114 };
115 
116 namespace FileMgr {
117 
118 /// Customized storage for refs derived from map entires in FileManager, using
119 /// the private optional_none_tag to keep it to the size of a single pointer.
120 template <class RefTy> class MapEntryOptionalStorage {
121   using optional_none_tag = typename RefTy::optional_none_tag;
122   RefTy MaybeRef;
123 
124 public:
125   MapEntryOptionalStorage() : MaybeRef(optional_none_tag()) {}
126 
127   template <class... ArgTypes>
128   explicit MapEntryOptionalStorage(llvm::in_place_t, ArgTypes &&...Args)
129       : MaybeRef(std::forward<ArgTypes>(Args)...) {}
130 
131   void reset() { MaybeRef = optional_none_tag(); }
132 
133   bool has_value() const { return MaybeRef.hasOptionalValue(); }
134   bool hasValue() const { return MaybeRef.hasOptionalValue(); }
135 
136   RefTy &value() & {
137     assert(has_value());
138     return MaybeRef;
139   }
140   RefTy &getValue() & {
141     assert(has_value());
142     return MaybeRef;
143   }
144   RefTy const &value() const & {
145     assert(has_value());
146     return MaybeRef;
147   }
148   RefTy const &getValue() const & {
149     assert(has_value());
150     return MaybeRef;
151   }
152   RefTy &&value() && {
153     assert(has_value());
154     return std::move(MaybeRef);
155   }
156   RefTy &&getValue() && {
157     assert(has_value());
158     return std::move(MaybeRef);
159   }
160 
161   template <class... Args> void emplace(Args &&...args) {
162     MaybeRef = RefTy(std::forward<Args>(args)...);
163   }
164 
165   MapEntryOptionalStorage &operator=(RefTy Ref) {
166     MaybeRef = Ref;
167     return *this;
168   }
169 };
170 
171 } // end namespace FileMgr
172 } // end namespace clang
173 
174 namespace llvm {
175 namespace optional_detail {
176 
177 /// Customize OptionalStorage<DirectoryEntryRef> to use DirectoryEntryRef and
178 /// its optional_none_tag to keep it the size of a single pointer.
179 template <>
180 class OptionalStorage<clang::DirectoryEntryRef>
181     : public clang::FileMgr::MapEntryOptionalStorage<clang::DirectoryEntryRef> {
182   using StorageImpl =
183       clang::FileMgr::MapEntryOptionalStorage<clang::DirectoryEntryRef>;
184 
185 public:
186   OptionalStorage() = default;
187 
188   template <class... ArgTypes>
189   explicit OptionalStorage(in_place_t, ArgTypes &&...Args)
190       : StorageImpl(in_place_t{}, std::forward<ArgTypes>(Args)...) {}
191 
192   OptionalStorage &operator=(clang::DirectoryEntryRef Ref) {
193     StorageImpl::operator=(Ref);
194     return *this;
195   }
196 };
197 
198 static_assert(sizeof(Optional<clang::DirectoryEntryRef>) ==
199                   sizeof(clang::DirectoryEntryRef),
200               "Optional<DirectoryEntryRef> must avoid size overhead");
201 
202 static_assert(
203     std::is_trivially_copyable<Optional<clang::DirectoryEntryRef>>::value,
204     "Optional<DirectoryEntryRef> should be trivially copyable");
205 
206 } // end namespace optional_detail
207 
208 /// Specialisation of DenseMapInfo for DirectoryEntryRef.
209 template <> struct DenseMapInfo<clang::DirectoryEntryRef> {
210   static inline clang::DirectoryEntryRef getEmptyKey() {
211     return clang::DirectoryEntryRef(
212         clang::DirectoryEntryRef::dense_map_empty_tag());
213   }
214 
215   static inline clang::DirectoryEntryRef getTombstoneKey() {
216     return clang::DirectoryEntryRef(
217         clang::DirectoryEntryRef::dense_map_tombstone_tag());
218   }
219 
220   static unsigned getHashValue(clang::DirectoryEntryRef Val) {
221     return hash_value(Val);
222   }
223 
224   static bool isEqual(clang::DirectoryEntryRef LHS,
225                       clang::DirectoryEntryRef RHS) {
226     // Catch the easy cases: both empty, both tombstone, or the same ref.
227     if (LHS.isSameRef(RHS))
228       return true;
229 
230     // Confirm LHS and RHS are valid.
231     if (LHS.isSpecialDenseMapKey() || RHS.isSpecialDenseMapKey())
232       return false;
233 
234     // It's safe to use operator==.
235     return LHS == RHS;
236   }
237 };
238 
239 } // end namespace llvm
240 
241 namespace clang {
242 
243 /// Wrapper around Optional<DirectoryEntryRef> that degrades to 'const
244 /// DirectoryEntry*', facilitating incremental patches to propagate
245 /// DirectoryEntryRef.
246 ///
247 /// This class can be used as return value or field where it's convenient for
248 /// an Optional<DirectoryEntryRef> to degrade to a 'const DirectoryEntry*'. The
249 /// purpose is to avoid code churn due to dances like the following:
250 /// \code
251 /// // Old code.
252 /// lvalue = rvalue;
253 ///
254 /// // Temporary code from an incremental patch.
255 /// Optional<DirectoryEntryRef> MaybeF = rvalue;
256 /// lvalue = MaybeF ? &MaybeF.getDirectoryEntry() : nullptr;
257 ///
258 /// // Final code.
259 /// lvalue = rvalue;
260 /// \endcode
261 ///
262 /// FIXME: Once DirectoryEntryRef is "everywhere" and DirectoryEntry::LastRef
263 /// and DirectoryEntry::getName have been deleted, delete this class and
264 /// replace instances with Optional<DirectoryEntryRef>.
265 class OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr
266     : public Optional<DirectoryEntryRef> {
267 public:
268   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr() = default;
269   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr(
270       OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &&) = default;
271   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr(
272       const OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &) = default;
273   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &
274   operator=(OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &&) = default;
275   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &
276   operator=(const OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &) = default;
277 
278   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr(llvm::NoneType) {}
279   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr(DirectoryEntryRef Ref)
280       : Optional<DirectoryEntryRef>(Ref) {}
281   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr(Optional<DirectoryEntryRef> MaybeRef)
282       : Optional<DirectoryEntryRef>(MaybeRef) {}
283 
284   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &operator=(llvm::NoneType) {
285     Optional<DirectoryEntryRef>::operator=(None);
286     return *this;
287   }
288   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &operator=(DirectoryEntryRef Ref) {
289     Optional<DirectoryEntryRef>::operator=(Ref);
290     return *this;
291   }
292   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr &
293   operator=(Optional<DirectoryEntryRef> MaybeRef) {
294     Optional<DirectoryEntryRef>::operator=(MaybeRef);
295     return *this;
296   }
297 
298   /// Degrade to 'const DirectoryEntry *' to allow  DirectoryEntry::LastRef and
299   /// DirectoryEntry::getName have been deleted, delete this class and replace
300   /// instances with Optional<DirectoryEntryRef>
301   operator const DirectoryEntry *() const {
302     return has_value() ? &value().getDirEntry() : nullptr;
303   }
304 };
305 
306 static_assert(std::is_trivially_copyable<
307                   OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr>::value,
308               "OptionalDirectoryEntryRefDegradesToDirectoryEntryPtr should be "
309               "trivially copyable");
310 
311 } // end namespace clang
312 
313 #endif // LLVM_CLANG_BASIC_DIRECTORYENTRY_H
314