Author: yronglin Date: 2024-07-22T13:16:51+08:00 New Revision: c91e85278cb87a35924d86463d3c0a379c64e9d1
URL: https://github.com/llvm/llvm-project/commit/c91e85278cb87a35924d86463d3c0a379c64e9d1 DIFF: https://github.com/llvm/llvm-project/commit/c91e85278cb87a35924d86463d3c0a379c64e9d1.diff LOG: Revert "[Clang] Implement P3034R1 Module Declarations Shouldn’t be Macros" (#99838) Reverts llvm/llvm-project#90574 Added: Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Basic/DiagnosticLexKinds.td clang/include/clang/Basic/IdentifierTable.h clang/include/clang/Basic/TokenKinds.def clang/include/clang/Lex/Preprocessor.h clang/include/clang/Lex/Token.h clang/include/clang/Parse/Parser.h clang/lib/Basic/IdentifierTable.cpp clang/lib/Frontend/PrintPreprocessedOutput.cpp clang/lib/Lex/PPLexerChange.cpp clang/lib/Lex/Preprocessor.cpp clang/lib/Lex/TokenConcatenation.cpp clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/Parser.cpp clang/test/CXX/module/basic/basic.link/module-declaration.cpp clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm clang/test/SemaCXX/modules.cppm clang/www/cxx_status.html Removed: clang/test/CXX/cpp/cpp.module/p2.cppm ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4ab6bd9de8ea9..7ac6ed934290d 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -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 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticLexKinds.td b/clang/include/clang/Basic/DiagnosticLexKinds.td index 08ece01009387..12d7b8c0205ee 100644 --- a/clang/include/clang/Basic/DiagnosticLexKinds.td +++ b/clang/include/clang/Basic/DiagnosticLexKinds.td @@ -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">; diff --git a/clang/include/clang/Basic/IdentifierTable.h b/clang/include/clang/Basic/IdentifierTable.h index f40f74d0355ad..ae9ebd9f59154 100644 --- a/clang/include/clang/Basic/IdentifierTable.h +++ b/clang/include/clang/Basic/IdentifierTable.h @@ -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 @@ class alignas(IdentifierInfoAlignment) IdentifierInfo { 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 @@ class IdentifierTable { // 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; } diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index 8db18c049b6d0..7f4912b9bcd96 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -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) diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 56aef99a3f38a..fc7d0053f2323 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -615,6 +615,10 @@ class Preprocessor { 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 @@ class Preprocessor { /// 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 @@ class Preprocessor { 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 diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index 2be3ad39529f0..4f29fb7d11415 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -235,9 +235,6 @@ class Token { 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; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index afcdacf02583a..93e60be512aae 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -3876,7 +3876,7 @@ class Parser : public CodeCompletionHandler { } bool ParseModuleName( - SourceLocation UseLoc, ArrayRef<Token> ModuleName, + SourceLocation UseLoc, SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path, bool IsImport); diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp index 97d830214f890..4f7ccaf4021d6 100644 --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -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 diff --git a/clang/lib/Frontend/PrintPreprocessedOutput.cpp b/clang/lib/Frontend/PrintPreprocessedOutput.cpp index 1fff88ccf0405..0592423c12eca 100644 --- a/clang/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/clang/lib/Frontend/PrintPreprocessedOutput.cpp @@ -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. diff --git a/clang/lib/Lex/PPLexerChange.cpp b/clang/lib/Lex/PPLexerChange.cpp index c3a903917e9ce..8221db46e06ac 100644 --- a/clang/lib/Lex/PPLexerChange.cpp +++ b/clang/lib/Lex/PPLexerChange.cpp @@ -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; } diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 2726fae344337..63e27e62cffc8 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -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; - } + 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); + } - // 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; + // 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); + }; - 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; - } + 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; } - } else { - Lex(Result); - } - if (Result.isOneOf(tok::identifier, tok::colon)) { - EnterToken(Result, /*IsReinject=*/false); - if (!LexModuleName(Result, /*IsImport=*/true)) + // 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; - 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); } + + 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 *) {}, diff --git a/clang/lib/Lex/TokenConcatenation.cpp b/clang/lib/Lex/TokenConcatenation.cpp index cdb636923b9e9..865879d180533 100644 --- a/clang/lib/Lex/TokenConcatenation.cpp +++ b/clang/lib/Lex/TokenConcatenation.cpp @@ -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, diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 577527d0318f2..7ce9a9cea1c7a 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -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: diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index afb2e1e416168..5ebe71e496a2e 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -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 diff --git a/clang/test/CXX/cpp/cpp.module/p2.cppm b/clang/test/CXX/cpp/cpp.module/p2.cppm deleted file mode 100644 index 966a88ccfa972..0000000000000 --- a/clang/test/CXX/cpp/cpp.module/p2.cppm +++ /dev/null @@ -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 diff --git a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp index 14bbc911febfc..d71358cc7a571 100644 --- a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp +++ b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp @@ -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}} - -//--- 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{{$}}}} +//--- M.cpp + +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 diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm index ecad4db32a7e9..873e4c0edeac2 100644 --- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm +++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.import/p1.cppm @@ -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,26 +34,11 @@ int use_2 = b; // ok int use_3 = c; // expected-error {{use of undeclared identifier 'c'}} //--- test.cpp -module; -module a.b; - -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-interface.cpp -module; -export module z; +#ifdef INTERFACE +export module MODULE_NAME; +#else +module MODULE_NAME; +#endif import x; @@ -68,10 +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-module-not-found.cpp -module; - import blarg; // expected-error {{module 'blarg' not found}} +int use_4 = c; // ok diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm index 267417bf5da2c..41204be76eafa 100644 --- a/clang/test/SemaCXX/modules.cppm +++ b/clang/test/SemaCXX/modules.cppm @@ -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 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 1f69a4e8a5620..a6ded8be3ae9e 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -182,7 +182,7 @@ <h2 id="cxx26">C++2c implementation status</h2> <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> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits