https://github.com/yronglin updated https://github.com/llvm/llvm-project/pull/144233
>From 7d469c759ffde22d346c079a3bedbd03922e315d Mon Sep 17 00:00:00 2001 From: yronglin <yronglin...@gmail.com> Date: Sun, 15 Jun 2025 01:16:41 +0800 Subject: [PATCH] [C++][Modules][P1857R3 2]: A module directive may only appear as the first preprocessing tokens in a file Signed-off-by: yronglin <yronglin...@gmail.com> --- clang/include/clang/Lex/Lexer.h | 3 + clang/include/clang/Lex/Preprocessor.h | 17 ++ clang/include/clang/Lex/Token.h | 12 +- clang/include/clang/Sema/Sema.h | 3 +- clang/lib/Lex/Lexer.cpp | 13 ++ clang/lib/Lex/PPDirectives.cpp | 3 + clang/lib/Lex/PPMacroExpansion.cpp | 3 + clang/lib/Lex/Preprocessor.cpp | 2 + clang/lib/Parse/Parser.cpp | 7 +- clang/lib/Sema/SemaModule.cpp | 15 +- clang/test/CXX/basic/basic.link/p1.cpp | 143 ++++++++++---- clang/test/CXX/basic/basic.link/p2.cpp | 26 +-- .../basic.scope/basic.scope.namespace/p2.cpp | 82 ++++++--- .../CXX/module/basic/basic.def.odr/p6.cppm | 174 ++++++++++++++---- .../basic/basic.link/module-declaration.cpp | 64 ++++--- clang/test/CXX/module/cpp.pre/module_decl.cpp | 8 + .../dcl.module/dcl.module.import/p1.cppm | 34 +++- .../dcl.module/dcl.module.interface/p1.cppm | 39 ++-- .../test/CXX/module/dcl.dcl/dcl.module/p1.cpp | 44 +++-- .../test/CXX/module/dcl.dcl/dcl.module/p5.cpp | 65 +++++-- clang/test/CXX/module/module.interface/p2.cpp | 26 ++- clang/test/CXX/module/module.unit/p8.cpp | 48 +++-- clang/test/Driver/modules.cpp | 31 ++-- clang/test/Modules/named-modules-adl-3.cppm | 1 + clang/test/Modules/reserved-names-1.cppm | 10 + .../reserved-names-system-header-1.cpp | 1 + .../reserved-names-system-header-2.cpp | 1 + clang/test/SemaCXX/modules.cppm | 75 +++++--- ...-aware-new-delete-transparent-contexts.cpp | 20 +- clang/unittests/Lex/LexerTest.cpp | 47 ++++- 30 files changed, 713 insertions(+), 304 deletions(-) create mode 100644 clang/test/CXX/module/cpp.pre/module_decl.cpp diff --git a/clang/include/clang/Lex/Lexer.h b/clang/include/clang/Lex/Lexer.h index bb65ae010cffa..ca812ba1583fb 100644 --- a/clang/include/clang/Lex/Lexer.h +++ b/clang/include/clang/Lex/Lexer.h @@ -143,6 +143,9 @@ class Lexer : public PreprocessorLexer { /// True if this is the first time we're lexing the input file. bool IsFirstTimeLexingFile; + /// True if current lexing token is the first pp-token. + bool IsFirstPPToken; + // NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n', // it also points to '\n.' const char *NewLinePtr; diff --git a/clang/include/clang/Lex/Preprocessor.h b/clang/include/clang/Lex/Preprocessor.h index 78be2bd64d61c..47830b428c8ad 100644 --- a/clang/include/clang/Lex/Preprocessor.h +++ b/clang/include/clang/Lex/Preprocessor.h @@ -350,6 +350,9 @@ class Preprocessor { /// Whether the last token we lexed was an '@'. bool LastTokenWasAt = false; + /// First pp-token in current translation unit. + std::optional<Token> FirstPPToken; + /// A position within a C++20 import-seq. class StdCXXImportSeq { public: @@ -1766,6 +1769,20 @@ class Preprocessor { std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current, bool ForHasEmbed); + /// Whether the preprocessor already seen the first pp-token in main file. + bool hasSeenMainFileFirstPPToken() const { return FirstPPToken.has_value(); } + + /// Record first pp-token and check if it has a Token::FirstPPToken flag. + void HandleMainFileFirstPPToken(const Token &Tok) { + if (!hasSeenMainFileFirstPPToken() && Tok.isFirstPPToken() && + SourceMgr.isWrittenInMainFile(Tok.getLocation())) + FirstPPToken = Tok; + } + + Token getMainFileFirstPPToken() const { + assert(FirstPPToken && "First main file pp-token doesn't exists"); + return *FirstPPToken; + } bool LexAfterModuleImport(Token &Result); void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks); diff --git a/clang/include/clang/Lex/Token.h b/clang/include/clang/Lex/Token.h index 4f29fb7d11415..d4dfd7b44d9af 100644 --- a/clang/include/clang/Lex/Token.h +++ b/clang/include/clang/Lex/Token.h @@ -86,9 +86,12 @@ class Token { // macro stringizing or charizing operator. CommaAfterElided = 0x200, // The comma following this token was elided (MS). IsEditorPlaceholder = 0x400, // This identifier is a placeholder. - IsReinjected = 0x800, // A phase 4 token that was produced before and - // re-added, e.g. via EnterTokenStream. Annotation - // tokens are *not* reinjected. + + IsReinjected = 0x800, // A phase 4 token that was produced before and + // re-added, e.g. via EnterTokenStream. Annotation + // tokens are *not* reinjected. + FirstPPToken = 0x1000, // This token is the first pp token in the + // translation unit. }; tok::TokenKind getKind() const { return Kind; } @@ -318,6 +321,9 @@ class Token { /// represented as characters between '<#' and '#>' in the source code. The /// lexer uses identifier tokens to represent placeholders. bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); } + + /// Returns true if this token is the first pp-token. + bool isFirstPPToken() const { return getFlag(FirstPPToken); } }; /// Information about the conditional stack (\#if directives) diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 29452bb37260d..9397546c8fc5d 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9822,7 +9822,8 @@ class Sema final : public SemaBase { DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path, ModuleIdPath Partition, - ModuleImportState &ImportState); + ModuleImportState &ImportState, + bool IntroducerIsFirstPPToken); /// The parser has processed a global-module-fragment declaration that begins /// the definition of the global module fragment of the current module unit. diff --git a/clang/lib/Lex/Lexer.cpp b/clang/lib/Lex/Lexer.cpp index 93200458f04b4..b61ea3b1614c7 100644 --- a/clang/lib/Lex/Lexer.cpp +++ b/clang/lib/Lex/Lexer.cpp @@ -174,6 +174,8 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr, ExtendedTokenMode = 0; NewLinePtr = nullptr; + + IsFirstPPToken = true; } /// Lexer constructor - Create a new lexer object for the specified buffer @@ -3725,6 +3727,11 @@ bool Lexer::Lex(Token &Result) { HasLeadingEmptyMacro = false; } + if (IsFirstPPToken) { + Result.setFlag(Token::FirstPPToken); + IsFirstPPToken = false; + } + bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine; IsAtPhysicalStartOfLine = false; bool isRawLex = isLexingRawMode(); @@ -3732,6 +3739,10 @@ bool Lexer::Lex(Token &Result) { bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine); // (After the LexTokenInternal call, the lexer might be destroyed.) assert((returnedToken || !isRawLex) && "Raw lex must succeed"); + + if (returnedToken && Result.isFirstPPToken() && PP && + !PP->hasSeenMainFileFirstPPToken()) + PP->HandleMainFileFirstPPToken(Result); return returnedToken; } @@ -4535,6 +4546,8 @@ const char *Lexer::convertDependencyDirectiveToken( Result.setFlag((Token::TokenFlags)DDTok.Flags); Result.setLength(DDTok.Length); BufferPtr = TokPtr + DDTok.Length; + if (PP && !PP->hasSeenMainFileFirstPPToken() && Result.isFirstPPToken()) + PP->HandleMainFileFirstPPToken(Result); return TokPtr; } diff --git a/clang/lib/Lex/PPDirectives.cpp b/clang/lib/Lex/PPDirectives.cpp index 04a30f66fb736..70934b9b1dec3 100644 --- a/clang/lib/Lex/PPDirectives.cpp +++ b/clang/lib/Lex/PPDirectives.cpp @@ -1242,6 +1242,9 @@ void Preprocessor::HandleDirective(Token &Result) { // pp-directive. bool ReadAnyTokensBeforeDirective =CurPPLexer->MIOpt.getHasReadAnyTokensVal(); + if (!hasSeenMainFileFirstPPToken()) + HandleMainFileFirstPPToken(Result); + // Save the '#' token in case we need to return it later. Token SavedHash = Result; diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 37ac1bf07e9c0..97bdeb873d699 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -469,6 +469,9 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // to disable the optimization in this case. if (CurPPLexer) CurPPLexer->MIOpt.ExpandedMacro(); + if (!hasSeenMainFileFirstPPToken()) + HandleMainFileFirstPPToken(Identifier); + // If this is a builtin macro, like __LINE__ or _Pragma, handle it specially. if (MI->isBuiltinMacro()) { if (Callbacks) diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 21fc7a2b6fae2..18b2f5f02d6c7 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -247,6 +247,8 @@ void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const { llvm::errs() << " [LeadingSpace]"; if (Tok.isExpandDisabled()) llvm::errs() << " [ExpandDisabled]"; + if (Tok.isFirstPPToken()) + llvm::errs() << " [First pp-token]"; if (Tok.needsCleaning()) { const char *Start = SourceMgr.getCharacterData(Tok.getLocation()); llvm::errs() << " [UnClean='" << StringRef(Start, Tok.getLength()) diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 788ed79e0c1fa..18f399aca59e8 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -2340,7 +2340,8 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() { Parser::DeclGroupPtrTy Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) { - SourceLocation StartLoc = Tok.getLocation(); + Token Introducer = Tok; + SourceLocation StartLoc = Introducer.getLocation(); Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export) ? Sema::ModuleDeclKind::Interface @@ -2359,7 +2360,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) { // Parse a global-module-fragment, if present. if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) { SourceLocation SemiLoc = ConsumeToken(); - if (ImportState != Sema::ModuleImportState::FirstDecl) { + if (!Introducer.isFirstPPToken()) { Diag(StartLoc, diag::err_global_module_introducer_not_at_start) << SourceRange(StartLoc, SemiLoc); return nullptr; @@ -2416,7 +2417,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) { ExpectAndConsumeSemi(diag::err_module_expected_semi); return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition, - ImportState); + ImportState, Introducer.isFirstPPToken()); } Decl *Parser::ParseModuleImport(SourceLocation AtLoc, diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 9fcaad48d3058..b2e432981098e 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -258,11 +258,11 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II, Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path, - ModuleIdPath Partition, ModuleImportState &ImportState) { + ModuleIdPath Partition, ModuleImportState &ImportState, + bool IntroducerIsFirstPPToken) { assert(getLangOpts().CPlusPlusModules && "should only have module decl in standard C++ modules"); - bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl; bool SeenGMF = ImportState == ModuleImportState::GlobalFragment; // If any of the steps here fail, we count that as invalidating C++20 // module state; @@ -328,14 +328,11 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, SeenGMF == (bool)this->TheGlobalModuleFragment) && "mismatched global module state"); - // In C++20, the module-declaration must be the first declaration if there - // is no global module fragment. - if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !SeenGMF) { + // In C++20, A module directive may only appear as the first preprocessing + // tokens in a file (excluding the global module fragment.). + if (getLangOpts().CPlusPlusModules && !IntroducerIsFirstPPToken && !SeenGMF) { Diag(ModuleLoc, diag::err_module_decl_not_at_start); - SourceLocation BeginLoc = - ModuleScopes.empty() - ? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()) - : ModuleScopes.back().BeginLoc; + SourceLocation BeginLoc = PP.getMainFileFirstPPToken().getLocation(); if (BeginLoc.isValid()) { Diag(BeginLoc, diag::note_global_module_introducer_missing) << FixItHint::CreateInsertion(BeginLoc, "module;\n"); diff --git a/clang/test/CXX/basic/basic.link/p1.cpp b/clang/test/CXX/basic/basic.link/p1.cpp index c6a119aa7f47c..26a5f025f42fe 100644 --- a/clang/test/CXX/basic/basic.link/p1.cpp +++ b/clang/test/CXX/basic/basic.link/p1.cpp @@ -1,57 +1,128 @@ -// RUN: %clang_cc1 -std=c++2a -verify %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s -// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s -// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s - -#ifndef NO_GLOBAL_FRAG -#ifdef EXPORT_FRAGS -export // expected-error {{global module fragment cannot be exported}} -#endif +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++2a -verify %t/M.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFrag.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDecl.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoPrivateFrag.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDeclAndNoPrivateFrag.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoPrivateFrag.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDecl.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm +// RUN: %clang_cc1 -std=c++2a -verify %t/ExportFrags.cppm + +//--- M.cppm module; -#ifdef NO_MODULE_DECL -// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}} -#endif -#endif +extern int a; // #a1 +export module Foo; + +int a; // expected-error {{declaration of 'a' in module Foo follows declaration in the global module}} + // expected-note@#a1 {{previous decl}} +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} +module :private; // #priv-frag +int b; // ok +module :private; // expected-error {{private module fragment redefined}} + // expected-note@#priv-frag {{previous definition is here}} + +//--- NoGlobalFrag.cppm + +extern int a; // #a1 +export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}} + // expected-note@-2 {{add 'module;' to the start of the file to introduce a global module fragment}} + +// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}} +// expected-note@#a1 {{previous decl}} + +int a; // #a2 +extern int b; +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} +module :private; // #priv-frag +int b; // ok +module :private; // expected-error {{private module fragment redefined}} +// expected-note@#priv-frag {{previous definition is here}} +//--- NoModuleDecl.cppm +module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}} extern int a; // #a1 +int a; // #a2 +extern int b; +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} +module :private; // expected-error {{private module fragment declaration with no preceding module declaration}} +int b; // ok -#ifndef NO_MODULE_DECL +//--- NoPrivateFrag.cppm +module; +extern int a; // #a1 export module Foo; -#ifdef NO_GLOBAL_FRAG -// expected-error@-2 {{module declaration must occur at the start of the translation unit}} + +// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}} +// expected-note@#a1 {{previous decl}} +int a; // #a2 +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} +int b; // ok + + +//--- NoModuleDeclAndNoPrivateFrag.cppm +module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}} +extern int a; // #a1 +int a; // #a2 +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} + +int b; // ok + +//--- NoGlobalFragAndNoPrivateFrag.cppm +extern int a; // #a1 +export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}} // expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}} -#endif // expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}} // expected-note@#a1 {{previous decl}} -#endif int a; // #a2 extern int b; module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} -#ifndef NO_PRIVATE_FRAG -#ifdef EXPORT_FRAGS -export // expected-error {{private module fragment cannot be exported}} -#endif +int b; // ok + +//--- NoGlobalFragAndNoModuleDecl.cppm +extern int a; // #a1 +int a; // #a2 +extern int b; +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} module :private; // #priv-frag -#ifdef NO_MODULE_DECL -// expected-error@-2 {{private module fragment declaration with no preceding module declaration}} -#endif -#endif +// expected-error@-1 {{private module fragment declaration with no preceding module declaration}} +int b; // ok + +//--- NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm +extern int a; // #a1 +int a; // #a2 +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} int b; // ok +//--- ExportFrags.cppm +export module; // expected-error {{global module fragment cannot be exported}} +extern int a; // #a1 +export module Foo; +// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}} +// expected-note@#a1 {{previous decl}} -#ifndef NO_PRIVATE_FRAG -#ifndef NO_MODULE_DECL +int a; // #a2 +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} + +module :private; // #priv-frag + +int b; // ok module :private; // expected-error {{private module fragment redefined}} -// expected-note@#priv-frag {{previous definition is here}} -#endif -#endif + // expected-note@#priv-frag {{previous definition is here}} diff --git a/clang/test/CXX/basic/basic.link/p2.cpp b/clang/test/CXX/basic/basic.link/p2.cpp index ccad42022ee80..94cbc62490b2f 100644 --- a/clang/test/CXX/basic/basic.link/p2.cpp +++ b/clang/test/CXX/basic/basic.link/p2.cpp @@ -1,16 +1,16 @@ -// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify -// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm -// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=M=%t.pcm +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -verify +// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -emit-module-interface -o %t.pcm +// RUN: %clang_cc1 -std=c++2a %t/pmf_in_implementation.cpp -verify -fmodule-file=M=%t.pcm -#ifdef EXPORT -// expected-no-diagnostics -export -#else -// expected-note@+2 {{add 'export' here}} -#endif -module M; -#ifndef EXPORT -// expected-error@+2 {{private module fragment in module implementation unit}} -#endif +//--- pmf_in_interface.cpp +// expected-no-diagnostics +export module M; module :private; + +//--- pmf_in_implementation.cpp +module M; // expected-note {{add 'export' here}} +module :private; // expected-error {{private module fragment in module implementation unit}} diff --git a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp index d70eb7de22c6a..fd0038b3f7745 100644 --- a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp +++ b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp @@ -1,14 +1,16 @@ // RUN: rm -rf %t -// RUN: mkdir -p %t -// RUN: echo '#ifndef FOO_H' > %t/foo.h -// RUN: echo '#define FOO_H' >> %t/foo.h -// RUN: echo 'extern int in_header;' >> %t/foo.h -// RUN: echo '#endif' >> %t/foo.h -// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface -DINTERFACE %s -o %t.pcm -// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm -DIMPLEMENTATION %s -verify -fno-modules-error-recovery -// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %s -verify -fno-modules-error-recovery - -#ifdef INTERFACE +// RUN: split-file %s %t +// RUN: %clang_cc1 -std=c++2a -I%t -emit-module-interface %t/interface.cppm -o %t.pcm +// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %t/implA.cppm -verify -fno-modules-error-recovery +// RUN: %clang_cc1 -std=c++2a -I%t -fmodule-file=A=%t.pcm %t/implB.cppm -verify -fno-modules-error-recovery + +//--- foo.h +#ifndef FOO_H +#define FOO_H +extern int in_header; +#endif + +//--- interface.cppm module; #include "foo.h" // FIXME: The following need to be moved to a header file. The global module @@ -22,11 +24,9 @@ static int internal; module :private; int not_exported_private; static int internal_private; -#else -#ifdef IMPLEMENTATION +//--- implA.cppm module; -#endif void test_early() { in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}} @@ -46,11 +46,7 @@ void test_early() { internal_private = 1; // expected-error {{undeclared identifier}} } -#ifdef IMPLEMENTATION module A; -#else -import A; -#endif void test_late() { in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} @@ -61,20 +57,54 @@ void test_late() { exported = 1; not_exported = 1; -#ifndef IMPLEMENTATION - // expected-error@-2 {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}} - // expected-n...@p2.cpp:18 {{'exported' declared here}} -#endif internal = 1; // expected-error {{use of undeclared identifier 'internal'}} not_exported_private = 1; -#ifndef IMPLEMENTATION - // FIXME: should not be visible here - // expected-error@-3 {{undeclared identifier}} -#endif internal_private = 1; // expected-error {{use of undeclared identifier 'internal_private'}} } -#endif +//--- implB.cppm +module; + +void test_early() { + in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}} + // expected-note@* {{not visible}} + + global_module_fragment = 1; // expected-error {{use of undeclared identifier 'global_module_fragment'}} + + exported = 1; // expected-error {{use of undeclared identifier 'exported'}} + + not_exported = 1; // expected-error {{use of undeclared identifier 'not_exported'}} + + // FIXME: We need better diagnostic message for static variable. + internal = 1; // expected-error {{use of undeclared identifier 'internal'}} + + not_exported_private = 1; // expected-error {{undeclared identifier}} + + internal_private = 1; // expected-error {{undeclared identifier}} +} + +export module B; +import A; + +void test_late() { + in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} + // expected-note@* {{not visible}} + + global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}} + + exported = 1; + + not_exported = 1; // expected-error {{use of undeclared identifier 'not_exported'; did you mean 'exported'?}} + // expected-note@* {{'exported' declared here}} + + internal = 1; // expected-error {{use of undeclared identifier 'internal'}} + + not_exported_private = 1; + // FIXME: should not be visible here + // expected-error@-2 {{undeclared identifier}} + + internal_private = 1; // expected-error {{use of undeclared identifier 'internal_private'}} +} \ No newline at end of file diff --git a/clang/test/CXX/module/basic/basic.def.odr/p6.cppm b/clang/test/CXX/module/basic/basic.def.odr/p6.cppm index 8e7917dc63ea5..c532e7ad40a10 100644 --- a/clang/test/CXX/module/basic/basic.def.odr/p6.cppm +++ b/clang/test/CXX/module/basic/basic.def.odr/p6.cppm @@ -3,29 +3,28 @@ // RUN: split-file %s %t // // RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm -// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm -DEXPORT -// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module.cppm -DUSING +// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module-export.cppm +// RUN: %clang_cc1 -std=c++20 -verify %t/global-vs-module-using.cppm // -// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/global-vs-module.cppm -o %t/M.pcm -DNO_GLOBAL -DEXPORT +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/M.cppm -o %t/M.pcm // RUN: %clang_cc1 -std=c++20 -verify %t/module-vs-global.cpp -fmodule-file=M=%t/M.pcm // // Some of the following tests intentionally have no -verify in their RUN // lines; we are testing that those cases do not produce errors. // -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -DMODULE_INTERFACE -verify -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -DMODULE_INTERFACE -DNO_IMPORT +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -verify +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -DNO_IMPORT // -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N.pcm -DMODULE_INTERFACE -DNO_ERRORS -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -verify +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N.pcm -DNO_ERRORS +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -verify // -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -DNO_IMPORT -verify +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N.pcm -DNO_IMPORT -verify // -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N-no-M.pcm -DMODULE_INTERFACE -DNO_ERRORS -DNO_IMPORT -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N-no-M.pcm -verify -// RUN: %clang_cc1 -std=c++20 %t/module-vs-module.cpp -fmodule-file=N=%t/N-no-M.pcm -DNO_IMPORT +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-interface.cpp -fmodule-file=M=%t/M.pcm -emit-module-interface -o %t/N-no-M.pcm -DNO_ERRORS -DNO_IMPORT +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=M=%t/M.pcm -fmodule-file=N=%t/N-no-M.pcm -verify +// RUN: %clang_cc1 -std=c++20 %t/module-vs-module-impl.cpp -fmodule-file=N=%t/N-no-M.pcm -DNO_IMPORT //--- global-vs-module.cppm -#ifndef NO_GLOBAL module; extern int var; // expected-note {{previous declaration is here}} int func(); // expected-note {{previous declaration is here}} @@ -40,11 +39,75 @@ template<typename> using type_tpl = int; // expected-note {{previous declaration typedef int type; namespace ns { using ::func; } namespace ns_alias = ns; -#endif export module M; -#ifdef USING +extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}} +int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}} +struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}} +using type = int; + +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; + +//--- global-vs-module-export.cppm +module; +extern int var; // expected-note {{previous declaration is here}} +int func(); // expected-note {{previous declaration is here}} +struct str; // expected-note {{previous declaration is here}} +using type = int; + +template<typename> extern int var_tpl; // expected-note {{previous declaration is here}} +template<typename> int func_tpl(); // expected-note {{previous declaration is here}} +template<typename> struct str_tpl; // expected-note {{previous declaration is here}} +template<typename> using type_tpl = int; // expected-note {{previous declaration is here}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; + +export module M; + +export { +extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}} +int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}} +struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}} +using type = int; + +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; +} + +//--- global-vs-module-using.cppm +module; +extern int var; // expected-note {{previous declaration is here}} +int func(); // expected-note {{previous declaration is here}} +struct str; // expected-note {{previous declaration is here}} +using type = int; + +template<typename> extern int var_tpl; // expected-note {{previous declaration is here}} +template<typename> int func_tpl(); // expected-note {{previous declaration is here}} +template<typename> struct str_tpl; // expected-note {{previous declaration is here}} +template<typename> using type_tpl = int; // expected-note {{previous declaration is here}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; + +export module M; + using ::var; using ::func; using ::str; @@ -53,11 +116,6 @@ using ::var_tpl; using ::func_tpl; using ::str_tpl; using ::type_tpl; -#endif - -#ifdef EXPORT -export { -#endif extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}} int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}} @@ -73,51 +131,87 @@ typedef int type; namespace ns { using ::func; } namespace ns_alias = ns; -#ifdef EXPORT +//--- M.cppm +export module M; + +export { +extern int var; // expected-error {{declaration of 'var' in module M follows declaration in the global module}} +int func(); // expected-error {{declaration of 'func' in module M follows declaration in the global module}} +struct str; // expected-error {{declaration of 'str' in module M follows declaration in the global module}} +using type = int; + +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module M follows declaration in the global module}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module M follows declaration in the global module}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module M follows declaration in the global module}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module M follows declaration in the global module}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; } -#endif //--- module-vs-global.cpp +module; import M; -extern int var; // expected-error {{declaration of 'var' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:35 {{previous}} -int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:36 {{previous}} -struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:37 {{previous}} +extern int var; // expected-error {{declaration of 'var' in the global module follows declaration in module M}} expected-n...@m.cppm:4 {{previous}} +int func(); // expected-error {{declaration of 'func' in the global module follows declaration in module M}} expected-n...@m.cppm:5 {{previous}} +struct str; // expected-error {{declaration of 'str' in the global module follows declaration in module M}} expected-n...@m.cppm:6 {{previous}} using type = int; -template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:40 {{previous}} -template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:41 {{previous}} -template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:42 {{previous}} -template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in the global module follows declaration in module M}} expected-n...@global-vs-module.cppm:43 {{previous}} +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in the global module follows declaration in module M}} expected-n...@m.cppm:9 {{previous}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in the global module follows declaration in module M}} expected-n...@m.cppm:10 {{previous}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in the global module follows declaration in module M}} expected-n...@m.cppm:11 {{previous}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in the global module follows declaration in module M}} expected-n...@m.cppm:12 {{previous}} typedef int type; namespace ns { using ::func; } namespace ns_alias = ns; -//--- module-vs-module.cpp -#ifdef MODULE_INTERFACE export module N; -#else -module N; -#endif + +//--- module-vs-module-interface.cpp +export module N; #ifndef NO_IMPORT import M; #endif #ifndef NO_ERRORS -extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:35 {{previous}} -int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:36 {{previous}} -struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:37 {{previous}} +extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-n...@m.cppm:4 {{previous}} +int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-n...@m.cppm:5 {{previous}} +struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-n...@m.cppm:6 {{previous}} using type = int; -template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:40 {{previous}} -template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:41 {{previous}} -template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:42 {{previous}} -template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-n...@global-vs-module.cppm:43 {{previous}} +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:9 {{previous}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:10 {{previous}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:11 {{previous}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:12 {{previous}} typedef int type; namespace ns { using ::func; } namespace ns_alias = ns; #endif +//--- module-vs-module-impl.cpp +module N; + +#ifndef NO_IMPORT +import M; +#endif + +#ifndef NO_ERRORS +extern int var; // expected-error {{declaration of 'var' in module N follows declaration in module M}} expected-n...@m.cppm:4 {{previous}} +int func(); // expected-error {{declaration of 'func' in module N follows declaration in module M}} expected-n...@m.cppm:5 {{previous}} +struct str; // expected-error {{declaration of 'str' in module N follows declaration in module M}} expected-n...@m.cppm:6 {{previous}} +using type = int; + +template<typename> extern int var_tpl; // expected-error {{declaration of 'var_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:9 {{previous}} +template<typename> int func_tpl(); // expected-error {{declaration of 'func_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:10 {{previous}} +template<typename> struct str_tpl; // expected-error {{declaration of 'str_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:11 {{previous}} +template<typename> using type_tpl = int; // expected-error {{declaration of 'type_tpl' in module N follows declaration in module M}} expected-n...@m.cppm:12 {{previous}} + +typedef int type; +namespace ns { using ::func; } +namespace ns_alias = ns; +#endif 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 d71358cc7a571..4bdcc9e5f278e 100644 --- a/clang/test/CXX/module/basic/basic.link/module-declaration.cpp +++ b/clang/test/CXX/module/basic/basic.link/module-declaration.cpp @@ -8,27 +8,19 @@ // 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/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 +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify -x c++ %t/z_impl.cppm +// 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/x_impl.cppm // // 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/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 +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/z_interface.cppm +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/x_interface.cppm // // Miscellaneous syntax. -// 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]]' +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/invalid_module_name.cppm +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/empty_attribute.cppm +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/fancy_attribute.cppm +// RUN: %clang_cc1 -std=c++20 -I%t -fmodule-file=x.y=%t/x.y.pcm -verify %t/maybe_unused_attribute.cppm //--- x.cppm export module x; @@ -38,17 +30,31 @@ int a, b; export module x.y; int c; -//--- 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 +//--- z_impl.cppm +module z; // expected-error {{module 'z' not found}} + +//--- x_impl.cppm +// expected-no-diagnostics +module x; + +//--- z_interface.cppm // expected-no-diagnostics -#endif +export module z; + +//--- x_interface.cppm +// expected-no-diagnostics +export module x; + +//--- invalid_module_name.cppm +export module z elderberry; // expected-error {{expected ';'}} \ + // expected-error {{a type specifier is required}} + +//--- empty_attribute.cppm +// expected-no-diagnostics +export module z [[]]; + +//--- fancy_attribute.cppm +export module z [[fancy]]; // expected-warning {{unknown attribute 'fancy' ignored}} + +//--- maybe_unused_attribute.cppm +export module z [[maybe_unused]]; // expected-error-re {{'maybe_unused' attribute cannot be applied to a module{{$}}}} diff --git a/clang/test/CXX/module/cpp.pre/module_decl.cpp b/clang/test/CXX/module/cpp.pre/module_decl.cpp new file mode 100644 index 0000000000000..6238347c167ac --- /dev/null +++ b/clang/test/CXX/module/cpp.pre/module_decl.cpp @@ -0,0 +1,8 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -verify -o %t/M.pcm + +// This is a comment +#define I32 int // expected-note {{add 'module;' to the start of the file to introduce a global module fragment}} +export module M; // expected-error {{module declaration must occur at the start of the translation unit}} +export I32 i32; 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 3670f9430ed4b..f65f050a3c7bd 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,10 +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.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 -DMODULE_NAME=a.b +// RUN: -verify %t/test.interface.cpp +// 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.implementation.cpp // 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 @@ -33,19 +33,33 @@ int use_2 = b; // ok // There is no relation between module x and module x.y. int use_3 = c; // expected-error {{use of undeclared identifier 'c'}} -//--- test.cpp -#ifdef INTERFACE -export module MODULE_NAME; -#else -module MODULE_NAME; -#endif +//--- test.interface.cpp +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-re {{unknown attribute 'blarg::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'}} + +import blarg; // expected-error {{module 'blarg' not found}} + +int use_4 = c; // ok + +//--- test.implementation.cpp +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 'blarg::noreturn' ignored}} +import x [[blarg::noreturn]]; // expected-warning-re {{unknown attribute 'blarg::noreturn' ignored{{.*}}}} import x.y; import x.; // expected-error {{expected a module name after 'import'}} diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm index 84ef85126c369..2158d7fa84b86 100644 --- a/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm +++ b/clang/test/CXX/module/dcl.dcl/dcl.module/dcl.module.interface/p1.cppm @@ -1,29 +1,26 @@ -// RUN: %clang_cc1 -std=c++20 %s -verify -emit-module-interface -o /dev/null -// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -verify -emit-module-interface -o %t -// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -verify -fmodule-file=A=%t -o /dev/null +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 %t/ExportDeclNotInModulePurview.cppm -verify -emit-module-interface -o /dev/null +// RUN: %clang_cc1 -std=c++20 %t/A.cppm -verify -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 %t/AddExport.cppm -verify -fmodule-file=A=%t/A.pcm -o /dev/null // -// RUN: %clang_cc1 -std=c++20 %s -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null -// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null -// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -DBUILT_AS_INTERFACE -emit-module-interface -verify -o /dev/null +// RUN: %clang_cc1 -std=c++20 %t/AddExport2.cppm -emit-module-interface -verify -o /dev/null -#if INTERFACE +//--- ExportDeclNotInModulePurview.cppm +// expected-error@* {{missing 'export module' declaration in module interface unit}} +export int b; // expected-error {{export declaration can only be used within a module purview}} + +//--- A.cppm // expected-no-diagnostics export module A; -#elif IMPLEMENTATION -module A; // #module-decl - #ifdef BUILT_AS_INTERFACE - // expected-error@-2 {{missing 'export' specifier in module declaration while building module interface}} - #define INTERFACE - #endif -#else // Not in a module -// expected-error@* {{missing 'export module' declaration in module interface unit}} -#endif +export int a; -#ifndef INTERFACE +//--- AddExport.cppm +module A; // #module-decl export int b; // expected-error {{export declaration can only be used within a module purview}} -#ifdef IMPLEMENTATION // expected-note@#module-decl {{add 'export' here}} -#endif -#else + +//--- AddExport2.cppm +module A; // expected-error {{missing 'export' specifier in module declaration while building module interface}} export int a; -#endif diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp b/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp index db86b5dd34c38..95d087e0f6c78 100644 --- a/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp +++ b/clang/test/CXX/module/dcl.dcl/dcl.module/p1.cpp @@ -1,14 +1,30 @@ -// RUN: %clang_cc1 -std=c++20 -verify %s -DFOO=export -DBAR=export -// RUN: %clang_cc1 -std=c++20 -verify %s -DFOO=export -DBAR= -// RUN: %clang_cc1 -std=c++20 %s -DFOO=export -emit-module-interface -o %t -// RUN: %clang_cc1 -std=c++20 %s -fmodule-file=foo=%t -DFOO= -// RUN: %clang_cc1 -std=c++20 %s -fmodule-file=foo=%t -DBAR=export -// RUN: %clang_cc1 -std=c++20 -verify %s -fmodule-file=foo=%t -DFOO= -DBAR=export - -#ifdef FOO -FOO module foo; // expected-note {{previous module declaration is here}} -#endif - -#ifdef BAR -BAR module bar; // expected-error {{translation unit contains multiple module declarations}} -#endif +// RUN: rm -rf %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -verify %t/A.cppm +// RUN: %clang_cc1 -std=c++20 -verify %t/B.cppm +// RUN: %clang_cc1 -std=c++20 %t/C.cppm -emit-module-interface -o %t/C.pcm +// RUN: %clang_cc1 -std=c++20 %t/D.cppm -fmodule-file=foo=%t/C.pcm +// RUN: %clang_cc1 -std=c++20 %t/E.cppm -fmodule-file=foo=%t/C.pcm +// RUN: %clang_cc1 -std=c++20 -verify %t/F.cppm -fmodule-file=foo=%t/C.pcm + +//--- A.cppm +export module foo; // expected-note {{previous module declaration is here}} +export module bar; // expected-error {{translation unit contains multiple module declarations}} + +//--- B.cppm +export module foo; // expected-note {{previous module declaration is here}} +module bar; // expected-error {{translation unit contains multiple module declarations}} + +//--- C.cppm +export module foo; + +//--- D.cppm +module foo; + +//--- E.cppm +export module bar; + +//--- F.cppm +module foo; // expected-note {{previous module declaration is here}} +export module bar; // expected-error {{translation unit contains multiple module declarations}} diff --git a/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp b/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp index ca100443a4c67..a0d30233809f9 100644 --- a/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp +++ b/clang/test/CXX/module/dcl.dcl/dcl.module/p5.cpp @@ -1,22 +1,47 @@ // RUN: rm -rf %t -// RUN: %clang_cc1 -std=c++20 -emit-module-interface %s -o %t -DINTERFACE -// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DIMPLEMENTATION -// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DEARLY_IMPLEMENTATION -// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t %s -verify -DUSER +// RUN: split-file %s %t +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/interface.cppm -o %t/interface.pcm +// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/implementation.cppm -verify -DIMPLEMENTATION +// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/early_impl.cppm -verify -DEARLY_IMPLEMENTATION +// RUN: %clang_cc1 -std=c++20 -fmodule-file=Foo=%t/interface.pcm %t/user.cppm -verify -DUSER + +//--- interface.cppm // expected-no-diagnostics +module; -#if defined(INTERFACE) || defined(EARLY_IMPLEMENTATION) || defined(IMPLEMENTATION) +template<typename T> struct type_template { + typedef T type; + void f(type); +}; + +template<typename T> void type_template<T>::f(type) {} + +template<int = 0, typename = int, template<typename> class = type_template> +struct default_template_args {}; + +export module Foo; + +//--- implementation.cppm +// expected-no-diagnostics module; -#endif -#ifdef USER -import Foo; -#endif +template<typename T> struct type_template { + typedef T type; + void f(type); +}; + +template<typename T> void type_template<T>::f(type) {} + +template<int = 0, typename = int, template<typename> class = type_template> +struct default_template_args {}; -#ifdef EARLY_IMPLEMENTATION module Foo; -#endif + +//--- early_impl.cppm +// expected-no-diagnostics +module; +module Foo; template<typename T> struct type_template { typedef T type; @@ -28,10 +53,16 @@ template<typename T> void type_template<T>::f(type) {} template<int = 0, typename = int, template<typename> class = type_template> struct default_template_args {}; -#ifdef INTERFACE -export module Foo; -#endif +//--- user.cppm +// expected-no-diagnostics +import Foo; -#ifdef IMPLEMENTATION -module Foo; -#endif +template<typename T> struct type_template { + typedef T type; + void f(type); +}; + +template<typename T> void type_template<T>::f(type) {} + +template<int = 0, typename = int, template<typename> class = type_template> +struct default_template_args {}; diff --git a/clang/test/CXX/module/module.interface/p2.cpp b/clang/test/CXX/module/module.interface/p2.cpp index 4f06b9f386869..8221c400ecd62 100644 --- a/clang/test/CXX/module/module.interface/p2.cpp +++ b/clang/test/CXX/module/module.interface/p2.cpp @@ -1,24 +1,26 @@ // RUN: rm -rf %t // RUN: mkdir -p %t +// RUN: split-file %s %t +// // RUN: %clang_cc1 -std=c++20 -x c++-header %S/Inputs/header.h -emit-header-unit -o %t/h.pcm -// RUN: %clang_cc1 -std=c++20 %s -DX_INTERFACE -emit-module-interface -o %t/x.pcm -// RUN: %clang_cc1 -std=c++20 %s -DY_INTERFACE -emit-module-interface -o %t/y.pcm -// RUN: %clang_cc1 -std=c++20 %s -DINTERFACE -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -emit-module-interface -o %t/m.pcm -// RUN: %clang_cc1 -std=c++20 %s -DIMPLEMENTATION -I%S/Inputs -fmodule-file=%t/h.pcm \ +// RUN: %clang_cc1 -std=c++20 %t/x.cppm -emit-module-interface -o %t/x.pcm +// RUN: %clang_cc1 -std=c++20 %t/y.cppm -emit-module-interface -o %t/y.pcm +// RUN: %clang_cc1 -std=c++20 %t/interface.cppm -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -emit-module-interface -o %t/m.pcm +// RUN: %clang_cc1 -std=c++20 %t/impl.cppm -I%S/Inputs -fmodule-file=%t/h.pcm \ // RUN: -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -fmodule-file=p2=%t/m.pcm -verify \ // RUN: -Wno-experimental-header-units -// RUN: %clang_cc1 -std=c++20 %s -DUSER -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=p2=%t/m.pcm \ +// RUN: %clang_cc1 -std=c++20 %t/user.cppm -I%S/Inputs -fmodule-file=%t/h.pcm -fmodule-file=p2=%t/m.pcm \ // RUN: -fmodule-file=X=%t/x.pcm -fmodule-file=Y=%t/y.pcm -Wno-experimental-header-units -verify -#if defined(X_INTERFACE) +//--- x.cppm export module X; export int x; -#elif defined(Y_INTERFACE) +//--- y.cppm export module Y; export int y; -#elif defined(INTERFACE) +//--- interface.cppm export module p2; export import X; import Y; // not exported @@ -39,7 +41,7 @@ namespace C {} namespace D { int f(); } export namespace D {} -#elif defined(IMPLEMENTATION) +//--- impl.cppm module p2; import "header.h"; @@ -66,7 +68,7 @@ void use() { int use_header() { return foo + bar::baz(); } -#elif defined(USER) +//--- user.cppm import p2; import "header.h"; @@ -96,7 +98,3 @@ void use() { } int use_header() { return foo + bar::baz(); } - -#else -#error unknown mode -#endif diff --git a/clang/test/CXX/module/module.unit/p8.cpp b/clang/test/CXX/module/module.unit/p8.cpp index a5c01c493558e..fb190257d3a23 100644 --- a/clang/test/CXX/module/module.unit/p8.cpp +++ b/clang/test/CXX/module/module.unit/p8.cpp @@ -1,37 +1,45 @@ -// RUN: echo 'export module foo; export int n;' > %t.cppm +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// RUN: echo 'export module foo;' > %t.cppm +// RUN: echo 'export int n;' >> %t.cppm // RUN: %clang_cc1 -std=c++2a %t.cppm -emit-module-interface -o %t.pcm -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=0 %s -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=1 %s -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=2 %s -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=3 %s -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=4 %s -// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=5 %s - -#if MODE == 0 +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=0 %t/A.cppm +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=1 %t/B.cppm +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=2 %t/C.cppm +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=3 %t/D.cppm +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=4 %t/E.cppm +// RUN: %clang_cc1 -std=c++2a -fmodule-file=foo=%t.pcm -verify -DMODE=5 %t/F.cppm + +//--- A.cppm // no module declaration +// expected-no-diagnostics -#elif MODE == 1 +//--- B.cppm // expected-no-diagnostics module foo; // Implementation, implicitly imports foo. #define IMPORTED -#elif MODE == 2 +int k = n; + +//--- C.cppm export module foo; -#elif MODE == 3 +int k = n; // expected-error {{use of undeclared identifier 'n'}} + +//--- D.cppm export module bar; // A different module -#elif MODE == 4 +int k = n; // expected-error {{use of undeclared identifier 'n'}} + +//--- E.cppm module foo:bar; // Partition implementation //#define IMPORTED (we don't import foo here) -#elif MODE == 5 +int k = n; // expected-error {{use of undeclared identifier 'n'}} + +//--- F.cppm export module foo:bar; // Partition interface //#define IMPORTED (we don't import foo here) -#endif - -int k = n; -#ifndef IMPORTED -// expected-error@-2 {{use of undeclared identifier 'n'}} -#endif +int k = n; // expected-error {{use of undeclared identifier 'n'}} diff --git a/clang/test/Driver/modules.cpp b/clang/test/Driver/modules.cpp index b0d1f2280d254..088a73230f81e 100644 --- a/clang/test/Driver/modules.cpp +++ b/clang/test/Driver/modules.cpp @@ -1,43 +1,48 @@ // RUN: rm -rf %t // RUN: mkdir %t +// RUN: split-file %s %t // Check compiling a module interface to a .pcm file. // -// RUN: %clang -std=c++2a -x c++-module --precompile %s -o %t/module.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE -// RUN: %clang -std=gnu++2a -x c++-module --precompile %s -o %t/module-gnu.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE +// RUN: %clang -std=c++2a -x c++-module --precompile %t/foo.cpp -o %t/foo.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE +// RUN: %clang -std=gnu++2a -x c++-module --precompile %t/foo.cpp -o %t/foo-gnu.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE // // CHECK-PRECOMPILE: -cc1 {{.*}} -emit-module-interface // CHECK-PRECOMPILE-SAME: -o {{.*}}.pcm // CHECK-PRECOMPILE-SAME: -x c++ -// CHECK-PRECOMPILE-SAME: modules.cpp +// CHECK-PRECOMPILE-SAME: foo.cpp // Check compiling a .pcm file to a .o file. // -// RUN: %clang -std=c++2a %t/module.pcm -S -o %t/module.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-COMPILE +// RUN: %clang -std=c++2a %t/foo.pcm -S -o %t/foo.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-COMPILE // // CHECK-COMPILE: -cc1 {{.*}} {{-emit-obj|-S}} -// CHECK-COMPILE-SAME: -o {{.*}}module{{2*}}.pcm.o +// CHECK-COMPILE-SAME: -o {{.*}}foo{{2*}}.pcm.o // CHECK-COMPILE-SAME: -x pcm // CHECK-COMPILE-SAME: {{.*}}.pcm // Check use of a .pcm file in another compilation. // -// RUN: %clang -std=c++2a -fmodule-file=%t/module.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE -// RUN: %clang -std=c++20 -fmodule-file=%t/module.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE -// RUN: %clang -std=gnu++20 -fmodule-file=%t/module-gnu.pcm -Dexport= %s -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE +// RUN: %clang -std=c++2a -fmodule-file=foo=%t/foo.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE +// RUN: %clang -std=c++20 -fmodule-file=foo=%t/foo.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE +// RUN: %clang -std=gnu++20 -fmodule-file=foo=%t/foo-gnu.pcm %t/foo_impl.cpp -S -o %t/module.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-USE // // CHECK-USE: -cc1 {{.*}} {{-emit-obj|-S}} -// CHECK-USE-SAME: -fmodule-file={{.*}}.pcm +// CHECK-USE-SAME: -fmodule-file=foo={{.*}}.pcm // CHECK-USE-SAME: -o {{.*}}.{{o|s}}{{"?}} {{.*}}-x c++ -// CHECK-USE-SAME: modules.cpp +// CHECK-USE-SAME: foo_impl.cpp // Check combining precompile and compile steps works. // -// RUN: %clang -std=c++2a -x c++-module %s -S -o %t/module2.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE --check-prefix=CHECK-COMPILE +// RUN: %clang -std=c++2a -x c++-module %t/foo.cpp -S -o %t/foo2.pcm.o -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE --check-prefix=CHECK-COMPILE // Check that .cppm is treated as a module implicitly. // -// RUN: cp %s %t/module.cppm -// RUN: %clang -std=c++2a --precompile %t/module.cppm -o %t/module.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE +// RUN: cp %t/foo.cpp %t/foo.cppm +// RUN: %clang -std=c++2a --precompile %t/foo.cppm -o %t/foo.pcm -v 2>&1 | FileCheck %s --check-prefix=CHECK-PRECOMPILE +//--- foo.cpp export module foo; + +//--- foo_impl.cpp +module foo; diff --git a/clang/test/Modules/named-modules-adl-3.cppm b/clang/test/Modules/named-modules-adl-3.cppm index d70946fa068b3..a3644b45a5347 100644 --- a/clang/test/Modules/named-modules-adl-3.cppm +++ b/clang/test/Modules/named-modules-adl-3.cppm @@ -58,6 +58,7 @@ void b(T x) { } //--- c.cppm +module; #ifdef EXPORT_OPERATOR // expected-no-diagnostics #endif diff --git a/clang/test/Modules/reserved-names-1.cppm b/clang/test/Modules/reserved-names-1.cppm index e780f1e35b3b7..35b264bcb573b 100644 --- a/clang/test/Modules/reserved-names-1.cppm +++ b/clang/test/Modules/reserved-names-1.cppm @@ -88,12 +88,14 @@ export module module; // expected-error {{'module' is an invalid name for a modu export module import; // expected-error {{'import' is an invalid name for a module}} //--- _Test.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif export module _Test; // loud-warning {{'_Test' is a reserved name for a module}} //--- __test.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif @@ -101,6 +103,7 @@ export module __test; // loud-warning {{'__test' is a reserved name for a module export int a = 43; //--- te__st.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif @@ -108,6 +111,7 @@ export module te__st; // loud-warning {{'te__st' is a reserved name for a module export int a = 43; //--- std.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif @@ -116,36 +120,42 @@ export module std; // loud-warning {{'std' is a reserved name for a module}} export int a = 43; //--- std.foo.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif export module std.foo;// loud-warning {{'std' is a reserved name for a module}} //--- std0.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif export module std0; // loud-warning {{'std0' is a reserved name for a module}} //--- std1000000.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif export module std1000000; // loud-warning {{'std1000000' is a reserved name for a module}} //--- should_diag._Test.cppm +module; #ifdef NODIAGNOSTICS // expected-no-diagnostics #endif export module should_diag._Test; // loud-warning {{'_Test' is a reserved name for a module}} //--- system-module.cppm +module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}} // Show that being in a system header doesn't save you from diagnostics about // use of an invalid module-name identifier. # 34 "reserved-names-1.cpp" 1 3 export module module; // expected-error {{'module' is an invalid name for a module}} //--- system._Test.import.cppm +module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}} # 34 "reserved-names-1.cpp" 1 3 export module _Test.import; // expected-error {{'import' is an invalid name for a module}} diff --git a/clang/test/Modules/reserved-names-system-header-1.cpp b/clang/test/Modules/reserved-names-system-header-1.cpp index 2db4c08add1d9..fa869483980f6 100644 --- a/clang/test/Modules/reserved-names-system-header-1.cpp +++ b/clang/test/Modules/reserved-names-system-header-1.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s // expected-no-diagnostics +module; // Show that we suppress the reserved identifier diagnostic in a system header. # 100 "file.cpp" 1 3 // Enter a system header export module std; diff --git a/clang/test/Modules/reserved-names-system-header-2.cpp b/clang/test/Modules/reserved-names-system-header-2.cpp index 2087f487721cb..d429e58dc1714 100644 --- a/clang/test/Modules/reserved-names-system-header-2.cpp +++ b/clang/test/Modules/reserved-names-system-header-2.cpp @@ -1,6 +1,7 @@ // RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s // expected-no-diagnostics +module; // Show that we suppress the reserved identifier diagnostic in a system header. # 100 "file.cpp" 1 3 // Enter a system header export module __test; diff --git a/clang/test/SemaCXX/modules.cppm b/clang/test/SemaCXX/modules.cppm index 41204be76eafa..5d0d6da44a2ed 100644 --- a/clang/test/SemaCXX/modules.cppm +++ b/clang/test/SemaCXX/modules.cppm @@ -1,19 +1,20 @@ -// 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: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t -#if TEST == 0 || TEST == 2 -// expected-no-diagnostics -#endif +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test0.cpp -o %t/test0.pcm -verify +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test1.cpp -o %t/test1.pcm -verify +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test2.cpp -fmodule-file=foo=%t/test0.pcm -o %t/test2.pcm -verify +// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/test3.cpp -fmodule-file=foo=%t/test0.pcm -o %t/test3.pcm -verify +//--- test0.cpp +// expected-no-diagnostics export module foo; static int m; int n; -#if TEST == 0 export { int a; int b; @@ -27,33 +28,18 @@ export void f() {} export struct T { } t; -#elif TEST == 3 -int use_a = a; // expected-error {{use of undeclared identifier 'a'}} -#undef foo -import foo; // expected-error {{imports must immediately follow the module declaration}} - -export {} -export { - ; // No diagnostic after P2615R1 DR -} -export { - static_assert(true); // No diagnostic after P2615R1 DR -} +//--- test1.cpp +export module foo; -int use_b = b; // expected-error{{use of undeclared identifier 'b'}} -int use_n = n; // FIXME: this should not be visible, because it is not exported +static int m; -extern int n; -static_assert(&n != p); // expected-error{{use of undeclared identifier 'p'}} -#endif +int n; -#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 // FIXME: Exports of declarations without external linkage are disallowed. // Exports of declarations with non-external-linkage types are disallowed. @@ -61,7 +47,6 @@ struct S { // 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 { @@ -71,4 +56,36 @@ export { // expected-note {{export block begins here}} } // namespace NestedExport } } -#endif + +//--- test2.cpp +// expected-no-diagnostics +export module foo; + +static int m; + +int n; + +//--- test3.cpp +export module bar; + +static int m; + +int n; + +int use_a = a; // expected-error {{use of undeclared identifier 'a'}} + +import foo; // expected-error {{imports must immediately follow the module declaration}} + +export {} +export { + ; // No diagnostic after P2615R1 DR +} +export { + static_assert(true); // No diagnostic after P2615R1 DR +} + +int use_b = b; // expected-error{{use of undeclared identifier 'b'}} +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'}} diff --git a/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp b/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp index 7c0b967a3c03f..30fea464a8dc5 100644 --- a/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp +++ b/clang/test/SemaCXX/type-aware-new-delete-transparent-contexts.cpp @@ -1,12 +1,22 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=0 -// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=1 -// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++26 -fexceptions -DTRANSPARENT_DECL=2 +// RUN: rm -rf %t +// RUN: mkdir %t +// RUN: split-file %s %t +// RUN: %clang_cc1 -fsyntax-only -verify %t/testing.cpp -std=c++26 -fexceptions -DTRANSPARENT_DECL=0 +// RUN: %clang_cc1 -fsyntax-only -verify %t/testing.cpp -std=c++26 -fexceptions -DTRANSPARENT_DECL=1 +// RUN: %clang_cc1 -fsyntax-only -verify %t/module_testing.cppm -std=c++26 -fexceptions -DTRANSPARENT_DECL=2 + +//--- module_testing.cppm // expected-no-diagnostics -#if TRANSPARENT_DECL==2 export module Testing; -#endif +#include "testing.inc" + +//--- testing.cpp +// expected-no-diagnostics +#include "testing.inc" + +//--- testing.inc namespace std { template <class T> struct type_identity {}; using size_t = __SIZE_TYPE__; diff --git a/clang/unittests/Lex/LexerTest.cpp b/clang/unittests/Lex/LexerTest.cpp index 381755d4d1b6f..33c8abbec35a3 100644 --- a/clang/unittests/Lex/LexerTest.cpp +++ b/clang/unittests/Lex/LexerTest.cpp @@ -49,7 +49,8 @@ class LexerTest : public ::testing::Test { } std::unique_ptr<Preprocessor> CreatePP(StringRef Source, - TrivialModuleLoader &ModLoader) { + TrivialModuleLoader &ModLoader, + StringRef PreDefines = {}) { std::unique_ptr<llvm::MemoryBuffer> Buf = llvm::MemoryBuffer::getMemBuffer(Source); SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf))); @@ -61,6 +62,8 @@ class LexerTest : public ::testing::Test { PPOpts, Diags, LangOpts, SourceMgr, HeaderInfo, ModLoader, /*IILookup =*/nullptr, /*OwnsHeaderSearch =*/false); + if (!PreDefines.empty()) + PP->setPredefines(PreDefines.str()); PP->Initialize(*Target); PP->EnterMainSourceFile(); return PP; @@ -769,4 +772,46 @@ TEST(LexerPreambleTest, PreambleBounds) { } } +TEST_F(LexerTest, CheckFirstPPToken) { + { + TrivialModuleLoader ModLoader; + auto PP = CreatePP("// This is a comment\n" + "int a;", + ModLoader); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(Tok.is(tok::kw_int)); + EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::kw_int)); + } + { + TrivialModuleLoader ModLoader; + auto PP = CreatePP("// This is a comment\n" + "#define FOO int\n" + "FOO a;", + ModLoader); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(Tok.is(tok::kw_int)); + EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::hash)); + } + + { + TrivialModuleLoader ModLoader; + auto PP = CreatePP("// This is a comment\n" + "FOO a;", + ModLoader, "#define FOO int\n"); + Token Tok; + PP->Lex(Tok); + EXPECT_TRUE(Tok.is(tok::kw_int)); + EXPECT_TRUE(PP->hasSeenMainFileFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().isFirstPPToken()); + EXPECT_TRUE(PP->getMainFileFirstPPToken().is(tok::identifier)); + EXPECT_TRUE( + PP->getMainFileFirstPPToken().getIdentifierInfo()->isStr("FOO")); + } +} } // anonymous namespace _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits