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