Skip to content

Commit 0f7aaeb

Browse files
committed
[C++20] [Modules] Allow export from language linkage
Close #71347 Previously I misread the concept of module purview. I thought if a declaration attached to a unnamed module, it can't be part of the module purview. But after the issue report, I recognized that module purview is more of a concept about locations instead of semantics. Concretely, the things in the language linkage after module declarations can be exported. This patch refactors `Module::isModulePurview()` and introduces some possible code cleanups.
1 parent e3d750c commit 0f7aaeb

File tree

18 files changed

+94
-80
lines changed

18 files changed

+94
-80
lines changed

clang/include/clang/Basic/Module.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,8 @@ class alignas(8) Module {
178178
/// eventually be exposed, for use in "private" modules.
179179
std::string ExportAsModule;
180180

181-
/// Does this Module scope describe part of the purview of a standard named
182-
/// C++ module?
183-
bool isModulePurview() const {
181+
/// Does this Module is a named module of a standard named module?
182+
bool isNamedModule() const {
184183
switch (Kind) {
185184
case ModuleInterfaceUnit:
186185
case ModuleImplementationUnit:

clang/include/clang/Lex/ModuleMap.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -556,8 +556,8 @@ class ModuleMap {
556556
/// parent.
557557
Module *createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
558558
Module *Parent = nullptr);
559-
Module *createImplicitGlobalModuleFragmentForModuleUnit(
560-
SourceLocation Loc, bool IsExported, Module *Parent = nullptr);
559+
Module *createImplicitGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
560+
Module *Parent);
561561

562562
/// Create a global module fragment for a C++ module interface unit.
563563
Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent,

clang/include/clang/Sema/Sema.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,14 +2317,9 @@ class Sema final {
23172317
clang::Module *TheGlobalModuleFragment = nullptr;
23182318

23192319
/// The implicit global module fragments of the current translation unit.
2320-
/// We would only create at most two implicit global module fragments to
2321-
/// avoid performance penalties when there are many language linkage
2322-
/// exports.
23232320
///
2324-
/// The contents in the implicit global module fragment can't be discarded
2325-
/// no matter if it is exported or not.
2321+
/// The contents in the implicit global module fragment can't be discarded.
23262322
clang::Module *TheImplicitGlobalModuleFragment = nullptr;
2327-
clang::Module *TheExportedImplicitGlobalModuleFragment = nullptr;
23282323

23292324
/// Namespace definitions that we will export when they finish.
23302325
llvm::SmallPtrSet<const NamespaceDecl*, 8> DeferredExportedNamespaces;
@@ -2336,18 +2331,15 @@ class Sema final {
23362331

23372332
/// Helper function to judge if we are in module purview.
23382333
/// Return false if we are not in a module.
2339-
bool isCurrentModulePurview() const {
2340-
return getCurrentModule() ? getCurrentModule()->isModulePurview() : false;
2341-
}
2334+
bool isCurrentModulePurview() const;
23422335

23432336
/// Enter the scope of the explicit global module fragment.
23442337
Module *PushGlobalModuleFragment(SourceLocation BeginLoc);
23452338
/// Leave the scope of the explicit global module fragment.
23462339
void PopGlobalModuleFragment();
23472340

23482341
/// Enter the scope of an implicit global module fragment.
2349-
Module *PushImplicitGlobalModuleFragment(SourceLocation BeginLoc,
2350-
bool IsExported);
2342+
Module *PushImplicitGlobalModuleFragment(SourceLocation BeginLoc);
23512343
/// Leave the scope of an implicit global module fragment.
23522344
void PopImplicitGlobalModuleFragment();
23532345

clang/include/clang/Serialization/ASTWriter.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ class ASTWriter : public ASTDeserializationListener,
746746
ASTReader *getChain() const { return Chain; }
747747

748748
bool isWritingStdCXXNamedModules() const {
749-
return WritingModule && WritingModule->isModulePurview();
749+
return WritingModule && WritingModule->isNamedModule();
750750
}
751751

752752
private:

clang/lib/AST/ASTContext.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1096,7 +1096,7 @@ ArrayRef<Decl *> ASTContext::getModuleInitializers(Module *M) {
10961096
}
10971097

10981098
void ASTContext::setCurrentNamedModule(Module *M) {
1099-
assert(M->isModulePurview());
1099+
assert(M->isNamedModule());
11001100
assert(!CurrentCXXNamedModule &&
11011101
"We should set named module for ASTContext for only once");
11021102
CurrentCXXNamedModule = M;

clang/lib/AST/Decl.cpp

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -579,27 +579,6 @@ static bool isSingleLineLanguageLinkage(const Decl &D) {
579579
return false;
580580
}
581581

582-
/// Determine whether D is declared in the purview of a named module.
583-
static bool isInModulePurview(const NamedDecl *D) {
584-
if (auto *M = D->getOwningModule())
585-
return M->isModulePurview();
586-
return false;
587-
}
588-
589-
static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
590-
// FIXME: Handle isModulePrivate.
591-
switch (D->getModuleOwnershipKind()) {
592-
case Decl::ModuleOwnershipKind::Unowned:
593-
case Decl::ModuleOwnershipKind::ReachableWhenImported:
594-
case Decl::ModuleOwnershipKind::ModulePrivate:
595-
return false;
596-
case Decl::ModuleOwnershipKind::Visible:
597-
case Decl::ModuleOwnershipKind::VisibleWhenImported:
598-
return isInModulePurview(D);
599-
}
600-
llvm_unreachable("unexpected module ownership kind");
601-
}
602-
603582
static bool isDeclaredInModuleInterfaceOrPartition(const NamedDecl *D) {
604583
if (auto *M = D->getOwningModule())
605584
return M->isInterfaceOrPartition();
@@ -1191,6 +1170,27 @@ Linkage NamedDecl::getLinkageInternal() const {
11911170
.getLinkage();
11921171
}
11931172

1173+
/// Determine whether D is attached to a named module.
1174+
static bool isInNamedModule(const NamedDecl *D) {
1175+
if (auto *M = D->getOwningModule())
1176+
return M->isNamedModule();
1177+
return false;
1178+
}
1179+
1180+
static bool isExportedFromModuleInterfaceUnit(const NamedDecl *D) {
1181+
// FIXME: Handle isModulePrivate.
1182+
switch (D->getModuleOwnershipKind()) {
1183+
case Decl::ModuleOwnershipKind::Unowned:
1184+
case Decl::ModuleOwnershipKind::ReachableWhenImported:
1185+
case Decl::ModuleOwnershipKind::ModulePrivate:
1186+
return false;
1187+
case Decl::ModuleOwnershipKind::Visible:
1188+
case Decl::ModuleOwnershipKind::VisibleWhenImported:
1189+
return isInNamedModule(D);
1190+
}
1191+
llvm_unreachable("unexpected module ownership kind");
1192+
}
1193+
11941194
/// Get the linkage from a semantic point of view. Entities in
11951195
/// anonymous namespaces are external (in c++98).
11961196
Linkage NamedDecl::getFormalLinkage() const {
@@ -1204,7 +1204,7 @@ Linkage NamedDecl::getFormalLinkage() const {
12041204
// [basic.namespace.general]/p2
12051205
// A namespace is never attached to a named module and never has a name with
12061206
// module linkage.
1207-
if (isInModulePurview(this) && InternalLinkage == Linkage::External &&
1207+
if (isInNamedModule(this) && InternalLinkage == Linkage::External &&
12081208
!isExportedFromModuleInterfaceUnit(
12091209
cast<NamedDecl>(this->getCanonicalDecl())) &&
12101210
!isa<NamespaceDecl>(this))

clang/lib/AST/DeclBase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1113,7 +1113,7 @@ bool Decl::isInAnotherModuleUnit() const {
11131113
if (M->isGlobalModule())
11141114
return false;
11151115

1116-
assert(M->isModulePurview() && "New module kind?");
1116+
assert(M->isNamedModule() && "New module kind?");
11171117
return M != getASTContext().getCurrentNamedModule();
11181118
}
11191119

clang/lib/CodeGen/CodeGenModule.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3859,7 +3859,7 @@ bool CodeGenModule::shouldEmitFunction(GlobalDecl GD) {
38593859
// We don't import function bodies from other named module units since that
38603860
// behavior may break ABI compatibility of the current unit.
38613861
if (const Module *M = F->getOwningModule();
3862-
M && M->isModulePurview() &&
3862+
M && M->getTopLevelModule()->isNamedModule() &&
38633863
getContext().getCurrentNamedModule() != M->getTopLevelModule() &&
38643864
!F->hasAttr<AlwaysInlineAttr>())
38653865
return false;

clang/lib/Frontend/ASTUnit.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -883,7 +883,7 @@ std::unique_ptr<ASTUnit> ASTUnit::LoadFromASTFile(
883883
PP.setCounterValue(Counter);
884884

885885
Module *M = HeaderInfo.lookupModule(AST->getLangOpts().CurrentModule);
886-
if (M && AST->getLangOpts().isCompilingModule() && M->isModulePurview())
886+
if (M && AST->getLangOpts().isCompilingModule() && M->isNamedModule())
887887
AST->Ctx->setCurrentNamedModule(M);
888888

889889
// Create an AST consumer, even though it isn't used.

clang/lib/Lex/ModuleMap.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -881,17 +881,17 @@ Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
881881
return Result;
882882
}
883883

884-
Module *ModuleMap::createImplicitGlobalModuleFragmentForModuleUnit(
885-
SourceLocation Loc, bool IsExported, Module *Parent) {
884+
Module *
885+
ModuleMap::createImplicitGlobalModuleFragmentForModuleUnit(SourceLocation Loc,
886+
Module *Parent) {
886887
assert(Parent && "We should only create an implicit global module fragment "
887888
"in a module purview");
888889
// Note: Here the `IsExplicit` parameter refers to the semantics in clang
889890
// modules. All the non-explicit submodules in clang modules will be exported
890891
// too. Here we simplify the implementation by using the concept.
891-
auto *Result = new Module(IsExported ? "<exported implicit global>"
892-
: "<implicit global>",
893-
Loc, Parent, /*IsFramework*/ false,
894-
/*IsExplicit*/ !IsExported, NumCreatedModules++);
892+
auto *Result =
893+
new Module("<implicit global>", Loc, Parent, /*IsFramework=*/false,
894+
/*IsExplicit=*/false, NumCreatedModules++);
895895
Result->Kind = Module::ImplicitGlobalModuleFragment;
896896
return Result;
897897
}

clang/lib/Sema/Sema.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1286,8 +1286,6 @@ void Sema::ActOnEndOfTranslationUnit() {
12861286
if (auto *FD = dyn_cast<FunctionDecl>(D)) {
12871287
bool DefInPMF = false;
12881288
if (auto *FDD = FD->getDefinition()) {
1289-
assert(FDD->getOwningModule() &&
1290-
FDD->getOwningModule()->isModulePurview());
12911289
DefInPMF = FDD->getOwningModule()->isPrivateModule();
12921290
if (!DefInPMF)
12931291
continue;

clang/lib/Sema/SemaDecl.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1684,8 +1684,8 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) {
16841684
return false;
16851685
}
16861686

1687-
bool NewIsModuleInterface = NewM && NewM->isModulePurview();
1688-
bool OldIsModuleInterface = OldM && OldM->isModulePurview();
1687+
bool NewIsModuleInterface = NewM && NewM->isNamedModule();
1688+
bool OldIsModuleInterface = OldM && OldM->isNamedModule();
16891689
if (NewIsModuleInterface || OldIsModuleInterface) {
16901690
// C++ Modules TS [basic.def.odr] 6.2/6.7 [sic]:
16911691
// if a declaration of D [...] appears in the purview of a module, all
@@ -1819,7 +1819,7 @@ bool Sema::IsRedefinitionInModule(const NamedDecl *New,
18191819
// [basic.def.odr]p14.3
18201820
// Each such definition shall not be attached to a named module
18211821
// ([module.unit]).
1822-
if ((NewM && NewM->isModulePurview()) || (OldM && OldM->isModulePurview()))
1822+
if ((NewM && NewM->isNamedModule()) || (OldM && OldM->isNamedModule()))
18231823
return true;
18241824

18251825
// Then New and Old lives in the same TU if their share one same module unit.
@@ -10165,8 +10165,7 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1016510165
// check at the end of the TU (or when the PMF starts) to see that we
1016610166
// have a definition at that point.
1016710167
if (isInline && !D.isFunctionDefinition() && getLangOpts().CPlusPlus20 &&
10168-
NewFD->hasOwningModule() &&
10169-
NewFD->getOwningModule()->isModulePurview()) {
10168+
NewFD->hasOwningModule() && NewFD->getOwningModule()->isNamedModule()) {
1017010169
PendingInlineFuncDecls.insert(NewFD);
1017110170
}
1017210171
}

clang/lib/Sema/SemaDeclCXX.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16798,8 +16798,7 @@ Decl *Sema::ActOnStartLinkageSpecification(Scope *S, SourceLocation ExternLoc,
1679816798
/// If the declaration is already in global module fragment, we don't
1679916799
/// need to attach it again.
1680016800
if (getLangOpts().CPlusPlusModules && isCurrentModulePurview()) {
16801-
Module *GlobalModule = PushImplicitGlobalModuleFragment(
16802-
ExternLoc, /*IsExported=*/D->isInExportDeclContext());
16801+
Module *GlobalModule = PushImplicitGlobalModuleFragment(ExternLoc);
1680316802
D->setLocalOwningModule(GlobalModule);
1680416803
}
1680516804

clang/lib/Sema/SemaLookup.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1593,7 +1593,6 @@ bool Sema::isUsableModule(const Module *M) {
15931593
// The global module fragment can be used to provide declarations that are
15941594
// attached to the global module and usable within the module unit.
15951595
if (M == TheGlobalModuleFragment || M == TheImplicitGlobalModuleFragment ||
1596-
M == TheExportedImplicitGlobalModuleFragment ||
15971596
// If M is the module we're parsing, it should be usable. This covers the
15981597
// private module fragment. The private module fragment is usable only if
15991598
// it is within the current module unit. And it must be the current
@@ -3911,7 +3910,7 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, SourceLocation Loc,
39113910
// exports are only valid in module purview and outside of any
39123911
// PMF (although a PMF should not even be present in a module
39133912
// with an import).
3914-
assert(FM && FM->isModulePurview() && !FM->isPrivateModule() &&
3913+
assert(FM && FM->isNamedModule() && !FM->isPrivateModule() &&
39153914
"bad export context");
39163915
// .. are attached to a named module M, do not appear in the
39173916
// translation unit containing the point of the lookup..

clang/lib/Sema/SemaModule.cpp

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -939,22 +939,20 @@ void Sema::PopGlobalModuleFragment() {
939939
ModuleScopes.pop_back();
940940
}
941941

942-
Module *Sema::PushImplicitGlobalModuleFragment(SourceLocation BeginLoc,
943-
bool IsExported) {
944-
Module **M = IsExported ? &TheExportedImplicitGlobalModuleFragment
945-
: &TheImplicitGlobalModuleFragment;
946-
if (!*M) {
942+
Module *Sema::PushImplicitGlobalModuleFragment(SourceLocation BeginLoc) {
943+
if (!TheImplicitGlobalModuleFragment) {
947944
ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap();
948-
*M = Map.createImplicitGlobalModuleFragmentForModuleUnit(
949-
BeginLoc, IsExported, getCurrentModule());
945+
TheImplicitGlobalModuleFragment =
946+
Map.createImplicitGlobalModuleFragmentForModuleUnit(BeginLoc,
947+
getCurrentModule());
950948
}
951-
assert(*M && "module creation should not fail");
949+
assert(TheImplicitGlobalModuleFragment && "module creation should not fail");
952950

953951
// Enter the scope of the global module.
954-
ModuleScopes.push_back({BeginLoc, *M,
952+
ModuleScopes.push_back({BeginLoc, TheImplicitGlobalModuleFragment,
955953
/*OuterVisibleModules=*/{}});
956-
VisibleModules.setVisible(*M, BeginLoc);
957-
return *M;
954+
VisibleModules.setVisible(TheImplicitGlobalModuleFragment, BeginLoc);
955+
return TheImplicitGlobalModuleFragment;
958956
}
959957

960958
void Sema::PopImplicitGlobalModuleFragment() {
@@ -963,3 +961,22 @@ void Sema::PopImplicitGlobalModuleFragment() {
963961
"left the wrong module scope, which is not global module fragment");
964962
ModuleScopes.pop_back();
965963
}
964+
965+
bool Sema::isCurrentModulePurview() const {
966+
if (!getCurrentModule())
967+
return false;
968+
969+
/// Does this Module scope describe part of the purview of a standard named
970+
/// C++ module?
971+
switch (getCurrentModule()->Kind) {
972+
case Module::ModuleInterfaceUnit:
973+
case Module::ModuleImplementationUnit:
974+
case Module::ModulePartitionInterface:
975+
case Module::ModulePartitionImplementation:
976+
case Module::PrivateModuleFragment:
977+
case Module::ImplicitGlobalModuleFragment:
978+
return true;
979+
default:
980+
return false;
981+
}
982+
}

clang/lib/Serialization/ASTWriterDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2443,7 +2443,7 @@ static bool isRequiredDecl(const Decl *D, ASTContext &Context,
24432443
// Named modules have different semantics than header modules. Every named
24442444
// module units owns a translation unit. So the importer of named modules
24452445
// doesn't need to deserilize everything ahead of time.
2446-
if (WritingModule && WritingModule->isModulePurview()) {
2446+
if (WritingModule && WritingModule->isNamedModule()) {
24472447
// The PragmaCommentDecl and PragmaDetectMismatchDecl are MSVC's extension.
24482448
// And the behavior of MSVC for such cases will leak this to the module
24492449
// users. Given pragma is not a standard thing, the compiler has the space

clang/test/Modules/export-language-linkage.cppm

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
// RUN: cd %t
44
//
55
// RUN: %clang_cc1 -std=c++20 %t/a.cppm -emit-module-interface -o %t/a.pcm
6-
// RUN: %clang_cc1 -std=c++20 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
7-
// RUN: %clang_cc1 -std=c++20 %t/c.cppm -fsyntax-only -verify
86
// RUN: %clang_cc1 -module-file-info %t/a.pcm | FileCheck %t/a.cppm
7+
// RUN: %clang_cc1 -std=c++20 %t/b.cpp -fmodule-file=a=%t/a.pcm -fsyntax-only -verify
8+
// RUN: %clang_cc1 -std=c++20 %t/c.cppm -emit-module-interface -o %t/c.pcm
9+
// RUN: %clang_cc1 -std=c++20 %t/d.cpp -fsyntax-only -verify -fmodule-file=c=%t/c.pcm
910

1011
//--- a.cppm
1112
export module a;
@@ -25,7 +26,6 @@ export {
2526
extern "C++" void unexported();
2627

2728
// CHECK: Sub Modules:
28-
// CHECK-NEXT: Implicit Module Fragment '<exported implicit global>'
2929
// CHECK-NEXT: Implicit Module Fragment '<implicit global>'
3030

3131
//--- b.cpp
@@ -45,8 +45,19 @@ int use() {
4545
//--- c.cppm
4646
export module c;
4747
extern "C++" {
48-
// We can't use `export` in an unnamed module.
49-
export int f(); // expected-error {{export declaration can only be used within a module purview}}
48+
export int f();
49+
int h();
5050
}
5151

52-
extern "C++" export int g(); // expected-error {{export declaration can only be used within a module purview}}
52+
extern "C++" export int g();
53+
54+
//--- d.cpp
55+
import c;
56+
int use() {
57+
return f() + g();
58+
}
59+
60+
int use_of_nonexported() {
61+
return h(); // expected-error {{missing '#include'; 'h' must be declared before it is used}}
62+
// [email protected]:4 {{declaration here is not visible}}
63+
}

clang/test/SemaCXX/modules.cppm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ struct S {
6262
// language rules right now, but (per personal correspondence between zygoloid
6363
// and gdr) is the intent.
6464
#if TEST == 1
65-
export {
65+
export { // expected-note {{export block begins here}}
6666
extern "C++" {
6767
namespace NestedExport {
68-
export { // expected-error {{export declaration can only be used within a module purview}}
68+
export { // expected-error {{export declaration appears within another export declaration}}
6969
int q;
7070
}
7171
} // namespace NestedExport

0 commit comments

Comments
 (0)