[clang] bf52ead - [C++20] [Modules] Support to export declarations in language linkage
Chuanqi Xu via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 2 18:33:59 PST 2023
Author: Chuanqi Xu
Date: 2023-03-03T10:31:48+08:00
New Revision: bf52ead24ca4fe1b73bceec7bba3abfe15541649
URL: https://github.com/llvm/llvm-project/commit/bf52ead24ca4fe1b73bceec7bba3abfe15541649
DIFF: https://github.com/llvm/llvm-project/commit/bf52ead24ca4fe1b73bceec7bba3abfe15541649.diff
LOG: [C++20] [Modules] Support to export declarations in language linkage
Close https://github.com/llvm/llvm-project/issues/60405
See the discussion in the above link for the background.
What the patch does:
- Rename `Module::ModuleKind::GlobalModuleFragment` to
`Module::ModuleKind::ExplicitGlobalModuleFragment`.
- Add another module kind `ImplicitGlobalModuleFragment` to
`ModuleKind`.
- Create an implicit global module fragment for the language linkage
declarations inside a module purview.
- If the language linkage lives inside the scope of an export decl,
the created modules is marked as exported to outer modules.
- In fact, Sema will only create at most 2 implicit global module
fragments to avoid creating a lot of unnecessary modules in the edging
case.
Reviewed By: iains
Differential Revision: https://reviews.llvm.org/D144367
Added:
clang/test/Modules/export-language-linkage.cppm
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/Module.h
clang/include/clang/Lex/ModuleMap.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/Decl.cpp
clang/lib/Frontend/FrontendActions.cpp
clang/lib/Lex/ModuleMap.cpp
clang/lib/Sema/Sema.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExprCXX.cpp
clang/lib/Sema/SemaLookup.cpp
clang/lib/Sema/SemaModule.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 23a3bbd58b964..3c152cab15c69 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -176,6 +176,8 @@ Bug Fixes in This Version
(`#60268 <https://github.com/llvm/llvm-project/issues/60268>`_)
- Fix crash when taking the address of a consteval lambda call operator.
(`#57682 <https://github.com/llvm/llvm-project/issues/57682>`_)
+- Clang now support export declarations in the language linkage.
+ (`#60405 <https://github.com/llvm/llvm-project/issues/60405>`_)
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h
index c042cf15d19be..387ce4d6e9b17 100644
--- a/clang/include/clang/Basic/Module.h
+++ b/clang/include/clang/Basic/Module.h
@@ -120,11 +120,17 @@ class alignas(8) Module {
/// This is a C++ 20 module partition implementation.
ModulePartitionImplementation,
- /// This is a fragment of the global module within some C++ module.
- GlobalModuleFragment,
+ /// This is the explicit Global Module Fragment of a modular TU.
+ /// As per C++ [module.global.frag].
+ ExplicitGlobalModuleFragment,
/// This is the private module fragment within some C++ module.
PrivateModuleFragment,
+
+ /// This is an implicit fragment of the global module which contains
+ /// only language linkage declarations (made in the purview of the
+ /// named module).
+ ImplicitGlobalModuleFragment,
};
/// The kind of this module.
@@ -170,7 +176,15 @@ class alignas(8) Module {
/// Does this Module scope describe a fragment of the global module within
/// some C++ module.
- bool isGlobalModule() const { return Kind == GlobalModuleFragment; }
+ bool isGlobalModule() const {
+ return isExplicitGlobalModule() || isImplicitGlobalModule();
+ }
+ bool isExplicitGlobalModule() const {
+ return Kind == ExplicitGlobalModuleFragment;
+ }
+ bool isImplicitGlobalModule() const {
+ return Kind == ImplicitGlobalModuleFragment;
+ }
bool isPrivateModule() const { return Kind == PrivateModuleFragment; }
diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h
index f9eb0be538c00..a0ddd13c11bfd 100644
--- a/clang/include/clang/Lex/ModuleMap.h
+++ b/clang/include/clang/Lex/ModuleMap.h
@@ -553,6 +553,8 @@ class ModuleMap {
/// parent.
Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
Module *Parent = nullptr);
+ Module *createImplicitGlobalModuleFragmentForModuleUnit(
+ SourceLocation Loc, bool IsExported, 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
index 768a1e4e8b850..60fed8603ec55 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2276,8 +2276,20 @@ class Sema final {
};
/// The modules we're currently parsing.
llvm::SmallVector<ModuleScope, 16> ModuleScopes;
- /// The global module fragment of the current translation unit.
- clang::Module *GlobalModuleFragment = nullptr;
+ /// The explicit global module fragment of the current translation unit.
+ /// The explicit Global Module Fragment, as specified in C++
+ /// [module.global.frag].
+ clang::Module *TheGlobalModuleFragment = nullptr;
+
+ /// The implicit global module fragments of the current translation unit.
+ /// We would only create at most two implicit global module fragments to
+ /// avoid performance penalties when there are many language linkage
+ /// exports.
+ ///
+ /// The contents in the implicit global module fragment can't be discarded
+ /// no matter if it is exported or not.
+ clang::Module *TheImplicitGlobalModuleFragment = nullptr;
+ clang::Module *TheExportedImplicitGlobalModuleFragment = nullptr;
/// Namespace definitions that we will export when they finish.
llvm::SmallPtrSet<const NamespaceDecl*, 8> DeferredExportedNamespaces;
@@ -2293,11 +2305,17 @@ class Sema final {
return getCurrentModule() ? getCurrentModule()->isModulePurview() : false;
}
- /// Enter the scope of the global module.
+ /// Enter the scope of the explicit global module fragment.
Module *PushGlobalModuleFragment(SourceLocation BeginLoc);
- /// Leave the scope of the global module.
+ /// Leave the scope of the explicit global module fragment.
void PopGlobalModuleFragment();
+ /// Enter the scope of an implicit global module fragment.
+ Module *PushImplicitGlobalModuleFragment(SourceLocation BeginLoc,
+ bool IsExported);
+ /// Leave the scope of an implicit global module fragment.
+ void PopImplicitGlobalModuleFragment();
+
VisibleModuleSet VisibleModules;
/// Cache for module units which is usable for current module.
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 9c72d7a7d928c..240e744ee6449 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -1590,7 +1590,8 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
return M;
case Module::ModuleHeaderUnit:
- case Module::GlobalModuleFragment: {
+ case Module::ExplicitGlobalModuleFragment:
+ case Module::ImplicitGlobalModuleFragment: {
// External linkage declarations in the global module have no owning module
// for linkage purposes. But internal linkage declarations in the global
// module fragment of a particular module are owned by that module for
diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp
index ccb983d580440..2aae41fe488ff 100644
--- a/clang/lib/Frontend/FrontendActions.cpp
+++ b/clang/lib/Frontend/FrontendActions.cpp
@@ -765,8 +765,10 @@ static StringRef ModuleKindName(Module::ModuleKind MK) {
return "Partition Implementation";
case Module::ModuleHeaderUnit:
return "Header Unit";
- case Module::GlobalModuleFragment:
+ case Module::ExplicitGlobalModuleFragment:
return "Global Module Fragment";
+ case Module::ImplicitGlobalModuleFragment:
+ return "Implicit Module Fragment";
case Module::PrivateModuleFragment:
return "Private Module Fragment";
}
diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp
index 15f4377541b48..5973b4ae06240 100644
--- a/clang/lib/Lex/ModuleMap.cpp
+++ b/clang/lib/Lex/ModuleMap.cpp
@@ -855,7 +855,7 @@ Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
Module *Parent) {
auto *Result = new Module("<global>", Loc, Parent, /*IsFramework*/ false,
/*IsExplicit*/ true, NumCreatedModules++);
- Result->Kind = Module::GlobalModuleFragment;
+ Result->Kind = Module::ExplicitGlobalModuleFragment;
// If the created module isn't owned by a parent, send it to PendingSubmodules
// to wait for its parent.
if (!Result->Parent)
@@ -863,6 +863,21 @@ Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
return Result;
}
+Module *ModuleMap::createImplicitGlobalModuleFragmentForModuleUnit(
+ SourceLocation Loc, bool IsExported, Module *Parent) {
+ assert(Parent && "We should only create an implicit global module fragment "
+ "in a module purview");
+ // Note: Here the `IsExplicit` parameter refers to the semantics in clang
+ // modules. All the non-explicit submodules in clang modules will be exported
+ // too. Here we simplify the implementation by using the concept.
+ auto *Result = new Module(IsExported ? "<exported implicit global>"
+ : "<implicit global>",
+ Loc, Parent, /*IsFramework*/ false,
+ /*IsExplicit*/ !IsExported, NumCreatedModules++);
+ Result->Kind = Module::ImplicitGlobalModuleFragment;
+ return Result;
+}
+
Module *
ModuleMap::createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
SourceLocation Loc) {
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 90dd5538d0fa8..72575470a78bc 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1202,8 +1202,8 @@ void Sema::ActOnEndOfTranslationUnit() {
// A global-module-fragment is only permitted within a module unit.
bool DiagnosedMissingModuleDeclaration = false;
- if (!ModuleScopes.empty() &&
- ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) {
+ if (!ModuleScopes.empty() && ModuleScopes.back().Module->Kind ==
+ Module::ExplicitGlobalModuleFragment) {
Diag(ModuleScopes.back().BeginLoc,
diag::err_module_declaration_missing_after_global_module_introducer);
DiagnosedMissingModuleDeclaration = true;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index cf74fcbc4cfa4..ae8b8d62bcf3b 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16362,14 +16362,8 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc,
/// If the declaration is already in global module fragment, we don't
/// need to attach it again.
if (getLangOpts().CPlusPlusModules && isCurrentModulePurview()) {
- Module *GlobalModule =
- PushGlobalModuleFragment(ExternLoc);
- /// According to [module.reach]p3.2,
- /// The declaration in global module fragment is reachable if it is not
- /// discarded. And the discarded declaration should be deleted. So it
- /// doesn't matter mark the declaration in global module fragment as
- /// reachable here.
- D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
+ Module *GlobalModule = PushImplicitGlobalModuleFragment(
+ ExternLoc, /*IsExported=*/D->isInExportDeclContext());
D->setLocalOwningModule(GlobalModule);
}
@@ -16395,7 +16389,7 @@ Decl *Sema::ActOnFinishLinkageSpecification(Scope *S,
// need to pop it.
if (getLangOpts().CPlusPlusModules && getCurrentModule() &&
getCurrentModule()->isGlobalModule() && getCurrentModule()->Parent)
- PopGlobalModuleFragment();
+ PopImplicitGlobalModuleFragment();
PopDeclContext();
return LinkageSpec;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index b0cf221a446f0..e7f3852ae34cd 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -3023,10 +3023,10 @@ void Sema::DeclareGlobalNewDelete() {
// The implicitly declared "std::bad_alloc" should live in global module
// fragment.
- if (GlobalModuleFragment) {
+ if (TheGlobalModuleFragment) {
getStdBadAlloc()->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
- getStdBadAlloc()->setLocalOwningModule(GlobalModuleFragment);
+ getStdBadAlloc()->setLocalOwningModule(TheGlobalModuleFragment);
}
}
if (!StdAlignValT && getLangOpts().AlignedAllocation) {
@@ -3038,10 +3038,10 @@ void Sema::DeclareGlobalNewDelete() {
// The implicitly declared "std::align_val_t" should live in global module
// fragment.
- if (GlobalModuleFragment) {
+ if (TheGlobalModuleFragment) {
AlignValT->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
- AlignValT->setLocalOwningModule(GlobalModuleFragment);
+ AlignValT->setLocalOwningModule(TheGlobalModuleFragment);
}
AlignValT->setIntegerType(Context.getSizeType());
@@ -3170,10 +3170,10 @@ void Sema::DeclareGlobalAllocationFunction(DeclarationName Name,
// module all the time. But in the implementation, the global module
// is only meaningful when we're in a module unit. So here we attach
// these allocation functions to global module conditionally.
- if (GlobalModuleFragment) {
+ if (TheGlobalModuleFragment) {
Alloc->setModuleOwnershipKind(
Decl::ModuleOwnershipKind::ReachableWhenImported);
- Alloc->setLocalOwningModule(GlobalModuleFragment);
+ Alloc->setLocalOwningModule(TheGlobalModuleFragment);
}
Alloc->addAttr(VisibilityAttr::CreateImplicit(
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 5a5428caf170e..aeeb49005ed1a 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -1579,7 +1579,7 @@ bool Sema::isUsableModule(const Module *M) {
// [module.global.frag]p1:
// The global module fragment can be used to provide declarations that are
// attached to the global module and usable within the module unit.
- if (M == GlobalModuleFragment ||
+ if (M == TheGlobalModuleFragment ||
// If M is the module we're parsing, it should be usable. This covers the
// private module fragment. The private module fragment is usable only if
// it is within the current module unit. And it must be the current
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp
index 59891ec2a8174..8c120d278d634 100644
--- a/clang/lib/Sema/SemaModule.cpp
+++ b/clang/lib/Sema/SemaModule.cpp
@@ -233,7 +233,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
}
assert((!getLangOpts().CPlusPlusModules ||
- SeenGMF == (bool)this->GlobalModuleFragment) &&
+ SeenGMF == (bool)this->TheGlobalModuleFragment) &&
"mismatched global module state");
// In C++20, the module-declaration must be the first declaration if there
@@ -358,7 +358,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
break;
}
- if (!this->GlobalModuleFragment) {
+ if (!this->TheGlobalModuleFragment) {
ModuleScopes.push_back({});
if (getLangOpts().ModulesLocalVisibility)
ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
@@ -408,10 +408,11 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
// C++20 [basic.link]/2:
// A private-module-fragment shall appear only in a primary module
// interface unit.
- switch (ModuleScopes.empty() ? Module::GlobalModuleFragment
+ switch (ModuleScopes.empty() ? Module::ExplicitGlobalModuleFragment
: ModuleScopes.back().Module->Kind) {
case Module::ModuleMapModule:
- case Module::GlobalModuleFragment:
+ case Module::ExplicitGlobalModuleFragment:
+ case Module::ImplicitGlobalModuleFragment:
case Module::ModulePartitionImplementation:
case Module::ModulePartitionInterface:
case Module::ModuleHeaderUnit:
@@ -958,25 +959,52 @@ Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) {
Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc) {
// We shouldn't create new global module fragment if there is already
// one.
- if (!GlobalModuleFragment) {
+ if (!TheGlobalModuleFragment) {
ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap();
- GlobalModuleFragment = Map.createGlobalModuleFragmentForModuleUnit(
+ TheGlobalModuleFragment = Map.createGlobalModuleFragmentForModuleUnit(
BeginLoc, getCurrentModule());
}
- assert(GlobalModuleFragment && "module creation should not fail");
+ assert(TheGlobalModuleFragment && "module creation should not fail");
// Enter the scope of the global module.
- ModuleScopes.push_back({BeginLoc, GlobalModuleFragment,
+ ModuleScopes.push_back({BeginLoc, TheGlobalModuleFragment,
/*ModuleInterface=*/false,
/*OuterVisibleModules=*/{}});
- VisibleModules.setVisible(GlobalModuleFragment, BeginLoc);
+ VisibleModules.setVisible(TheGlobalModuleFragment, BeginLoc);
- return GlobalModuleFragment;
+ return TheGlobalModuleFragment;
}
void Sema::PopGlobalModuleFragment() {
- assert(!ModuleScopes.empty() && getCurrentModule()->isGlobalModule() &&
+ assert(!ModuleScopes.empty() &&
+ getCurrentModule()->isExplicitGlobalModule() &&
+ "left the wrong module scope, which is not global module fragment");
+ ModuleScopes.pop_back();
+}
+
+Module *Sema::PushImplicitGlobalModuleFragment(SourceLocation BeginLoc,
+ bool IsExported) {
+ Module **M = IsExported ? &TheExportedImplicitGlobalModuleFragment
+ : &TheImplicitGlobalModuleFragment;
+ if (!*M) {
+ ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap();
+ *M = Map.createImplicitGlobalModuleFragmentForModuleUnit(
+ BeginLoc, IsExported, getCurrentModule());
+ }
+ assert(*M && "module creation should not fail");
+
+ // Enter the scope of the global module.
+ ModuleScopes.push_back({BeginLoc, *M,
+ /*ModuleInterface=*/false,
+ /*OuterVisibleModules=*/{}});
+ VisibleModules.setVisible(*M, BeginLoc);
+ return *M;
+}
+
+void Sema::PopImplicitGlobalModuleFragment() {
+ assert(!ModuleScopes.empty() &&
+ getCurrentModule()->isImplicitGlobalModule() &&
"left the wrong module scope, which is not global module fragment");
ModuleScopes.pop_back();
}
diff --git a/clang/test/Modules/export-language-linkage.cppm b/clang/test/Modules/export-language-linkage.cppm
new file mode 100644
index 0000000000000..331a620aaad06
--- /dev/null
+++ b/clang/test/Modules/export-language-linkage.cppm
@@ -0,0 +1,52 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
+// RUN: %clang_cc1 -std=c++20 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
+// RUN: %clang_cc1 -std=c++20 %t/c.cppm -fsyntax-only -verify
+// RUN: %clang_cc1 -module-file-info %t/a.pcm | FileCheck %t/a.cppm
+
+//--- a.cppm
+export module a;
+export extern "C++" int foo() { return 43; }
+export extern "C++" {
+ int a();
+ int b();
+ int c();
+}
+
+export {
+ extern "C++" void f1();
+ extern "C++" void f2();
+ extern "C++" void f3();
+}
+
+extern "C++" void unexported();
+
+// CHECK: Sub Modules:
+// CHECK-NEXT: Implicit Module Fragment '<exported implicit global>'
+// CHECK-NEXT: Implicit Module Fragment '<implicit global>'
+
+//--- b.cpp
+import a;
+int use() {
+ a();
+ b();
+ c();
+ f1();
+ f2();
+ f3();
+ unexported(); // expected-error {{missing '#include'; 'unexported' must be declared before it is used}}
+ // expected-note at a.cppm:15 {{declaration here is not visible}}
+ return foo();
+}
+
+//--- c.cppm
+export module c;
+extern "C++" {
+ // We can't use `export` in an unnamed module.
+ export int f(); // expected-error {{export declaration can only be used within a module purview}}
+}
+
+extern "C++" export int g(); // expected-error {{export declaration can only be used within a module purview}}
More information about the cfe-commits
mailing list