diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 1d223f47a9aec0052ade4b207dc19139788683a2..a55762622e5dfa58106817e888914f1ae9b89afd 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -312,6 +312,18 @@ class ASTContext : public RefCountedBase<ASTContext> { /// definitions of that entity. llvm::DenseMap<NamedDecl*, llvm::TinyPtrVector<Module*>> MergedDefModules; + /// \brief Initializers for a module, in order. Each Decl will be either + /// something that has a semantic effect on startup (such as a variable with + /// a non-constant initializer), or an ImportDecl (which recursively triggers + /// initialization of another module). + struct PerModuleInitializers { + llvm::SmallVector<Decl*, 4> Initializers; + llvm::SmallVector<uint32_t, 4> LazyInitializers; + + void resolve(ASTContext &Ctx); + }; + llvm::DenseMap<Module*, PerModuleInitializers*> ModuleInitializers; + public: /// \brief A type synonym for the TemplateOrInstantiation mapping. typedef llvm::PointerUnion<VarTemplateDecl *, MemberSpecializationInfo *> @@ -883,6 +895,17 @@ public: return MergedIt->second; } + /// Add a declaration to the list of declarations that are initialized + /// for a module. This will typically be a global variable (with internal + /// linkage) that runs module initializers, such as the iostream initializer, + /// or an ImportDecl nominating another module that has initializers. + void addModuleInitializer(Module *M, Decl *Init); + + void addLazyModuleInitializers(Module *M, ArrayRef<uint32_t> IDs); + + /// Get the initializations to perform when importing a module, if any. + ArrayRef<Decl*> getModuleInitializers(Module *M); + TranslationUnitDecl *getTranslationUnitDecl() const { return TUDecl; } ExternCContextDecl *getExternCContextDecl() const; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 25aa805dac4bd60935bf37b49a9e43a5eae5f9ef..93c413597a900b3e13493042387ea59b8be4da91 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1390,8 +1390,14 @@ private: bool RequireCompleteTypeImpl(SourceLocation Loc, QualType T, TypeDiagnoser *Diagnoser); + struct ModuleScope { + clang::Module *Module; + VisibleModuleSet OuterVisibleModules; + }; + /// The modules we're currently parsing. + llvm::SmallVector<ModuleScope, 16> ModuleScopes; + VisibleModuleSet VisibleModules; - llvm::SmallVector<VisibleModuleSet, 16> VisibleModulesStack; Module *CachedFakeTopLevelModule; diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index 79c6a062225705cc6ee92b6bc969be280966a964..4ce7e21bc4736b6bbb4f9d72ee928cf7b958d752 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -684,6 +684,9 @@ namespace clang { /// \brief Specifies a header that is private to this submodule but /// must be textually included. SUBMODULE_PRIVATE_TEXTUAL_HEADER = 15, + /// \brief Specifies some declarations with initializers that must be + /// emitted to initialize the module. + SUBMODULE_INITIALIZERS = 16, }; /// \brief Record types used within a comments block. diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index e54c88fc054b1d20f08330f1614094e29247a446..fadcda0c4b00e4bba8bee8cf28eff341a36dc3ae 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -901,6 +901,67 @@ void ASTContext::deduplicateMergedDefinitonsFor(NamedDecl *ND) { Merged.erase(std::remove(Merged.begin(), Merged.end(), nullptr), Merged.end()); } +void ASTContext::PerModuleInitializers::resolve(ASTContext &Ctx) { + if (LazyInitializers.empty()) + return; + + auto *Source = Ctx.getExternalSource(); + assert(Source && "lazy initializers but no external source"); + + auto LazyInits = std::move(LazyInitializers); + LazyInitializers.clear(); + + for (auto ID : LazyInits) + Initializers.push_back(Source->GetExternalDecl(ID)); + + assert(LazyInitializers.empty() && + "GetExternalDecl for lazy module initializer added more inits"); +} + +void ASTContext::addModuleInitializer(Module *M, Decl *D) { + // One special case: if we add a module initializer that imports another + // module, and that module's only initializer is an ImportDecl, simplify. + if (auto *ID = dyn_cast<ImportDecl>(D)) { + auto It = ModuleInitializers.find(ID->getImportedModule()); + + // Maybe the ImportDecl does nothing at all. (Common case.) + if (It == ModuleInitializers.end()) + return; + + // Maybe the ImportDecl only imports another ImportDecl. + auto &Imported = *It->second; + if (Imported.Initializers.size() + Imported.LazyInitializers.size() == 1) { + Imported.resolve(*this); + auto *OnlyDecl = Imported.Initializers.front(); + if (isa<ImportDecl>(OnlyDecl)) + D = OnlyDecl; + } + } + + auto *&Inits = ModuleInitializers[M]; + if (!Inits) + Inits = new (*this) PerModuleInitializers; + Inits->Initializers.push_back(D); +} + +void ASTContext::addLazyModuleInitializers(Module *M, ArrayRef<uint32_t> IDs) { + auto *&Inits = ModuleInitializers[M]; + if (!Inits) + Inits = new (*this) PerModuleInitializers; + Inits->LazyInitializers.insert(Inits->LazyInitializers.end(), + IDs.begin(), IDs.end()); +} + +ArrayRef<Decl*> ASTContext::getModuleInitializers(Module *M) { + auto It = ModuleInitializers.find(M); + if (It == ModuleInitializers.end()) + return None; + + auto *Inits = It->second; + Inits->resolve(*this); + return Inits->Initializers; +} + ExternCContextDecl *ASTContext::getExternCContextDecl() const { if (!ExternCContext) ExternCContext = ExternCContextDecl::Create(*this, getTranslationUnitDecl()); @@ -8575,6 +8636,8 @@ bool ASTContext::DeclMustBeEmitted(const Decl *D) { return !D->getDeclContext()->isDependentContext(); else if (isa<OMPDeclareReductionDecl>(D)) return !D->getDeclContext()->isDependentContext(); + else if (isa<ImportDecl>(D)) + return true; else return false; diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 0ef5b741243361c1a421e3d20582ac43cc71eb12..f73e85dd42b5dd38af6815f00ba63a597f05e392 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -3910,13 +3910,19 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { case Decl::Import: { auto *Import = cast<ImportDecl>(D); - // Ignore import declarations that come from imported modules. - if (Import->getImportedOwningModule()) + // If we've already imported this module, we're done. + if (!ImportedModules.insert(Import->getImportedModule())) break; - if (CGDebugInfo *DI = getModuleDebugInfo()) - DI->EmitImportDecl(*Import); - ImportedModules.insert(Import->getImportedModule()); + // Emit debug information for direct imports. + if (!Import->getImportedOwningModule()) { + if (CGDebugInfo *DI = getModuleDebugInfo()) + DI->EmitImportDecl(*Import); + } + + // Emit the module initializers. + for (auto *D : Context.getModuleInitializers(Import->getImportedModule())) + EmitTopLevelDecl(D); break; } diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 32c02e177143a5a6e96f0c13761ba75dab3f13d4..683905b1480c276bc4b305429d670b407c944a25 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -10473,6 +10473,11 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // Require the destructor. if (const RecordType *recordType = baseType->getAs<RecordType>()) FinalizeVarWithDestructor(var, recordType); + + // If this variable must be emitted, add it as an initializer for the current + // module. + if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, var); } /// \brief Determines if a variable's alignment is dependent. @@ -15095,11 +15100,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, IdentifierLocs.push_back(Path[I].second); } - ImportDecl *Import = ImportDecl::Create(Context, - Context.getTranslationUnitDecl(), + TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); + ImportDecl *Import = ImportDecl::Create(Context, TU, AtLoc.isValid()? AtLoc : ImportLoc, Mod, IdentifierLocs); - Context.getTranslationUnitDecl()->addDecl(Import); + if (!ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, Import); + TU->addDecl(Import); return Import; } @@ -15115,13 +15122,7 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { TUKind == TU_Module && getSourceManager().isWrittenInMainFile(DirectiveLoc); - // Similarly, if we're in the implementation of a module, don't - // synthesize an illegal module import. FIXME: Why not? - bool ShouldAddImport = - !IsInModuleIncludes && - (getLangOpts().CompilingModule || - getLangOpts().CurrentModule.empty() || - getLangOpts().CurrentModule != Mod->getTopLevelModuleName()); + bool ShouldAddImport = !IsInModuleIncludes; // If this module import was due to an inclusion directive, create an // implicit import declaration to capture it in the AST. @@ -15130,6 +15131,8 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, DirectiveLoc, Mod, DirectiveLoc); + if (!ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, ImportD); TU->addDecl(ImportD); Consumer.HandleImplicitImportDecl(ImportD); } @@ -15141,8 +15144,11 @@ void Sema::ActOnModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) { checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); + ModuleScopes.push_back({}); + ModuleScopes.back().Module = Mod; if (getLangOpts().ModulesLocalVisibility) - VisibleModulesStack.push_back(std::move(VisibleModules)); + ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules); + VisibleModules.setVisible(Mod, DirectiveLoc); } @@ -15150,8 +15156,11 @@ void Sema::ActOnModuleEnd(SourceLocation DirectiveLoc, Module *Mod) { checkModuleImportContext(*this, Mod, DirectiveLoc, CurContext); if (getLangOpts().ModulesLocalVisibility) { - VisibleModules = std::move(VisibleModulesStack.back()); - VisibleModulesStack.pop_back(); + assert(!ModuleScopes.empty() && ModuleScopes.back().Module == Mod && + "left the wrong module scope"); + VisibleModules = std::move(ModuleScopes.back().OuterVisibleModules); + ModuleScopes.pop_back(); + VisibleModules.setVisible(Mod, DirectiveLoc); // Leaving a module hides namespace names, so our visible namespace cache // is now out of date. diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index a41eef39edfc7d2ffcc4b3c6e072dff24dda092d..19df1e304235ae1c783d86a65528bf809fbf74fa 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -1367,8 +1367,9 @@ Module *Sema::getOwningModule(Decl *Entity) { auto &SrcMgr = PP.getSourceManager(); SourceLocation StartLoc = SrcMgr.getLocForStartOfFile(SrcMgr.getMainFileID()); - auto &TopLevel = - VisibleModulesStack.empty() ? VisibleModules : VisibleModulesStack[0]; + auto &TopLevel = ModuleScopes.empty() + ? VisibleModules + : ModuleScopes[0].OuterVisibleModules; TopLevel.setVisible(CachedFakeTopLevelModule, StartLoc); } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index b35bd7bd329b4ebcd9b6f07624773afa17d2e3fa..6bfae07b82b259b6ac1f7e82ce81f0b797fbadce 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4673,6 +4673,13 @@ ASTReader::ReadSubmoduleBlock(ModuleFile &F, unsigned ClientLoadCapabilities) { UnresolvedModuleRefs.push_back(Unresolved); break; } + + case SUBMODULE_INITIALIZERS: + SmallVector<uint32_t, 16> Inits; + for (auto &ID : Record) + Inits.push_back(getGlobalDeclID(F, ID)); + Context.addLazyModuleInitializers(CurrentModule, Inits); + break; } } } diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 9094a01ad710212e74908139b949af4392a825ca..184e7efe84fa77ed808d0defda07416f0b484175 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -2493,10 +2493,16 @@ inline void ASTReader::LoadedDecl(unsigned Index, Decl *D) { /// This routine should return true for anything that might affect /// code generation, e.g., inline function definitions, Objective-C /// declarations with metadata, etc. -static bool isConsumerInterestedIn(Decl *D, bool HasBody) { +static bool isConsumerInterestedIn(ASTContext &Ctx, Decl *D, bool HasBody) { // An ObjCMethodDecl is never considered as "interesting" because its // implementation container always is. + // An ImportDecl or VarDecl imported from a module will get emitted when + // we import the relevant module. + if ((isa<ImportDecl>(D) || isa<VarDecl>(D)) && Ctx.DeclMustBeEmitted(D) && + D->getImportedOwningModule()) + return false; + if (isa<FileScopeAsmDecl>(D) || isa<ObjCProtocolDecl>(D) || isa<ObjCImplDecl>(D) || @@ -3473,7 +3479,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // AST consumer might need to know about, queue it. // We don't pass it to the consumer immediately because we may be in recursive // loading, and some declarations may still be initializing. - if (isConsumerInterestedIn(D, Reader.hasPendingBody())) + if (isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) InterestingDecls.push_back(D); return D; @@ -3488,7 +3494,7 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { auto UpdateOffsets = std::move(UpdI->second); DeclUpdateOffsets.erase(UpdI); - bool WasInteresting = isConsumerInterestedIn(D, false); + bool WasInteresting = isConsumerInterestedIn(Context, D, false); for (auto &FileAndOffset : UpdateOffsets) { ModuleFile *F = FileAndOffset.first; uint64_t Offset = FileAndOffset.second; @@ -3509,7 +3515,7 @@ void ASTReader::loadDeclUpdateRecords(serialization::DeclID ID, Decl *D) { // We might have made this declaration interesting. If so, remember that // we need to hand it off to the consumer. if (!WasInteresting && - isConsumerInterestedIn(D, Reader.hasPendingBody())) { + isConsumerInterestedIn(Context, D, Reader.hasPendingBody())) { InterestingDecls.push_back(D); WasInteresting = true; } diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index 314eee0d439ece8ee7e55b454ef3a8e5f78dc9e0..dbba5fd191307a284731c2e053f0804e4db9d423 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -1017,6 +1017,7 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(SUBMODULE_PRIVATE_HEADER); RECORD(SUBMODULE_TEXTUAL_HEADER); RECORD(SUBMODULE_PRIVATE_TEXTUAL_HEADER); + RECORD(SUBMODULE_INITIALIZERS); // Comments Block. BLOCK(COMMENTS_BLOCK); @@ -2417,7 +2418,9 @@ unsigned ASTWriter::getLocalOrImportedSubmoduleID(Module *Mod) { if (Known != SubmoduleIDs.end()) return Known->second; - if (Mod->getTopLevelModule() != WritingModule) + auto *Top = Mod->getTopLevelModule(); + if (Top != WritingModule && + !Top->fullModuleNameIs(StringRef(getLangOpts().CurrentModule))) return 0; return SubmoduleIDs[Mod] = NextSubmoduleID++; @@ -2649,6 +2652,13 @@ void ASTWriter::WriteSubmodules(Module *WritingModule) { Stream.EmitRecordWithBlob(ConfigMacroAbbrev, Record, CM); } + // Emit the initializers, if any. + RecordData Inits; + for (Decl *D : Context->getModuleInitializers(Mod)) + Inits.push_back(GetDeclRef(D)); + if (!Inits.empty()) + Stream.EmitRecord(SUBMODULE_INITIALIZERS, Inits); + // Queue up the submodules of this module. for (auto *M : Mod->submodules()) Q.push(M); @@ -4514,6 +4524,17 @@ uint64_t ASTWriter::WriteASTCore(Sema &SemaRef, StringRef isysroot, // If we're emitting a module, write out the submodule information. if (WritingModule) WriteSubmodules(WritingModule); + else if (!getLangOpts().CurrentModule.empty()) { + // If we're building a PCH in the implementation of a module, we may need + // the description of the current module. + // + // FIXME: We may need other modules that we did not load from an AST file, + // such as if a module declares a 'conflicts' on a different module. + Module *M = PP.getHeaderSearchInfo().getModuleMap().findModule( + getLangOpts().CurrentModule); + if (M && !M->IsFromModuleFile) + WriteSubmodules(M); + } Stream.EmitRecord(SPECIAL_TYPES, SpecialTypes); diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 50c538558e29811a0381bda78501b1bce0bfddfa..8205d60ca72a2b8033f977cc32150a6dd09d5bea 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -2122,11 +2122,11 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context, D->hasAttr<OMPDeclareTargetDeclAttr>()) return true; - // ImportDecl is used by codegen to determine the set of imported modules to - // search for inputs for automatic linking; include it if it has a semantic - // effect. - if (isa<ImportDecl>(D) && !WritingModule) - return true; + if (WritingModule && (isa<VarDecl>(D) || isa<ImportDecl>(D))) { + // These declarations are part of the module initializer, and are emitted + // if and when the module is imported, rather than being emitted eagerly. + return false; + } return Context.DeclMustBeEmitted(D); } diff --git a/test/Modules/Inputs/unused-global-init/init.h b/test/Modules/Inputs/unused-global-init/init.h new file mode 100644 index 0000000000000000000000000000000000000000..29a932ae65b4edbc23c071c8b1d4fe4a335e44a4 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/init.h @@ -0,0 +1 @@ +struct Init { Init(); ~Init(); } init; diff --git a/test/Modules/Inputs/unused-global-init/module.modulemap b/test/Modules/Inputs/unused-global-init/module.modulemap new file mode 100644 index 0000000000000000000000000000000000000000..c40f0efeb5e390a95f7823cf6c932d8c7ac33132 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/module.modulemap @@ -0,0 +1,3 @@ +module used { header "used.h" } +module unused { header "unused.h" } +module init { module a { header "init.h" } module b { header "other.h" } } diff --git a/test/Modules/Inputs/unused-global-init/other.h b/test/Modules/Inputs/unused-global-init/other.h new file mode 100644 index 0000000000000000000000000000000000000000..c6be1ad4c43dc2d1a508024a045e094b1cfdfc85 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/other.h @@ -0,0 +1 @@ +// other.h diff --git a/test/Modules/Inputs/unused-global-init/unused.h b/test/Modules/Inputs/unused-global-init/unused.h new file mode 100644 index 0000000000000000000000000000000000000000..06c2a44ba21f88c8821a8aa6b1215111e06269b7 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/unused.h @@ -0,0 +1 @@ +// unused.h diff --git a/test/Modules/Inputs/unused-global-init/used.h b/test/Modules/Inputs/unused-global-init/used.h new file mode 100644 index 0000000000000000000000000000000000000000..689a13f4c8d36f1208ea27e4e3234a79c6adcf52 --- /dev/null +++ b/test/Modules/Inputs/unused-global-init/used.h @@ -0,0 +1,2 @@ +// used.h +#include "init.h" diff --git a/test/Modules/odr.cpp b/test/Modules/odr.cpp index 30143968ffb2d17f4ab2163e8ebd40fe0769f80d..9cdbb4f0806d45ba0ed8bb13038c0cf80aa1dbd2 100644 --- a/test/Modules/odr.cpp +++ b/test/Modules/odr.cpp @@ -15,9 +15,9 @@ bool b = F<int>{0} == F<int>{1}; int x = f() + g(); // expected-note@a.h:5 {{definition has no member 'e2'}} -// expected-note@b.h:3 {{declaration of 'f' does not match}} -// expected-note@b.h:1 {{definition has no member 'n'}} +// expected-note@a.h:3 {{declaration of 'f' does not match}} +// expected-note@a.h:1 {{definition has no member 'm'}} // expected-error@b.h:5 {{'E::e2' from module 'b' is not present in definition of 'E' in module 'a'}} -// expected-error@a.h:3 {{'Y::f' from module 'a' is not present in definition of 'Y' in module 'b'}} -// expected-error@a.h:2 {{'Y::n' from module 'a' is not present in definition of 'Y' in module 'b'}} +// expected-error@b.h:3 {{'Y::f' from module 'b' is not present in definition of 'Y' in module 'a'}} +// expected-error@b.h:2 {{'Y::m' from module 'b' is not present in definition of 'Y' in module 'a'}} diff --git a/test/Modules/templates.mm b/test/Modules/templates.mm index 190084ad2b740457376364716db1c8986c89901c..cd80e2480bfeb2176c8be6d7152ccc8c35b3fd04 100644 --- a/test/Modules/templates.mm +++ b/test/Modules/templates.mm @@ -12,10 +12,10 @@ void testInlineRedeclEarly() { @import templates_right; -// CHECK-DAG: @list_left = global %class.List { %"struct.List<int>::node"* null, i32 8 }, align 8 -// CHECK-DAG: @list_right = global %class.List { %"struct.List<int>::node"* null, i32 12 }, align 8 -// CHECK-DAG: @_ZZ15testMixedStructvE1l = {{.*}} constant %class.List { %{{.*}}* null, i32 1 }, align 8 -// CHECK-DAG: @_ZZ15testMixedStructvE1r = {{.*}} constant %class.List { %{{.*}}* null, i32 2 }, align 8 +// CHECK-DAG: @list_left = global %[[LIST:.*]] { %[[LISTNODE:.*]]* null, i32 8 }, align 8 +// CHECK-DAG: @list_right = global %[[LIST]] { %[[LISTNODE]]* null, i32 12 }, align 8 +// CHECK-DAG: @_ZZ15testMixedStructvE1l = {{.*}} constant %[[LIST]] { %{{.*}}* null, i32 1 }, align 8 +// CHECK-DAG: @_ZZ15testMixedStructvE1r = {{.*}} constant %[[LIST]] { %{{.*}}* null, i32 2 }, align 8 // CHECK-DAG: @_ZN29WithUndefinedStaticDataMemberIA_iE9undefinedE = external global void testTemplateClasses() { diff --git a/test/Modules/unused-global-init.cpp b/test/Modules/unused-global-init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bd7a7f5a627bc29fee41d3d4ce3d5856dfcb089 --- /dev/null +++ b/test/Modules/unused-global-init.cpp @@ -0,0 +1,37 @@ +// RUN: rm -rf %t +// +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=init -o %t/init.pcm +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=unused -o %t/unused.pcm -fmodule-file=%t/init.pcm +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-module %S/Inputs/unused-global-init/module.modulemap -fmodule-name=used -o %t/used.pcm -fmodule-file=%t/init.pcm +// +// No module file: init.h performs init. +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DINIT | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DUSED | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -DOTHER -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s +// +// With module files: if there is a transitive import of any part of the +// module, we run its global initializers (even if the imported piece is not +// visible here). +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DINIT | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DOTHER | FileCheck --check-prefix=CHECK-NO-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUSED | FileCheck --check-prefix=CHECK-INIT %s +// RUN: %clang_cc1 -fmodules -fno-implicit-modules -x c++ -I %S/Inputs/unused-global-init -triple %itanium_abi_triple -emit-llvm -o - %s -fmodule-file=%t/used.pcm -fmodule-file=%t/unused.pcm -DUNUSED | FileCheck --check-prefix=CHECK-NO-INIT %s + +#ifdef INIT +#include "init.h" +#endif + +#ifdef OTHER +#include "other.h" +#endif + +#ifdef USED +#include "used.h" +#endif + +#ifdef UNUSED +#include "unused.h" +#endif + +// CHECK-INIT: call {{.*}}@_ZN4InitC +// CHECK-NO-INIT-NOT: call {{.*}}@_ZN4InitC