diff --git a/clang/include/clang/AST/ASTMutationListener.h b/clang/include/clang/AST/ASTMutationListener.h index 8879f9f3229ff..2c4ec2ce67f36 100644 --- a/clang/include/clang/AST/ASTMutationListener.h +++ b/clang/include/clang/AST/ASTMutationListener.h @@ -27,6 +27,7 @@ namespace clang { class FunctionTemplateDecl; class Module; class NamedDecl; + class NamespaceDecl; class ObjCCategoryDecl; class ObjCContainerDecl; class ObjCInterfaceDecl; @@ -35,6 +36,7 @@ namespace clang { class QualType; class RecordDecl; class TagDecl; + class TranslationUnitDecl; class ValueDecl; class VarDecl; class VarTemplateDecl; @@ -147,6 +149,31 @@ class ASTMutationListener { virtual void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) {} + /// The parser find the named module declaration. + virtual void EnteringModulePurview() {} + + /// An mangling number was added to a Decl + /// + /// \param D The decl that got a mangling number + /// + /// \param Number The mangling number that was added to the Decl + virtual void AddedManglingNumber(const Decl *D, unsigned Number) {} + + /// An static local number was added to a Decl + /// + /// \param D The decl that got a static local number + /// + /// \param Number The static local number that was added to the Decl + virtual void AddedStaticLocalNumbers(const Decl *D, unsigned Number) {} + + /// An anonymous namespace was added the translation unit decl + /// + /// \param TU The translation unit decl that got a new anonymous namespace + /// + /// \param AnonNamespace The anonymous namespace that was added + virtual void AddedAnonymousNamespace(const TranslationUnitDecl *TU, + NamespaceDecl *AnonNamespace) {} + // NOTE: If new methods are added they should also be added to // MultiplexASTMutationListener. }; diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index 01af50ca694fd..91449ebcceec3 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -120,7 +120,7 @@ class TranslationUnitDecl : public Decl, ASTContext &getASTContext() const { return Ctx; } NamespaceDecl *getAnonymousNamespace() const { return AnonymousNamespace; } - void setAnonymousNamespace(NamespaceDecl *D) { AnonymousNamespace = D; } + void setAnonymousNamespace(NamespaceDecl *D); static TranslationUnitDecl *Create(ASTContext &C); diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 443f770310470..892d7363e06dd 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -399,6 +399,11 @@ class ASTWriter : public ASTDeserializationListener, /// record containing modifications to them. DeclUpdateMap DeclUpdates; + /// DeclUpdates added during parsing the GMF. We split these from + /// DeclUpdates since we want to add these updates in GMF on need. + /// Only meaningful for reduced BMI. + DeclUpdateMap DeclUpdatesFromGMF; + using FirstLatestDeclMap = llvm::DenseMap; /// Map of first declarations from a chained PCH that point to the @@ -866,6 +871,11 @@ class ASTWriter : public ASTDeserializationListener, void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + void EnteringModulePurview() override; + void AddedManglingNumber(const Decl *D, unsigned) override; + void AddedStaticLocalNumbers(const Decl *D, unsigned) override; + void AddedAnonymousNamespace(const TranslationUnitDecl *, + NamespaceDecl *AnonNamespace) override; }; /// AST and semantic-analysis consumer that generates a diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 6ce233704a588..bcae6d705b780 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12245,8 +12245,13 @@ QualType ASTContext::getRealTypeForBitwidth(unsigned DestWidth, } void ASTContext::setManglingNumber(const NamedDecl *ND, unsigned Number) { - if (Number > 1) - MangleNumbers[ND] = Number; + if (Number <= 1) + return; + + MangleNumbers[ND] = Number; + + if (Listener) + Listener->AddedManglingNumber(ND, Number); } unsigned ASTContext::getManglingNumber(const NamedDecl *ND, @@ -12265,8 +12270,13 @@ unsigned ASTContext::getManglingNumber(const NamedDecl *ND, } void ASTContext::setStaticLocalNumber(const VarDecl *VD, unsigned Number) { - if (Number > 1) - StaticLocalNumbers[VD] = Number; + if (Number <= 1) + return; + + StaticLocalNumbers[VD] = Number; + + if (Listener) + Listener->AddedStaticLocalNumbers(VD, Number); } unsigned ASTContext::getStaticLocalNumber(const VarDecl *VD) const { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 33b6f8611f216..63388d79bbf21 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5274,6 +5274,13 @@ TranslationUnitDecl *TranslationUnitDecl::Create(ASTContext &C) { return new (C, (DeclContext *)nullptr) TranslationUnitDecl(C); } +void TranslationUnitDecl::setAnonymousNamespace(NamespaceDecl *D) { + AnonymousNamespace = D; + + if (ASTMutationListener *Listener = Ctx.getASTMutationListener()) + Listener->AddedAnonymousNamespace(this, D); +} + void PragmaCommentDecl::anchor() {} PragmaCommentDecl *PragmaCommentDecl::Create(const ASTContext &C, diff --git a/clang/lib/Frontend/MultiplexConsumer.cpp b/clang/lib/Frontend/MultiplexConsumer.cpp index 737877329c9ce..744ea70cc24de 100644 --- a/clang/lib/Frontend/MultiplexConsumer.cpp +++ b/clang/lib/Frontend/MultiplexConsumer.cpp @@ -20,6 +20,9 @@ using namespace clang; namespace clang { +class NamespaceDecl; +class TranslationUnitDecl; + MultiplexASTDeserializationListener::MultiplexASTDeserializationListener( const std::vector& L) : Listeners(L) { @@ -115,6 +118,11 @@ class MultiplexASTMutationListener : public ASTMutationListener { void RedefinedHiddenDefinition(const NamedDecl *D, Module *M) override; void AddedAttributeToRecord(const Attr *Attr, const RecordDecl *Record) override; + void EnteringModulePurview() override; + void AddedManglingNumber(const Decl *D, unsigned) override; + void AddedStaticLocalNumbers(const Decl *D, unsigned) override; + void AddedAnonymousNamespace(const TranslationUnitDecl *, + NamespaceDecl *AnonNamespace) override; private: std::vector Listeners; @@ -238,6 +246,27 @@ void MultiplexASTMutationListener::AddedAttributeToRecord( L->AddedAttributeToRecord(Attr, Record); } +void MultiplexASTMutationListener::EnteringModulePurview() { + for (auto *L : Listeners) + L->EnteringModulePurview(); +} + +void MultiplexASTMutationListener::AddedManglingNumber(const Decl *D, + unsigned Number) { + for (auto *L : Listeners) + L->AddedManglingNumber(D, Number); +} +void MultiplexASTMutationListener::AddedStaticLocalNumbers(const Decl *D, + unsigned Number) { + for (auto *L : Listeners) + L->AddedStaticLocalNumbers(D, Number); +} +void MultiplexASTMutationListener::AddedAnonymousNamespace( + const TranslationUnitDecl *TU, NamespaceDecl *AnonNamespace) { + for (auto *L : Listeners) + L->AddedAnonymousNamespace(TU, AnonNamespace); +} + } // end namespace clang MultiplexConsumer::MultiplexConsumer( diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 2ddf9d70263a0..67658c93ed3ba 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTMutationListener.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaInternal.h" @@ -475,6 +476,9 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, getASTContext().setCurrentNamedModule(Mod); + if (auto *Listener = getASTMutationListener()) + Listener->EnteringModulePurview(); + // We already potentially made an implicit import (in the case of a module // implementation unit importing its interface). Make this module visible // and return the import decl to be added to the current TU. diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index b2a078b6d80f4..c3cf242391b74 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -5026,27 +5026,6 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, GetDeclRef(D); } - // If the translation unit has an anonymous namespace, and we don't already - // have an update block for it, write it as an update block. - // FIXME: Why do we not do this if there's already an update block? - if (NamespaceDecl *NS = TU->getAnonymousNamespace()) { - ASTWriter::UpdateRecord &Record = DeclUpdates[TU]; - if (Record.empty()) - Record.push_back(DeclUpdate(UPD_CXX_ADDED_ANONYMOUS_NAMESPACE, NS)); - } - - // Add update records for all mangling numbers and static local numbers. - // These aren't really update records, but this is a convenient way of - // tagging this rare extra data onto the declarations. - for (const auto &Number : Context.MangleNumbers) - if (!Number.first->isFromASTFile()) - DeclUpdates[Number.first].push_back(DeclUpdate(UPD_MANGLING_NUMBER, - Number.second)); - for (const auto &Number : Context.StaticLocalNumbers) - if (!Number.first->isFromASTFile()) - DeclUpdates[Number.first].push_back(DeclUpdate(UPD_STATIC_LOCAL_NUMBER, - Number.second)); - // Make sure visible decls, added to DeclContexts previously loaded from // an AST file, are registered for serialization. Likewise for template // specializations added to imported templates. @@ -5315,6 +5294,41 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, return backpatchSignature(); } +void ASTWriter::EnteringModulePurview() { + // In C++20 named modules, all entities before entering the module purview + // lives in the GMF. + if (GeneratingReducedBMI) + DeclUpdatesFromGMF.swap(DeclUpdates); +} + +// Add update records for all mangling numbers and static local numbers. +// These aren't really update records, but this is a convenient way of +// tagging this rare extra data onto the declarations. +void ASTWriter::AddedManglingNumber(const Decl *D, unsigned Number) { + if (D->isFromASTFile()) + return; + + DeclUpdates[D].push_back(DeclUpdate(UPD_MANGLING_NUMBER, Number)); +} +void ASTWriter::AddedStaticLocalNumbers(const Decl *D, unsigned Number) { + if (D->isFromASTFile()) + return; + + DeclUpdates[D].push_back(DeclUpdate(UPD_STATIC_LOCAL_NUMBER, Number)); +} + +void ASTWriter::AddedAnonymousNamespace(const TranslationUnitDecl *TU, + NamespaceDecl *AnonNamespace) { + // If the translation unit has an anonymous namespace, and we don't already + // have an update block for it, write it as an update block. + // FIXME: Why do we not do this if there's already an update block? + if (NamespaceDecl *NS = TU->getAnonymousNamespace()) { + ASTWriter::UpdateRecord &Record = DeclUpdates[TU]; + if (Record.empty()) + Record.push_back(DeclUpdate(UPD_CXX_ADDED_ANONYMOUS_NAMESPACE, NS)); + } +} + void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { // Keep writing types, declarations, and declaration update records // until we've emitted all of them. @@ -5860,6 +5874,14 @@ DeclID ASTWriter::GetDeclRef(const Decl *D) { return 0; } + // If the DeclUpdate from the GMF gets touched, emit it. + if (auto *Iter = DeclUpdatesFromGMF.find(D); + Iter != DeclUpdatesFromGMF.end()) { + for (DeclUpdate &Update : Iter->second) + DeclUpdates[D].push_back(Update); + DeclUpdatesFromGMF.erase(Iter); + } + // If D comes from an AST file, its declaration ID is already known and // fixed. if (D->isFromASTFile()) diff --git a/clang/test/Modules/reduced-bmi-empty-module-purview.cppm b/clang/test/Modules/reduced-bmi-empty-module-purview.cppm new file mode 100644 index 0000000000000..54a1e9b77e6e6 --- /dev/null +++ b/clang/test/Modules/reduced-bmi-empty-module-purview.cppm @@ -0,0 +1,96 @@ +// Test that we won't write additional information into the Reduced BMI if the +// module purview is empty. +// +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-reduced-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-reduced-module-interface -o %t/A.pcm \ +// RUN: -fmodule-file=M=%t/M.pcm +// RUN: llvm-bcanalyzer --dump --disable-histogram --show-binary-blobs %t/A.pcm > %t/A.dump +// RUN: cat %t/A.dump | FileCheck %t/A.cppm +// +// RUN: %clang_cc1 -std=c++20 %t/A1.cppm -emit-reduced-module-interface -o %t/A1.pcm \ +// RUN: -fmodule-file=M=%t/M.pcm +// RUN: llvm-bcanalyzer --dump --disable-histogram --show-binary-blobs %t/A1.pcm > %t/A1.dump +// RUN: cat %t/A1.dump | FileCheck %t/A1.cppm + +//--- foo.h +namespace ns { +template +class A { + +}; + +extern template class A; + +inline A a() { return A(); } +template +A _av_ = A(); + +auto _av_1 = _av_; +auto _av_2 = _av_; + +template <> +class A { + +}; + +void func(A, ...) { + +} + +} + +struct S { + union { + unsigned int V; + struct { + int v1; + int v2; + ns::A a1; + } WESQ; + }; + + union { + double d; + struct { + int v1; + unsigned v2; + ns::A a1; + } Another; + }; +}; + +//--- M.cppm +module; +#include "foo.h" +export module M; +export namespace nv { + using ns::A; + using ns::a; + using ns::_av_; + + using ns::func; +} +using ::S; + +//--- A.cppm +module; +#include "foo.h" +export module A; +import M; + +// CHECK-NOT: