Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2195,6 +2195,7 @@ }; /// The modules we're currently parsing. llvm::SmallVector ModuleScopes; + Module *GlobalModule = nullptr; /// Namespace definitions that we will export when they finish. llvm::SmallPtrSet DeferredExportedNamespaces; @@ -2204,6 +2205,11 @@ return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; } + Module *getOrCreateGlobalModule(SourceLocation BeginLoc); + + /// Try to attach Declaration to global module. See C++ [module.unit]p7.2.3 + void HandleLinkSpecDeclInModule(Decl *D); + VisibleModuleSet VisibleModules; public: Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -9339,6 +9339,8 @@ } } + HandleLinkSpecDeclInModule(NewFD); + if (isFriend) { if (FunctionTemplate) { FunctionTemplate->setObjectOfFriendDecl(); @@ -15567,6 +15569,31 @@ return false; } +/// Try to attach a Decl to the global module if following conditions +/// are met. +/// +/// C++ [module.unit]p7.2.3 +/// - Otherwise, if the declaration +/// - ... +/// - ... +/// - appears within a linkage-specification, +/// it is attached to the global module. +void Sema::HandleLinkSpecDeclInModule(Decl *D) { + Module *M = D->getOwningModule(); + + if (!M || M->Kind != Module::ModuleInterfaceUnit) + return; + + auto *DC = D->getDeclContext(); + + if (DC->isExternCContext() || DC->isExternCXXContext()) { + Module *Global = getOrCreateGlobalModule(D->getBeginLoc()); + D->setLocalOwningModule(Global); + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Unowned); + return; + } +} + /// This is invoked when we see 'struct foo' or 'struct {'. In the /// former case, Name will be non-null. In the later case, Name will be null. /// TagSpec indicates what kind of tag this is. TUK indicates whether this is a @@ -16407,6 +16434,8 @@ New->setModulePrivate(); } + HandleLinkSpecDeclInModule(New); + // If this is a specialization of a member class (of a class template), // check the specialization. if (isMemberSpecialization && CheckMemberSpecialization(New, Previous)) Index: clang/lib/Sema/SemaModule.cpp =================================================================== --- clang/lib/Sema/SemaModule.cpp +++ clang/lib/Sema/SemaModule.cpp @@ -66,10 +66,12 @@ return nullptr; } + assert(!GlobalModule && + "Global module already created but was not covered above."); // We start in the global module; all those declarations are implicitly // module-private (though they do not have module linkage). auto &Map = PP.getHeaderSearchInfo().getModuleMap(); - auto *GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc); + GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc); assert(GlobalModule && "module creation should not fail"); // Enter the scope of the global module. @@ -708,3 +710,19 @@ return D; } + +Module *Sema::getOrCreateGlobalModule(SourceLocation BeginLoc) { + if (!GlobalModule) { + auto &Map = PP.getHeaderSearchInfo().getModuleMap(); + GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(BeginLoc); + assert(GlobalModule && "module creation should not fail"); + + // Enter the scope of the global module. + ModuleScopes.push_back({}); + ModuleScopes.back().BeginLoc = BeginLoc; + ModuleScopes.back().Module = GlobalModule; + ModuleScopes.back().ImplicitGlobalModuleFragment = true; + VisibleModules.setVisible(GlobalModule, BeginLoc); + } + return GlobalModule; +} Index: clang/test/CXX/module/module.linkage_specification/Inputs/h1.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h1.h @@ -0,0 +1,6 @@ +extern "C" void foo(); +extern "C" { +void bar(); +int baz(); +double double_func(); +} Index: clang/test/CXX/module/module.linkage_specification/Inputs/h2.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h2.h @@ -0,0 +1 @@ +extern "C++" class CPP; Index: clang/test/CXX/module/module.linkage_specification/Inputs/h4.h =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/Inputs/h4.h @@ -0,0 +1 @@ +extern "C" struct C; Index: clang/test/CXX/module/module.linkage_specification/p1.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p1.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h1.h" + +export module x; + +extern "C" void foo() { + return; +} +extern "C" { +void bar() { + return; +} +int baz() { + return 3; +} +double double_func() { + return 5.0; +} +} Index: clang/test/CXX/module/module.linkage_specification/p2.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p2.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h2.h" + +export module x; + +extern "C++" class CPP {}; Index: clang/test/CXX/module/module.linkage_specification/p3.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p3.cpp @@ -0,0 +1,7 @@ +// This tests whether the global module would be created when the program don't declare it explicitly. +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +export module x; + +extern "C" void foo(); +extern "C++" class CPP {}; Index: clang/test/CXX/module/module.linkage_specification/p4.cpp =================================================================== --- /dev/null +++ clang/test/CXX/module/module.linkage_specification/p4.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++2a %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h4.h" + +export module x; + +extern "C" struct C { + int a; + int b; + double d; +}; Index: clang/test/CodeGenCXX/Inputs/module-extern-C.h =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/Inputs/module-extern-C.h @@ -0,0 +1,7 @@ +extern "C" void foo(); +extern "C" { +void bar(); +int baz(); +double double_func(); +} +extern "C++" class CPP; Index: clang/test/CodeGenCXX/module-extern-C.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/module-extern-C.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++2a -emit-llvm -triple %itanium_abi_triple -o - %s | FileCheck %s + +module; + +#include "Inputs/module-extern-C.h" + +export module x; + +// CHECK: define dso_local void @foo() +extern "C" void foo() { + return; +} + +extern "C" { +// CHECK: define dso_local void @bar() +void bar() { + return; +} +// CHECK: define dso_local i32 @baz() +int baz() { + return 3; +} +// CHECK: define dso_local double @double_func() +double double_func() { + return 5.0; +} +}