Author: Chuanqi Xu Date: 2024-04-12T12:51:58+08:00 New Revision: f21ead06750b670cf8ce72d6666e3550b04056a2
URL: https://github.com/llvm/llvm-project/commit/f21ead06750b670cf8ce72d6666e3550b04056a2 DIFF: https://github.com/llvm/llvm-project/commit/f21ead06750b670cf8ce72d6666e3550b04056a2.diff LOG: [C++20] [Modules] [Reduced BMI] Remove unreachable decls GMF in redued BMI (#88359) Following of https://github.com/llvm/llvm-project/pull/76930 This follows the idea of "only writes what we writes", which I think is the most natural and efficient way to implement this optimization. We start writing the BMI from the first declaration in module purview instead of the global module fragment, so that everything in the GMF untouched won't be written in the BMI naturally. The exception is, as I said in https://github.com/llvm/llvm-project/pull/76930, when we write a declaration we need to write its decl context, and when we write the decl context, we need to write everything from it. So when we see `std::vector`, we basically need to write everything under namespace std. This violates our intention. To fix this, this patch delays the writing of namespace in the GMF. >From my local measurement, the size of the BMI decrease to 90M from 112M for a local modules build. I think this is significant. This feature will be covered under the experimental reduced BMI so that it won't affect any existing users. So I'd like to land this when the CI gets green. Documents will be added seperately. Added: clang/test/CXX/module/module.glob.frag/cxx20-10-4-ex2.cppm Modified: clang/include/clang/Serialization/ASTBitCodes.h clang/include/clang/Serialization/ASTReader.h clang/include/clang/Serialization/ASTWriter.h clang/lib/Serialization/ASTReader.cpp clang/lib/Serialization/ASTReaderDecl.cpp clang/lib/Serialization/ASTWriter.cpp clang/lib/Serialization/ASTWriterDecl.cpp clang/test/Modules/inconsistent-deduction-guide-linkage.cppm clang/test/Modules/named-modules-adl-2.cppm clang/test/Modules/named-modules-adl.cppm clang/test/Modules/placement-new-reachable.cpp clang/test/Modules/polluted-operator.cppm clang/test/Modules/pr62589.cppm clang/test/Modules/preferred_name.cppm clang/test/Modules/redundant-template-default-arg3.cpp clang/test/Modules/template-function-specialization.cpp Removed: ################################################################################ diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index f762116fea956c..500098dd3dab1d 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -698,6 +698,10 @@ enum ASTRecordTypes { /// Record code for an unterminated \#pragma clang assume_nonnull begin /// recorded in a preamble. PP_ASSUME_NONNULL_LOC = 67, + + /// Record code for lexical and visible block for delayed namespace in + /// reduced BMI. + DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD = 68, }; /// Record types used within a source manager block. diff --git a/clang/include/clang/Serialization/ASTReader.h b/clang/include/clang/Serialization/ASTReader.h index e8b9f28690d9fa..6656c1c58dec9d 100644 --- a/clang/include/clang/Serialization/ASTReader.h +++ b/clang/include/clang/Serialization/ASTReader.h @@ -517,6 +517,20 @@ class ASTReader /// in the chain. DeclUpdateOffsetsMap DeclUpdateOffsets; + using DelayedNamespaceOffsetMapTy = llvm::DenseMap< + serialization::DeclID, + std::pair</*LexicalOffset*/ uint64_t, /*VisibleOffset*/ uint64_t>>; + + /// Mapping from global declaration IDs to the lexical and visible block + /// offset for delayed namespace in reduced BMI. + /// + /// We can't use the existing DeclUpdate mechanism since the DeclUpdate + /// may only be applied in an outer most read. However, we need to know + /// whether or not a DeclContext has external storage during the recursive + /// reading. So we need to apply the offset immediately after we read the + /// namespace as if it is not delayed. + DelayedNamespaceOffsetMapTy DelayedNamespaceOffsetMap; + struct PendingUpdateRecord { Decl *D; serialization::GlobalDeclID ID; diff --git a/clang/include/clang/Serialization/ASTWriter.h b/clang/include/clang/Serialization/ASTWriter.h index 214eb3601148b0..443f7703104700 100644 --- a/clang/include/clang/Serialization/ASTWriter.h +++ b/clang/include/clang/Serialization/ASTWriter.h @@ -201,6 +201,16 @@ class ASTWriter : public ASTDeserializationListener, /// The declarations and types to emit. std::queue<DeclOrType> DeclTypesToEmit; + /// The delayed namespace to emit. Only meaningful for reduced BMI. + /// + /// In reduced BMI, we want to elide the unreachable declarations in + /// the global module fragment. However, in ASTWriterDecl, when we see + /// a namespace, all the declarations in the namespace would be emitted. + /// So the optimization become meaningless. To solve the issue, we + /// delay recording all the declarations until we emit all the declarations. + /// Then we can safely record the reached declarations only. + llvm::SmallVector<NamespaceDecl *, 16> DelayedNamespace; + /// The first ID number we can use for our own declarations. serialization::DeclID FirstDeclID = serialization::NUM_PREDEF_DECL_IDS; @@ -529,7 +539,8 @@ class ASTWriter : public ASTDeserializationListener, void WriteType(QualType T); bool isLookupResultExternal(StoredDeclsList &Result, DeclContext *DC); - bool isLookupResultEntirelyExternal(StoredDeclsList &Result, DeclContext *DC); + bool isLookupResultEntirelyExternalOrUnreachable(StoredDeclsList &Result, + DeclContext *DC); void GenerateNameLookupTable(const DeclContext *DC, llvm::SmallVectorImpl<char> &LookupTable); @@ -704,6 +715,15 @@ class ASTWriter : public ASTDeserializationListener, /// declaration. serialization::DeclID getDeclID(const Decl *D); + /// Whether or not the declaration got emitted. If not, it wouldn't be + /// emitted. + /// + /// This may only be called after we've done the job to write the + /// declarations (marked by DoneWritingDeclsAndTypes). + /// + /// A declaration may only be omitted in reduced BMI. + bool wasDeclEmitted(const Decl *D) const; + unsigned getAnonymousDeclarationNumber(const NamedDecl *D); /// Add a string to the given record. @@ -798,6 +818,10 @@ class ASTWriter : public ASTDeserializationListener, return WritingModule && WritingModule->isNamedModule(); } + bool isGeneratingReducedBMI() const { return GeneratingReducedBMI; } + + bool getDoneWritingDeclsAndTypes() const { return DoneWritingDeclsAndTypes; } + private: // ASTDeserializationListener implementation void ReaderInitialized(ASTReader *Reader) override; diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp index 4f6987f92fc82e..ce96ce2bdbcce6 100644 --- a/clang/lib/Serialization/ASTReader.cpp +++ b/clang/lib/Serialization/ASTReader.cpp @@ -3795,6 +3795,29 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F, } break; + case DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD: { + if (Record.size() % 3 != 0) + return llvm::createStringError( + std::errc::illegal_byte_sequence, + "invalid DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD block in AST " + "file"); + for (unsigned I = 0, N = Record.size(); I != N; I += 3) { + GlobalDeclID ID = getGlobalDeclID(F, Record[I]); + + uint64_t BaseOffset = F.DeclsBlockStartOffset; + assert(BaseOffset && "Invalid DeclsBlockStartOffset for module file!"); + uint64_t LexicalOffset = Record[I + 1] ? BaseOffset + Record[I + 1] : 0; + uint64_t VisibleOffset = Record[I + 2] ? BaseOffset + Record[I + 2] : 0; + + DelayedNamespaceOffsetMap[ID] = {LexicalOffset, VisibleOffset}; + + assert(!GetExistingDecl(ID) && + "We shouldn't load the namespace in the front of delayed " + "namespace lexical and visible block"); + } + break; + } + case OBJC_CATEGORIES_MAP: if (F.LocalNumObjCCategoriesInMap != 0) return llvm::createStringError( diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 5b7b9f19a106c3..ff72fc30c718f8 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -4125,6 +4125,15 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // offsets for its tables of lexical and visible declarations. if (auto *DC = dyn_cast<DeclContext>(D)) { std::pair<uint64_t, uint64_t> Offsets = Reader.VisitDeclContext(DC); + + // Get the lexical and visible block for the delayed namespace. + // It is sufficient to judge if ID is in DelayedNamespaceOffsetMap. + // But it may be more efficient to filter the other cases. + if (!Offsets.first && !Offsets.second && isa<NamespaceDecl>(D)) + if (auto Iter = DelayedNamespaceOffsetMap.find(ID); + Iter != DelayedNamespaceOffsetMap.end()) + Offsets = Iter->second; + if (Offsets.first && ReadLexicalDeclContextStorage(*Loc.F, DeclsCursor, Offsets.first, DC)) return nullptr; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index 330dbd6233502f..d9ba10ab608783 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -869,6 +869,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(WEAK_UNDECLARED_IDENTIFIERS); RECORD(PENDING_IMPLICIT_INSTANTIATIONS); RECORD(UPDATE_VISIBLE); + RECORD(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD); RECORD(DECL_UPDATE_OFFSETS); RECORD(DECL_UPDATES); RECORD(CUDA_SPECIAL_DECL_REFS); @@ -3026,10 +3027,12 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Stream.EmitRecordWithBlob(ConfigMacroAbbrev, Record, CM); } - // Emit the initializers, if any. + // Emit the reachable initializers. + // The initializer may only be unreachable in reduced BMI. RecordData Inits; for (Decl *D : Context->getModuleInitializers(Mod)) - Inits.push_back(GetDeclRef(D)); + if (wasDeclEmitted(D)) + Inits.push_back(GetDeclRef(D)); if (!Inits.empty()) Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits); @@ -3208,6 +3211,9 @@ uint64_t ASTWriter::WriteDeclContextLexicalBlock(ASTContext &Context, uint64_t Offset = Stream.GetCurrentBitNo(); SmallVector<uint32_t, 128> KindDeclPairs; for (const auto *D : DC->decls()) { + if (DoneWritingDeclsAndTypes && !wasDeclEmitted(D)) + continue; + KindDeclPairs.push_back(D->getKind()); KindDeclPairs.push_back(GetDeclRef(D)); } @@ -3862,8 +3868,14 @@ class ASTDeclContextNameLookupTrait { data_type getData(const Coll &Decls) { unsigned Start = DeclIDs.size(); for (NamedDecl *D : Decls) { - DeclIDs.push_back( - Writer.GetDeclRef(getDeclForLocalLookup(Writer.getLangOpts(), D))); + NamedDecl *DeclForLocalLookup = + getDeclForLocalLookup(Writer.getLangOpts(), D); + + if (Writer.getDoneWritingDeclsAndTypes() && + !Writer.wasDeclEmitted(DeclForLocalLookup)) + continue; + + DeclIDs.push_back(Writer.GetDeclRef(DeclForLocalLookup)); } return std::make_pair(Start, DeclIDs.size()); } @@ -3972,11 +3984,20 @@ bool ASTWriter::isLookupResultExternal(StoredDeclsList &Result, DC->hasNeedToReconcileExternalVisibleStorage(); } -bool ASTWriter::isLookupResultEntirelyExternal(StoredDeclsList &Result, - DeclContext *DC) { - for (auto *D : Result.getLookupResult()) - if (!getDeclForLocalLookup(getLangOpts(), D)->isFromASTFile()) - return false; +bool ASTWriter::isLookupResultEntirelyExternalOrUnreachable( + StoredDeclsList &Result, DeclContext *DC) { + for (auto *D : Result.getLookupResult()) { + auto *LocalD = getDeclForLocalLookup(getLangOpts(), D); + if (LocalD->isFromASTFile()) + continue; + + // We can only be sure whether the local declaration is reachable + // after we done writing the declarations and types. + if (DoneWritingDeclsAndTypes && !wasDeclEmitted(LocalD)) + continue; + + return false; + } return true; } @@ -4014,8 +4035,17 @@ ASTWriter::GenerateNameLookupTable(const DeclContext *ConstDC, // don't need to write an entry for the name at all. If we can't // write out a lookup set without performing more deserialization, // just skip this entry. - if (isLookupResultExternal(Result, DC) && - isLookupResultEntirelyExternal(Result, DC)) + // + // Also in reduced BMI, we'd like to avoid writing unreachable + // declarations in GMF, so we need to avoid writing declarations + // that entirely external or unreachable. + // + // FIMXE: It looks sufficient to test + // isLookupResultEntirelyExternalOrUnreachable here. But due to bug we have + // to test isLookupResultExternal here. See + // https://github.com/llvm/llvm-project/issues/61065 for details. + if ((GeneratingReducedBMI || isLookupResultExternal(Result, DC)) && + isLookupResultEntirelyExternalOrUnreachable(Result, DC)) continue; // We also skip empty results. If any of the results could be external and @@ -4206,9 +4236,15 @@ uint64_t ASTWriter::WriteDeclContextVisibleBlock(ASTContext &Context, continue; } - for (NamedDecl *ND : Result) - if (!ND->isFromASTFile()) - GetDeclRef(ND); + for (NamedDecl *ND : Result) { + if (ND->isFromASTFile()) + continue; + + if (DoneWritingDeclsAndTypes && !wasDeclEmitted(ND)) + continue; + + GetDeclRef(ND); + } } return 0; @@ -4976,9 +5012,18 @@ ASTFileSignature ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, const TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); // Force all top level declarations to be emitted. - for (const auto *D : TU->noload_decls()) - if (!D->isFromASTFile()) - GetDeclRef(D); + // + // We start emitting top level declarations from the module purview to + // implement the eliding unreachable declaration feature. + for (const auto *D : TU->noload_decls()) { + if (D->isFromASTFile()) + continue; + + if (GeneratingReducedBMI && D->isFromExplicitGlobalModule()) + continue; + + 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. @@ -5288,24 +5333,59 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) { WriteDecl(Context, DOT.getDecl()); } } while (!DeclUpdates.empty()); - Stream.ExitBlock(); DoneWritingDeclsAndTypes = true; + // DelayedNamespace is only meaningful in reduced BMI. + // See the comments of DelayedNamespace for details. + assert(DelayedNamespace.empty() || GeneratingReducedBMI); + RecordData DelayedNamespaceRecord; + for (NamespaceDecl *NS : DelayedNamespace) { + uint64_t LexicalOffset = WriteDeclContextLexicalBlock(Context, NS); + uint64_t VisibleOffset = WriteDeclContextVisibleBlock(Context, NS); + + // Write the offset relative to current block. + if (LexicalOffset) + LexicalOffset -= DeclTypesBlockStartOffset; + + if (VisibleOffset) + VisibleOffset -= DeclTypesBlockStartOffset; + + DelayedNamespaceRecord.push_back(getDeclID(NS)); + DelayedNamespaceRecord.push_back(LexicalOffset); + DelayedNamespaceRecord.push_back(VisibleOffset); + } + + // The process of writing lexical and visible block for delayed namespace + // shouldn't introduce any new decls, types or update to emit. + assert(DeclTypesToEmit.empty()); + assert(DeclUpdates.empty()); + + Stream.ExitBlock(); + // These things can only be done once we've written out decls and types. WriteTypeDeclOffsets(); if (!DeclUpdatesOffsetsRecord.empty()) Stream.EmitRecord(DECL_UPDATE_OFFSETS, DeclUpdatesOffsetsRecord); + if (!DelayedNamespaceRecord.empty()) + Stream.EmitRecord(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD, + DelayedNamespaceRecord); + const TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); // Create a lexical update block containing all of the declarations in the // translation unit that do not come from other AST files. SmallVector<uint32_t, 128> NewGlobalKindDeclPairs; for (const auto *D : TU->noload_decls()) { - if (!D->isFromASTFile()) { - NewGlobalKindDeclPairs.push_back(D->getKind()); - NewGlobalKindDeclPairs.push_back(GetDeclRef(D)); - } + if (D->isFromASTFile()) + continue; + + // In reduced BMI, skip unreached declarations. + if (!wasDeclEmitted(D)) + continue; + + NewGlobalKindDeclPairs.push_back(D->getKind()); + NewGlobalKindDeclPairs.push_back(GetDeclRef(D)); } auto Abv = std::make_shared<llvm::BitCodeAbbrev>(); @@ -5814,6 +5894,21 @@ DeclID ASTWriter::getDeclID(const Decl *D) { return DeclIDs[D]; } +bool ASTWriter::wasDeclEmitted(const Decl *D) const { + assert(D); + + assert(DoneWritingDeclsAndTypes && + "wasDeclEmitted should only be called after writing declarations"); + + if (D->isFromASTFile()) + return true; + + bool Emitted = DeclIDs.contains(D); + assert((Emitted || GeneratingReducedBMI) && + "The declaration can only be omitted in reduced BMI."); + return Emitted; +} + void ASTWriter::associateDeclWithFile(const Decl *D, DeclID ID) { assert(ID); assert(D); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 87e773be54fae9..4b417909751fb1 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1719,6 +1719,15 @@ void ASTDeclWriter::VisitClassTemplateDecl(ClassTemplateDecl *D) { if (D->isFirstDecl()) AddTemplateSpecializations(D); + + // Force emitting the corresponding deduction guide in reduced BMI mode. + // Otherwise, the deduction guide may be optimized out incorrectly. + if (Writer.isGeneratingReducedBMI()) { + auto Name = Context.DeclarationNames.getCXXDeductionGuideName(D); + for (auto *DG : D->getDeclContext()->noload_lookup(Name)) + Writer.GetDeclRef(DG); + } + Code = serialization::DECL_CLASS_TEMPLATE; } @@ -1963,8 +1972,22 @@ void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { "You need to update the serializer after you change the " "DeclContextBits"); - Record.AddOffset(Writer.WriteDeclContextLexicalBlock(Context, DC)); - Record.AddOffset(Writer.WriteDeclContextVisibleBlock(Context, DC)); + uint64_t LexicalOffset = 0; + uint64_t VisibleOffset = 0; + + if (Writer.isGeneratingReducedBMI() && isa<NamespaceDecl>(DC) && + cast<NamespaceDecl>(DC)->isFromExplicitGlobalModule()) { + // In reduced BMI, delay writing lexical and visible block for namespace + // in the global module fragment. See the comments of DelayedNamespace for + // details. + Writer.DelayedNamespace.push_back(cast<NamespaceDecl>(DC)); + } else { + LexicalOffset = Writer.WriteDeclContextLexicalBlock(Context, DC); + VisibleOffset = Writer.WriteDeclContextVisibleBlock(Context, DC); + } + + Record.AddOffset(LexicalOffset); + Record.AddOffset(VisibleOffset); } const Decl *ASTWriter::getFirstLocalDecl(const Decl *D) { diff --git a/clang/test/CXX/module/module.glob.frag/cxx20-10-4-ex2.cppm b/clang/test/CXX/module/module.glob.frag/cxx20-10-4-ex2.cppm new file mode 100644 index 00000000000000..edb208cdf4de4b --- /dev/null +++ b/clang/test/CXX/module/module.glob.frag/cxx20-10-4-ex2.cppm @@ -0,0 +1,58 @@ +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 %t/std-10-4-ex2-interface.cppm -emit-reduced-module-interface \ +// RUN: -o %t/M.pcm -Wno-unused-value +// RUN: %clang_cc1 -std=c++20 %t/std-10-4-ex2-implementation.cpp -fmodule-file=M=%t/M.pcm \ +// RUN: -fsyntax-only -verify -Wno-unused-value + +//--- std-10-4-ex2.h + +namespace N { +struct X {}; +int d(); +int e(); +inline int f(X, int = d()) { return e(); } +int g(X); +int h(X); +} // namespace N + +//--- std-10-4-ex2-interface.cppm +module; +#include "std-10-4-ex2.h" +export module M; + +template <typename T> int use_f() { + N::X x; // N::X, N, and :: are decl-reachable from use_f + return f(x, 123); // N::f is decl-reachable from use_f, + // N::e is indirectly decl-reachable from use_f + // because it is decl-reachable from N::f, and + // N::d is decl-reachable from use_f + // because it is decl-reachable from N::f + // even though it is not used in this call +} + +template <typename T> int use_g() { + N::X x; // N::X, N, and :: are decl-reachable from use_g + return g((T(), x)); // N::g is not decl-reachable from use_g +} + +template <typename T> int use_h() { + N::X x; // N::X, N, and :: are decl-reachable from use_h + return h((T(), x)); // N::h is not decl-reachable from use_h, but + // N::h is decl-reachable from use_h<int> +} + +int k = use_h<int>(); +// use_h<int> is decl-reachable from k, so +// N::h is decl-reachable from k + +//--- std-10-4-ex2-implementation.cpp +module M; + +int a = use_f<int>(); +int b = use_g<int>(); +// expected-er...@std-10-4-ex2-interface.cppm:17 {{use of undeclared identifier 'g'}} +// expected-note@-2 {{in instantiation of function template specialization 'use_g<int>' requested here}} +int c = use_h<int>(); diff --git a/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm b/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm index 3991e47ce21513..eaf033d1d92839 100644 --- a/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm +++ b/clang/test/Modules/inconsistent-deduction-guide-linkage.cppm @@ -26,6 +26,10 @@ module; #include "C.h" export module B; +namespace foo { + export using foo::bar; +} + //--- C.h namespace foo { template<class T, class U> struct bar { // expected-error {{declaration of 'bar' in module baz:A follows declaration in the global module}} // expected-note {{previous declaration is here}} @@ -40,6 +44,10 @@ module; #include "E.h" export module D; +namespace foo { + export using foo::bar; +} + //--- D-part.cppm export module D:part; import D; diff --git a/clang/test/Modules/named-modules-adl-2.cppm b/clang/test/Modules/named-modules-adl-2.cppm index a14b9a68d74e41..88f58d85bc7c7b 100644 --- a/clang/test/Modules/named-modules-adl-2.cppm +++ b/clang/test/Modules/named-modules-adl-2.cppm @@ -7,7 +7,7 @@ // RUN: %clang_cc1 -std=c++20 %t/c.cppm -fmodule-file=a=%t/a.pcm -fmodule-file=b=%t/b.pcm -fsyntax-only -verify // RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm -// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -emit-reduced-module-interface -o %t/b.pcm +// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -emit-reduced-module-interface -o %t/b.pcm -DREDUCED // RUN: %clang_cc1 -std=c++20 %t/c.cppm -fmodule-file=a=%t/a.pcm -fmodule-file=b=%t/b.pcm -fsyntax-only -verify //--- a.cppm @@ -35,6 +35,11 @@ void b() { a(s()); } +#ifdef REDUCED +// Mention it to avoid the compiler optimizing it out. +using ::operator+; +#endif + //--- c.cppm // expected-no-diagnostics export module c; diff --git a/clang/test/Modules/named-modules-adl.cppm b/clang/test/Modules/named-modules-adl.cppm index ef250023f91e75..079d816200b2c7 100644 --- a/clang/test/Modules/named-modules-adl.cppm +++ b/clang/test/Modules/named-modules-adl.cppm @@ -5,7 +5,7 @@ // RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify -// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm +// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-reduced-module-interface -o %t/a.pcm -DREDUCED // RUN: %clang_cc1 -std=c++20 %t/b.cppm -fmodule-file=a=%t/a.pcm -fsyntax-only -verify //--- a.h @@ -28,6 +28,11 @@ void a(T x) { n::s() + x; } +#ifdef REDUCED +// Use it to make sure it is not optimized out in reduced BMI. +using n::operator+; +#endif + //--- b.cppm // expected-no-diagnostics export module b; diff --git a/clang/test/Modules/placement-new-reachable.cpp b/clang/test/Modules/placement-new-reachable.cpp index 6b495a60306bc1..2440294704742b 100644 --- a/clang/test/Modules/placement-new-reachable.cpp +++ b/clang/test/Modules/placement-new-reachable.cpp @@ -31,12 +31,17 @@ export struct B { void *ptr; }; +// The use of operator new in the current module unit is only in the non-inline +// function definitions. So it may be optimized out. +using ::operator new; + //--- Use.cpp // expected-no-diagnostics import A; void bar(int *); void foo(void *ptr) { - A<int>(nullptr); // Good. It should be OK to construct A. - void *p = ::operator new(sizeof(int), ptr); // Bad. The function shouldn't be visible here. + A<int> a(nullptr); // Good. It should be OK to construct A. + B b(nullptr); + void *p = ::operator new(sizeof(int), ptr); // Bad. The placement allocation in module A is not visible. void *q = new (ptr) int(43); // Good. We don't call the placement allocation function directly. } diff --git a/clang/test/Modules/polluted-operator.cppm b/clang/test/Modules/polluted-operator.cppm index 2179fa098064ae..45cc5e37d6a640 100644 --- a/clang/test/Modules/polluted-operator.cppm +++ b/clang/test/Modules/polluted-operator.cppm @@ -12,7 +12,8 @@ // RUN: -emit-module-interface -DSKIP_ODR_CHECK_IN_GMF -o %t/b.pcm -verify // RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/a.cppm -o %t/a.pcm -// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -emit-reduced-module-interface -o %t/b.pcm -verify +// RUN: %clang_cc1 -std=c++20 %t/b.cppm -fprebuilt-module-path=%t -emit-reduced-module-interface \ +// RUN: -o %t/b.pcm -verify -DREDUCED //--- foo.h @@ -53,18 +54,26 @@ module; #include "foo.h" #include "bar.h" export module a; +export namespace std { + using std::variant; + using std::_Traits; + using std::operator&&; +} //--- b.cppm module; #include "bar.h" export module b; import a; +export namespace std { + using std::variant; + using std::_Traits; + using std::operator&&; +} #ifdef SKIP_ODR_CHECK_IN_GMF // expected-no-diagnostics #else // expected-error@* {{has diff erent definitions in diff erent modules; first diff erence is defined here found data member '_S_copy_ctor' with an initializer}} // expected-note@* {{but in 'a.<global>' found data member '_S_copy_ctor' with a diff erent initializer}} -// expected-error@* {{from module 'a.<global>' is not present in definition of 'variant<_Types...>' provided earlier}} -// expected-note@* {{declaration of 'swap' does not match}} #endif diff --git a/clang/test/Modules/pr62589.cppm b/clang/test/Modules/pr62589.cppm index c5aec3ed81846f..54f2ecef22e18d 100644 --- a/clang/test/Modules/pr62589.cppm +++ b/clang/test/Modules/pr62589.cppm @@ -73,6 +73,10 @@ export module a; export using ::a; export using ::a_view; +// We need to mention the 'operator==' explicitly to make sure it won't be +// discarded. +export using ::operator==; + //--- b.cpp // expected-no-diagnostics import a; diff --git a/clang/test/Modules/preferred_name.cppm b/clang/test/Modules/preferred_name.cppm index 2f17058678455c..806781a81c5ca7 100644 --- a/clang/test/Modules/preferred_name.cppm +++ b/clang/test/Modules/preferred_name.cppm @@ -42,6 +42,7 @@ inline foo_templ<char> bar() module; #include "foo.h" export module A; +export using ::foo_templ; //--- Use.cppm // expected-no-diagnostics @@ -49,6 +50,7 @@ module; #include "foo.h" export module Use; import A; +export using ::foo_templ; //--- Use1.cpp import A; // expected-warning@foo.h:8 {{attribute declaration must precede definition}} diff --git a/clang/test/Modules/redundant-template-default-arg3.cpp b/clang/test/Modules/redundant-template-default-arg3.cpp index e4464c40e97687..da6fe01a5357bf 100644 --- a/clang/test/Modules/redundant-template-default-arg3.cpp +++ b/clang/test/Modules/redundant-template-default-arg3.cpp @@ -91,6 +91,16 @@ int v9; module; #include "foo.h" export module foo; +export using ::v; +export using ::v2; +export using ::my_array; +export using ::v3; +export using ::v4; +export using ::v5; +export using ::v6; +export using ::v7; +export using ::v8; +export using ::v9; //--- use.cpp import foo; diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp index 1b6bf2de6ba1d9..d5d7d7e8123988 100644 --- a/clang/test/Modules/template-function-specialization.cpp +++ b/clang/test/Modules/template-function-specialization.cpp @@ -6,7 +6,7 @@ // RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only // RUN: %clang_cc1 -std=c++20 -emit-reduced-module-interface %t/foo.cppm -o %t/foo.pcm -// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/Use.cpp -verify -fsyntax-only -DREDUCED //--- foo.cppm module; @@ -51,10 +51,16 @@ import foo; void use() { foo<short>(); foo<int>(); +#ifdef REDUCED + // In reduced BMI, the foo2 template function is optimized out. + foo2<short>(); // expected-error {{use of undeclared identifier 'foo2'}} + foo2<int>(); // expected-error {{use of undeclared identifier 'foo2'}} +#else foo2<short>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} // expected-note@* {{declaration here is not visible}} foo2<int>(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} // expected-note@* {{declaration here is not visible}} +#endif foo3<short>(); foo3<int>(); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits