[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
This commit is contained in:
@@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -120,11 +120,17 @@ public:
|
||||
/// 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 @@ public:
|
||||
|
||||
/// 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; }
|
||||
|
||||
|
||||
@@ -553,6 +553,8 @@ public:
|
||||
/// 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,
|
||||
|
||||
@@ -2276,8 +2276,20 @@ private:
|
||||
};
|
||||
/// 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 @@ private:
|
||||
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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
52
clang/test/Modules/export-language-linkage.cppm
Normal file
52
clang/test/Modules/export-language-linkage.cppm
Normal file
@@ -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@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}}
|
||||
Reference in New Issue
Block a user