202 changes: 177 additions & 25 deletions clang/lib/Parse/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,8 @@ void Parser::Initialize() {
Ident_sealed = nullptr;
Ident_override = nullptr;
Ident_GNU_final = nullptr;
Ident_import = nullptr;
Ident_module = nullptr;

Ident_super = &PP.getIdentifierTable().get("super");

Expand Down Expand Up @@ -512,6 +514,11 @@ void Parser::Initialize() {
PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block);
}

if (getLangOpts().CPlusPlusModules) {
Ident_import = PP.getIdentifierInfo("import");
Ident_module = PP.getIdentifierInfo("module");
}

Actions.Initialize();

// Prime the lexer look-ahead.
Expand All @@ -525,14 +532,24 @@ void Parser::LateTemplateParserCleanupCallback(void *P) {
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds);
}

/// Parse the first top-level declaration in a translation unit.
///
/// translation-unit:
/// [C] external-declaration
/// [C] translation-unit external-declaration
/// [C++] top-level-declaration-seq[opt]
/// [C++20] global-module-fragment[opt] module-declaration
/// top-level-declaration-seq[opt] private-module-fragment[opt]
///
/// Note that in C, it is an error if there is no first declaration.
bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
Actions.ActOnStartOfTranslationUnit();

// C11 6.9p1 says translation units must have at least one top-level
// declaration. C++ doesn't have this restriction. We also don't want to
// complain if we have a precompiled header, although technically if the PCH
// is empty we should still emit the (pedantic) diagnostic.
bool NoTopLevelDecls = ParseTopLevelDecl(Result);
bool NoTopLevelDecls = ParseTopLevelDecl(Result, true);
if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() &&
!getLangOpts().CPlusPlus)
Diag(diag::ext_empty_translation_unit);
Expand All @@ -542,7 +559,11 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {

/// ParseTopLevelDecl - Parse one top-level declaration, return whatever the
/// action tells us to. This returns true if the EOF was encountered.
bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
///
/// top-level-declaration:
/// declaration
/// [C++20] module-import-declaration
bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) {
DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds);

// Skip over the EOF token, flagging end of previous input for incremental
Expand All @@ -557,13 +578,46 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
return false;

case tok::kw_export:
if (NextToken().isNot(tok::kw_module))
switch (NextToken().getKind()) {
case tok::kw_module:
goto module_decl;

// Note: no need to handle kw_import here. We only form kw_import under
// the Modules TS, and in that case 'export import' is parsed as an
// export-declaration containing an import-declaration.

// Recognize context-sensitive C++20 'export module' and 'export import'
// declarations.
case tok::identifier: {
IdentifierInfo *II = NextToken().getIdentifierInfo();
if ((II == Ident_module || II == Ident_import) &&
GetLookAheadToken(2).isNot(tok::coloncolon)) {
if (II == Ident_module)
goto module_decl;
else
goto import_decl;
}
break;
LLVM_FALLTHROUGH;
}

default:
break;
}
break;

case tok::kw_module:
Result = ParseModuleDecl();
module_decl:
Result = ParseModuleDecl(IsFirstDecl);
return false;

// tok::kw_import is handled by ParseExternalDeclaration. (Under the Modules
// TS, an import can occur within an export block.)
import_decl: {
Decl *ImportDecl = ParseModuleImport(SourceLocation());
Result = Actions.ConvertDeclToDeclGroup(ImportDecl);
return false;
}

case tok::annot_module_include:
Actions.ActOnModuleInclude(Tok.getLocation(),
reinterpret_cast<Module *>(
Expand Down Expand Up @@ -595,6 +649,21 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) {
//else don't tell Sema that we ended parsing: more input might come.
return true;

case tok::identifier:
// C++2a [basic.link]p3:
// A token sequence beginning with 'export[opt] module' or
// 'export[opt] import' and not immediately followed by '::'
// is never interpreted as the declaration of a top-level-declaration.
if ((Tok.getIdentifierInfo() == Ident_module ||
Tok.getIdentifierInfo() == Ident_import) &&
NextToken().isNot(tok::coloncolon)) {
if (Tok.getIdentifierInfo() == Ident_module)
goto module_decl;
else
goto import_decl;
}
break;

default:
break;
}
Expand Down Expand Up @@ -2074,46 +2143,91 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
Braces.consumeClose();
}

/// Parse a C++ Modules TS module declaration, which appears at the beginning
/// of a module interface, module partition, or module implementation file.
/// Parse a declaration beginning with the 'module' keyword or C++20
/// context-sensitive keyword (optionally preceded by 'export').
///
/// module-declaration: [Modules TS + P0273R0 + P0629R0]
/// 'export'[opt] 'module' 'partition'[opt]
/// module-name attribute-specifier-seq[opt] ';'
/// module-declaration: [Modules TS + P0629R0]
/// 'export'[opt] 'module' module-name attribute-specifier-seq[opt] ';'
///
/// Note that 'partition' is a context-sensitive keyword.
Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
/// global-module-fragment: [C++2a]
/// 'module' ';' top-level-declaration-seq[opt]
/// module-declaration: [C++2a]
/// 'export'[opt] 'module' module-name module-partition[opt]
/// attribute-specifier-seq[opt] ';'
/// private-module-fragment: [C++2a]
/// 'module' ':' 'private' ';' top-level-declaration-seq[opt]
Parser::DeclGroupPtrTy Parser::ParseModuleDecl(bool IsFirstDecl) {
SourceLocation StartLoc = Tok.getLocation();

Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export)
? Sema::ModuleDeclKind::Interface
: Sema::ModuleDeclKind::Implementation;

assert(Tok.is(tok::kw_module) && "not a module declaration");
assert(
(Tok.is(tok::kw_module) ||
(Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_module)) &&
"not a module declaration");
SourceLocation ModuleLoc = ConsumeToken();

if (Tok.is(tok::identifier) && NextToken().is(tok::identifier) &&
Tok.getIdentifierInfo()->isStr("partition")) {
// If 'partition' is present, this must be a module interface unit.
if (MDK != Sema::ModuleDeclKind::Interface)
Diag(Tok.getLocation(), diag::err_module_implementation_partition)
<< FixItHint::CreateInsertion(ModuleLoc, "export ");
MDK = Sema::ModuleDeclKind::Partition;
// Attributes appear after the module name, not before.
if (Tok.is(tok::l_square))
CheckProhibitedCXX11Attribute();

// Parse a global-module-fragment, if present.
if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) {
SourceLocation SemiLoc = ConsumeToken();
if (!IsFirstDecl) {
Diag(StartLoc, diag::err_global_module_introducer_not_at_start)
<< SourceRange(StartLoc, SemiLoc);
return nullptr;
}
if (MDK == Sema::ModuleDeclKind::Interface) {
Diag(StartLoc, diag::err_module_fragment_exported)
<< /*global*/0 << FixItHint::CreateRemoval(StartLoc);
}
return Actions.ActOnGlobalModuleFragmentDecl(ModuleLoc);
}

// Parse a private-module-fragment, if present.
if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon) &&
NextToken().is(tok::kw_private)) {
if (MDK == Sema::ModuleDeclKind::Interface) {
Diag(StartLoc, diag::err_module_fragment_exported)
<< /*private*/1 << FixItHint::CreateRemoval(StartLoc);
}
ConsumeToken();
SourceLocation PrivateLoc = ConsumeToken();
if (Tok.is(tok::l_square))
CheckProhibitedCXX11Attribute();
ExpectAndConsumeSemi(diag::err_private_module_fragment_expected_semi);
return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc);
}

SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
return nullptr;

// Parse the optional module-partition.
if (Tok.is(tok::colon)) {
SourceLocation ColonLoc = ConsumeToken();
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false))
return nullptr;

// FIXME: Support module partition declarations.
Diag(ColonLoc, diag::err_unsupported_module_partition)
<< SourceRange(ColonLoc, Partition.back().second);
// Recover by parsing as a non-partition.
}

// We don't support any module attributes yet; just parse them and diagnose.
ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_module_attr);

ExpectAndConsumeSemi(diag::err_module_expected_semi);

return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path);
return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, IsFirstDecl);
}

/// Parse a module import declaration. This is essentially the same for
Expand All @@ -2124,17 +2238,50 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
/// '@' 'import' module-name ';'
/// [ModTS] module-import-declaration:
/// 'import' module-name attribute-specifier-seq[opt] ';'
/// [C++2a] module-import-declaration:
/// 'export'[opt] 'import' module-name
/// attribute-specifier-seq[opt] ';'
/// 'export'[opt] 'import' module-partition
/// attribute-specifier-seq[opt] ';'
/// 'export'[opt] 'import' header-name
/// attribute-specifier-seq[opt] ';'
Decl *Parser::ParseModuleImport(SourceLocation AtLoc) {
assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import)
SourceLocation StartLoc = AtLoc.isInvalid() ? Tok.getLocation() : AtLoc;

SourceLocation ExportLoc;
TryConsumeToken(tok::kw_export, ExportLoc);

assert((AtLoc.isInvalid() ? Tok.isOneOf(tok::kw_import, tok::identifier)
: Tok.isObjCAtKeyword(tok::objc_import)) &&
"Improper start to module import");
bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
SourceLocation ImportLoc = ConsumeToken();
SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc;

SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
Module *HeaderUnit = nullptr;

if (Tok.is(tok::header_name)) {
// This is a header import that the preprocessor decided we should skip
// because it was malformed in some way. Parse and ignore it; it's already
// been diagnosed.
ConsumeToken();
} else if (Tok.is(tok::annot_header_unit)) {
// This is a header import that the preprocessor mapped to a module import.
HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
ConsumeAnnotationToken();
} else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) {
SourceLocation ColonLoc = ConsumeToken();
if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
return nullptr;

// FIXME: Support module partition import.
Diag(ColonLoc, diag::err_unsupported_module_partition)
<< SourceRange(ColonLoc, Path.back().second);
return nullptr;
} else {
if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
return nullptr;
}

ParsedAttributesWithRange Attrs(AttrFactory);
MaybeParseCXX11Attributes(Attrs);
Expand All @@ -2147,7 +2294,12 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc) {
return nullptr;
}

DeclResult Import = Actions.ActOnModuleImport(StartLoc, ImportLoc, Path);
DeclResult Import;
if (HeaderUnit)
Import =
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
else if (!Path.empty())
Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path);
ExpectAndConsumeSemi(diag::err_module_expected_semi);
if (Import.isInvalid())
return nullptr;
Expand Down
32 changes: 15 additions & 17 deletions clang/lib/Sema/Sema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -849,24 +849,11 @@ void Sema::ActOnStartOfTranslationUnit() {
if (getLangOpts().ModulesTS &&
(getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface ||
getLangOpts().getCompilingModule() == LangOptions::CMK_None)) {
// We start in an implied global module fragment.
SourceLocation StartOfTU =
SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID());

// 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(StartOfTU);
assert(GlobalModule && "module creation should not fail");

// Enter the scope of the global module.
ModuleScopes.push_back({});
ModuleScopes.back().Module = GlobalModule;
VisibleModules.setVisible(GlobalModule, StartOfTU);

// All declarations created from now on are owned by the global module.
auto *TU = Context.getTranslationUnitDecl();
TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
TU->setLocalOwningModule(GlobalModule);
ActOnGlobalModuleFragmentDecl(StartOfTU);
ModuleScopes.back().ImplicitGlobalModuleFragment = true;
}
}

Expand Down Expand Up @@ -997,13 +984,24 @@ void Sema::ActOnEndOfTranslationUnit() {
checkUndefinedButUsed(*this);
}

// A global-module-fragment is only permitted within a module unit.
bool DiagnosedMissingModuleDeclaration = false;
if (!ModuleScopes.empty() &&
ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment &&
!ModuleScopes.back().ImplicitGlobalModuleFragment) {
Diag(ModuleScopes.back().BeginLoc,
diag::err_module_declaration_missing_after_global_module_introducer);
DiagnosedMissingModuleDeclaration = true;
}

if (TUKind == TU_Module) {
// If we are building a module interface unit, we need to have seen the
// module declaration by now.
if (getLangOpts().getCompilingModule() ==
LangOptions::CMK_ModuleInterface &&
(ModuleScopes.empty() ||
ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit)) {
ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) &&
!DiagnosedMissingModuleDeclaration) {
// FIXME: Make a better guess as to where to put the module declaration.
Diag(getSourceManager().getLocForStartOfFile(
getSourceManager().getMainFileID()),
Expand Down
121 changes: 101 additions & 20 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17016,12 +17016,44 @@ static void checkModuleImportContext(Sema &S, Module *M,
}
}

Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
SourceLocation ModuleLoc,
ModuleDeclKind MDK,
ModuleIdPath Path) {
assert(getLangOpts().ModulesTS &&
"should only have module decl in modules TS");
Sema::DeclGroupPtrTy
Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
if (!ModuleScopes.empty() &&
ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) {
// Under -std=c++2a -fmodules-ts, we can find an explicit 'module;' after
// already implicitly entering the global module fragment. That's OK.
assert(getLangOpts().CPlusPlusModules && getLangOpts().ModulesTS &&
"unexpectedly encountered multiple global module fragment decls");
ModuleScopes.back().BeginLoc = ModuleLoc;
return nullptr;
}

// 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);
assert(GlobalModule && "module creation should not fail");

// Enter the scope of the global module.
ModuleScopes.push_back({});
ModuleScopes.back().BeginLoc = ModuleLoc;
ModuleScopes.back().Module = GlobalModule;
VisibleModules.setVisible(GlobalModule, ModuleLoc);

// All declarations created from now on are owned by the global module.
auto *TU = Context.getTranslationUnitDecl();
TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible);
TU->setLocalOwningModule(GlobalModule);

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

Sema::DeclGroupPtrTy
Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) {
assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
"should only have module decl in Modules TS or C++20");

// A module implementation unit requires that we are not compiling a module
// of any kind. A module interface unit requires that we are not compiling a
Expand Down Expand Up @@ -17051,19 +17083,40 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
return nullptr;
}

assert(ModuleScopes.size() == 1 && "expected to be at global module scope");
assert(ModuleScopes.size() <= 1 && "expected to be at global module scope");

// FIXME: Most of this work should be done by the preprocessor rather than
// here, in order to support macro import.

// Only one module-declaration is permitted per source file.
if (ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
if (!ModuleScopes.empty() &&
ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) {
Diag(ModuleLoc, diag::err_module_redeclaration);
Diag(VisibleModules.getImportLoc(ModuleScopes.back().Module),
diag::note_prev_module_declaration);
return nullptr;
}

// Find the global module fragment we're adopting into this module, if any.
Module *GlobalModuleFragment = nullptr;
if (!ModuleScopes.empty() &&
ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment)
GlobalModuleFragment = ModuleScopes.back().Module;

// In C++20, the module-declaration must be the first declaration if there
// is no global module fragment.
if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !GlobalModuleFragment) {
Diag(ModuleLoc, diag::err_module_decl_not_at_start);
SourceLocation BeginLoc =
ModuleScopes.empty()
? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
: ModuleScopes.back().BeginLoc;
if (BeginLoc.isValid()) {
Diag(BeginLoc, diag::note_global_module_introducer_missing)
<< FixItHint::CreateInsertion(BeginLoc, "module;\n");
}
}

// Flatten the dots in a module name. Unlike Clang's hierarchical module map
// modules, the dots here are just another character that can appear in a
// module name.
Expand Down Expand Up @@ -17105,15 +17158,11 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,

// Create a Module for the module that we're defining.
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
ModuleScopes.front().Module);
GlobalModuleFragment);
assert(Mod && "module creation should not fail");
break;
}

case ModuleDeclKind::Partition:
// FIXME: Check we are in a submodule of the named module.
return nullptr;

case ModuleDeclKind::Implementation:
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
PP.getIdentifierInfo(ModuleName), Path[0].second);
Expand All @@ -17124,12 +17173,19 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
// Create an empty module interface unit for error recovery.
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
ModuleScopes.front().Module);
GlobalModuleFragment);
}
break;
}

// Switch from the global module to the named module.
if (!GlobalModuleFragment) {
ModuleScopes.push_back({});
if (getLangOpts().ModulesLocalVisibility)
ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules);
}

// Switch from the global module fragment (if any) to the named module.
ModuleScopes.back().BeginLoc = StartLoc;
ModuleScopes.back().Module = Mod;
ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation;
VisibleModules.setVisible(Mod, ModuleLoc);
Expand All @@ -17146,6 +17202,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
}

DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc,
ModuleIdPath Path) {
// Flatten the module path for a Modules TS module name.
Expand All @@ -17167,6 +17224,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
if (!Mod)
return true;

return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
}

DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc,
Module *Mod, ModuleIdPath Path) {
VisibleModules.setVisible(Mod, ImportLoc);

checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
Expand All @@ -17176,12 +17240,15 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
// silently ignoring the import.
// Import-from-implementation is valid in the Modules TS. FIXME: Should we
// warn on a redundant import of the current module?
// FIXME: Import of a module from an implementation partition of the same
// module is permitted.
if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule &&
(getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS))
(getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) {
Diag(ImportLoc, getLangOpts().isCompilingModule()
? diag::err_module_self_import
: diag::err_module_import_in_implementation)
<< Mod->getFullModuleName() << getLangOpts().CurrentModule;
}

SmallVector<SourceLocation, 2> IdentifierLocs;
Module *ModCheck = Mod;
Expand All @@ -17195,16 +17262,30 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
IdentifierLocs.push_back(Path[I].second);
}

// If this was a header import, pad out with dummy locations.
// FIXME: Pass in and use the location of the header-name token in this case.
if (Path.empty()) {
for (; ModCheck; ModCheck = ModCheck->Parent) {
IdentifierLocs.push_back(SourceLocation());
}
}

ImportDecl *Import = ImportDecl::Create(Context, CurContext, StartLoc,
Mod, IdentifierLocs);
CurContext->addDecl(Import);

// Sequence initialization of the imported module before that of the current
// module, if any.
if (!ModuleScopes.empty())
Context.addModuleInitializer(ModuleScopes.back().Module, Import);
CurContext->addDecl(Import);

// Re-export the module if needed.
if (Import->isExported() &&
!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface)
getCurrentModule()->Exports.emplace_back(Mod, false);
if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) {
if (ExportLoc.isValid() || Import->isExported())
getCurrentModule()->Exports.emplace_back(Mod, false);
} else if (ExportLoc.isValid()) {
Diag(ExportLoc, diag::err_export_not_in_module_interface);
}

return Import;
}
Expand Down
37 changes: 37 additions & 0 deletions clang/test/CXX/basic/basic.link/p1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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 -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
#ifdef EXPORT_FRAGS
export // expected-error {{global module fragment cannot be exported}}
#endif
module; // #glob-frag
#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

// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
// expected-note@#a1 {{previous decl}}
#endif

int a; // #a2
extern int b;

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

#ifdef EXPORT_FRAGS
export // expected-error {{private module fragment cannot be exported}}
#endif
module :private;

int b; // ok
53 changes: 53 additions & 0 deletions clang/test/CXX/basic/basic.link/p3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// RUN: %clang_cc1 -std=c++2a -verify %s
// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=1
// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=2

module;

#if IMPORT_ERROR != 2
struct import { struct inner {}; };
#endif
struct module { struct inner {}; };

constexpr int n = 123;

export module m; // #1

// Import errors are fatal, so we test them in isolation.
#if IMPORT_ERROR == 1
import x = {}; // expected-error {{module 'x' not found}}

#elif IMPORT_ERROR == 2
struct X;
template<int> struct import;
template<> struct import<n> {
static X y;
};

// This is not valid because the 'import <n>' is a pp-import, even though it
// grammatically can't possibly be an import declaration.
struct X {} import<n>::y; // expected-error {{'n' file not found}}

#else
module y = {}; // expected-error {{multiple module declarations}} expected-error 2{{}}
// expected-note@#1 {{previous module declaration}}

::import x = {};
::module y = {};

import::inner xi = {};
module::inner yi = {};

namespace N {
module a;
import b;
}

extern "C++" module cxxm;
extern "C++" import cxxi;

template<typename T> module module_var_template;

// This is a variable named 'import' that shadows the type 'import' above.
struct X {} import;
#endif
4 changes: 4 additions & 0 deletions clang/test/CXX/module/module.unit/p3.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// RUN: %clang_cc1 -std=c++2a -verify %s

export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}}
import :baz; // expected-error {{sorry, module partitions are not yet supported}}
34 changes: 13 additions & 21 deletions clang/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,39 @@
//
// Module implementation for unknown and known module. (The former is ill-formed.)
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=1 -DEXPORT= -DPARTITION= -DMODULE_NAME=z
// RUN: -DTEST=1 -DEXPORT= -DMODULE_NAME=z
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=2 -DEXPORT= -DPARTITION= -DMODULE_NAME=x
// RUN: -DTEST=2 -DEXPORT= -DMODULE_NAME=x
//
// Module interface for unknown and known module. (The latter is ill-formed due to
// redefinition.)
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=3 -DEXPORT=export -DPARTITION= -DMODULE_NAME=z
// RUN: -DTEST=3 -DEXPORT=export -DMODULE_NAME=z
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=4 -DEXPORT=export -DPARTITION= -DMODULE_NAME=x
//
// Defining a module partition.
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=5 -DEXPORT=export -DPARTITION=partition -DMODULE_NAME=z
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=6 -DEXPORT= -DPARTITION=partition -DMODULE_NAME=z
// RUN: -DTEST=4 -DEXPORT=export -DMODULE_NAME=x
//
// Miscellaneous syntax.
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=7 -DEXPORT= -DPARTITION=elderberry -DMODULE_NAME=z
// RUN: -DTEST=7 -DEXPORT=export -DMODULE_NAME='z elderberry'
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=8 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[]]'
// RUN: -DTEST=8 -DEXPORT=export -DMODULE_NAME='z [[]]'
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=9 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[fancy]]'
// RUN: -DTEST=9 -DEXPORT=export -DMODULE_NAME='z [[fancy]]'
// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \
// RUN: -DTEST=10 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[maybe_unused]]'
// RUN: -DTEST=10 -DEXPORT=export -DMODULE_NAME='z [[maybe_unused]]'

EXPORT module PARTITION MODULE_NAME;
EXPORT module MODULE_NAME;
#if TEST == 4
// expected-error@-2 {{redefinition of module 'x'}}
// expected-note-re@module-declaration.cpp:* {{loaded from '{{.*[/\\]}}x.pcm'}}
#elif TEST == 6
// expected-error@-5 {{module partition must be declared 'export'}}
#elif TEST == 7
// expected-error@-7 {{expected ';'}} expected-error@-7 {{requires a type specifier}} expected-error@-7 {{definition of module 'elderberry' is not available}}
// expected-error@-5 {{expected ';'}} expected-error@-5 {{requires a type specifier}}
#elif TEST == 9
// expected-warning@-9 {{unknown attribute 'fancy' ignored}}
// expected-warning@-7 {{unknown attribute 'fancy' ignored}}
#elif TEST == 10
// expected-error-re@-11 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
// expected-error-re@-9 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
#elif TEST == 1
// expected-error@-13 {{definition of module 'z' is not available}}
// expected-error@-11 {{definition of module 'z' is not available}}
#else
// expected-no-diagnostics
#endif