Revert "[Clang] Implement P3034R1 Module Declarations Shouldn’t be Macros" (#99838)
Reverts llvm/llvm-project#90574
This commit is contained in:
parent
7b28cc0c59
commit
c91e85278c
@ -285,8 +285,6 @@ C++2c Feature Support
|
||||
|
||||
- Implemented `P2963R3 Ordering of constraints involving fold expressions <https://wg21.link/P2963R3>`_.
|
||||
|
||||
- Implemented `P3034R1 Module Declarations Shouldn’t be Macros <https://wg21.link/P3034R1>`_.
|
||||
|
||||
|
||||
Resolutions to C++ Defect Reports
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -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">;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -3876,7 +3876,7 @@ private:
|
||||
}
|
||||
|
||||
bool ParseModuleName(
|
||||
SourceLocation UseLoc, ArrayRef<Token> ModuleName,
|
||||
SourceLocation UseLoc,
|
||||
SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path,
|
||||
bool IsImport);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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,6 +161,7 @@ void Preprocessor::EnterMacro(Token &Tok, SourceLocation ILEnd,
|
||||
PushIncludeMacroStack();
|
||||
CurDirLookup = nullptr;
|
||||
CurTokenLexer = std::move(TokLexer);
|
||||
if (CurLexerCallback != CLK_LexAfterModuleImport)
|
||||
CurLexerCallback = CLK_TokenLexer;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
case tok::colon:
|
||||
ModuleDeclState.handleColon();
|
||||
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();
|
||||
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,8 +1155,31 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
|
||||
if (LexHeaderName(Result))
|
||||
return true;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
if (Result.is(tok::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).
|
||||
@ -1405,39 +1247,30 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) {
|
||||
EnterTokens(Suffix);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
Lex(Result);
|
||||
|
||||
// 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 (Result.isOneOf(tok::identifier, tok::colon)) {
|
||||
EnterToken(Result, /*IsReinject=*/false);
|
||||
if (!LexModuleName(Result, /*IsImport=*/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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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 *) {},
|
||||
|
@ -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,
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
return nullptr;
|
||||
}
|
||||
auto *Info = Tok.getAnnotationValueAs<ModuleNameInfo *>();
|
||||
ConsumeAnnotationToken();
|
||||
if (Info->hasPartitionName()) {
|
||||
SourceLocation ColonLoc = Info->getColonToken().getLocation();
|
||||
} 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, Info->getPartitionName(), Path,
|
||||
/*IsImport=*/true))
|
||||
else if (ParseModuleName(ColonLoc, Path, /*IsImport*/ true))
|
||||
return nullptr;
|
||||
else
|
||||
IsPartition = true;
|
||||
} else if (ParseModuleName(ImportLoc, Info->getModuleName(), Path,
|
||||
/*IsImport=*/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)) {
|
||||
// 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(ModuleName.back(), diag::err_module_expected_ident) << IsImport;
|
||||
SkipUntil(tok::semi, StopBeforeMatch);
|
||||
|
||||
Diag(Tok, diag::err_module_expected_ident) << IsImport;
|
||||
SkipUntil(tok::semi);
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
/// Try recover parser when module annotation appears where it must not
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -182,7 +182,7 @@ C++23, informally referred to as C++26.</p>
|
||||
<tr>
|
||||
<td>Module Declarations Shouldn’t 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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user