]> granicus.if.org Git - clang/commitdiff
Don't eagerly load all conversion operators when loading a class declaration
authorRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 30 Aug 2013 04:46:40 +0000 (04:46 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Fri, 30 Aug 2013 04:46:40 +0000 (04:46 +0000)
from a PCH/module.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@189646 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTUnresolvedSet.h
include/clang/AST/ASTVector.h
include/clang/AST/DeclCXX.h
include/clang/AST/UnresolvedSet.h
include/clang/Serialization/ASTReader.h
lib/AST/DeclCXX.cpp
lib/Serialization/ASTReader.cpp
lib/Serialization/ASTWriter.cpp
test/PCH/check-deserializations.cpp

index f9f4306bfa12649b40c9ff9a535cef73d5553f04..e8be67006c5b3bca80de1df4d6bb36a1d48e4e35 100644 (file)
@@ -22,12 +22,21 @@ namespace clang {
 
 /// \brief An UnresolvedSet-like class which uses the ASTContext's allocator.
 class ASTUnresolvedSet {
-  typedef ASTVector<DeclAccessPair> DeclsTy;
+  struct DeclsTy : ASTVector<DeclAccessPair> {
+    DeclsTy() {}
+    DeclsTy(ASTContext &C, unsigned N) : ASTVector<DeclAccessPair>(C, N) {}
+
+    bool isLazy() const { return getTag(); }
+    void setLazy(bool Lazy) { setTag(Lazy); }
+  };
+
   DeclsTy Decls;
 
   ASTUnresolvedSet(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
   void operator=(const ASTUnresolvedSet &) LLVM_DELETED_FUNCTION;
 
+  friend class LazyASTUnresolvedSet;
+
 public:
   ASTUnresolvedSet() {}
   ASTUnresolvedSet(ASTContext &C, unsigned N) : Decls(C, N) {}
@@ -48,7 +57,7 @@ public:
   /// Replaces the given declaration with the new one, once.
   ///
   /// \return true if the set changed
-  bool replace(const NamedDeclOld, NamedDecl *New, AccessSpecifier AS) {
+  bool replace(const NamedDecl *Old, NamedDecl *New, AccessSpecifier AS) {
     for (DeclsTy::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
       if (I->getDecl() == Old) {
         I->set(New, AS);
@@ -76,7 +85,29 @@ public:
   DeclAccessPair &operator[](unsigned I) { return Decls[I]; }
   const DeclAccessPair &operator[](unsigned I) const { return Decls[I]; }
 };
-  
+
+/// \brief An UnresolvedSet-like class that might not have been loaded from the
+/// external AST source yet.
+class LazyASTUnresolvedSet {
+  mutable ASTUnresolvedSet Impl;
+
+  void getFromExternalSource(ASTContext &C) const;
+
+public:
+  ASTUnresolvedSet &get(ASTContext &C) const {
+    if (Impl.Decls.isLazy())
+      getFromExternalSource(C);
+    return Impl;
+  }
+
+  void reserve(ASTContext &C, unsigned N) { Impl.reserve(C, N); }
+  void addLazyDecl(ASTContext &C, uintptr_t ID, AccessSpecifier AS) {
+    assert(Impl.empty() || Impl.Decls.isLazy());
+    Impl.Decls.setLazy(true);
+    Impl.addDecl(C, reinterpret_cast<NamedDecl*>(ID << 2), AS);
+  }
+};
+
 } // namespace clang
 
 #endif
index 71a687869a3f2eb5500c5084e888fe6e5a48b2ba..6db918eaa631548b51f3d0ba77f82e43e8ea8825 100644 (file)
@@ -55,16 +55,24 @@ namespace clang {
 
 template<typename T>
 class ASTVector {
-  T *Begin, *End, *Capacity;
+private:
+  T *Begin, *End;
+  llvm::PointerIntPair<T*, 1, bool> Capacity;
 
   void setEnd(T *P) { this->End = P; }
 
+protected:
+  // Make a tag bit available to users of this class.
+  // FIXME: This is a horrible hack.
+  bool getTag() const { return Capacity.getInt(); }
+  void setTag(bool B) { Capacity.setInt(B); }
+
 public:
   // Default ctor - Initialize to empty.
-  ASTVector() : Begin(NULL), End(NULL), Capacity(NULL) { }
+  ASTVector() : Begin(0), End(0), Capacity(0, false) {}
 
   ASTVector(const ASTContext &C, unsigned N)
-  : Begin(NULL), End(NULL), Capacity(NULL) {
+      : Begin(0), End(0), Capacity(0, false) {
     reserve(C, N);
   }
 
@@ -156,7 +164,7 @@ public:
   }
 
   void push_back(const_reference Elt, const ASTContext &C) {
-    if (End < Capacity) {
+    if (End < this->capacity_ptr()) {
     Retry:
       new (End) T(Elt);
       ++End;
@@ -167,13 +175,13 @@ public:
   }
 
   void reserve(const ASTContext &C, unsigned N) {
-    if (unsigned(Capacity-Begin) < N)
+    if (unsigned(this->capacity_ptr()-Begin) < N)
       grow(C, N);
   }
 
   /// capacity - Return the total number of elements in the currently allocated
   /// buffer.
-  size_t capacity() const { return Capacity - Begin; }
+  size_t capacity() const { return this->capacity_ptr() - Begin; }
 
   /// append - Add the specified range to the end of the SmallVector.
   ///
@@ -220,7 +228,7 @@ public:
       return this->end()-1;
     }
 
-    if (this->End < this->Capacity) {
+    if (this->End < this->capacity_ptr()) {
     Retry:
       new (this->end()) T(this->back());
       this->setEnd(this->end()+1);
@@ -365,13 +373,16 @@ private:
   }
 
 protected:
-  iterator capacity_ptr() { return (iterator)this->Capacity; }
+  const_iterator capacity_ptr() const {
+    return (iterator) Capacity.getPointer();
+  }
+  iterator capacity_ptr() { return (iterator)Capacity.getPointer(); }
 };
 
 // Define this out-of-line to dissuade the C++ compiler from inlining it.
 template <typename T>
 void ASTVector<T>::grow(const ASTContext &C, size_t MinSize) {
-  size_t CurCapacity = Capacity-Begin;
+  size_t CurCapacity = this->capacity();
   size_t CurSize = size();
   size_t NewCapacity = 2*CurCapacity;
   if (NewCapacity < MinSize)
@@ -394,7 +405,7 @@ void ASTVector<T>::grow(const ASTContext &C, size_t MinSize) {
   // ASTContext never frees any memory.
   Begin = NewElts;
   End = NewElts+CurSize;
-  Capacity = Begin+NewCapacity;
+  Capacity.setPointer(Begin+NewCapacity);
 }
 
 } // end: clang namespace
index f96032c61b92bb9c6036fef2533a20dbb60dac6d..f8f2e41fe62966048075adcf442ac56a765100c8 100644 (file)
@@ -473,14 +473,14 @@ class CXXRecordDecl : public RecordDecl {
     /// inherited conversion functions).
     ///
     /// Each of the entries in this overload set is a CXXConversionDecl.
-    ASTUnresolvedSet Conversions;
+    LazyASTUnresolvedSet Conversions;
 
     /// \brief The conversion functions of this C++ class and all those
     /// inherited conversion functions that are visible in this class.
     ///
     /// Each of the entries in this overload set is a CXXConversionDecl or a
     /// FunctionTemplateDecl.
-    ASTUnresolvedSet VisibleConversions;
+    LazyASTUnresolvedSet VisibleConversions;
 
     /// \brief The declaration which defines this record.
     CXXRecordDecl *Definition;
@@ -1014,10 +1014,10 @@ public:
 
   typedef UnresolvedSetIterator conversion_iterator;
   conversion_iterator conversion_begin() const {
-    return data().Conversions.begin();
+    return data().Conversions.get(getASTContext()).begin();
   }
   conversion_iterator conversion_end() const {
-    return data().Conversions.end();
+    return data().Conversions.get(getASTContext()).end();
   }
 
   /// Removes a conversion function from this class.  The conversion
index c33474906a6783f6d83cf2ab3e64c3323e1d086d..759af2537f75c655360b20cfe65084d73f60711e 100644 (file)
@@ -51,6 +51,7 @@ public:
   typedef std::iterator_traits<IteratorTy>::iterator_category iterator_category;
 
   NamedDecl *getDecl() const { return ir->getDecl(); }
+  void setDecl(NamedDecl *ND) const { return ir->setDecl(ND); }
   AccessSpecifier getAccess() const { return ir->getAccess(); }
   void setAccess(AccessSpecifier AS) { ir->setAccess(AS); }
   DeclAccessPair getPair() const { return *ir; }
index ef4d8860b1afd7d516dacb4b3cec351328062253..0354e824dd44c2426b8c580773d20789d270e2fd 100644 (file)
@@ -88,7 +88,7 @@ class TypeLocReader;
 struct HeaderFileInfo;
 class VersionTuple;
 class TargetOptions;
-class ASTUnresolvedSet;
+class LazyASTUnresolvedSet;
 
 /// \brief Abstract interface for callback invocations by the ASTReader.
 ///
@@ -1745,7 +1745,7 @@ public:
                            unsigned &Idx);
 
   /// \brief Read a UnresolvedSet structure.
-  void ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set,
+  void ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set,
                          const RecordData &Record, unsigned &Idx);
 
   /// \brief Read a C++ base specifier.
index ccd8220a6840ab5d20a1eb7ad4f9f51cf39ee868..4d963cf4b3c6624d7174858810731d656ad4cf82 100644 (file)
@@ -35,6 +35,17 @@ AccessSpecDecl *AccessSpecDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
   return new (Mem) AccessSpecDecl(EmptyShell());
 }
 
+void LazyASTUnresolvedSet::getFromExternalSource(ASTContext &C) const {
+  ExternalASTSource *Source = C.getExternalSource();
+  assert(Impl.Decls.isLazy() && "getFromExternalSource for non-lazy set");
+  assert(Source && "getFromExternalSource with no external source");
+
+  for (ASTUnresolvedSet::iterator I = Impl.begin(); I != Impl.end(); ++I)
+    I.setDecl(cast<NamedDecl>(Source->GetExternalDecl(
+        reinterpret_cast<uintptr_t>(I.getDecl()) >> 2)));
+  Impl.Decls.setLazy(false);
+}
+
 CXXRecordDecl::DefinitionData::DefinitionData(CXXRecordDecl *D)
   : UserDeclaredConstructor(false), UserDeclaredSpecialMembers(0),
     Aggregate(true), PlainOldData(true), Empty(true), Polymorphic(false),
@@ -552,18 +563,16 @@ void CXXRecordDecl::addedMember(Decl *D) {
 
       if (Conversion->getPrimaryTemplate()) {
         // We don't record specializations.
-      } else if (FunTmpl) {
-        if (FunTmpl->getPreviousDecl())
-          data().Conversions.replace(FunTmpl->getPreviousDecl(),
-                                     FunTmpl, AS);
-        else
-          data().Conversions.addDecl(getASTContext(), FunTmpl, AS);
       } else {
-        if (Conversion->getPreviousDecl())
-          data().Conversions.replace(Conversion->getPreviousDecl(),
-                                     Conversion, AS);
+        ASTContext &Ctx = getASTContext();
+        ASTUnresolvedSet &Conversions = data().Conversions.get(Ctx);
+        NamedDecl *Primary =
+            FunTmpl ? cast<NamedDecl>(FunTmpl) : cast<NamedDecl>(Conversion);
+        if (Primary->getPreviousDecl())
+          Conversions.replace(cast<NamedDecl>(Primary->getPreviousDecl()),
+                              Primary, AS);
         else
-          data().Conversions.addDecl(getASTContext(), Conversion, AS);
+          Conversions.addDecl(Ctx, Primary, AS);
       }
     }
 
@@ -880,10 +889,13 @@ void CXXRecordDecl::addedMember(Decl *D) {
   }
   
   // Handle using declarations of conversion functions.
-  if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(D))
+  if (UsingShadowDecl *Shadow = dyn_cast<UsingShadowDecl>(D)) {
     if (Shadow->getDeclName().getNameKind()
-          == DeclarationName::CXXConversionFunctionName)
-      data().Conversions.addDecl(getASTContext(), Shadow, Shadow->getAccess());
+          == DeclarationName::CXXConversionFunctionName) {
+      ASTContext &Ctx = getASTContext();
+      data().Conversions.get(Ctx).addDecl(Ctx, Shadow, Shadow->getAccess());
+    }
+  }
 }
 
 void CXXRecordDecl::finishedDefaultedOrDeletedMember(CXXMethodDecl *D) {
@@ -1083,16 +1095,21 @@ static void CollectVisibleConversions(ASTContext &Context,
 /// in current class; including conversion function templates.
 std::pair<CXXRecordDecl::conversion_iterator,CXXRecordDecl::conversion_iterator>
 CXXRecordDecl::getVisibleConversionFunctions() {
-  // If root class, all conversions are visible.
-  if (bases_begin() == bases_end())
-    return std::make_pair(data().Conversions.begin(), data().Conversions.end());
-  // If visible conversion list is already evaluated, return it.
-  if (!data().ComputedVisibleConversions) {
-    CollectVisibleConversions(getASTContext(), this, data().VisibleConversions);
-    data().ComputedVisibleConversions = true;
+  ASTContext &Ctx = getASTContext();
+
+  ASTUnresolvedSet *Set;
+  if (bases_begin() == bases_end()) {
+    // If root class, all conversions are visible.
+    Set = &data().Conversions.get(Ctx);
+  } else {
+    Set = &data().VisibleConversions.get(Ctx);
+    // If visible conversion list is not evaluated, evaluate it.
+    if (!data().ComputedVisibleConversions) {
+      CollectVisibleConversions(Ctx, this, *Set);
+      data().ComputedVisibleConversions = true;
+    }
   }
-  return std::make_pair(data().VisibleConversions.begin(),
-                        data().VisibleConversions.end());
+  return std::make_pair(Set->begin(), Set->end());
 }
 
 void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) {
@@ -1107,7 +1124,7 @@ void CXXRecordDecl::removeConversion(const NamedDecl *ConvDecl) {
   // with sufficiently large numbers of directly-declared conversions
   // that asymptotic behavior matters.
 
-  ASTUnresolvedSet &Convs = data().Conversions;
+  ASTUnresolvedSet &Convs = data().Conversions.get(getASTContext());
   for (unsigned I = 0, E = Convs.size(); I != E; ++I) {
     if (Convs[I].getDecl() == ConvDecl) {
       Convs.erase(I);
@@ -1233,8 +1250,7 @@ void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) {
   }
   
   // Set access bits correctly on the directly-declared conversions.
-  for (UnresolvedSetIterator I = data().Conversions.begin(), 
-                             E = data().Conversions.end(); 
+  for (conversion_iterator I = conversion_begin(), E = conversion_end();
        I != E; ++I)
     I.setAccess((*I)->getAccess());
 }
index 6a2db82c4369fbbc843b20b772103914818399ef..5b55390730d6587a9c098d549380e47db13b6282 100644 (file)
@@ -6972,14 +6972,14 @@ ReadTemplateArgumentList(SmallVectorImpl<TemplateArgument> &TemplArgs,
 }
 
 /// \brief Read a UnresolvedSet structure.
-void ASTReader::ReadUnresolvedSet(ModuleFile &F, ASTUnresolvedSet &Set,
+void ASTReader::ReadUnresolvedSet(ModuleFile &F, LazyASTUnresolvedSet &Set,
                                   const RecordData &Record, unsigned &Idx) {
   unsigned NumDecls = Record[Idx++];
   Set.reserve(Context, NumDecls);
   while (NumDecls--) {
-    NamedDecl *D = ReadDeclAs<NamedDecl>(F, Record, Idx);
+    DeclID ID = ReadDeclID(F, Record, Idx);
     AccessSpecifier AS = (AccessSpecifier)Record[Idx++];
-    Set.addDecl(Context, D, AS);
+    Set.addLazyDecl(Context, ID, AS);
   }
 }
 
index 7f1bd9b882ec1b78286b3db4fe1999c79caca74a..f8e8b5d7ada7ac6d8035a03e0af36d95868a14d1 100644 (file)
@@ -5117,8 +5117,8 @@ void ASTWriter::AddCXXDefinitionData(const CXXRecordDecl *D, RecordDataImpl &Rec
     AddCXXBaseSpecifiersRef(Data.getVBases(), Data.getVBases() + Data.NumVBases, 
                             Record);
 
-  AddUnresolvedSet(Data.Conversions, Record);
-  AddUnresolvedSet(Data.VisibleConversions, Record);
+  AddUnresolvedSet(Data.Conversions.get(*Context), Record);
+  AddUnresolvedSet(Data.VisibleConversions.get(*Context), Record);
   // Data.Definition is the owning decl, no need to write it. 
   AddDeclRef(D->getFirstFriend(), Record);
   
index 66eb5b480b9cc41c0b4c2948dcaab5b6176060c9..e4dafb7f54af6b2b630f2f15099b9091eee5e03c 100644 (file)
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -emit-pch -o %t.1 %s
-// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -include-pch %t.1 -emit-pch -o %t.2 %s
-// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -include-pch %t.2 -emit-llvm-only %s
+// RUN: %clang_cc1 -error-on-deserialized-decl S1_keyfunc -error-on-deserialized-decl S3 -include-pch %t.1 -emit-pch -o %t.2 %s
+// RUN: %clang_cc1 -error-on-deserialized-decl S1_method -error-on-deserialized-decl S3 -include-pch %t.2 -emit-llvm-only %s
 
 #ifndef HEADER1
 #define HEADER1
@@ -11,17 +11,24 @@ struct S1 {
   virtual void S1_keyfunc();
 };
 
+struct S3 {};
+
+struct S2 {
+  operator S3();
+};
+
 #elif !defined(HEADER2)
 #define HEADER2
 
 // Chained PCH.
-S1 *p;
+S1 *s1;
+S2 *s2;
 
 #else
 
 // Using the headers.
 
-void test(S1*) {
+void test(S1*, S2*) {
 }
 
 #endif