diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -153,6 +153,10 @@ return Kind == ModuleInterfaceUnit || Kind == PrivateModuleFragment; } + /// Does this Module scope describe a fragment of the global module within + /// some C++ module. + bool isGlobalModule() const { return Kind == GlobalModuleFragment; } + private: /// The submodules of this module, indexed by name. std::vector SubModules; diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -538,8 +538,11 @@ /// /// We model the global module fragment as a submodule of the module /// interface unit. Unfortunately, we can't create the module interface - /// unit's Module until later, because we don't know what it will be called. - Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc); + /// unit's Module until later, because we don't know what it will be called + /// usually. See C++20 [module.unit]/7.2 for the case we could know its + /// parent. + Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc, + Module *Parent = nullptr); /// Create a global module fragment for a C++ module interface unit. Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent, diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2222,6 +2222,11 @@ return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; } + /// Enter the scope of the global module. + Module *PushGlobalModuleFragment(SourceLocation BeginLoc, bool IsImplicit); + /// Leave the scope of the global module. + void PopGlobalModuleFragment(); + VisibleModuleSet VisibleModules; public: diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -832,12 +832,16 @@ return std::make_pair(Result, true); } -Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc) { - PendingSubmodules.emplace_back( - new Module("", Loc, nullptr, /*IsFramework*/ false, - /*IsExplicit*/ true, NumCreatedModules++)); - PendingSubmodules.back()->Kind = Module::GlobalModuleFragment; - return PendingSubmodules.back().get(); +Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc, + Module *Parent) { + auto *Result = new Module("", Loc, Parent, /*IsFramework*/ false, + /*IsExplicit*/ true, NumCreatedModules++); + Result->Kind = Module::GlobalModuleFragment; + // If the created module isn't owned by a parent, send it to PendingSubmodules + // to wait for its parent. + if (!Result->Parent) + PendingSubmodules.emplace_back(Result); + return Result; } Module * diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16146,6 +16146,20 @@ LinkageSpecDecl *D = LinkageSpecDecl::Create(Context, CurContext, ExternLoc, LangStr->getExprLoc(), Language, LBraceLoc.isValid()); + + /// C++ [module.unit]p7.2.3 + /// - Otherwise, if the declaration + /// - ... + /// - ... + /// - appears within a linkage-specification, + /// it is attached to the global module. + if (getLangOpts().CPlusPlusModules) { + Module *GlobalModule = + PushGlobalModuleFragment(ExternLoc, /*IsImplicit=*/true); + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); + D->setLocalOwningModule(GlobalModule); + } + CurContext->addDecl(D); PushDeclContext(S, D); return D; @@ -16162,6 +16176,10 @@ LinkageSpecDecl* LSDecl = cast(LinkageSpec); LSDecl->setRBraceLoc(RBraceLoc); } + + if (getLangOpts().CPlusPlusModules) + PopGlobalModuleFragment(); + PopDeclContext(); return LinkageSpec; } diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -68,15 +68,8 @@ // 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); - assert(GlobalModule && "module creation should not fail"); - - // Enter the scope of the global module. - ModuleScopes.push_back({}); - ModuleScopes.back().BeginLoc = ModuleLoc; - ModuleScopes.back().Module = GlobalModule; - VisibleModules.setVisible(GlobalModule, ModuleLoc); + Module *GlobalModule = + PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false); // All declarations created from now on are owned by the global module. auto *TU = Context.getTranslationUnitDecl(); @@ -708,3 +701,25 @@ return D; } + +Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc, + bool IsImplicit) { + ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap(); + Module *GlobalModule = + Map.createGlobalModuleFragmentForModuleUnit(BeginLoc, getCurrentModule()); + assert(GlobalModule && "module creation should not fail"); + + // Enter the scope of the global module. + ModuleScopes.push_back({BeginLoc, GlobalModule, + /*ModuleInterface=*/false, + /*ImplicitGlobalModuleFragment=*/IsImplicit}); + VisibleModules.setVisible(GlobalModule, BeginLoc); + + return GlobalModule; +} + +void Sema::PopGlobalModuleFragment() { + assert(!ModuleScopes.empty() && getCurrentModule()->isGlobalModule() && + "left the wrong module scope, which is not global module fragment"); + ModuleScopes.pop_back(); +} diff --git a/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm b/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/Inputs/CPP.cppm @@ -0,0 +1,8 @@ +module; +#include "h2.h" +export module X; + +extern "C++" class CPP { +public: + void print() {} +}; diff --git a/clang/test/CXX/module/module.unit/p7/Inputs/h1.h b/clang/test/CXX/module/module.unit/p7/Inputs/h1.h new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/Inputs/h1.h @@ -0,0 +1,12 @@ +extern "C" void foo(); +extern "C" { +void bar(); +int baz(); +double double_func(); +} + +extern "C++" { +void bar_cpp(); +int baz_cpp(); +double double_func_cpp(); +} diff --git a/clang/test/CXX/module/module.unit/p7/Inputs/h2.h b/clang/test/CXX/module/module.unit/p7/Inputs/h2.h new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/Inputs/h2.h @@ -0,0 +1 @@ +extern "C++" class CPP; diff --git a/clang/test/CXX/module/module.unit/p7/Inputs/h4.h b/clang/test/CXX/module/module.unit/p7/Inputs/h4.h new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/Inputs/h4.h @@ -0,0 +1 @@ +extern "C" struct C; diff --git a/clang/test/CXX/module/module.unit/p7/Inputs/h5.h b/clang/test/CXX/module/module.unit/p7/Inputs/h5.h new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/Inputs/h5.h @@ -0,0 +1 @@ +extern "C++" int a; diff --git a/clang/test/CXX/module/module.unit/p7/t1.cpp b/clang/test/CXX/module/module.unit/p7/t1.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t1.cpp @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -std=c++20 %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; +} +} + +extern "C++" { +void bar_cpp() { + return; +} +int baz_cpp() { + return 3; +} +double double_func_cpp() { + return 5.0; +} +} diff --git a/clang/test/CXX/module/module.unit/p7/t2.cpp b/clang/test/CXX/module/module.unit/p7/t2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t2.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++20 %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h2.h" + +export module x; + +extern "C++" class CPP {}; diff --git a/clang/test/CXX/module/module.unit/p7/t3.cpp b/clang/test/CXX/module/module.unit/p7/t3.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t3.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++20 %s -verify +// expected-no-diagnostics +export module x; + +extern "C" void foo(); +extern "C++" class CPP {}; diff --git a/clang/test/CXX/module/module.unit/p7/t4.cpp b/clang/test/CXX/module/module.unit/p7/t4.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t4.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++20 %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h4.h" + +export module x; + +extern "C" struct C { + int a; + int b; + double d; +}; diff --git a/clang/test/CXX/module/module.unit/p7/t5.cpp b/clang/test/CXX/module/module.unit/p7/t5.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t5.cpp @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -std=c++20 %s -verify +// expected-no-diagnostics +module; + +#include "Inputs/h4.h" + +export module x; + +extern "C++" int a = 5; diff --git a/clang/test/CXX/module/module.unit/p7/t6.cpp b/clang/test/CXX/module/module.unit/p7/t6.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.unit/p7/t6.cpp @@ -0,0 +1,15 @@ +// RUN: rm -fr %t +// RUN: mkdir %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %S/Inputs/CPP.cppm -I%S/Inputs -o %t/X.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %s -verify +module; +#include "Inputs/h2.h" +export module use; +import X; +void printX(CPP *cpp) { + cpp->print(); // expected-error {{'CPP' must be defined before it is used}} + // expected-error@-1 {{'CPP' must be defined before it is used}} + // expected-error@-2 {{no member named 'print' in 'CPP'}} + // expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}} + // expected-note@Inputs/CPP.cppm:5 {{definition here is not reachable}} +} diff --git a/clang/test/CodeGenCXX/Inputs/module-extern-C.h b/clang/test/CodeGenCXX/Inputs/module-extern-C.h new file mode 100644 --- /dev/null +++ b/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; diff --git a/clang/test/CodeGenCXX/module-extern-C.cpp b/clang/test/CodeGenCXX/module-extern-C.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGenCXX/module-extern-C.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -std=c++20 -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; +} +}