98 changes: 86 additions & 12 deletions clang/lib/Sema/SemaModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
// 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.createGlobalModuleForInterfaceUnit(ModuleLoc);
auto *GlobalModule = Map.createGlobalModuleFragmentForModuleUnit(ModuleLoc);
assert(GlobalModule && "module creation should not fail");

// Enter the scope of the global module.
Expand Down Expand Up @@ -128,7 +128,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,

// Only one module-declaration is permitted per source file.
if (!ModuleScopes.empty() &&
ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
ModuleScopes.back().Module->isModulePurview()) {
Diag(ModuleLoc, diag::err_module_redeclaration);
Diag(VisibleModules.getImportLoc(ModuleScopes.back().Module),
diag::note_prev_module_declaration);
Expand Down Expand Up @@ -220,6 +220,9 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
ModuleScopes.push_back({});
if (getLangOpts().ModulesLocalVisibility)
ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
} else {
// We're done with the global module fragment now.
ActOnEndOfTranslationUnitFragment(TUFragmentKind::Global);
}

// Switch from the global module fragment (if any) to the named module.
Expand All @@ -239,6 +242,68 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
return nullptr;
}

Sema::DeclGroupPtrTy
Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
SourceLocation PrivateLoc) {
// C++20 [basic.link]/2:
// A private-module-fragment shall appear only in a primary module
// interface unit.
switch (ModuleScopes.empty() ? Module::GlobalModuleFragment
: ModuleScopes.back().Module->Kind) {
case Module::ModuleMapModule:
case Module::GlobalModuleFragment:
Diag(PrivateLoc, diag::err_private_module_fragment_not_module);
return nullptr;

case Module::PrivateModuleFragment:
Diag(PrivateLoc, diag::err_private_module_fragment_redefined);
Diag(ModuleScopes.back().BeginLoc, diag::note_previous_definition);
return nullptr;

case Module::ModuleInterfaceUnit:
break;
}

if (!ModuleScopes.back().ModuleInterface) {
Diag(PrivateLoc, diag::err_private_module_fragment_not_module_interface);
Diag(ModuleScopes.back().BeginLoc,
diag::note_not_module_interface_add_export)
<< FixItHint::CreateInsertion(ModuleScopes.back().BeginLoc, "export ");
return nullptr;
}

// FIXME: Check this isn't a module interface partition.
// FIXME: Check that this translation unit does not import any partitions;
// such imports would violate [basic.link]/2's "shall be the only module unit"
// restriction.

// We've finished the public fragment of the translation unit.
ActOnEndOfTranslationUnitFragment(TUFragmentKind::Normal);

auto &Map = PP.getHeaderSearchInfo().getModuleMap();
Module *PrivateModuleFragment =
Map.createPrivateModuleFragmentForInterfaceUnit(
ModuleScopes.back().Module, PrivateLoc);
assert(PrivateModuleFragment && "module creation should not fail");

// Enter the scope of the private module fragment.
ModuleScopes.push_back({});
ModuleScopes.back().BeginLoc = ModuleLoc;
ModuleScopes.back().Module = PrivateModuleFragment;
ModuleScopes.back().ModuleInterface = true;
VisibleModules.setVisible(PrivateModuleFragment, ModuleLoc);

// All declarations created from now on are scoped to the private module
// fragment (and are neither visible nor reachable in importers of the module
// interface).
auto *TU = Context.getTranslationUnitDecl();
TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
TU->setLocalOwningModule(PrivateModuleFragment);

// FIXME: Consider creating an explicit representation of this declaration.
return nullptr;
}

DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc,
Expand Down Expand Up @@ -451,17 +516,26 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc,
SourceLocation LBraceLoc) {
ExportDecl *D = ExportDecl::Create(Context, CurContext, ExportLoc);

// C++ Modules TS draft:
// An export-declaration shall appear in the purview of a module other than
// the global module.
if (ModuleScopes.empty() || !ModuleScopes.back().ModuleInterface)
Diag(ExportLoc, diag::err_export_not_in_module_interface);
// C++20 [module.interface]p1:
// An export-declaration shall appear only [...] in the purview of a module
// interface unit. An export-declaration shall not appear directly or
// indirectly within an unnamed namespace or a private-module-fragment.
// FIXME: Check for the unnamed namespace case.
if (ModuleScopes.empty() || !ModuleScopes.back().Module->isModulePurview()) {
Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
} else if (!ModuleScopes.back().ModuleInterface) {
Diag(ExportLoc, diag::err_export_not_in_module_interface) << 1;
Diag(ModuleScopes.back().BeginLoc,
diag::note_not_module_interface_add_export)
<< FixItHint::CreateInsertion(ModuleScopes.back().BeginLoc, "export ");
} else if (ModuleScopes.back().Module->Kind ==
Module::PrivateModuleFragment) {
Diag(ExportLoc, diag::err_export_in_private_module_fragment);
Diag(ModuleScopes.back().BeginLoc, diag::note_private_module_fragment);
}

// An export-declaration [...] shall not contain more than one
// export keyword.
//
// The intent here is that an export-declaration cannot appear within another
// export-declaration.
// [...] its declaration or declaration-seq shall not contain an
// export-declaration.
if (D->isExported())
Diag(ExportLoc, diag::err_export_within_export);

Expand Down
40 changes: 30 additions & 10 deletions clang/test/CXX/basic/basic.link/p1.cpp
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s

#ifdef NO_GLOBAL_FRAG
// expected-error@#mod-decl {{module declaration must occur at the start of the translation unit}}
// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
#else
#ifndef NO_GLOBAL_FRAG
#ifdef EXPORT_FRAGS
export // expected-error {{global module fragment cannot be exported}}
#endif
module; // #glob-frag
module;
#ifdef NO_MODULE_DECL
// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}}
#endif
#endif

extern int a; // #a1

#ifdef NO_MODULE_DECL
// expected-error@#glob-frag {{missing 'module' declaration at end of global module fragment introduced here}}
#else
export module Foo; // #mod-decl
#ifndef NO_MODULE_DECL
export module Foo;
#ifdef NO_GLOBAL_FRAG
// expected-error@-2 {{module declaration must occur at the start of the translation unit}}
// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
#endif

// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
// expected-note@#a1 {{previous decl}}
Expand All @@ -29,9 +36,22 @@ extern int b;

module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}

#ifndef NO_PRIVATE_FRAG
#ifdef EXPORT_FRAGS
export // expected-error {{private module fragment cannot be exported}}
#endif
module :private;
module :private; // #priv-frag
#ifdef NO_MODULE_DECL
// expected-error@-2 {{private module fragment declaration with no preceding module declaration}}
#endif
#endif

int b; // ok


#ifndef NO_PRIVATE_FRAG
#ifndef NO_MODULE_DECL
module :private; // expected-error {{private module fragment redefined}}
// expected-note@#priv-frag {{previous definition is here}}
#endif
#endif
16 changes: 16 additions & 0 deletions clang/test/CXX/basic/basic.link/p2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify
// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm
// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=%t.pcm

#ifdef EXPORT
// expected-no-diagnostics
export
#else
// expected-note@+2 {{add 'export' here}}
#endif
module M;

#ifndef EXPORT
// expected-error@+2 {{private module fragment in module implementation unit}}
#endif
module :private;
38 changes: 38 additions & 0 deletions clang/test/CXX/module/module.interface/p1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// RUN: %clang_cc1 -std=c++2a %s -DERRORS -verify
// RUN: %clang_cc1 -std=c++2a %s -emit-module-interface -o %t.pcm
// RUN: %clang_cc1 -std=c++2a %s -fmodule-file=%t.pcm -DIMPLEMENTATION -verify -Db=b2 -Dc=c2

module;

#ifdef ERRORS
export int a; // expected-error {{after the module declaration}}
#endif

#ifndef IMPLEMENTATION
export
#else
// expected-error@#1 {{can only be used within a module interface unit}}
// expected-error@#2 {{can only be used within a module interface unit}}
// expected-note@+2 1+{{add 'export'}}
#endif
module M;

export int b; // #1
namespace N {
export int c; // #2
}

#ifdef ERRORS
namespace {
export int d1; // FIXME: invalid
namespace X {
export int d2; // FIXME: invalid
}
}

export export int e; // expected-error {{within another export declaration}}
export { export int f; } // expected-error {{within another export declaration}}

module :private; // expected-note {{private module fragment begins here}}
export int priv; // expected-error {{export declaration cannot be used in a private module fragment}}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
// expected-no-diagnostics
export module A;
#elif IMPLEMENTATION
module A;
module A; // #module-decl
#ifdef BUILT_AS_INTERFACE
// expected-error@-2 {{missing 'export' specifier in module declaration while building module interface}}
#define INTERFACE
Expand All @@ -23,6 +23,9 @@ module A;

#ifndef INTERFACE
export int b; // expected-error {{export declaration can only be used within a module interface unit}}
#ifdef IMPLEMENTATION
// expected-note@#module-decl {{add 'export' here}}
#endif
#else
export int a;
#endif