Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -541,6 +541,13 @@ IntrusiveRefCntPtr ExternalSource; ASTMutationListener *Listener = nullptr; + /// Generation counter for this AST, used by attached external AST sources to + /// signal and detect modifications to the AST. + /// + /// Counter must be increased when an external source added new redeclaration + /// for an existing decl. + uint32_t CurrentGeneration = 0; + /// \brief Contains parents of a node. using ParentVector = llvm::SmallVector; @@ -1062,6 +1069,15 @@ /// with this AST context, if any. ASTMutationListener *getASTMutationListener() const { return Listener; } + uint32_t getGeneration() const { return CurrentGeneration; } + uint32_t incrementGeneration() { + uint32_t OldGeneration = CurrentGeneration; + CurrentGeneration++; + assert(CurrentGeneration > OldGeneration && + "Overflowed generation counter"); + return OldGeneration; + } + void PrintStats() const; const SmallVectorImpl& getTypes() const { return Types; } @@ -2955,18 +2971,27 @@ C.Deallocate(Ptr); } +// Note, this is implemented here so that ExternalASTSource.h doesn't need to +// include ASTContext.h. We explicitly instantiate it for all relevant types +// in ASTContext.cpp. + /// \brief Create the representation of a LazyGenerationalUpdatePtr. template typename clang::LazyGenerationalUpdatePtr::ValueType clang::LazyGenerationalUpdatePtr::makeValue( const clang::ASTContext &Ctx, T Value) { - // Note, this is implemented here so that ExternalASTSource.h doesn't need to - // include ASTContext.h. We explicitly instantiate it for all relevant types - // in ASTContext.cpp. - if (auto *Source = Ctx.getExternalSource()) - return new (Ctx) LazyData(Source, Value); + if (Ctx.getExternalSource()) + return new (Ctx) LazyData(&Ctx, Value); return Value; } +template +typename clang::ExternalASTSource * +clang::LazyGenerationalUpdatePtr::getExternalSource( + const clang::ASTContext &Ctx) { + return Ctx.getExternalSource(); +} + #endif // LLVM_CLANG_AST_ASTCONTEXT_H Index: include/clang/AST/ExternalASTSource.h =================================================================== --- include/clang/AST/ExternalASTSource.h +++ include/clang/AST/ExternalASTSource.h @@ -64,10 +64,6 @@ class ExternalASTSource : public RefCountedBase { friend class ExternalSemaSource; - /// Generation number for this external AST source. Must be increased - /// whenever we might have added new redeclarations for existing decls. - uint32_t CurrentGeneration = 0; - /// \brief Whether this AST source also provides information for /// semantic analysis. bool SemaSource = false; @@ -95,7 +91,7 @@ /// \brief Get the current generation of this AST source. This number /// is incremented each time the AST source lazily extends an existing /// entity. - uint32_t getGeneration() const { return CurrentGeneration; } + uint32_t getGeneration(const ASTContext &C) const; /// \brief Resolve a declaration ID into a declaration, potentially /// building a new declaration. @@ -413,12 +409,12 @@ /// A cache of the value of this pointer, in the most recent generation in /// which we queried it. struct LazyData { - ExternalASTSource *ExternalSource; + const ASTContext *Context; uint32_t LastGeneration = 0; T LastValue; - LazyData(ExternalASTSource *Source, T Value) - : ExternalSource(Source), LastValue(Value) {} + LazyData(const ASTContext *Context, T Value) + : Context(Context), LastValue(Value) {} }; // Our value is represented as simply T if there is no external AST source. @@ -429,6 +425,7 @@ // Defined in ASTContext.h static ValueType makeValue(const ASTContext &Ctx, T Value); + static ExternalASTSource *getExternalSource(const ASTContext &Ctx); public: explicit LazyGenerationalUpdatePtr(const ASTContext &Ctx, T Value = T()) @@ -460,9 +457,12 @@ /// Get the value of this pointer, updating its owner if necessary. T get(Owner O) { if (LazyData *LazyVal = Value.template dyn_cast()) { - if (LazyVal->LastGeneration != LazyVal->ExternalSource->getGeneration()) { - LazyVal->LastGeneration = LazyVal->ExternalSource->getGeneration(); - (LazyVal->ExternalSource->*Update)(O); + ExternalASTSource *ExternalSource = getExternalSource(*LazyVal->Context); + if (LazyVal->LastGeneration != + ExternalSource->getGeneration(*LazyVal->Context)) { + LazyVal->LastGeneration = + ExternalSource->getGeneration(*LazyVal->Context); + (ExternalSource->*Update)(O); } return LazyVal->LastValue; } Index: include/clang/Serialization/ASTReader.h =================================================================== --- include/clang/Serialization/ASTReader.h +++ include/clang/Serialization/ASTReader.h @@ -1260,6 +1260,15 @@ : Mod(Mod), ImportedBy(ImportedBy), ImportLoc(ImportLoc) {} }; + using ExternalSemaSource::getGeneration; + /// Get the generation of the ASTContext we read into. + /// Returns 0 if we don't read into an ASTContext and have no generations. + uint32_t getGeneration() const { + if (ContextObj) + return getGeneration(*ContextObj); + return 0u; + } + ASTReadResult ReadASTCore(StringRef FileName, ModuleKind Type, SourceLocation ImportLoc, ModuleFile *ImportedBy, SmallVectorImpl &Loaded, Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -9972,3 +9972,7 @@ clang::LazyGenerationalUpdatePtr< const Decl *, Decl *, &ExternalASTSource::CompleteRedeclChain>::makeValue( const clang::ASTContext &Ctx, Decl *Value); +template clang::ExternalASTSource * +clang::LazyGenerationalUpdatePtr:: + getExternalSource(const clang::ASTContext &Ctx); Index: lib/AST/ExternalASTSource.cpp =================================================================== --- lib/AST/ExternalASTSource.cpp +++ lib/AST/ExternalASTSource.cpp @@ -27,6 +27,10 @@ ExternalASTSource::~ExternalASTSource() = default; +uint32_t ExternalASTSource::getGeneration(const ASTContext &C) const { + return C.getGeneration(); +} + llvm::Optional ExternalASTSource::getSourceDescriptor(unsigned ID) { return None; @@ -121,19 +125,5 @@ void ExternalASTSource::getMemoryBufferSizes(MemoryBufferSizes &sizes) const {} uint32_t ExternalASTSource::incrementGeneration(ASTContext &C) { - uint32_t OldGeneration = CurrentGeneration; - - // Make sure the generation of the topmost external source for the context is - // incremented. That might not be us. - auto *P = C.getExternalSource(); - if (P && P != this) - CurrentGeneration = P->incrementGeneration(C); - else { - // FIXME: Only bump the generation counter if the current generation number - // has been observed? - if (!++CurrentGeneration) - llvm::report_fatal_error("generation counter overflowed", false); - } - - return OldGeneration; + return C.incrementGeneration(); } Index: unittests/CMakeLists.txt =================================================================== --- unittests/CMakeLists.txt +++ unittests/CMakeLists.txt @@ -31,3 +31,4 @@ add_subdirectory(libclang) endif() add_subdirectory(Rename) +add_subdirectory(Serialization) Index: unittests/Serialization/CMakeLists.txt =================================================================== --- /dev/null +++ unittests/Serialization/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + Support + ) + +add_clang_unittest(SerializationTests + GenerationCounterTest.cpp + ) + +target_link_libraries(SerializationTests + PRIVATE + clangAST + clangSerialization + clangBasic + clangFrontend + clangTooling + ) Index: unittests/Serialization/GenerationCounterTest.cpp =================================================================== --- /dev/null +++ unittests/Serialization/GenerationCounterTest.cpp @@ -0,0 +1,68 @@ +//===- unittest/Serialization/GenerationCounterTest.cpp -------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Tests the generation counter in the ExternalASTSource. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/Sema/MultiplexExternalSemaSource.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace { +// Just allows us to easier increment the generation without actually having +// to modify the AST in some way. +class ASTSourceTester : public clang::ExternalSemaSource { +public: + void testIncrementGeneration(clang::ASTContext &C) { incrementGeneration(C); } +}; +} // namespace + +namespace clang { + +TEST(GenerationCounter, MultipleConsumers) { + // Three sources which always should see the same generation counter value + // once they have been attached to the ASTContext. + ASTSourceTester Source1, Source2, NewSource; + + // Create a real ASTContext. + std::unique_ptr ASTUnit = tooling::buildASTFromCode("int main() {}"); + clang::ASTContext &C = ASTUnit->getASTContext(); + + // Attach the first two sources with a multiplexer. + MultiplexExternalSemaSource *Multiplexer = + new MultiplexExternalSemaSource(Source1, Source2); + C.setExternalSource(Multiplexer); + + auto OldGeneration = Source1.getGeneration(C); + + // Pretend each source modifies the AST and increments the generation counter. + // After each step the generation counter needs to be identical for each + // source (but different than the previous counter value). + Source1.testIncrementGeneration(C); + ASSERT_EQ(Source1.getGeneration(C), Source2.getGeneration(C)); + ASSERT_EQ(Source1.getGeneration(C), Multiplexer->getGeneration(C)); + ASSERT_NE(Source1.getGeneration(C), OldGeneration); + OldGeneration = Source1.getGeneration(C); + + Source2.testIncrementGeneration(C); + ASSERT_EQ(Source1.getGeneration(C), Source2.getGeneration(C)); + ASSERT_EQ(Source1.getGeneration(C), Multiplexer->getGeneration(C)); + ASSERT_NE(Source1.getGeneration(C), OldGeneration); + + // Just add the last source which should also directly inherit the correct + // generation counter value. + Multiplexer->addSource(NewSource); + ASSERT_EQ(NewSource.getGeneration(C), Source1.getGeneration(C)); + ASSERT_EQ(NewSource.getGeneration(C), Source2.getGeneration(C)); + ASSERT_EQ(NewSource.getGeneration(C), Multiplexer->getGeneration(C)); +} + +} // namespace clang