forked from OSchip/llvm-project
[C++20][Modules][3/8] Initial handling for module partitions.
This implements the parsing and recognition of module partition CMIs and removes the FIXMEs in the parser. Module partitions are recognised in the base computation of visibility, however additional amendments to visibility follow in subsequent patches. Differential Revision: https://reviews.llvm.org/D118586
This commit is contained in:
parent
e1d4d1c242
commit
69350e569d
|
@ -4467,6 +4467,16 @@ public:
|
||||||
/// @import std.vector;
|
/// @import std.vector;
|
||||||
/// \endcode
|
/// \endcode
|
||||||
///
|
///
|
||||||
|
/// A C++20 module import declaration imports the named module or partition.
|
||||||
|
/// Periods are permitted in C++20 module names, but have no semantic meaning.
|
||||||
|
/// For example:
|
||||||
|
/// \code
|
||||||
|
/// import NamedModule;
|
||||||
|
/// import :SomePartition; // Must be a partition of the current module.
|
||||||
|
/// import Names.Like.this; // Allowed.
|
||||||
|
/// import :and.Also.Partition.names;
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
/// Import declarations can also be implicitly generated from
|
/// Import declarations can also be implicitly generated from
|
||||||
/// \#include/\#import directives.
|
/// \#include/\#import directives.
|
||||||
class ImportDecl final : public Decl,
|
class ImportDecl final : public Decl,
|
||||||
|
|
|
@ -1541,9 +1541,11 @@ def err_private_module_fragment_expected_semi : Error<
|
||||||
"expected ';' after private module fragment declaration">;
|
"expected ';' after private module fragment declaration">;
|
||||||
def err_missing_before_module_end : Error<"expected %0 at end of module">;
|
def err_missing_before_module_end : Error<"expected %0 at end of module">;
|
||||||
def err_unsupported_module_partition : Error<
|
def err_unsupported_module_partition : Error<
|
||||||
"sorry, module partitions are not yet supported">;
|
"module partitions are only supported for C++20 onwards">;
|
||||||
def err_import_not_allowed_here : Error<
|
def err_import_not_allowed_here : Error<
|
||||||
"imports must immediately follow the module declaration">;
|
"imports must immediately follow the module declaration">;
|
||||||
|
def err_partition_import_outside_module : Error<
|
||||||
|
"module partition imports must be within a module purview">;
|
||||||
def err_import_in_wrong_fragment : Error<
|
def err_import_in_wrong_fragment : Error<
|
||||||
"module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;
|
"module%select{| partition}0 imports cannot be in the %select{global|private}1 module fragment">;
|
||||||
|
|
||||||
|
|
|
@ -2210,6 +2210,7 @@ private:
|
||||||
SourceLocation BeginLoc;
|
SourceLocation BeginLoc;
|
||||||
clang::Module *Module = nullptr;
|
clang::Module *Module = nullptr;
|
||||||
bool ModuleInterface = false;
|
bool ModuleInterface = false;
|
||||||
|
bool IsPartition = false;
|
||||||
bool ImplicitGlobalModuleFragment = false;
|
bool ImplicitGlobalModuleFragment = false;
|
||||||
VisibleModuleSet OuterVisibleModules;
|
VisibleModuleSet OuterVisibleModules;
|
||||||
};
|
};
|
||||||
|
@ -2962,7 +2963,7 @@ public:
|
||||||
/// of a module interface or implementation.
|
/// of a module interface or implementation.
|
||||||
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
|
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
|
||||||
SourceLocation ModuleLoc, ModuleDeclKind MDK,
|
SourceLocation ModuleLoc, ModuleDeclKind MDK,
|
||||||
ModuleIdPath Path,
|
ModuleIdPath Path, ModuleIdPath Partition,
|
||||||
ModuleImportState &ImportState);
|
ModuleImportState &ImportState);
|
||||||
|
|
||||||
/// The parser has processed a global-module-fragment declaration that begins
|
/// The parser has processed a global-module-fragment declaration that begins
|
||||||
|
@ -2983,10 +2984,12 @@ public:
|
||||||
/// could be the location of an '@', 'export', or 'import'.
|
/// could be the location of an '@', 'export', or 'import'.
|
||||||
/// \param ExportLoc The location of the 'export' keyword, if any.
|
/// \param ExportLoc The location of the 'export' keyword, if any.
|
||||||
/// \param ImportLoc The location of the 'import' keyword.
|
/// \param ImportLoc The location of the 'import' keyword.
|
||||||
/// \param Path The module access path.
|
/// \param Path The module toplevel name as an access path.
|
||||||
|
/// \param Partition The module partition name as an access path.
|
||||||
DeclResult ActOnModuleImport(SourceLocation StartLoc,
|
DeclResult ActOnModuleImport(SourceLocation StartLoc,
|
||||||
SourceLocation ExportLoc,
|
SourceLocation ExportLoc,
|
||||||
SourceLocation ImportLoc, ModuleIdPath Path);
|
SourceLocation ImportLoc, ModuleIdPath Path,
|
||||||
|
ModuleIdPath Partition = {});
|
||||||
DeclResult ActOnModuleImport(SourceLocation StartLoc,
|
DeclResult ActOnModuleImport(SourceLocation StartLoc,
|
||||||
SourceLocation ExportLoc,
|
SourceLocation ExportLoc,
|
||||||
SourceLocation ImportLoc, Module *M,
|
SourceLocation ImportLoc, Module *M,
|
||||||
|
|
|
@ -2364,20 +2364,19 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
|
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
|
||||||
if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false))
|
if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// Parse the optional module-partition.
|
// Parse the optional module-partition.
|
||||||
|
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
|
||||||
if (Tok.is(tok::colon)) {
|
if (Tok.is(tok::colon)) {
|
||||||
SourceLocation ColonLoc = ConsumeToken();
|
SourceLocation ColonLoc = ConsumeToken();
|
||||||
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
|
if (!getLangOpts().CPlusPlusModules)
|
||||||
if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false))
|
Diag(ColonLoc, diag::err_unsupported_module_partition)
|
||||||
|
<< SourceRange(ColonLoc, Partition.back().second);
|
||||||
|
// Recover by ignoring the partition name.
|
||||||
|
else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
|
||||||
return nullptr;
|
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.
|
// We don't support any module attributes yet; just parse them and diagnose.
|
||||||
|
@ -2387,18 +2386,19 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
|
||||||
|
|
||||||
ExpectAndConsumeSemi(diag::err_module_expected_semi);
|
ExpectAndConsumeSemi(diag::err_module_expected_semi);
|
||||||
|
|
||||||
return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, ImportState);
|
return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
|
||||||
|
ImportState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a module import declaration. This is essentially the same for
|
/// Parse a module import declaration. This is essentially the same for
|
||||||
/// Objective-C and the C++ Modules TS, except for the leading '@' (in ObjC)
|
/// Objective-C and C++20 except for the leading '@' (in ObjC) and the
|
||||||
/// and the trailing optional attributes (in C++).
|
/// trailing optional attributes (in C++).
|
||||||
///
|
///
|
||||||
/// [ObjC] @import declaration:
|
/// [ObjC] @import declaration:
|
||||||
/// '@' 'import' module-name ';'
|
/// '@' 'import' module-name ';'
|
||||||
/// [ModTS] module-import-declaration:
|
/// [ModTS] module-import-declaration:
|
||||||
/// 'import' module-name attribute-specifier-seq[opt] ';'
|
/// 'import' module-name attribute-specifier-seq[opt] ';'
|
||||||
/// [C++2a] module-import-declaration:
|
/// [C++20] module-import-declaration:
|
||||||
/// 'export'[opt] 'import' module-name
|
/// 'export'[opt] 'import' module-name
|
||||||
/// attribute-specifier-seq[opt] ';'
|
/// attribute-specifier-seq[opt] ';'
|
||||||
/// 'export'[opt] 'import' module-partition
|
/// 'export'[opt] 'import' module-partition
|
||||||
|
@ -2418,9 +2418,10 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||||
bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
|
bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import);
|
||||||
SourceLocation ImportLoc = ConsumeToken();
|
SourceLocation ImportLoc = ConsumeToken();
|
||||||
|
|
||||||
|
// For C++20 modules, we can have "name" or ":Partition name" as valid input.
|
||||||
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
|
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
|
||||||
|
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
|
||||||
Module *HeaderUnit = nullptr;
|
Module *HeaderUnit = nullptr;
|
||||||
|
|
||||||
if (Tok.is(tok::header_name)) {
|
if (Tok.is(tok::header_name)) {
|
||||||
// This is a header import that the preprocessor decided we should skip
|
// 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
|
// because it was malformed in some way. Parse and ignore it; it's already
|
||||||
|
@ -2430,17 +2431,16 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||||
// This is a header import that the preprocessor mapped to a module import.
|
// This is a header import that the preprocessor mapped to a module import.
|
||||||
HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
|
HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
|
||||||
ConsumeAnnotationToken();
|
ConsumeAnnotationToken();
|
||||||
} else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) {
|
} else if (Tok.is(tok::colon)) {
|
||||||
SourceLocation ColonLoc = ConsumeToken();
|
SourceLocation ColonLoc = ConsumeToken();
|
||||||
if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
|
if (!getLangOpts().CPlusPlusModules)
|
||||||
|
Diag(ColonLoc, diag::err_unsupported_module_partition)
|
||||||
|
<< SourceRange(ColonLoc, Partition.back().second);
|
||||||
|
// Recover by leaving partition empty.
|
||||||
|
else if (ParseModuleName(ColonLoc, Partition, /*IsImport*/ true))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
// FIXME: Support module partition import.
|
|
||||||
Diag(ColonLoc, diag::err_unsupported_module_partition)
|
|
||||||
<< SourceRange(ColonLoc, Path.back().second);
|
|
||||||
return nullptr;
|
|
||||||
} else {
|
} else {
|
||||||
if (ParseModuleName(ImportLoc, Path, /*IsImport*/true))
|
if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2457,21 +2457,24 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||||
|
|
||||||
// Diagnose mis-imports.
|
// Diagnose mis-imports.
|
||||||
bool SeenError = true;
|
bool SeenError = true;
|
||||||
|
bool HasPart = !Partition.empty();
|
||||||
switch (ImportState) {
|
switch (ImportState) {
|
||||||
case Sema::ModuleImportState::ImportAllowed:
|
case Sema::ModuleImportState::ImportAllowed:
|
||||||
SeenError = false;
|
SeenError = false;
|
||||||
break;
|
break;
|
||||||
case Sema::ModuleImportState::FirstDecl:
|
case Sema::ModuleImportState::FirstDecl:
|
||||||
case Sema::ModuleImportState::NotACXX20Module:
|
case Sema::ModuleImportState::NotACXX20Module:
|
||||||
// TODO: These cases will be an error when partitions are implemented.
|
// We can only import a partition within a module purview.
|
||||||
SeenError = false;
|
if (HasPart)
|
||||||
|
Diag(ImportLoc, diag::err_partition_import_outside_module);
|
||||||
|
else
|
||||||
|
SeenError = false;
|
||||||
break;
|
break;
|
||||||
case Sema::ModuleImportState::GlobalFragment:
|
case Sema::ModuleImportState::GlobalFragment:
|
||||||
// We can only have pre-processor directives in the global module
|
// We can only have pre-processor directives in the global module
|
||||||
// fragment. We can, however have a header unit import here.
|
// fragment. We can, however have a header unit import here.
|
||||||
if (!HeaderUnit)
|
if (!HeaderUnit)
|
||||||
// We do not have partition support yet, so first arg is 0.
|
Diag(ImportLoc, diag::err_import_in_wrong_fragment) << HasPart << 0;
|
||||||
Diag(ImportLoc, diag::err_import_in_wrong_fragment) << 0 << 0;
|
|
||||||
else
|
else
|
||||||
SeenError = false;
|
SeenError = false;
|
||||||
break;
|
break;
|
||||||
|
@ -2482,8 +2485,7 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||||
SeenError = false;
|
SeenError = false;
|
||||||
break;
|
break;
|
||||||
case Sema::ModuleImportState::PrivateFragment:
|
case Sema::ModuleImportState::PrivateFragment:
|
||||||
// We do not have partition support yet, so first arg is 0.
|
Diag(ImportLoc, diag::err_import_in_wrong_fragment) << HasPart << 1;
|
||||||
Diag(ImportLoc, diag::err_import_in_wrong_fragment) << 0 << 1;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (SeenError) {
|
if (SeenError) {
|
||||||
|
@ -2495,8 +2497,9 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
|
||||||
if (HeaderUnit)
|
if (HeaderUnit)
|
||||||
Import =
|
Import =
|
||||||
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
|
Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit);
|
||||||
else if (!Path.empty())
|
else if (!Path.empty() || !Partition.empty())
|
||||||
Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path);
|
Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path,
|
||||||
|
Partition);
|
||||||
ExpectAndConsumeSemi(diag::err_module_expected_semi);
|
ExpectAndConsumeSemi(diag::err_module_expected_semi);
|
||||||
if (Import.isInvalid())
|
if (Import.isInvalid())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -1608,6 +1608,14 @@ bool Sema::CheckRedeclarationModuleOwnership(NamedDecl *New, NamedDecl *Old) {
|
||||||
if (OldM && OldM->Kind == Module::PrivateModuleFragment)
|
if (OldM && OldM->Kind == Module::PrivateModuleFragment)
|
||||||
OldM = OldM->Parent;
|
OldM = OldM->Parent;
|
||||||
|
|
||||||
|
// If we have a decl in a module partition, it is part of the containing
|
||||||
|
// module (which is the only thing that can be importing it).
|
||||||
|
if (NewM && OldM &&
|
||||||
|
(OldM->Kind == Module::ModulePartitionInterface ||
|
||||||
|
OldM->Kind == Module::ModulePartitionImplementation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (NewM == OldM)
|
if (NewM == OldM)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -1561,8 +1561,12 @@ llvm::DenseSet<Module*> &Sema::getLookupModules() {
|
||||||
static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) {
|
static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) {
|
||||||
// If M is the global module fragment of a module that we've not yet finished
|
// If M is the global module fragment of a module that we've not yet finished
|
||||||
// parsing, then it must be part of the current module.
|
// parsing, then it must be part of the current module.
|
||||||
|
// If it's a partition, then it must be visible to an importer (since only
|
||||||
|
// another partition or the named module can import it).
|
||||||
return M->getTopLevelModuleName() == LangOpts.CurrentModule ||
|
return M->getTopLevelModuleName() == LangOpts.CurrentModule ||
|
||||||
(M->Kind == Module::GlobalModuleFragment && !M->Parent);
|
(M->Kind == Module::GlobalModuleFragment && !M->Parent) ||
|
||||||
|
M->Kind == Module::ModulePartitionInterface ||
|
||||||
|
M->Kind == Module::ModulePartitionImplementation;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) {
|
bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) {
|
||||||
|
|
|
@ -54,6 +54,23 @@ static void checkModuleImportContext(Sema &S, Module *M,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We represent the primary and partition names as 'Paths' which are sections
|
||||||
|
// of the hierarchical access path for a clang module. However for C++20
|
||||||
|
// the periods in a name are just another character, and we will need to
|
||||||
|
// flatten them into a string.
|
||||||
|
static std::string stringFromPath(ModuleIdPath Path) {
|
||||||
|
std::string Name;
|
||||||
|
if (Path.empty())
|
||||||
|
return Name;
|
||||||
|
|
||||||
|
for (auto &Piece : Path) {
|
||||||
|
if (!Name.empty())
|
||||||
|
Name += ".";
|
||||||
|
Name += Piece.first->getName();
|
||||||
|
}
|
||||||
|
return Name;
|
||||||
|
}
|
||||||
|
|
||||||
Sema::DeclGroupPtrTy
|
Sema::DeclGroupPtrTy
|
||||||
Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
|
Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
|
||||||
if (!ModuleScopes.empty() &&
|
if (!ModuleScopes.empty() &&
|
||||||
|
@ -80,11 +97,10 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
|
Sema::DeclGroupPtrTy
|
||||||
SourceLocation ModuleLoc,
|
Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
|
||||||
ModuleDeclKind MDK,
|
ModuleDeclKind MDK, ModuleIdPath Path,
|
||||||
ModuleIdPath Path,
|
ModuleIdPath Partition, ModuleImportState &ImportState) {
|
||||||
ModuleImportState &ImportState) {
|
|
||||||
assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
|
assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) &&
|
||||||
"should only have module decl in Modules TS or C++20");
|
"should only have module decl in Modules TS or C++20");
|
||||||
|
|
||||||
|
@ -163,19 +179,20 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
|
||||||
// Flatten the dots in a module name. Unlike Clang's hierarchical module map
|
// 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
|
// modules, the dots here are just another character that can appear in a
|
||||||
// module name.
|
// module name.
|
||||||
std::string ModuleName;
|
std::string ModuleName = stringFromPath(Path);
|
||||||
for (auto &Piece : Path) {
|
bool IsPartition = !Partition.empty();
|
||||||
if (!ModuleName.empty())
|
if (IsPartition) {
|
||||||
ModuleName += ".";
|
ModuleName += ":";
|
||||||
ModuleName += Piece.first->getName();
|
ModuleName += stringFromPath(Partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If a module name was explicitly specified on the command line, it must be
|
// If a module name was explicitly specified on the command line, it must be
|
||||||
// correct.
|
// correct.
|
||||||
if (!getLangOpts().CurrentModule.empty() &&
|
if (!getLangOpts().CurrentModule.empty() &&
|
||||||
getLangOpts().CurrentModule != ModuleName) {
|
getLangOpts().CurrentModule != ModuleName) {
|
||||||
Diag(Path.front().second, diag::err_current_module_name_mismatch)
|
Diag(Path.front().second, diag::err_current_module_name_mismatch)
|
||||||
<< SourceRange(Path.front().second, Path.back().second)
|
<< SourceRange(Path.front().second, IsPartition
|
||||||
|
? Partition.back().second
|
||||||
|
: Path.back().second)
|
||||||
<< getLangOpts().CurrentModule;
|
<< getLangOpts().CurrentModule;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -202,6 +219,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
|
||||||
// Create a Module for the module that we're defining.
|
// Create a Module for the module that we're defining.
|
||||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||||
GlobalModuleFragment);
|
GlobalModuleFragment);
|
||||||
|
if (IsPartition)
|
||||||
|
Mod->Kind = Module::ModulePartitionInterface;
|
||||||
assert(Mod && "module creation should not fail");
|
assert(Mod && "module creation should not fail");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -209,14 +228,26 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
|
||||||
case ModuleDeclKind::Implementation:
|
case ModuleDeclKind::Implementation:
|
||||||
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
|
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
|
||||||
PP.getIdentifierInfo(ModuleName), Path[0].second);
|
PP.getIdentifierInfo(ModuleName), Path[0].second);
|
||||||
Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
|
if (IsPartition) {
|
||||||
Module::AllVisible,
|
// Create an interface, but note that it is an implementation
|
||||||
/*IsInclusionDirective=*/false);
|
// unit.
|
||||||
if (!Mod) {
|
|
||||||
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
|
|
||||||
// Create an empty module interface unit for error recovery.
|
|
||||||
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||||
GlobalModuleFragment);
|
GlobalModuleFragment);
|
||||||
|
Mod->Kind = Module::ModulePartitionImplementation;
|
||||||
|
} else {
|
||||||
|
// C++20 A module-declaration that contains neither an export-
|
||||||
|
// keyword nor a module-partition implicitly imports the primary
|
||||||
|
// module interface unit of the module as if by a module-import-
|
||||||
|
// declaration.
|
||||||
|
Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
|
||||||
|
Module::AllVisible,
|
||||||
|
/*IsInclusionDirective=*/false);
|
||||||
|
if (!Mod) {
|
||||||
|
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
|
||||||
|
// Create an empty module interface unit for error recovery.
|
||||||
|
Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName,
|
||||||
|
GlobalModuleFragment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +264,9 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
|
||||||
// Switch from the global module fragment (if any) to the named module.
|
// Switch from the global module fragment (if any) to the named module.
|
||||||
ModuleScopes.back().BeginLoc = StartLoc;
|
ModuleScopes.back().BeginLoc = StartLoc;
|
||||||
ModuleScopes.back().Module = Mod;
|
ModuleScopes.back().Module = Mod;
|
||||||
ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation;
|
ModuleScopes.back().ModuleInterface =
|
||||||
|
(MDK != ModuleDeclKind::Implementation || IsPartition);
|
||||||
|
ModuleScopes.back().IsPartition = IsPartition;
|
||||||
VisibleModules.setVisible(Mod, ModuleLoc);
|
VisibleModules.setVisible(Mod, ModuleLoc);
|
||||||
|
|
||||||
// From now on, we have an owning module for all declarations we see.
|
// From now on, we have an owning module for all declarations we see.
|
||||||
|
@ -317,17 +350,40 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc,
|
||||||
|
|
||||||
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||||
SourceLocation ExportLoc,
|
SourceLocation ExportLoc,
|
||||||
SourceLocation ImportLoc,
|
SourceLocation ImportLoc, ModuleIdPath Path,
|
||||||
ModuleIdPath Path) {
|
ModuleIdPath Partition) {
|
||||||
// Flatten the module path for a C++20 or Modules TS module name.
|
|
||||||
|
bool IsPartition = !Partition.empty();
|
||||||
|
bool Cxx20Mode = getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS;
|
||||||
|
assert((!IsPartition || Cxx20Mode) && "partition seen in non-C++20 code?");
|
||||||
|
assert((!IsPartition || Path.empty()) &&
|
||||||
|
"trying to import a partition with its named module specified?");
|
||||||
|
|
||||||
|
// For a C++20 module name, flatten into a single identifier with the source
|
||||||
|
// location of the first component.
|
||||||
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc;
|
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc;
|
||||||
|
|
||||||
std::string ModuleName;
|
std::string ModuleName;
|
||||||
if (getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS) {
|
if (IsPartition) {
|
||||||
for (auto &Piece : Path) {
|
// We already checked that we are in a module purview in the parser.
|
||||||
if (!ModuleName.empty())
|
assert(!ModuleScopes.empty() && "in a module purview, but no module?");
|
||||||
ModuleName += ".";
|
Module *NamedMod = ModuleScopes.back().Module;
|
||||||
ModuleName += Piece.first->getName();
|
if (ModuleScopes.back().IsPartition) {
|
||||||
|
// We're importing a partition into a partition, find the name of the
|
||||||
|
// owning named module.
|
||||||
|
size_t P = NamedMod->Name.find_first_of(":");
|
||||||
|
ModuleName = NamedMod->Name.substr(0, P + 1);
|
||||||
|
} else {
|
||||||
|
// We're importing a partition into the named module itself (either the
|
||||||
|
// interface or an implementation TU).
|
||||||
|
ModuleName = NamedMod->Name;
|
||||||
|
ModuleName += ":";
|
||||||
}
|
}
|
||||||
|
ModuleName += stringFromPath(Partition);
|
||||||
|
ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Partition[0].second};
|
||||||
|
Partition = ModuleIdPath(ModuleNameLoc);
|
||||||
|
} else if (Cxx20Mode) {
|
||||||
|
ModuleName = stringFromPath(Path);
|
||||||
ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second};
|
ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second};
|
||||||
Path = ModuleIdPath(ModuleNameLoc);
|
Path = ModuleIdPath(ModuleNameLoc);
|
||||||
}
|
}
|
||||||
|
@ -340,13 +396,14 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Module *Mod =
|
Module *Mod = getModuleLoader().loadModule(
|
||||||
getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible,
|
ImportLoc, IsPartition ? Partition : Path, Module::AllVisible,
|
||||||
/*IsInclusionDirective=*/false);
|
/*IsInclusionDirective=*/false);
|
||||||
if (!Mod)
|
if (!Mod)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path);
|
return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod,
|
||||||
|
IsPartition ? Partition : Path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determine whether \p D is lexically within an export-declaration.
|
/// Determine whether \p D is lexically within an export-declaration.
|
||||||
|
@ -359,8 +416,8 @@ static const ExportDecl *getEnclosingExportDecl(const Decl *D) {
|
||||||
|
|
||||||
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||||
SourceLocation ExportLoc,
|
SourceLocation ExportLoc,
|
||||||
SourceLocation ImportLoc,
|
SourceLocation ImportLoc, Module *Mod,
|
||||||
Module *Mod, ModuleIdPath Path) {
|
ModuleIdPath Path) {
|
||||||
VisibleModules.setVisible(Mod, ImportLoc);
|
VisibleModules.setVisible(Mod, ImportLoc);
|
||||||
|
|
||||||
checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
|
checkModuleImportContext(*this, Mod, ImportLoc, CurContext);
|
||||||
|
@ -379,22 +436,26 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<SourceLocation, 2> IdentifierLocs;
|
SmallVector<SourceLocation, 2> IdentifierLocs;
|
||||||
Module *ModCheck = Mod;
|
|
||||||
for (unsigned I = 0, N = Path.size(); I != N; ++I) {
|
|
||||||
// If we've run out of module parents, just drop the remaining identifiers.
|
|
||||||
// We need the length to be consistent.
|
|
||||||
if (!ModCheck)
|
|
||||||
break;
|
|
||||||
ModCheck = ModCheck->Parent;
|
|
||||||
|
|
||||||
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()) {
|
if (Path.empty()) {
|
||||||
for (; ModCheck; ModCheck = ModCheck->Parent) {
|
// 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.
|
||||||
|
for (Module *ModCheck = Mod; ModCheck; ModCheck = ModCheck->Parent)
|
||||||
IdentifierLocs.push_back(SourceLocation());
|
IdentifierLocs.push_back(SourceLocation());
|
||||||
|
} else if (getLangOpts().CPlusPlusModules && !Mod->Parent) {
|
||||||
|
// A single identifier for the whole name.
|
||||||
|
IdentifierLocs.push_back(Path[0].second);
|
||||||
|
} else {
|
||||||
|
Module *ModCheck = Mod;
|
||||||
|
for (unsigned I = 0, N = Path.size(); I != N; ++I) {
|
||||||
|
// If we've run out of module parents, just drop the remaining
|
||||||
|
// identifiers. We need the length to be consistent.
|
||||||
|
if (!ModCheck)
|
||||||
|
break;
|
||||||
|
ModCheck = ModCheck->Parent;
|
||||||
|
|
||||||
|
IdentifierLocs.push_back(Path[I].second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -420,6 +481,10 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
|
||||||
// An export-declaration shall inhabit a namespace scope and appear in the
|
// An export-declaration shall inhabit a namespace scope and appear in the
|
||||||
// purview of a module interface unit.
|
// purview of a module interface unit.
|
||||||
Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
|
Diag(ExportLoc, diag::err_export_not_in_module_interface) << 0;
|
||||||
|
} else if (getLangOpts().isCompilingModule()) {
|
||||||
|
Module *ThisModule = PP.getHeaderSearchInfo().lookupModule(
|
||||||
|
getLangOpts().CurrentModule, ExportLoc, false, false);
|
||||||
|
assert(ThisModule && "was expecting a module if building one");
|
||||||
}
|
}
|
||||||
|
|
||||||
return Import;
|
return Import;
|
||||||
|
@ -457,6 +522,12 @@ void Sema::BuildModuleInclude(SourceLocation DirectiveLoc, Module *Mod) {
|
||||||
|
|
||||||
getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc);
|
getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc);
|
||||||
VisibleModules.setVisible(Mod, DirectiveLoc);
|
VisibleModules.setVisible(Mod, DirectiveLoc);
|
||||||
|
|
||||||
|
if (getLangOpts().isCompilingModule()) {
|
||||||
|
Module *ThisModule = PP.getHeaderSearchInfo().lookupModule(
|
||||||
|
getLangOpts().CurrentModule, DirectiveLoc, false, false);
|
||||||
|
assert(ThisModule && "was expecting a module if building one");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) {
|
void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) {
|
||||||
|
@ -757,8 +828,9 @@ Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc,
|
||||||
// Enter the scope of the global module.
|
// Enter the scope of the global module.
|
||||||
ModuleScopes.push_back({BeginLoc, GlobalModuleFragment,
|
ModuleScopes.push_back({BeginLoc, GlobalModuleFragment,
|
||||||
/*ModuleInterface=*/false,
|
/*ModuleInterface=*/false,
|
||||||
|
/*IsPartition=*/false,
|
||||||
/*ImplicitGlobalModuleFragment=*/IsImplicit,
|
/*ImplicitGlobalModuleFragment=*/IsImplicit,
|
||||||
/*VisibleModuleSet*/ {}});
|
/*OuterVisibleModules=*/{}});
|
||||||
VisibleModules.setVisible(GlobalModuleFragment, BeginLoc);
|
VisibleModules.setVisible(GlobalModuleFragment, BeginLoc);
|
||||||
|
|
||||||
return GlobalModuleFragment;
|
return GlobalModuleFragment;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||||
|
|
||||||
export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}}
|
export module foo:bar;
|
||||||
import :baz; // expected-error {{sorry, module partitions are not yet supported}}
|
import :baz; // expected-error {{module 'foo:baz' not found}}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
#elif MODE == 1
|
#elif MODE == 1
|
||||||
// expected-no-diagnostics
|
// expected-no-diagnostics
|
||||||
module foo;
|
module foo; // Implementation, implicitly imports foo.
|
||||||
#define IMPORTED
|
#define IMPORTED
|
||||||
|
|
||||||
#elif MODE == 2
|
#elif MODE == 2
|
||||||
|
@ -21,15 +21,15 @@ export module foo; // expected-error {{redefinition of module 'foo'}}
|
||||||
#define IMPORTED
|
#define IMPORTED
|
||||||
|
|
||||||
#elif MODE == 3
|
#elif MODE == 3
|
||||||
export module bar;
|
export module bar; // A different module
|
||||||
|
|
||||||
#elif MODE == 4
|
#elif MODE == 4
|
||||||
module foo:bar; // expected-error {{not yet supported}}
|
module foo:bar; // Partition implementation
|
||||||
#define IMPORTED // FIXME
|
//#define IMPORTED (we don't import foo here)
|
||||||
|
|
||||||
#elif MODE == 5
|
#elif MODE == 5
|
||||||
export module foo:bar; // expected-error {{not yet supported}} expected-error {{redefinition}} expected-note@* {{loaded from}}
|
export module foo:bar; // Partition interface
|
||||||
#define IMPORTED // FIXME
|
//#define IMPORTED (we don't import foo here)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// RUN: rm -rf %t
|
||||||
|
// RUN: mkdir -p %t
|
||||||
|
// RUN: split-file %s %t
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition1.cpp \
|
||||||
|
// RUN: -o %t/A_part1.pcm
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition2.cpp \
|
||||||
|
// RUN: -o %t/A_part2.pcm
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/partition3.cpp \
|
||||||
|
// RUN: -o %t/A_part3.pcm
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/moduleA.cpp \
|
||||||
|
// RUN: -fmodule-file=%t/A_part1.pcm -fmodule-file=%t/A_part2.pcm \
|
||||||
|
// RUN: -fmodule-file=%t/A_part3.pcm -o %t/A.pcm
|
||||||
|
|
||||||
|
// expected-no-diagnostics
|
||||||
|
|
||||||
|
//--- partition1.cpp
|
||||||
|
|
||||||
|
export module A:Part1;
|
||||||
|
|
||||||
|
int part1();
|
||||||
|
|
||||||
|
//--- partition2.cpp
|
||||||
|
|
||||||
|
export module A:Part2;
|
||||||
|
|
||||||
|
int part2();
|
||||||
|
|
||||||
|
//--- partition3.cpp
|
||||||
|
|
||||||
|
export module A:Part3;
|
||||||
|
|
||||||
|
int part3();
|
||||||
|
|
||||||
|
//--- moduleA.cpp
|
||||||
|
|
||||||
|
export module A;
|
||||||
|
|
||||||
|
import :Part1;
|
||||||
|
export import :Part2;
|
||||||
|
import :Part3;
|
||||||
|
|
||||||
|
int foo();
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Module Partition diagnostics
|
||||||
|
|
||||||
|
// RUN: rm -rf %t
|
||||||
|
// RUN: mkdir -p %t
|
||||||
|
// RUN: split-file %s %t
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/bad-import.cpp -verify
|
||||||
|
|
||||||
|
// RUN: %clang_cc1 -std=c++20 -fsyntax-only %t/bad-partition.cpp -verify
|
||||||
|
|
||||||
|
//--- bad-import.cpp
|
||||||
|
|
||||||
|
import :B; // expected-error {{module partition imports must be within a module purview}}
|
||||||
|
|
||||||
|
//--- bad-partition.cpp
|
||||||
|
|
||||||
|
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
|
||||||
|
|
||||||
|
import :Part; // expected-error {{module partition imports cannot be in the global module fragment}}
|
Loading…
Reference in New Issue