/// 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 *>
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;
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;
/// \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.
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());
return !D->getDeclContext()->isDependentContext();
else if (isa<OMPDeclareReductionDecl>(D))
return !D->getDeclContext()->isDependentContext();
+ else if (isa<ImportDecl>(D))
+ return true;
else
return false;
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;
}
// 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.
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;
}
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.
ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU,
DirectiveLoc, Mod,
DirectiveLoc);
+ if (!ModuleScopes.empty())
+ Context.addModuleInitializer(ModuleScopes.back().Module, ImportD);
TU->addDecl(ImportD);
Consumer.HandleImplicitImportDecl(ImportD);
}
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);
}
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.
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);
}
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;
}
}
}
/// 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) ||
// 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;
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;
// 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;
}
RECORD(SUBMODULE_PRIVATE_HEADER);
RECORD(SUBMODULE_TEXTUAL_HEADER);
RECORD(SUBMODULE_PRIVATE_TEXTUAL_HEADER);
+ RECORD(SUBMODULE_INITIALIZERS);
// Comments Block.
BLOCK(COMMENTS_BLOCK);
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++;
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);
// 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);
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);
}
--- /dev/null
+struct Init { Init(); ~Init(); } init;
--- /dev/null
+module used { header "used.h" }
+module unused { header "unused.h" }
+module init { module a { header "init.h" } module b { header "other.h" } }
--- /dev/null
+// other.h
--- /dev/null
+// unused.h
--- /dev/null
+// used.h
+#include "init.h"
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'}}
@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() {
--- /dev/null
+// 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