dexonsmith updated this revision to Diff 301138.
dexonsmith retitled this revision from "FileManager: Improve the FileEntryRef 
API and add MaybeFileEntryRef" to "FileManager: Improve the FileEntryRef API 
and customize Optional<FileEntryRef>".
dexonsmith edited the summary of this revision.
dexonsmith added a comment.

Dropped `MaybeFileEntryRef`, instead customizing `Optional<FileEntryRef>`.


CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D89834/new/

https://reviews.llvm.org/D89834

Files:
  clang/include/clang/Basic/FileEntry.h
  clang/unittests/Basic/CMakeLists.txt
  clang/unittests/Basic/FileEntryTest.cpp
  clang/unittests/Basic/FileManagerTest.cpp

Index: clang/unittests/Basic/FileManagerTest.cpp
===================================================================
--- clang/unittests/Basic/FileManagerTest.cpp
+++ clang/unittests/Basic/FileManagerTest.cpp
@@ -350,6 +350,58 @@
             f2 ? *f2 : nullptr);
 }
 
+TEST_F(FileManagerTest, getFileRefEquality) {
+  auto StatCache = std::make_unique<FakeStatCache>();
+  StatCache->InjectDirectory("dir", 40);
+  StatCache->InjectFile("dir/f1.cpp", 41);
+  StatCache->InjectFile("dir/f1-also.cpp", 41);
+  StatCache->InjectFile("dir/f1-redirect.cpp", 41, "dir/f1.cpp");
+  StatCache->InjectFile("dir/f2.cpp", 42);
+  manager.setStatCache(std::move(StatCache));
+
+  auto F1 = manager.getFileRef("dir/f1.cpp");
+  auto F1Again = manager.getFileRef("dir/f1.cpp");
+  auto F1Also = manager.getFileRef("dir/f1-also.cpp");
+  auto F1Redirect = manager.getFileRef("dir/f1-redirect.cpp");
+  auto F2 = manager.getFileRef("dir/f2.cpp");
+
+  // Check Expected<FileEntryRef> for error.
+  ASSERT_FALSE(!F1);
+  ASSERT_FALSE(!F1Also);
+  ASSERT_FALSE(!F1Again);
+  ASSERT_FALSE(!F1Redirect);
+  ASSERT_FALSE(!F2);
+
+  // Check names.
+  EXPECT_EQ("dir/f1.cpp", F1->getName());
+  EXPECT_EQ("dir/f1.cpp", F1Again->getName());
+  EXPECT_EQ("dir/f1-also.cpp", F1Also->getName());
+  EXPECT_EQ("dir/f1.cpp", F1Redirect->getName());
+  EXPECT_EQ("dir/f2.cpp", F2->getName());
+
+  // Compare against FileEntry*.
+  EXPECT_EQ(&F1->getFileEntry(), *F1);
+  EXPECT_EQ(*F1, &F1->getFileEntry());
+  EXPECT_NE(&F2->getFileEntry(), *F1);
+  EXPECT_NE(*F1, &F2->getFileEntry());
+
+  // Compare using ==.
+  EXPECT_EQ(*F1, *F1Also);
+  EXPECT_EQ(*F1, *F1Again);
+  EXPECT_EQ(*F1, *F1Redirect);
+  EXPECT_EQ(*F1Also, *F1Redirect);
+  EXPECT_NE(*F2, *F1);
+  EXPECT_NE(*F2, *F1Also);
+  EXPECT_NE(*F2, *F1Again);
+  EXPECT_NE(*F2, *F1Redirect);
+
+  // Compare using isSameRef.
+  EXPECT_TRUE(F1->isSameRef(*F1Again));
+  EXPECT_TRUE(F1->isSameRef(*F1Redirect));
+  EXPECT_FALSE(F1->isSameRef(*F1Also));
+  EXPECT_FALSE(F1->isSameRef(*F2));
+}
+
 // getFile() Should return the same entry as getVirtualFile if the file actually
 // is a virtual file, even if the name is not exactly the same (but is after
 // normalisation done by the file system, like on Windows). This can be checked
Index: clang/unittests/Basic/FileEntryTest.cpp
===================================================================
--- /dev/null
+++ clang/unittests/Basic/FileEntryTest.cpp
@@ -0,0 +1,101 @@
+//===- unittests/Basic/FileEntryTest.cpp - Test FileEntry/FileEntryRef ----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/FileEntry.h"
+#include "llvm/ADT/StringMap.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+using namespace clang;
+
+namespace {
+
+using MapEntry = FileEntryRef::MapEntry;
+using MapValue = FileEntryRef::MapValue;
+using MapType = StringMap<llvm::ErrorOr<MapValue>>;
+
+FileEntryRef addRef(MapType &M, StringRef Name, FileEntry &E) {
+  return FileEntryRef(*M.insert({Name, MapValue(E)}).first);
+}
+
+TEST(FileEntryTest, FileEntryRef) {
+  MapType Refs;
+  FileEntry E1, E2;
+  FileEntryRef R1 = addRef(Refs, "1", E1);
+  FileEntryRef R2 = addRef(Refs, "2", E2);
+  FileEntryRef R1Also = addRef(Refs, "1-also", E1);
+
+  EXPECT_EQ("1", R1.getName());
+  EXPECT_EQ("2", R2.getName());
+  EXPECT_EQ("1-also", R1Also.getName());
+
+  EXPECT_EQ(&E1, &R1.getFileEntry());
+  EXPECT_EQ(&E2, &R2.getFileEntry());
+  EXPECT_EQ(&E1, &R1Also.getFileEntry());
+
+  const FileEntry *CE1 = R1;
+  EXPECT_EQ(CE1, &E1);
+}
+
+TEST(FileEntryTest, OptionalFileEntryRef) {
+  MapType Refs;
+  FileEntry E1, E2;
+  Optional<FileEntryRef> M0;
+  Optional<FileEntryRef> M1 = addRef(Refs, "1", E1);
+  Optional<FileEntryRef> M2 = addRef(Refs, "2", E2);
+  Optional<FileEntryRef> M1Also = addRef(Refs, "1-also", E1);
+
+  EXPECT_EQ(StringRef("1"), M1->getName());
+  EXPECT_EQ(StringRef("2"), M2->getName());
+  EXPECT_EQ(StringRef("1-also"), M1Also->getName());
+
+  EXPECT_EQ(&E1, &M1->getFileEntry());
+  EXPECT_EQ(&E2, &M2->getFileEntry());
+  EXPECT_EQ(&E1, &M1Also->getFileEntry());
+
+  const FileEntry *CE1 = M1;
+  EXPECT_EQ(CE1, &E1);
+}
+
+TEST(FileEntryTest, equals) {
+  MapType Refs;
+  FileEntry E1, E2;
+  FileEntryRef R1 = addRef(Refs, "1", E1);
+  FileEntryRef R2 = addRef(Refs, "2", E2);
+  FileEntryRef R1Also = addRef(Refs, "1-also", E1);
+
+  EXPECT_EQ(R1, &E1);
+  EXPECT_EQ(&E1, R1);
+  EXPECT_EQ(R1, R1Also);
+  EXPECT_NE(R1, &E2);
+  EXPECT_NE(&E2, R1);
+  EXPECT_NE(R1, R2);
+
+  Optional<FileEntryRef> M0;
+  Optional<FileEntryRef> M1 = R1;
+
+  EXPECT_EQ(M1, &E1);
+  EXPECT_EQ(&E1, M1);
+  EXPECT_NE(M1, &E2);
+  EXPECT_NE(&E2, M1);
+}
+
+TEST(FileEntryTest, isSameRef) {
+  MapType Refs;
+  FileEntry E1, E2;
+  FileEntryRef R1 = addRef(Refs, "1", E1);
+  FileEntryRef R2 = addRef(Refs, "2", E2);
+  FileEntryRef R1Also = addRef(Refs, "1-also", E1);
+
+  EXPECT_TRUE(R1.isSameRef(FileEntryRef(R1)));
+  EXPECT_TRUE(R1.isSameRef(FileEntryRef(R1.getMapEntry())));
+  EXPECT_FALSE(R1.isSameRef(R2));
+  EXPECT_FALSE(R1.isSameRef(R1Also));
+}
+
+} // end namespace
Index: clang/unittests/Basic/CMakeLists.txt
===================================================================
--- clang/unittests/Basic/CMakeLists.txt
+++ clang/unittests/Basic/CMakeLists.txt
@@ -5,6 +5,7 @@
 add_clang_unittest(BasicTests
   CharInfoTest.cpp
   DiagnosticTest.cpp
+  FileEntryTest.cpp
   FileManagerTest.cpp
   LineOffsetMappingTest.cpp
   SourceManagerTest.cpp
Index: clang/include/clang/Basic/FileEntry.h
===================================================================
--- clang/include/clang/Basic/FileEntry.h
+++ clang/include/clang/Basic/FileEntry.h
@@ -38,9 +38,9 @@
 /// accessed by the FileManager's client.
 class FileEntryRef {
 public:
-  const StringRef getName() const { return Entry->first(); }
+  StringRef getName() const { return ME->first(); }
   const FileEntry &getFileEntry() const {
-    return *Entry->second->V.get<FileEntry *>();
+    return *ME->second->V.get<FileEntry *>();
   }
 
   inline bool isValid() const;
@@ -49,12 +49,26 @@
   inline const llvm::sys::fs::UniqueID &getUniqueID() const;
   inline time_t getModificationTime() const;
 
+  /// Check if the underlying FileEntry is the same, intentially ignoring
+  /// whether the file was referenced with the same spelling of the filename.
   friend bool operator==(const FileEntryRef &LHS, const FileEntryRef &RHS) {
-    return LHS.Entry == RHS.Entry;
+    return &LHS.getFileEntry() == &RHS.getFileEntry();
+  }
+  friend bool operator==(const FileEntry *LHS, const FileEntryRef &RHS) {
+    return LHS == &RHS.getFileEntry();
+  }
+  friend bool operator==(const FileEntryRef &LHS, const FileEntry *RHS) {
+    return &LHS.getFileEntry() == RHS;
   }
   friend bool operator!=(const FileEntryRef &LHS, const FileEntryRef &RHS) {
     return !(LHS == RHS);
   }
+  friend bool operator!=(const FileEntry *LHS, const FileEntryRef &RHS) {
+    return !(LHS == RHS);
+  }
+  friend bool operator!=(const FileEntryRef &LHS, const FileEntry *RHS) {
+    return !(LHS == RHS);
+  }
 
   struct MapValue;
 
@@ -75,20 +89,137 @@
     MapValue(MapEntry &ME) : V(&ME) {}
   };
 
-private:
-  friend class FileManager;
+  /// Check if RHS referenced the file in exactly the same way.
+  bool isSameRef(const FileEntryRef &RHS) const { return ME == RHS.ME; }
+
+  /// Allow FileEntryRef to degrade into FileEntry* to facilitate incremental
+  /// adoption.
+  operator const FileEntry *() const { return &getFileEntry(); }
 
   FileEntryRef() = delete;
-  explicit FileEntryRef(const MapEntry &Entry)
-      : Entry(&Entry) {
-    assert(Entry.second && "Expected payload");
-    assert(Entry.second->V && "Expected non-null");
-    assert(Entry.second->V.is<FileEntry *>() && "Expected FileEntry");
+  explicit FileEntryRef(const MapEntry &ME) : ME(&ME) {
+    assert(ME.second && "Expected payload");
+    assert(ME.second->V && "Expected non-null");
+    assert(ME.second->V.is<FileEntry *>() && "Expected FileEntry");
+  }
+
+  /// Expose the underlying MapEntry to simplify packing in a PointerIntPair or
+  /// PointerUnion and allow construction in Optional.
+  const clang::FileEntryRef::MapEntry &getMapEntry() const { return *ME; }
+
+private:
+  const MapEntry *ME;
+};
+
+static_assert(sizeof(FileEntryRef) == sizeof(const FileEntry *),
+              "FileEntryRef must avoid size overhead");
+
+} // end namespace clang
+
+namespace llvm {
+
+/// Customize Optional<FileEntryRef> to use FileEntryRef::MapEntry directly
+/// in order to keep it to the size of a pointer.
+///
+/// Note that customizing OptionalStorage would not work, since the default
+/// implementation of Optional returns references and pointers to the stored
+/// value, and this customization does not store a full FileEntryRef.
+template <> class Optional<clang::FileEntryRef> {
+  const clang::FileEntryRef::MapEntry *ME = nullptr;
+
+public:
+  using value_type = clang::FileEntryRef;
+
+  Optional() {}
+  Optional(NoneType) {}
+
+  Optional(clang::FileEntryRef Ref) : ME(&Ref.getMapEntry()) {}
+  Optional(const Optional &O) = default;
+
+  /// Create a new object by constructing it in place with the given arguments.
+  template <typename... ArgTypes> void emplace(ArgTypes &&...Args) {
+    clang::FileEntryRef Ref(std::forward<ArgTypes>(Args)...);
+    *this = Ref;
   }
 
-  const MapEntry *Entry;
+  static Optional create(const clang::FileEntryRef *Ref) {
+    return Ref ? Optional(*Ref) : Optional();
+  }
+
+  Optional &operator=(clang::FileEntryRef Ref) {
+    ME = &Ref.getMapEntry();
+    return *this;
+  }
+  Optional &operator=(const Optional &O) = default;
+
+  void reset() { ME = nullptr; }
+
+  /// Type for use as a "pointer" return for the arrow operator, encapsulating
+  /// a real FileEntryRef.
+  class pointer {
+    friend class Optional;
+
+  public:
+    const clang::FileEntryRef *operator->() const { return &Ref; }
+
+    pointer() = delete;
+    pointer(const pointer &p) = default;
+
+  private:
+    explicit pointer(clang::FileEntryRef Ref) : Ref(Ref) {}
+    clang::FileEntryRef Ref;
+  };
+
+  clang::FileEntryRef getValue() const LLVM_LVALUE_FUNCTION {
+    assert(ME && "Dereferencing None?");
+    return clang::FileEntryRef(*ME);
+  }
+
+  explicit operator bool() const { return hasValue(); }
+  bool hasValue() const { return ME; }
+  pointer operator->() const { return pointer(getValue()); }
+  clang::FileEntryRef operator*() const LLVM_LVALUE_FUNCTION {
+    return getValue();
+  }
+
+  template <typename U>
+  clang::FileEntryRef getValueOr(U &&value) const LLVM_LVALUE_FUNCTION {
+    return hasValue() ? getValue() : std::forward<U>(value);
+  }
+
+  /// Apply a function to the value if present; otherwise return None.
+  template <class Function>
+  auto map(const Function &F) const LLVM_LVALUE_FUNCTION
+      -> Optional<decltype(F(getValue()))> {
+    if (*this)
+      return F(getValue());
+    return None;
+  }
+
+  /// Allow Optional<FileEntryRef> to degrade into FileEntry* to facilitate
+  /// incremental adoption of FileEntryRef. Once FileEntry::getName, remaining
+  /// uses of this should be cleaned up and this can be removed as well.
+  operator const clang::FileEntry *() const {
+    return hasValue() ? &getValue().getFileEntry() : nullptr;
+  }
+
+  /// Allow construction from the underlying pointer type to simplify
+  /// constructing from a PointerIntPair or PointerUnion.
+  explicit Optional(const clang::FileEntryRef::MapEntry *ME) : ME(ME) {}
+
+  /// Expose the underlying pointer type to simplify packing in a
+  /// PointerIntPair or PointerUnion.
+  const clang::FileEntryRef::MapEntry *getMapEntry() const { return ME; }
 };
 
+static_assert(sizeof(Optional<clang::FileEntryRef>) ==
+                  sizeof(clang::FileEntryRef),
+              "Optional<FileEntryRef> must avoid size overhead");
+
+} // end namespace llvm
+
+namespace clang {
+
 /// Cached information about one file (either on disk
 /// or in the virtual file system).
 ///
@@ -144,10 +275,6 @@
   bool isNamedPipe() const { return IsNamedPipe; }
 
   void closeFile() const;
-
-  // Only for use in tests to see if deferred opens are happening, rather than
-  // relying on RealPathName being empty.
-  bool isOpenForTests() const { return File != nullptr; }
 };
 
 bool FileEntryRef::isValid() const { return getFileEntry().isValid(); }
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D89834: Fi... Duncan P. N. Exon Smith via Phabricator via cfe-commits

Reply via email to