Revert "[Clang] Implement P3034R1 Module Declarations Shouldn’t be Macros" (#99838)

Reverts llvm/llvm-project#90574
This commit is contained in:
yronglin 2024-07-22 13:16:51 +08:00 committed by GitHub
parent 7b28cc0c59
commit c91e85278c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 265 additions and 719 deletions

View File

@ -285,8 +285,6 @@ C++2c Feature Support
- Implemented `P2963R3 Ordering of constraints involving fold expressions <https://wg21.link/P2963R3>`_.
- Implemented `P3034R1 Module Declarations Shouldnt be Macros <https://wg21.link/P3034R1>`_.
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -952,11 +952,6 @@ def warn_module_conflict : Warning<
InGroup<ModuleConflict>;
// C++20 modules
def err_module_decl_cannot_be_macros : Error<
"the module name in a module%select{| partition}0 declaration cannot contain "
"an object-like macro %1">;
def err_unxepected_paren_in_module_decl : Error<
"unexpected '(' after the module name in a module%select{| partition}0 declaration">;
def err_header_import_semi_in_macro : Error<
"semicolon terminating header import declaration cannot be produced "
"by a macro">;

View File

@ -180,10 +180,6 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsModulesImport : 1;
// True if this is the 'module' contextual keyword.
LLVM_PREFERRED_TYPE(bool)
unsigned IsModulesDecl : 1;
// True if this is a mangled OpenMP variant name.
LLVM_PREFERRED_TYPE(bool)
unsigned IsMangledOpenMPVariantName : 1;
@ -200,7 +196,7 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
LLVM_PREFERRED_TYPE(bool)
unsigned IsFinal : 1;
// 21 bits left in a 64-bit word.
// 22 bits left in a 64-bit word.
// Managed by the language front-end.
void *FETokenInfo = nullptr;
@ -216,8 +212,8 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo {
IsCPPOperatorKeyword(false), NeedsHandleIdentifier(false),
IsFromAST(false), ChangedAfterLoad(false), FEChangedAfterLoad(false),
RevertedTokenID(false), OutOfDate(false), IsModulesImport(false),
IsModulesDecl(false), IsMangledOpenMPVariantName(false),
IsDeprecatedMacro(false), IsRestrictExpansion(false), IsFinal(false) {}
IsMangledOpenMPVariantName(false), IsDeprecatedMacro(false),
IsRestrictExpansion(false), IsFinal(false) {}
public:
IdentifierInfo(const IdentifierInfo &) = delete;
@ -524,18 +520,6 @@ public:
RecomputeNeedsHandleIdentifier();
}
/// Determine whether this is the contextual keyword \c module.
bool isModulesDeclaration() const { return IsModulesDecl; }
/// Set whether this identifier is the contextual keyword \c module.
void setModulesDeclaration(bool I) {
IsModulesDecl = I;
if (I)
NeedsHandleIdentifier = true;
else
RecomputeNeedsHandleIdentifier();
}
/// Determine whether this is the mangled name of an OpenMP variant.
bool isMangledOpenMPVariantName() const { return IsMangledOpenMPVariantName; }
@ -756,8 +740,6 @@ public:
// If this is the 'import' contextual keyword, mark it as such.
if (Name == "import")
II->setModulesImport(true);
else if (Name == "module")
II->setModulesDeclaration(true);
return *II;
}

View File

@ -1003,9 +1003,6 @@ ANNOTATION(module_include)
ANNOTATION(module_begin)
ANNOTATION(module_end)
// Annotations for C++, Clang and Objective-C named modules.
ANNOTATION(module_name)
// Annotation for a header_name token that has been looked up and transformed
// into the name of a header unit.
ANNOTATION(header_unit)

View File

@ -615,6 +615,10 @@ private:
ModuleDeclSeq ModuleDeclState;
/// Whether the module import expects an identifier next. Otherwise,
/// it expects a '.' or ';'.
bool ModuleImportExpectsIdentifier = false;
/// The identifier and source location of the currently-active
/// \#pragma clang arc_cf_code_audited begin.
std::pair<IdentifierInfo *, SourceLocation> PragmaARCCFCodeAuditedInfo;
@ -1740,14 +1744,11 @@ public:
/// Lex a token, forming a header-name token if possible.
bool LexHeaderName(Token &Result, bool AllowMacroExpansion = true);
/// Lex a module name or a partition name.
bool LexModuleName(Token &Result, bool IsImport);
/// Lex the parameters for an #embed directive, returns nullopt on error.
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
bool ForHasEmbed);
bool LexAfterModuleImport(Token &Result);
bool LexAfterModuleDecl(Token &Result);
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
void makeModuleVisible(Module *M, SourceLocation Loc);
@ -3038,9 +3039,6 @@ private:
static bool CLK_LexAfterModuleImport(Preprocessor &P, Token &Result) {
return P.LexAfterModuleImport(Result);
}
static bool CLK_LexAfterModuleDecl(Preprocessor &P, Token &Result) {
return P.LexAfterModuleDecl(Result);
}
};
/// Abstract base class that describes a handler that will receive
@ -3073,77 +3071,6 @@ struct EmbedAnnotationData {
/// Registry of pragma handlers added by plugins
using PragmaHandlerRegistry = llvm::Registry<PragmaHandler>;
/// Represents module or partition name token sequance.
///
/// module-name:
/// module-name-qualifier[opt] identifier
///
/// partition-name: [C++20]
/// : module-name-qualifier[opt] identifier
///
/// module-name-qualifier
/// module-name-qualifier[opt] identifier .
///
/// This class can only be created by the preprocessor and guarantees that the
/// two source array being contiguous in memory and only contains 3 kind of
/// tokens (identifier, '.' and ':'). And only available when the preprocessor
/// returns annot_module_name token.
///
/// For exmaple:
///
/// export module m.n:c.d
///
/// The module name array has 3 tokens ['m', '.', 'n'].
/// The partition name array has 4 tokens [':', 'c', '.', 'd'].
///
/// When import a partition in a named module fragment (Eg. import :part1;),
/// the module name array will be empty, and the partition name array has 2
/// tokens.
///
/// When we meet a private-module-fragment (Eg. module :private;), preprocessor
/// will not return a annot_module_name token, but will return 2 separate tokens
/// [':', 'kw_private'].
class ModuleNameInfo {
friend class Preprocessor;
ArrayRef<Token> ModuleName;
ArrayRef<Token> PartitionName;
ModuleNameInfo(ArrayRef<Token> AnnotToks, std::optional<unsigned> ColonIndex);
public:
/// Return the contiguous token array.
ArrayRef<Token> getTokens() const {
if (ModuleName.empty())
return PartitionName;
if (PartitionName.empty())
return ModuleName;
return ArrayRef(ModuleName.begin(), PartitionName.end());
}
bool hasModuleName() const { return !ModuleName.empty(); }
bool hasPartitionName() const { return !PartitionName.empty(); }
ArrayRef<Token> getModuleName() const { return ModuleName; }
ArrayRef<Token> getPartitionName() const { return PartitionName; }
Token getColonToken() const {
assert(hasPartitionName() && "Do not have a partition name");
return getPartitionName().front();
}
/// Under the standard C++ Modules, the dot is just part of the module name,
/// and not a real hierarchy separator. Flatten such module names now.
std::string getFlatName() const;
/// Build a module id path from the contiguous token array, both include
/// module name and partition name.
void getModuleIdPath(
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const;
/// Build a module id path from \param ModuleName.
static void getModuleIdPath(
ArrayRef<Token> ModuleName,
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path);
};
} // namespace clang
#endif // LLVM_CLANG_LEX_PREPROCESSOR_H

View File

@ -235,9 +235,6 @@ public:
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
return PtrData;
}
template <class T> T getAnnotationValueAs() const {
return static_cast<T>(getAnnotationValue());
}
void setAnnotationValue(void *val) {
assert(isAnnotation() && "Used AnnotVal on non-annotation token");
PtrData = val;

View File

@ -3876,7 +3876,7 @@ private:
}
bool ParseModuleName(
SourceLocation UseLoc, ArrayRef<Token> ModuleName,
SourceLocation UseLoc,
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
bool IsImport);

View File

@ -322,9 +322,8 @@ void IdentifierTable::AddKeywords(const LangOptions &LangOpts) {
if (LangOpts.IEEE128)
AddKeyword("__ieee128", tok::kw___float128, KEYALL, LangOpts, *this);
// Add the 'import' and 'module' contextual keyword.
// Add the 'import' contextual keyword.
get("import").setModulesImport(true);
get("module").setModulesDeclaration(true);
}
/// Checks if the specified token kind represents a keyword in the

View File

@ -758,10 +758,9 @@ void PrintPPOutputPPCallbacks::HandleWhitespaceBeforeTok(const Token &Tok,
// These tokens are not expanded to anything and don't need whitespace before
// them.
if (Tok.is(tok::eof) ||
(Tok.isAnnotation() && Tok.isNot(tok::annot_header_unit) &&
Tok.isNot(tok::annot_module_begin) && Tok.isNot(tok::annot_module_end) &&
Tok.isNot(tok::annot_module_name) &&
Tok.isNot(tok::annot_repl_input_end) && Tok.isNot(tok::annot_embed)))
(Tok.isAnnotation() && !Tok.is(tok::annot_header_unit) &&
!Tok.is(tok::annot_module_begin) && !Tok.is(tok::annot_module_end) &&
!Tok.is(tok::annot_repl_input_end) && !Tok.is(tok::annot_embed)))
return;
// EmittedDirectiveOnThisLine takes priority over RequireSameLine.
@ -952,11 +951,6 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok,
PP.Lex(Tok);
IsStartOfLine = true;
continue;
} else if (Tok.is(tok::annot_module_name)) {
auto *Info = static_cast<ModuleNameInfo *>(Tok.getAnnotationValue());
*Callbacks->OS << Info->getFlatName();
PP.Lex(Tok);
continue;
} else if (Tok.is(tok::annot_header_unit)) {
// This is a header-name that has been (effectively) converted into a
// module-name.

View File

@ -122,8 +122,7 @@ void Preprocessor::EnterSourceFileWithLexer(Lexer *TheLexer,
CurPPLexer = TheLexer;
CurDirLookup = CurDir;
CurLexerSubmodule = nullptr;
if (CurLexerCallback != CLK_LexAfterModuleImport &&
CurLexerCallback != CLK_LexAfterModuleDecl)
if (CurLexerCallback != CLK_LexAfterModuleImport)
CurLexerCallback = TheLexer->isDependencyDirectivesLexer()
? CLK_DependencyDirectivesLexer
: CLK_Lexer;
@ -162,7 +161,8 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation ILEnd,
PushIncludeMacroStack();
CurDirLookup = nullptr;
CurTokenLexer = std::move(TokLexer);
CurLexerCallback = CLK_TokenLexer;
if (CurLexerCallback != CLK_LexAfterModuleImport)
CurLexerCallback = CLK_TokenLexer;
}
/// EnterTokenStream - Add a "macro" context to the top of the include stack,
@ -216,8 +216,7 @@ void Preprocessor::EnterTokenStream(const Token *Toks, unsigned NumToks,
PushIncludeMacroStack();
CurDirLookup = nullptr;
CurTokenLexer = std::move(TokLexer);
if (CurLexerCallback != CLK_LexAfterModuleImport &&
CurLexerCallback != CLK_LexAfterModuleDecl)
if (CurLexerCallback != CLK_LexAfterModuleImport)
CurLexerCallback = CLK_TokenLexer;
}

View File

@ -860,15 +860,9 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) {
ModuleImportLoc = Identifier.getLocation();
NamedModuleImportPath.clear();
IsAtImport = true;
ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
}
if ((II.isModulesDeclaration() || Identifier.is(tok::kw_module)) &&
!InMacroArgs && !DisableMacroExpansion &&
(getLangOpts().CPlusPlusModules || getLangOpts().DebuggerSupport) &&
CurLexerCallback != CLK_CachingLexer) {
CurLexerCallback = CLK_LexAfterModuleDecl;
}
return true;
}
@ -911,7 +905,6 @@ void Preprocessor::Lex(Token &Result) {
// This token is injected to represent the translation of '#include "a.h"'
// into "import a.h;". Mimic the notional ';'.
case tok::annot_module_include:
case tok::annot_repl_input_end:
case tok::semi:
TrackGMFState.handleSemi();
StdCXXImportSeqState.handleSemi();
@ -926,30 +919,12 @@ void Preprocessor::Lex(Token &Result) {
StdCXXImportSeqState.handleExport();
ModuleDeclState.handleExport();
break;
case tok::annot_module_name: {
auto *Info = static_cast<ModuleNameInfo *>(Result.getAnnotationValue());
for (const auto &Tok : Info->getTokens()) {
switch (Tok.getKind()) {
case tok::identifier:
ModuleDeclState.handleIdentifier(Tok.getIdentifierInfo());
break;
case tok::period:
ModuleDeclState.handlePeriod();
break;
case tok::colon:
ModuleDeclState.handleColon();
break;
default:
llvm_unreachable("Unexpected token in module name");
}
}
if (ModuleDeclState.isModuleCandidate())
break;
TrackGMFState.handleMisc();
StdCXXImportSeqState.handleMisc();
ModuleDeclState.handleMisc();
case tok::colon:
ModuleDeclState.handleColon();
break;
case tok::period:
ModuleDeclState.handlePeriod();
break;
}
case tok::identifier:
// Check "import" and "module" when there is no open bracket. The two
// identifiers are not meaningful with open brackets.
@ -961,17 +936,17 @@ void Preprocessor::Lex(Token &Result) {
ModuleImportLoc = Result.getLocation();
NamedModuleImportPath.clear();
IsAtImport = false;
ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
}
break;
}
if (Result.getIdentifierInfo()->isModulesDeclaration()) {
} else if (Result.getIdentifierInfo() == getIdentifierInfo("module")) {
TrackGMFState.handleModule(StdCXXImportSeqState.afterTopLevelSeq());
ModuleDeclState.handleModule();
CurLexerCallback = CLK_LexAfterModuleDecl;
break;
}
}
ModuleDeclState.handleIdentifier(Result.getIdentifierInfo());
if (ModuleDeclState.isModuleCandidate())
break;
[[fallthrough]];
@ -1146,151 +1121,6 @@ void Preprocessor::CollectPpImportSuffix(SmallVectorImpl<Token> &Toks) {
}
}
ModuleNameInfo::ModuleNameInfo(ArrayRef<Token> AnnotToks,
std::optional<unsigned> ColonIndex) {
assert(!AnnotToks.empty() && "Named module token cannot be empty.");
if (!ColonIndex.has_value())
ColonIndex = AnnotToks.size();
ModuleName = ArrayRef(AnnotToks.begin(), AnnotToks.begin() + *ColonIndex);
PartitionName = ArrayRef(AnnotToks.begin() + *ColonIndex, AnnotToks.end());
assert(ModuleName.end() == PartitionName.begin());
}
std::string ModuleNameInfo::getFlatName() const {
std::string FlatModuleName;
for (auto &Tok : getTokens()) {
switch (Tok.getKind()) {
case tok::identifier:
FlatModuleName += Tok.getIdentifierInfo()->getName();
break;
case tok::period:
FlatModuleName += '.';
break;
case tok::colon:
FlatModuleName += ':';
break;
default:
llvm_unreachable("Unexpected token in module name");
}
}
return FlatModuleName;
}
void ModuleNameInfo::getModuleIdPath(
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) const {
return getModuleIdPath(getTokens(), Path);
}
void ModuleNameInfo::getModuleIdPath(
ArrayRef<Token> ModuleName,
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path) {
for (const auto &Tok : ModuleName) {
if (Tok.is(tok::identifier))
Path.push_back(
std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
}
}
/// Lex a module name or a partition name.
///
/// module-name:
/// module-name-qualifier[opt] identifier
///
/// partition-name: [C++20]
/// : module-name-qualifier[opt] identifier
///
/// module-name-qualifier
/// module-name-qualifier[opt] identifier .
bool Preprocessor::LexModuleName(Token &Result, bool IsImport) {
bool ExpectsIdentifier = true, IsLexingPartition = false;
SmallVector<Token, 8> ModuleName;
std::optional<unsigned> ColonTokIndex;
auto LexNextToken = [&](Token &Tok) {
if (IsImport)
Lex(Tok);
else
LexUnexpandedToken(Tok);
};
while (true) {
LexNextToken(Result);
if (ExpectsIdentifier && Result.is(tok::identifier)) {
auto *MI = getMacroInfo(Result.getIdentifierInfo());
if (getLangOpts().CPlusPlusModules && !IsImport && MI &&
MI->isObjectLike()) {
Diag(Result, diag::err_module_decl_cannot_be_macros)
<< Result.getLocation() << IsLexingPartition
<< Result.getIdentifierInfo();
}
ModuleName.push_back(Result);
ExpectsIdentifier = false;
continue;
}
if (!ExpectsIdentifier && Result.is(tok::period)) {
ModuleName.push_back(Result);
ExpectsIdentifier = true;
continue;
}
// Module partition only allowed in C++20 Modules.
if (getLangOpts().CPlusPlusModules && Result.is(tok::colon)) {
// Handle the form like: import :P;
// If the token after ':' is not an identifier, this is a invalid module
// name.
if (ModuleName.empty()) {
Token Tmp;
LexNextToken(Tmp);
EnterToken(Tmp, /*IsReiject=*/false);
// A private-module-fragment:
// export module :private;
if (!IsImport && Tmp.is(tok::kw_private))
return true;
// import :N;
if (IsImport && Tmp.isNot(tok::identifier))
return false;
} else if (!ExpectsIdentifier) {
ExpectsIdentifier = true;
}
IsLexingPartition = true;
ColonTokIndex = ModuleName.size();
ModuleName.push_back(Result);
continue;
}
// [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
// preprocessing token [...]
//
// We only emit diagnostic in the preprocessor, and in the parser we skip
// invalid tokens and recover from errors.
if (getLangOpts().CPlusPlusModules && !ExpectsIdentifier &&
Result.is(tok::l_paren))
Diag(Result, diag::err_unxepected_paren_in_module_decl)
<< IsLexingPartition;
break;
}
// Put the last token back to stream, it's not a valid part of module name.
// We lexed it unexpanded but it might be a valid macro expansion
Result.clearFlag(Token::DisableExpand);
auto ToksCopy = std::make_unique<Token[]>(1);
*ToksCopy.get() = Result;
EnterTokenStream(std::move(ToksCopy), 1,
/*DisableMacroExpansion=*/false,
/*IsReinject=*/false);
if (ModuleName.empty())
return false;
Result.startToken();
Result.setKind(tok::annot_module_name);
Result.setLocation(ModuleName.front().getLocation());
Result.setAnnotationEndLoc(ModuleName.back().getLocation());
auto AnnotToks = ArrayRef(ModuleName).copy(getPreprocessorAllocator());
ModuleNameInfo *Info =
new (getPreprocessorAllocator()) ModuleNameInfo(AnnotToks, ColonTokIndex);
Result.setAnnotationValue(static_cast<void *>(Info));
return true;
}
/// Lex a token following the 'import' contextual keyword.
///
@ -1315,17 +1145,6 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
// Figure out what kind of lexer we actually have.
recomputeCurLexerKind();
// Allocate a holding buffer for a sequence of tokens and introduce it into
// the token stream.
auto EnterTokens = [this](ArrayRef<Token> Toks) {
auto ToksCopy = std::make_unique<Token[]>(Toks.size());
std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
EnterTokenStream(std::move(ToksCopy), Toks.size(),
/*DisableMacroExpansion*/ true, /*IsReinject*/ false);
};
SmallVector<Token, 32> Suffix;
// Lex the next token. The header-name lexing rules are used at the start of
// a pp-import.
//
@ -1336,108 +1155,122 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
if (LexHeaderName(Result))
return true;
// Check for a header-name.
if (Result.is(tok::header_name)) {
// Enter the header-name token into the token stream; a Lex action cannot
// both return a token and cache tokens (doing so would corrupt the token
// cache if the call to Lex comes from CachingLex / PeekAhead).
Suffix.push_back(Result);
// Consume the pp-import-suffix and expand any macros in it now. We'll add
// it back into the token stream later.
CollectPpImportSuffix(Suffix);
if (Suffix.back().isNot(tok::semi)) {
// This is not a pp-import after all.
EnterTokens(Suffix);
return false;
}
// C++2a [cpp.module]p1:
// The ';' preprocessing-token terminating a pp-import shall not have
// been produced by macro replacement.
SourceLocation SemiLoc = Suffix.back().getLocation();
if (SemiLoc.isMacroID())
Diag(SemiLoc, diag::err_header_import_semi_in_macro);
// Reconstitute the import token.
Token ImportTok;
ImportTok.startToken();
ImportTok.setKind(tok::kw_import);
ImportTok.setLocation(ModuleImportLoc);
ImportTok.setIdentifierInfo(getIdentifierInfo("import"));
ImportTok.setLength(6);
auto Action = HandleHeaderIncludeOrImport(
/*HashLoc*/ SourceLocation(), ImportTok, Suffix.front(), SemiLoc);
switch (Action.Kind) {
case ImportAction::None:
break;
case ImportAction::ModuleBegin:
// Let the parser know we're textually entering the module.
Suffix.emplace_back();
Suffix.back().startToken();
Suffix.back().setKind(tok::annot_module_begin);
Suffix.back().setLocation(SemiLoc);
Suffix.back().setAnnotationEndLoc(SemiLoc);
Suffix.back().setAnnotationValue(Action.ModuleForHeader);
[[fallthrough]];
case ImportAction::ModuleImport:
case ImportAction::HeaderUnitImport:
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);
Suffix[0].setAnnotationEndLoc(Suffix[0].getLocation());
Suffix[0].setAnnotationValue(Action.ModuleForHeader);
// FIXME: Call the moduleImport callback?
break;
case ImportAction::Failure:
assert(TheModuleLoader.HadFatalFailure &&
"This should be an early exit only to a fatal error");
Result.setKind(tok::eof);
CurLexer->cutOffLexing();
EnterTokens(Suffix);
return true;
}
EnterTokens(Suffix);
return false;
if (Result.is(tok::colon) && ModuleDeclState.isNamedModule()) {
std::string Name = ModuleDeclState.getPrimaryName().str();
Name += ":";
NamedModuleImportPath.push_back(
{getIdentifierInfo(Name), Result.getLocation()});
CurLexerCallback = CLK_LexAfterModuleImport;
return true;
}
} else {
Lex(Result);
}
if (Result.isOneOf(tok::identifier, tok::colon)) {
EnterToken(Result, /*IsReinject=*/false);
if (!LexModuleName(Result, /*IsImport=*/true))
return true;
auto *Info = Result.getAnnotationValueAs<ModuleNameInfo *>();
if (getLangOpts().CPlusPlusModules) {
// Under the standard C++ Modules, the dot is just part of the module
// name, and not a real hierarchy separator. Flatten such module names
// now.
//
// FIXME: Is this the right level to be performing this transformation?
std::string FlatModuleName;
if (Info->getTokens().front().is(tok::colon)) {
// Import a module partition allowed in C++20 Modules.
// We can import a partition in named module TU.
if (NamedModuleImportPath.empty() && ModuleDeclState.isNamedModule())
FlatModuleName = llvm::Twine(ModuleDeclState.getPrimaryName())
.concat(Info->getFlatName())
.str();
else
return true;
} else {
FlatModuleName = Info->getFlatName();
}
NamedModuleImportPath.emplace_back(getIdentifierInfo(FlatModuleName),
Result.getLocation());
} else {
Info->getModuleIdPath(NamedModuleImportPath);
// Allocate a holding buffer for a sequence of tokens and introduce it into
// the token stream.
auto EnterTokens = [this](ArrayRef<Token> Toks) {
auto ToksCopy = std::make_unique<Token[]>(Toks.size());
std::copy(Toks.begin(), Toks.end(), ToksCopy.get());
EnterTokenStream(std::move(ToksCopy), Toks.size(),
/*DisableMacroExpansion*/ true, /*IsReinject*/ false);
};
bool ImportingHeader = Result.is(tok::header_name);
// Check for a header-name.
SmallVector<Token, 32> Suffix;
if (ImportingHeader) {
// Enter the header-name token into the token stream; a Lex action cannot
// both return a token and cache tokens (doing so would corrupt the token
// cache if the call to Lex comes from CachingLex / PeekAhead).
Suffix.push_back(Result);
// Consume the pp-import-suffix and expand any macros in it now. We'll add
// it back into the token stream later.
CollectPpImportSuffix(Suffix);
if (Suffix.back().isNot(tok::semi)) {
// This is not a pp-import after all.
EnterTokens(Suffix);
return false;
}
// C++2a [cpp.module]p1:
// The ';' preprocessing-token terminating a pp-import shall not have
// been produced by macro replacement.
SourceLocation SemiLoc = Suffix.back().getLocation();
if (SemiLoc.isMacroID())
Diag(SemiLoc, diag::err_header_import_semi_in_macro);
// Reconstitute the import token.
Token ImportTok;
ImportTok.startToken();
ImportTok.setKind(tok::kw_import);
ImportTok.setLocation(ModuleImportLoc);
ImportTok.setIdentifierInfo(getIdentifierInfo("import"));
ImportTok.setLength(6);
auto Action = HandleHeaderIncludeOrImport(
/*HashLoc*/ SourceLocation(), ImportTok, Suffix.front(), SemiLoc);
switch (Action.Kind) {
case ImportAction::None:
break;
case ImportAction::ModuleBegin:
// Let the parser know we're textually entering the module.
Suffix.emplace_back();
Suffix.back().startToken();
Suffix.back().setKind(tok::annot_module_begin);
Suffix.back().setLocation(SemiLoc);
Suffix.back().setAnnotationEndLoc(SemiLoc);
Suffix.back().setAnnotationValue(Action.ModuleForHeader);
[[fallthrough]];
case ImportAction::ModuleImport:
case ImportAction::HeaderUnitImport:
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);
Suffix[0].setAnnotationEndLoc(Suffix[0].getLocation());
Suffix[0].setAnnotationValue(Action.ModuleForHeader);
// FIXME: Call the moduleImport callback?
break;
case ImportAction::Failure:
assert(TheModuleLoader.HadFatalFailure &&
"This should be an early exit only to a fatal error");
Result.setKind(tok::eof);
CurLexer->cutOffLexing();
EnterTokens(Suffix);
return true;
}
EnterTokens(Suffix);
return false;
}
// The token sequence
//
// import identifier (. identifier)*
//
// indicates a module import directive. We already saw the 'import'
// contextual keyword, so now we're looking for the identifiers.
if (ModuleImportExpectsIdentifier && Result.getKind() == tok::identifier) {
// We expected to see an identifier here, and we did; continue handling
// identifiers.
NamedModuleImportPath.push_back(
std::make_pair(Result.getIdentifierInfo(), Result.getLocation()));
ModuleImportExpectsIdentifier = false;
CurLexerCallback = CLK_LexAfterModuleImport;
return true;
}
// If we're expecting a '.' or a ';', and we got a '.', then wait until we
// see the next identifier. (We can also see a '[[' that begins an
// attribute-specifier-seq here under the Standard C++ Modules.)
if (!ModuleImportExpectsIdentifier && Result.getKind() == tok::period) {
ModuleImportExpectsIdentifier = true;
CurLexerCallback = CLK_LexAfterModuleImport;
return true;
}
// If we didn't recognize a module name at all, this is not a (valid) import.
@ -1458,6 +1291,24 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
SemiLoc = Suffix.back().getLocation();
}
// Under the standard C++ Modules, the dot is just part of the module name,
// and not a real hierarchy separator. Flatten such module names now.
//
// FIXME: Is this the right level to be performing this transformation?
std::string FlatModuleName;
if (getLangOpts().CPlusPlusModules) {
for (auto &Piece : NamedModuleImportPath) {
// If the FlatModuleName ends with colon, it implies it is a partition.
if (!FlatModuleName.empty() && FlatModuleName.back() != ':')
FlatModuleName += ".";
FlatModuleName += Piece.first->getName();
}
SourceLocation FirstPathLoc = NamedModuleImportPath[0].second;
NamedModuleImportPath.clear();
NamedModuleImportPath.push_back(
std::make_pair(getIdentifierInfo(FlatModuleName), FirstPathLoc));
}
Module *Imported = nullptr;
// We don't/shouldn't load the standard c++20 modules when preprocessing.
if (getLangOpts().Modules && !isInImportingCXXNamedModules()) {
@ -1479,33 +1330,6 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
return true;
}
/// Lex a token following the 'module' contextual keyword.
///
/// [cpp.module]/p2:
/// The pp-tokens, if any, of a pp-module shall be of the form:
/// pp-module-name pp-module-partition[opt] pp-tokens[opt]
///
/// where the pp-tokens (if any) shall not begin with a ( preprocessing token
/// and the grammar non-terminals are defined as:
/// pp-module-name:
/// pp-module-name-qualifierp[opt] identifier
/// pp-module-partition:
/// : pp-module-name-qualifier[opt] identifier
/// pp-module-name-qualifier:
/// identifier .
/// pp-module-name-qualifier identifier .
/// No identifier in the pp-module-name or pp-module-partition shall currently
/// be defined as an object-like macro.
///
/// [cpp.module]/p3:
/// Any preprocessing tokens after the module preprocessing token in the module
/// directive are processed just as in normal text.
bool Preprocessor::LexAfterModuleDecl(Token &Result) {
// Figure out what kind of lexer we actually have.
recomputeCurLexerKind();
return LexModuleName(Result, /*IsImport=*/false);
}
void Preprocessor::makeModuleVisible(Module *M, SourceLocation Loc) {
CurSubmoduleState->VisibleModules.setVisible(
M, Loc, [](Module *) {},

View File

@ -160,13 +160,6 @@ static char GetFirstChar(const Preprocessor &PP, const Token &Tok) {
bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
const Token &PrevTok,
const Token &Tok) const {
// If previous token is a module name, we need avoid concat it with current
// token, otherwise, there will has an extra space between 'M' and ';' for the
// following code:
//
// import M;
if (PrevTok.is(tok::annot_module_name))
return false;
// Conservatively assume that every annotation token that has a printable
// form requires whitespace.
if (PrevTok.isAnnotation())
@ -197,9 +190,6 @@ bool TokenConcatenation::AvoidConcat(const Token &PrevPrevTok,
return true;
ConcatInfo &= ~aci_avoid_equal;
}
if (Tok.is(tok::annot_module_name))
return true;
if (Tok.isAnnotation()) {
// Modules annotation can show up when generated automatically for includes.
assert(Tok.isOneOf(tok::annot_module_include, tok::annot_module_begin,

View File

@ -3958,13 +3958,7 @@ void Parser::ParseDeclarationSpecifiers(
// We're done with the declaration-specifiers.
goto DoneWithDeclSpec;
case tok::annot_module_name: {
PP.EnterTokenStream(
Tok.getAnnotationValueAs<ModuleNameInfo *>()->getTokens(),
/*DisableMacroExpansion=*/true, /*IsReinject=*/false);
ConsumeAnyToken();
[[fallthrough]];
}
// typedef-name
case tok::kw___super:
case tok::kw_decltype:

View File

@ -2511,28 +2511,18 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
}
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path;
if (Tok.isNot(tok::annot_module_name)) {
Diag(Tok, diag::err_module_expected_ident) << /*IsImport=*/false;
SkipUntil(tok::semi, StopBeforeMatch);
return nullptr;
}
auto *Info = Tok.getAnnotationValueAs<ModuleNameInfo *>();
ConsumeAnnotationToken();
if (ParseModuleName(ModuleLoc, Info->getModuleName(), Path,
/*IsImport=*/false))
if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false))
return nullptr;
// Parse the optional module-partition.
SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition;
if (Info->hasPartitionName()) {
SourceLocation ColonLoc = Info->getColonToken().getLocation();
if (Tok.is(tok::colon)) {
SourceLocation ColonLoc = ConsumeToken();
if (!getLangOpts().CPlusPlusModules)
Diag(ColonLoc, diag::err_unsupported_module_partition)
<< SourceRange(ColonLoc, Partition.back().second);
// Recover by ignoring the partition name.
else if (ParseModuleName(ModuleLoc, Info->getPartitionName(), Partition,
/*IsImport=*/false))
else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false))
return nullptr;
}
@ -2591,32 +2581,18 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
// This is a header import that the preprocessor mapped to a module import.
HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue());
ConsumeAnnotationToken();
} else {
if (Tok.isNot(tok::annot_module_name)) {
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompletion().CodeCompleteModuleImport(ImportLoc, Path);
return nullptr;
}
Diag(Tok, diag::err_module_expected_ident) << /*IsImport=*/true;
SkipUntil(tok::semi, StopBeforeMatch);
} else if (Tok.is(tok::colon)) {
SourceLocation ColonLoc = ConsumeToken();
if (!getLangOpts().CPlusPlusModules)
Diag(ColonLoc, diag::err_unsupported_module_partition)
<< SourceRange(ColonLoc, Path.back().second);
// Recover by leaving partition empty.
else if (ParseModuleName(ColonLoc, Path, /*IsImport*/ true))
return nullptr;
}
auto *Info = Tok.getAnnotationValueAs<ModuleNameInfo *>();
ConsumeAnnotationToken();
if (Info->hasPartitionName()) {
SourceLocation ColonLoc = Info->getColonToken().getLocation();
if (!getLangOpts().CPlusPlusModules)
Diag(ColonLoc, diag::err_unsupported_module_partition)
<< SourceRange(ColonLoc, Path.back().second);
// Recover by leaving partition empty.
else if (ParseModuleName(ColonLoc, Info->getPartitionName(), Path,
/*IsImport=*/true))
return nullptr;
else
IsPartition = true;
} else if (ParseModuleName(ImportLoc, Info->getModuleName(), Path,
/*IsImport=*/true))
else
IsPartition = true;
} else {
if (ParseModuleName(ImportLoc, Path, /*IsImport*/ true))
return nullptr;
}
@ -2713,31 +2689,32 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc,
/// module-name-qualifier:
/// module-name-qualifier[opt] identifier '.'
bool Parser::ParseModuleName(
SourceLocation UseLoc, ArrayRef<Token> ModuleName,
SourceLocation UseLoc,
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
bool IsImport) {
ModuleNameInfo::getModuleIdPath(ModuleName, Path);
// Eg. import A.B.
if (ModuleName.back().isNot(tok::identifier)) {
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompletion().CodeCompleteModuleImport(UseLoc, Path);
// Parse the module path.
while (true) {
if (!Tok.is(tok::identifier)) {
if (Tok.is(tok::code_completion)) {
cutOffParsing();
Actions.CodeCompletion().CodeCompleteModuleImport(UseLoc, Path);
return true;
}
Diag(Tok, diag::err_module_expected_ident) << IsImport;
SkipUntil(tok::semi);
return true;
}
Diag(ModuleName.back(), diag::err_module_expected_ident) << IsImport;
SkipUntil(tok::semi, StopBeforeMatch);
return true;
}
// [cpp.module]/p2: where the pp-tokens (if any) shall not begin with a (
// preprocessing token [...]
//
// Skip unitl ';' to recovery.
if (getLangOpts().CPlusPlusModules && Tok.is(tok::l_paren)) {
SkipUntil(tok::semi, StopBeforeMatch);
return true;
// Record this part of the module path.
Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation()));
ConsumeToken();
if (Tok.isNot(tok::period))
return false;
ConsumeToken();
}
return false;
}
/// Try recover parser when module annotation appears where it must not

View File

@ -1,88 +0,0 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/B.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/C.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/D.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/E.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/F.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/G.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/H.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/I.cppm -triple x86_64-linux-gnu -verify
// RUN: %clang_cc1 -std=c++20 %t/J.cppm -triple x86_64-linux-gnu -verify
//--- version.h
#ifndef VERSION_H
#define VERSION_H
#define VERSION libv5
#define A a
#define B b
#define C c
#define FUNC_LIKE(X) function_like_##X
#define ATTRS [[]]
#define SEMICOLON ;
#endif // VERSION_H
//--- A.cppm
module;
#include "version.h"
export module VERSION; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'VERSION'}}
//--- B.cppm
module;
#include "version.h"
export module A.B; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}}
//--- C.cppm
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
#include "version.h"
export module A.FUNC_LIKE(foo):C; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{unexpected '(' after the module name in a module declaration}}
//--- D.cppm
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
#include "version.h"
export module B.A.FUNC_LIKE(bar):C; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'B'}} \
// expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{unexpected '(' after the module name in a module declaration}}
//--- E.cppm
module;
#include "version.h"
export module a.FUNC_LIKE:c; // OK, FUNC_LIKE would not be treated as a macro name.
// expected-no-diagnostics
//--- F.cppm
module;
#include "version.h"
export module a.FUNC_LIKE:c ATTRS; // OK, FUNC_LIKE would not be treated as a macro name.
// expected-no-diagnostics
//--- G.cppm
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
#include "version.h"
export module A.FUNC_LIKE(B c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{unexpected '(' after the module name in a module declaration}}
//--- H.cppm
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
#include "version.h"
export module A.FUNC_LIKE(B,). c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{unexpected '(' after the module name in a module declaration}}
//--- I.cppm
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
#include "version.h"
export module A.FUNC_LIKE(B,) c:C ATTRS // expected-error {{the module name in a module declaration cannot contain an object-like macro 'A'}} \
// expected-error {{unexpected '(' after the module name in a module declaration}}
//--- J.cppm
module;
#include "version.h"
export module unexpanded : unexpanded ATTRS SEMICOLON // OK, ATTRS and SEMICOLON can be expanded.
// expected-no-diagnostics

View File

@ -8,19 +8,27 @@
// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fmodule-file=x=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm
//
// Module implementation for unknown and known module. (The former is ill-formed.)
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M1.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x=%t/x.pcm -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M2.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
// RUN: -DTEST=1 -DEXPORT= -DMODULE_NAME=z
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x=%t/x.pcm -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/M.cpp \
// 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++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M3.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M4.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=3 -DEXPORT=export -DMODULE_NAME=z
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=4 -DEXPORT=export -DMODULE_NAME=x
//
// Miscellaneous syntax.
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M5.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M6.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M7.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M8.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=7 -DEXPORT=export -DMODULE_NAME='z elderberry'
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=8 -DEXPORT=export -DMODULE_NAME='z [[]]'
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=9 -DEXPORT=export -DMODULE_NAME='z [[fancy]]'
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/M.cpp \
// RUN: -DTEST=10 -DEXPORT=export -DMODULE_NAME='z [[maybe_unused]]'
//--- x.cppm
export module x;
@ -30,26 +38,17 @@ int a, b;
export module x.y;
int c;
//--- M1.cpp
module z; // expected-error {{module 'z' not found}}
//--- M.cpp
//--- M2.cpp
module x; // expected-no-diagnostics
//--- M3.cpp
export module z; // expected-no-diagnostics
//--- M4.cpp
export module x; // expected-no-diagnostics
//--- M5.cpp
export module z elderberry; // expected-error {{expected ';'}} expected-error {{a type specifier is required}}
//--- M6.cpp
export module z [[]]; // expected-no-diagnostics
//--- M7.cpp
export module z [[fancy]]; // expected-warning {{unknown attribute 'fancy' ignored}}
//--- M8.cpp
export module z [[maybe_unused]]; // expected-error-re {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
EXPORT module MODULE_NAME;
#if TEST == 7
// expected-error@-2 {{expected ';'}} expected-error@-2 {{a type specifier is required}}
#elif TEST == 9
// expected-warning@-4 {{unknown attribute 'fancy' ignored}}
#elif TEST == 10
// expected-error-re@-6 {{'maybe_unused' attribute cannot be applied to a module{{$}}}}
#elif TEST == 1
// expected-error@-8 {{module 'z' not found}}
#else
// expected-no-diagnostics
#endif

View File

@ -6,12 +6,10 @@
// RUN: %clang_cc1 -std=c++20 -emit-module-interface -fmodule-file=x=%t/x.pcm %t/x.y.cppm -o %t/x.y.pcm
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.b.cppm -o %t/a.b.pcm
//
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test-interface.cpp \
// RUN: -DINTERFACE
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test.cpp \
// RUN: -DMODULE_NAME=z -DINTERFACE
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm \
// RUN: -fmodule-file=a.b=%t/a.b.pcm -verify %t/test.cpp
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm \
// RUN: -verify %t/test-module-not-found.cpp
// RUN: -fmodule-file=a.b=%t/a.b.pcm -verify %t/test.cpp -DMODULE_NAME=a.b
// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -fmodule-file=x=%t/x.pcm -verify %t/test.x.cpp
//--- x.cppm
@ -36,8 +34,11 @@ int use_2 = b; // ok
int use_3 = c; // expected-error {{use of undeclared identifier 'c'}}
//--- test.cpp
module;
module a.b;
#ifdef INTERFACE
export module MODULE_NAME;
#else
module MODULE_NAME;
#endif
import x;
@ -50,28 +51,6 @@ import x.y;
import x.; // expected-error {{expected a module name after 'import'}}
import .x; // expected-error {{expected a module name after 'import'}}
int use_4 = c; // ok
//--- test-interface.cpp
module;
export module z;
import x;
import x [[]];
import x [[foo]]; // expected-warning {{unknown attribute 'foo' ignored}}
import x [[noreturn]]; // expected-error {{'noreturn' attribute cannot be applied to a module import}}
import x [[blarg::noreturn]]; // expected-warning {{unknown attribute 'noreturn' ignored}}
import x.y;
import x.; // expected-error {{expected a module name after 'import'}}
import .x; // expected-error {{expected a module name after 'import'}}
int use_4 = c; // ok
//--- test-module-not-found.cpp
module;
import blarg; // expected-error {{module 'blarg' not found}}
int use_4 = c; // ok

View File

@ -1,17 +1,19 @@
// RUN: rm -rf %t
// RUN: mkdir -p %t
// RUN: split-file %s %t
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o %t.0.pcm -verify -DTEST=0
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o %t.1.pcm -verify -DTEST=1
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -fmodule-file=foo=%t.0.pcm -o %t.2.pcm -verify -DTEST=2
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify -Dfoo=bar -DTEST=3
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/A.cppm -o %t.0.pcm -verify
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/B.cppm -o %t.1.pcm -verify
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/C.cppm -fmodule-file=foo=%t.0.pcm -o %t.2.pcm -verify
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/D.cppm -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/E.cppm -fmodule-file=foo=%t.0.pcm -o %t.3.pcm -verify -Dfoo=bar
#if TEST == 0 || TEST == 2
// expected-no-diagnostics
#endif
//--- A.cppm
export module foo;
static int m;
int n;
#if TEST == 0
export {
int a;
int b;
@ -25,43 +27,7 @@ export void f() {}
export struct T {
} t;
// expected-no-diagnostics
//--- B.cppm
export module foo;
static int m;
int n;
struct S {
export int n; // expected-error {{expected member name or ';'}}
export static int n; // expected-error {{expected member name or ';'}}
};
// FIXME: Exports of declarations without external linkage are disallowed.
// Exports of declarations with non-external-linkage types are disallowed.
// Cannot export within another export. This isn't precisely covered by the
// language rules right now, but (per personal correspondence between zygoloid
// and gdr) is the intent.
export { // expected-note {{export block begins here}}
extern "C++" {
namespace NestedExport {
export { // expected-error {{export declaration appears within another export declaration}}
int q;
}
} // namespace NestedExport
}
}
//--- C.cppm
export module foo;
static int m;
int n;
// expected-no-diagnostics
//--- D.cppm
export module foo;
static int m;
int n;
#elif TEST == 3
int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
#undef foo
@ -80,12 +46,29 @@ int use_n = n; // FIXME: this should not be visible, because it is not exported
extern int n;
static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}}
#endif
//--- E.cppm
export module foo; // expected-error {{the module name in a module declaration cannot contain an object-like macro 'foo'}}
static int m;
int n;
int use_a = a; // expected-error {{use of undeclared identifier 'a'}}
#if TEST == 1
struct S {
export int n; // expected-error {{expected member name or ';'}}
export static int n; // expected-error {{expected member name or ';'}}
};
#endif
#undef foo
import foo; // expected-error {{imports must immediately follow the module declaration}}
// FIXME: Exports of declarations without external linkage are disallowed.
// Exports of declarations with non-external-linkage types are disallowed.
// Cannot export within another export. This isn't precisely covered by the
// language rules right now, but (per personal correspondence between zygoloid
// and gdr) is the intent.
#if TEST == 1
export { // expected-note {{export block begins here}}
extern "C++" {
namespace NestedExport {
export { // expected-error {{export declaration appears within another export declaration}}
int q;
}
} // namespace NestedExport
}
}
#endif

View File

@ -182,7 +182,7 @@ C++23, informally referred to as C++26.</p>
<tr>
<td>Module Declarations Shouldnt be Macros</td>
<td><a href="https://wg21.link/P3034R1">P3034R1</a> (<a href="#dr">DR</a>)</td>
<td class="unreleased" align="center">Clang 19</td>
<td class="none" align="center">No</td>
</tr>
<tr>
<td>Trivial infinite loops are not Undefined Behavior</td>