[c++20] Parsing support for module-declarations, import-declarations,

and the global and private module fragment.

For now, the private module fragment introducer is ignored, but use of
the global module fragment introducer should be properly enforced.

llvm-svn: 358353
This commit is contained in:
Richard Smith 2019-04-14 08:06:59 +00:00
parent de20429cfc
commit d652bdd05f
14 changed files with 461 additions and 98 deletions

View File

@ -1223,15 +1223,22 @@ def err_unexpected_module_decl : Error<
"module declaration can only appear at the top level">;
def err_module_expected_ident : Error<
"expected a module name after '%select{module|import}0'">;
def err_module_implementation_partition : Error<
"module partition must be declared 'export'">;
def err_attribute_not_module_attr : Error<
"%0 attribute cannot be applied to a module">;
def err_attribute_not_import_attr : Error<
"%0 attribute cannot be applied to a module import">;
def err_module_expected_semi : Error<
"expected ';' after module name">;
def err_global_module_introducer_not_at_start : Error<
"'module;' introducing a global module fragment can appear only "
"at the start of the translation unit">;
def err_module_fragment_exported : Error<
"%select{global|private}0 module fragment cannot be exported">;
def err_private_module_fragment_expected_semi : Error<
"expected ';' after private module fragment declaration">;
def err_missing_before_module_end : Error<"expected %0 at end of module">;
def err_unsupported_module_partition : Error<
"sorry, module partitions are not yet supported">;
def err_export_empty : Error<"export declaration cannot be empty">;
}

View File

@ -9199,7 +9199,7 @@ let CategoryName = "Modules Issue" in {
def err_module_decl_in_module_map_module : Error<
"'module' declaration found while building module from module map">;
def err_module_decl_in_header_module : Error<
"'module' declaration found while building header module">;
"'module' declaration found while building header unit">;
def err_module_interface_implementation_mismatch : Error<
"missing 'export' specifier in module declaration while "
"building module interface">;
@ -9217,6 +9217,9 @@ def err_module_redeclaration : Error<
def note_prev_module_declaration : Note<"previous module declaration is here">;
def err_module_declaration_missing : Error<
"missing 'export module' declaration in module interface unit">;
def err_module_declaration_missing_after_global_module_introducer : Error<
"missing 'module' declaration at end of global module fragment "
"introduced here">;
def err_module_private_specialization : Error<
"%select{template|partial|member}0 specialization cannot be "
"declared __module_private__">;
@ -9254,7 +9257,12 @@ def err_module_self_import : Error<
def err_module_import_in_implementation : Error<
"@import of module '%0' in implementation of '%1'; use #import">;
// C++ Modules TS
// C++ Modules
def err_module_decl_not_at_start : Error<
"module declaration must occur at the start of the translation unit">;
def note_global_module_introducer_missing : Note<
"add 'module;' to the start of the file to introduce a "
"global module fragment">;
def err_export_within_export : Error<
"export declaration appears within another export declaration">;
def err_export_not_in_module_interface : Error<

View File

@ -2172,6 +2172,7 @@ private:
None,
ModuleBegin,
ModuleImport,
SkippedModuleImport,
} Kind;
Module *ModuleForHeader = nullptr;

View File

@ -150,11 +150,15 @@ class Parser : public CodeCompletionHandler {
IdentifierInfo *Ident_language, *Ident_defined_in,
*Ident_generated_declaration;
/// C++0x contextual keywords.
/// C++11 contextual keywords.
mutable IdentifierInfo *Ident_final;
mutable IdentifierInfo *Ident_GNU_final;
mutable IdentifierInfo *Ident_override;
// C++2a contextual keywords.
mutable IdentifierInfo *Ident_import;
mutable IdentifierInfo *Ident_module;
// C++ type trait keywords that can be reverted to identifiers and still be
// used as type traits.
llvm::SmallDenseMap<IdentifierInfo *, tok::TokenKind> RevertibleTypeTraits;
@ -424,7 +428,7 @@ public:
/// ParseTopLevelDecl - Parse one top-level declaration. Returns true if
/// the EOF was encountered.
bool ParseTopLevelDecl(DeclGroupPtrTy &Result);
bool ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl = false);
bool ParseTopLevelDecl() {
DeclGroupPtrTy Result;
return ParseTopLevelDecl(Result);
@ -2996,7 +3000,7 @@ private:
//===--------------------------------------------------------------------===//
// Modules
DeclGroupPtrTy ParseModuleDecl();
DeclGroupPtrTy ParseModuleDecl(bool IsFirstDecl);
Decl *ParseModuleImport(SourceLocation AtLoc);
bool parseMisplacedModuleImport();
bool tryParseMisplacedModuleImport() {

View File

@ -1624,8 +1624,10 @@ private:
TypeDiagnoser *Diagnoser);
struct ModuleScope {
SourceLocation BeginLoc;
clang::Module *Module = nullptr;
bool ModuleInterface = false;
bool ImplicitGlobalModuleFragment = false;
VisibleModuleSet OuterVisibleModules;
};
/// The modules we're currently parsing.
@ -2155,24 +2157,43 @@ public:
enum class ModuleDeclKind {
Interface, ///< 'export module X;'
Implementation, ///< 'module X;'
Partition, ///< 'module partition X;'
};
/// The parser has processed a module-declaration that begins the definition
/// of a module interface or implementation.
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
SourceLocation ModuleLoc, ModuleDeclKind MDK,
ModuleIdPath Path);
ModuleIdPath Path, bool IsFirstDecl);
/// The parser has processed a global-module-fragment declaration that begins
/// the definition of the global module fragment of the current module unit.
/// \param ModuleLoc The location of the 'module' keyword.
DeclGroupPtrTy ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc);
/// The parser has processed a private-module-fragment declaration that begins
/// the definition of the private module fragment of the current module unit.
/// \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();
}
/// The parser has processed a module import declaration.
///
/// \param AtLoc The location of the '@' symbol, if any.
///
/// \param StartLoc The location of the first token in the declaration. This
/// could be the location of an '@', 'export', or 'import'.
/// \param ExportLoc The location of the 'export' keyword, if any.
/// \param ImportLoc The location of the 'import' keyword.
///
/// \param Path The module access path.
DeclResult ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc,
ModuleIdPath Path);
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc, ModuleIdPath Path);
DeclResult ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ExportLoc,
SourceLocation ImportLoc, Module *M,
ModuleIdPath Path = {});
/// The parser has processed a module import translated from a
/// #include or similar preprocessing directive.

View File

@ -1648,6 +1648,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc,
EndLoc, LookupFrom, LookupFromFile);
switch (Action.Kind) {
case ImportAction::None:
case ImportAction::SkippedModuleImport:
break;
case ImportAction::ModuleBegin:
EnterAnnotationToken(SourceRange(HashLoc, EndLoc),
@ -2034,6 +2035,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
switch (Action) {
case Skip:
// If we don't need to enter the file, stop now.
if (Module *M = SuggestedModule.getModule())
return {ImportAction::SkippedModuleImport, M};
return {ImportAction::None};
case IncludeLimitReached:
@ -2117,6 +2120,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport(
return {ImportAction::ModuleBegin, M};
}
assert(!IsImportDecl && "failed to diagnose missing module for import decl");
return {ImportAction::None};
}

View File

@ -1168,6 +1168,7 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
LLVM_FALLTHROUGH;
case ImportAction::ModuleImport:
case ImportAction::SkippedModuleImport:
// We chose to import (or textually enter) the file. Convert the
// header-name token into a header unit annotation token.
Suffix[0].setKind(tok::annot_header_unit);

View File

@ -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");
@ -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.
@ -525,6 +532,16 @@ 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();
@ -532,7 +549,7 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) {
// 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);
@ -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
@ -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 *>(
@ -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;
}
@ -2074,38 +2143,83 @@ 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);
@ -2113,7 +2227,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() {
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
@ -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);
@ -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;

View File

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

View File

@ -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
@ -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.
@ -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);
@ -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);
@ -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.
@ -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);
@ -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;
@ -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);
if (!ModuleScopes.empty())
Context.addModuleInitializer(ModuleScopes.back().Module, Import);
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);
// 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;
}

View File

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

View File

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

View File

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

View File

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