[c++2a] Add semantic support for private module fragments.

llvm-svn: 358713
This commit is contained in:
Richard Smith 2019-04-18 21:12:54 +00:00
parent 4664916017
commit a5bbbfef15
13 changed files with 326 additions and 97 deletions

View File

@ -9276,8 +9276,20 @@ def note_global_module_introducer_missing : Note<
def err_export_within_export : Error<
"export declaration appears within another export declaration">;
def err_export_not_in_module_interface : Error<
"export declaration can only be used within a module interface unit after "
"the module declaration">;
"export declaration can only be used within a module interface unit"
"%select{ after the module declaration|}0">;
def err_export_in_private_module_fragment : Error<
"export declaration cannot be used in a private module fragment">;
def note_private_module_fragment : Note<
"private module fragment begins here">;
def err_private_module_fragment_not_module : Error<
"private module fragment declaration with no preceding module declaration">;
def err_private_module_fragment_redefined : Error<
"private module fragment redefined">;
def err_private_module_fragment_not_module_interface : Error<
"private module fragment in module implementation unit">;
def note_not_module_interface_add_export : Note<
"add 'export' here if this is intended to be a module interface unit">;
def ext_equivalent_internal_linkage_decl_in_modules : ExtWarn<
"ambiguous use of internal linkage declaration %0 defined in multiple modules">,

View File

@ -77,9 +77,11 @@ public:
/// This is a C++ Modules TS module interface unit.
ModuleInterfaceUnit,
/// This is a fragment of the global module within some C++ Modules
/// TS module.
/// This is a fragment of the global module within some C++ module.
GlobalModuleFragment,
/// This is the private module fragment within some C++ module.
PrivateModuleFragment,
};
/// The kind of this module.
@ -111,6 +113,11 @@ public:
/// eventually be exposed, for use in "private" modules.
std::string ExportAsModule;
/// Does this Module scope describe part of the purview of a named C++ module?
bool isModulePurview() const {
return Kind == ModuleInterfaceUnit || Kind == PrivateModuleFragment;
}
private:
/// The submodules of this module, indexed by name.
std::vector<Module *> SubModules;

View File

@ -520,14 +520,18 @@ public:
bool IsFramework,
bool IsExplicit);
/// Create a 'global module' for a C++ Modules TS module interface unit.
/// Create a global module fragment for a C++ module unit.
///
/// We model the global module 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 *createGlobalModuleForInterfaceUnit(SourceLocation Loc);
/// 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);
/// Create a new module for a C++ Modules TS module interface unit.
/// Create a global module fragment for a C++ module interface unit.
Module *createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
SourceLocation Loc);
/// Create a new module for a C++ module interface unit.
/// The module must not already exist, and will be configured for the current
/// compilation.
///

View File

@ -1386,8 +1386,21 @@ public:
void emitAndClearUnusedLocalTypedefWarnings();
enum TUFragmentKind {
/// The global module fragment, between 'module;' and a module-declaration.
Global,
/// A normal translation unit fragment. For a non-module unit, this is the
/// entire translation unit. Otherwise, it runs from the module-declaration
/// to the private-module-fragment (if any) or the end of the TU (if not).
Normal,
/// The private module fragment, between 'module :private;' and the end of
/// the translation unit.
Private
};
void ActOnStartOfTranslationUnit();
void ActOnEndOfTranslationUnit();
void ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind);
void CheckDelegatingCtorCycles();
@ -2180,10 +2193,7 @@ public:
/// \param ModuleLoc The location of the 'module' keyword.
/// \param PrivateLoc The location of the 'private' keyword.
DeclGroupPtrTy ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
SourceLocation PrivateLoc) {
// FIXME
return DeclGroupPtrTy();
}
SourceLocation PrivateLoc);
/// The parser has processed a module import declaration.
///

View File

@ -567,6 +567,13 @@ static bool isSingleLineLanguageLinkage(const Decl &D) {
return false;
}
/// Determine whether D is declared in the purview of a named module.
static bool isInModulePurview(const NamedDecl *D) {
if (auto *M = D->getOwningModule())
return M->isModulePurview();
return false;
}
static bool isExportedFromModuleIntefaceUnit(const NamedDecl *D) {
// FIXME: Handle isModulePrivate.
switch (D->getModuleOwnershipKind()) {
@ -575,8 +582,7 @@ static bool isExportedFromModuleIntefaceUnit(const NamedDecl *D) {
return false;
case Decl::ModuleOwnershipKind::Visible:
case Decl::ModuleOwnershipKind::VisibleWhenImported:
if (auto *M = D->getOwningModule())
return M->Kind == Module::ModuleInterfaceUnit;
return isInModulePurview(D);
}
llvm_unreachable("unexpected module ownership kind");
}
@ -586,9 +592,8 @@ static LinkageInfo getInternalLinkageFor(const NamedDecl *D) {
// as "module-internal linkage", which means that they have internal linkage
// formally but can be indirectly accessed from outside the module via inline
// functions and templates defined within the module.
if (auto *M = D->getOwningModule())
if (M->Kind == Module::ModuleInterfaceUnit)
return LinkageInfo(ModuleInternalLinkage, DefaultVisibility, false);
if (isInModulePurview(D))
return LinkageInfo(ModuleInternalLinkage, DefaultVisibility, false);
return LinkageInfo::internal();
}
@ -598,11 +603,9 @@ static LinkageInfo getExternalLinkageFor(const NamedDecl *D) {
// - A name declared at namespace scope that does not have internal linkage
// by the previous rules and that is introduced by a non-exported
// declaration has module linkage.
if (auto *M = D->getOwningModule())
if (M->Kind == Module::ModuleInterfaceUnit)
if (!isExportedFromModuleIntefaceUnit(
cast<NamedDecl>(D->getCanonicalDecl())))
return LinkageInfo(ModuleLinkage, DefaultVisibility, false);
if (isInModulePurview(D) &&
!isExportedFromModuleIntefaceUnit(cast<NamedDecl>(D->getCanonicalDecl())))
return LinkageInfo(ModuleLinkage, DefaultVisibility, false);
return LinkageInfo::external();
}
@ -1507,6 +1510,11 @@ Module *Decl::getOwningModuleForLinkage(bool IgnoreLinkage) const {
}
return InternalLinkage ? M->Parent : nullptr;
}
case Module::PrivateModuleFragment:
// The private module fragment is part of its containing module for linkage
// purposes.
return M->Parent;
}
llvm_unreachable("unknown module kind");

View File

@ -806,7 +806,7 @@ std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name,
return std::make_pair(Result, true);
}
Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) {
Module *ModuleMap::createGlobalModuleFragmentForModuleUnit(SourceLocation Loc) {
PendingSubmodules.emplace_back(
new Module("<global>", Loc, nullptr, /*IsFramework*/ false,
/*IsExplicit*/ true, NumCreatedModules++));
@ -814,6 +814,16 @@ Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) {
return PendingSubmodules.back().get();
}
Module *
ModuleMap::createPrivateModuleFragmentForInterfaceUnit(Module *Parent,
SourceLocation Loc) {
auto *Result =
new Module("<private>", Loc, Parent, /*IsFramework*/ false,
/*IsExplicit*/ true, NumCreatedModules++);
Result->Kind = Module::PrivateModuleFragment;
return Result;
}
Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
StringRef Name,
Module *GlobalModule) {

View File

@ -857,20 +857,13 @@ void Sema::ActOnStartOfTranslationUnit() {
}
}
/// ActOnEndOfTranslationUnit - This is called at the very end of the
/// translation unit when EOF is reached and all but the top-level scope is
/// popped.
void Sema::ActOnEndOfTranslationUnit() {
assert(DelayedDiagnostics.getCurrentPool() == nullptr
&& "reached end of translation unit with a pool attached?");
// If code completion is enabled, don't perform any end-of-translation-unit
// work.
if (PP.isCodeCompletionEnabled())
void Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
// No explicit actions are required at the end of the global module fragment.
if (Kind == TUFragmentKind::Global)
return;
// Transfer late parsed template instantiations over to the pending template
// instantiation list. During normal compliation, the late template parser
// instantiation list. During normal compilation, the late template parser
// will be installed and instantiating these templates will succeed.
//
// If we are building a TU prefix for serialization, it is also safe to
@ -883,50 +876,79 @@ void Sema::ActOnEndOfTranslationUnit() {
LateParsedInstantiations.end());
LateParsedInstantiations.clear();
// If DefinedUsedVTables ends up marking any virtual member functions it
// might lead to more pending template instantiations, which we then need
// to instantiate.
DefineUsedVTables();
// C++: Perform implicit template instantiations.
//
// FIXME: When we perform these implicit instantiations, we do not
// carefully keep track of the point of instantiation (C++ [temp.point]).
// This means that name lookup that occurs within the template
// instantiation will always happen at the end of the translation unit,
// so it will find some names that are not required to be found. This is
// valid, but we could do better by diagnosing if an instantiation uses a
// name that was not visible at its first point of instantiation.
if (ExternalSource) {
// Load pending instantiations from the external source.
SmallVector<PendingImplicitInstantiation, 4> Pending;
ExternalSource->ReadPendingInstantiations(Pending);
for (auto PII : Pending)
if (auto Func = dyn_cast<FunctionDecl>(PII.first))
Func->setInstantiationIsPending(true);
PendingInstantiations.insert(PendingInstantiations.begin(),
Pending.begin(), Pending.end());
}
{
llvm::TimeTraceScope TimeScope("PerformPendingInstantiations",
StringRef(""));
PerformPendingInstantiations();
}
assert(LateParsedInstantiations.empty() &&
"end of TU template instantiation should not create more "
"late-parsed templates");
}
/// ActOnEndOfTranslationUnit - This is called at the very end of the
/// translation unit when EOF is reached and all but the top-level scope is
/// popped.
void Sema::ActOnEndOfTranslationUnit() {
assert(DelayedDiagnostics.getCurrentPool() == nullptr
&& "reached end of translation unit with a pool attached?");
// If code completion is enabled, don't perform any end-of-translation-unit
// work.
if (PP.isCodeCompletionEnabled())
return;
// Complete translation units and modules define vtables and perform implicit
// instantiations. PCH files do not.
if (TUKind != TU_Prefix) {
DiagnoseUseOfUnimplementedSelectors();
// If DefinedUsedVTables ends up marking any virtual member functions it
// might lead to more pending template instantiations, which we then need
// to instantiate.
DefineUsedVTables();
// C++: Perform implicit template instantiations.
//
// FIXME: When we perform these implicit instantiations, we do not
// carefully keep track of the point of instantiation (C++ [temp.point]).
// This means that name lookup that occurs within the template
// instantiation will always happen at the end of the translation unit,
// so it will find some names that are not required to be found. This is
// valid, but we could do better by diagnosing if an instantiation uses a
// name that was not visible at its first point of instantiation.
if (ExternalSource) {
// Load pending instantiations from the external source.
SmallVector<PendingImplicitInstantiation, 4> Pending;
ExternalSource->ReadPendingInstantiations(Pending);
for (auto PII : Pending)
if (auto Func = dyn_cast<FunctionDecl>(PII.first))
Func->setInstantiationIsPending(true);
PendingInstantiations.insert(PendingInstantiations.begin(),
Pending.begin(), Pending.end());
}
{
llvm::TimeTraceScope TimeScope("PerformPendingInstantiations",
StringRef(""));
PerformPendingInstantiations();
}
assert(LateParsedInstantiations.empty() &&
"end of TU template instantiation should not create more "
"late-parsed templates");
ActOnEndOfTranslationUnitFragment(
!ModuleScopes.empty() && ModuleScopes.back().Module->Kind ==
Module::PrivateModuleFragment
? TUFragmentKind::Private
: TUFragmentKind::Normal);
if (LateTemplateParserCleanup)
LateTemplateParserCleanup(OpaqueParser);
CheckDelayedMemberExceptionSpecs();
} else {
// If we are building a TU prefix for serialization, it is safe to transfer
// these over, even though they are not parsed. The end of the TU should be
// outside of any eager template instantiation scope, so when this AST is
// deserialized, these templates will not be parsed until the end of the
// combined TU.
PendingInstantiations.insert(PendingInstantiations.end(),
LateParsedInstantiations.begin(),
LateParsedInstantiations.end());
LateParsedInstantiations.clear();
}
DiagnoseUnterminatedPragmaPack();
@ -1000,7 +1022,7 @@ void Sema::ActOnEndOfTranslationUnit() {
if (getLangOpts().getCompilingModule() ==
LangOptions::CMK_ModuleInterface &&
(ModuleScopes.empty() ||
ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) &&
!ModuleScopes.back().Module->isModulePurview()) &&
!DiagnosedMissingModuleDeclaration) {
// FIXME: Make a better guess as to where to put the module declaration.
Diag(getSourceManager().getLocForStartOfFile(

View File

@ -1461,12 +1461,17 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) {
Module *NewM = New->getOwningModule();
Module *OldM = Old->getOwningModule();
if (NewM && NewM->Kind == Module::PrivateModuleFragment)
NewM = NewM->Parent;
if (OldM && OldM->Kind == Module::PrivateModuleFragment)
OldM = OldM->Parent;
if (NewM == OldM)
return false;
// FIXME: Check proclaimed-ownership-declarations here too.
bool NewIsModuleInterface = NewM && NewM->Kind == Module::ModuleInterfaceUnit;
bool OldIsModuleInterface = OldM && OldM->Kind == Module::ModuleInterfaceUnit;
bool NewIsModuleInterface = NewM && NewM->isModulePurview();
bool OldIsModuleInterface = OldM && OldM->isModulePurview();
if (NewIsModuleInterface || OldIsModuleInterface) {
// C++ Modules TS [basic.def.odr] 6.2/6.7 [sic]:
// if a declaration of D [...] appears in the purview of a module, all

View File

@ -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.
@ -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);
@ -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.
@ -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,
@ -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);

View File

@ -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}}
@ -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

View File

@ -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;

View File

@ -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

View File

@ -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
@ -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