Skip to content

Commit 3346cae

Browse files
committed
[C++][Modules][P1857R3 2]: A module directive may only appear as the first preprocessing tokens in a file
Signed-off-by: yronglin <[email protected]>
1 parent 2cb32e2 commit 3346cae

File tree

25 files changed

+637
-299
lines changed

25 files changed

+637
-299
lines changed

clang/include/clang/Lex/Lexer.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ class Lexer : public PreprocessorLexer {
143143
/// True if this is the first time we're lexing the input file.
144144
bool IsFirstTimeLexingFile;
145145

146+
/// True if current lexing token is the first pp-token.
147+
bool IsFirstPPToken;
148+
146149
// NewLinePtr - A pointer to new line character '\n' being lexed. For '\r\n',
147150
// it also points to '\n.'
148151
const char *NewLinePtr;

clang/include/clang/Lex/Preprocessor.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,9 @@ class Preprocessor {
350350
/// Whether the last token we lexed was an '@'.
351351
bool LastTokenWasAt = false;
352352

353+
/// First pp-token in current translation unit.
354+
Token FirstPPToken;
355+
353356
/// A position within a C++20 import-seq.
354357
class StdCXXImportSeq {
355358
public:
@@ -1766,6 +1769,8 @@ class Preprocessor {
17661769
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
17671770
bool ForHasEmbed);
17681771

1772+
void setFirstPPToken(const Token &Tok) { FirstPPToken = Tok; }
1773+
Token getFirstPPToken() const { return FirstPPToken; }
17691774
bool LexAfterModuleImport(Token &Result);
17701775
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
17711776

clang/include/clang/Lex/Token.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,12 @@ class Token {
8686
// macro stringizing or charizing operator.
8787
CommaAfterElided = 0x200, // The comma following this token was elided (MS).
8888
IsEditorPlaceholder = 0x400, // This identifier is a placeholder.
89-
IsReinjected = 0x800, // A phase 4 token that was produced before and
90-
// re-added, e.g. via EnterTokenStream. Annotation
91-
// tokens are *not* reinjected.
89+
90+
IsReinjected = 0x800, // A phase 4 token that was produced before and
91+
// re-added, e.g. via EnterTokenStream. Annotation
92+
// tokens are *not* reinjected.
93+
FirstPPToken = 0x1000, // This token is the first pp token in the
94+
// translation unit.
9295
};
9396

9497
tok::TokenKind getKind() const { return Kind; }
@@ -318,6 +321,9 @@ class Token {
318321
/// represented as characters between '<#' and '#>' in the source code. The
319322
/// lexer uses identifier tokens to represent placeholders.
320323
bool isEditorPlaceholder() const { return getFlag(IsEditorPlaceholder); }
324+
325+
/// Returns true if this token is the first pp-token.
326+
bool isFirstPPToken() const { return getFlag(FirstPPToken); }
321327
};
322328

323329
/// Information about the conditional stack (\#if directives)

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9822,7 +9822,8 @@ class Sema final : public SemaBase {
98229822
DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc,
98239823
SourceLocation ModuleLoc, ModuleDeclKind MDK,
98249824
ModuleIdPath Path, ModuleIdPath Partition,
9825-
ModuleImportState &ImportState);
9825+
ModuleImportState &ImportState,
9826+
bool IntroducerIsFirstPPToken);
98269827

98279828
/// The parser has processed a global-module-fragment declaration that begins
98289829
/// the definition of the global module fragment of the current module unit.

clang/lib/Lex/Lexer.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ void Lexer::InitLexer(const char *BufStart, const char *BufPtr,
174174
ExtendedTokenMode = 0;
175175

176176
NewLinePtr = nullptr;
177+
178+
IsFirstPPToken = true;
177179
}
178180

179181
/// Lexer constructor - Create a new lexer object for the specified buffer
@@ -3725,13 +3727,21 @@ bool Lexer::Lex(Token &Result) {
37253727
HasLeadingEmptyMacro = false;
37263728
}
37273729

3730+
if (IsFirstPPToken) {
3731+
Result.setFlag(Token::FirstPPToken);
3732+
IsFirstPPToken = false;
3733+
}
3734+
37283735
bool atPhysicalStartOfLine = IsAtPhysicalStartOfLine;
37293736
IsAtPhysicalStartOfLine = false;
37303737
bool isRawLex = isLexingRawMode();
37313738
(void) isRawLex;
37323739
bool returnedToken = LexTokenInternal(Result, atPhysicalStartOfLine);
37333740
// (After the LexTokenInternal call, the lexer might be destroyed.)
37343741
assert((returnedToken || !isRawLex) && "Raw lex must succeed");
3742+
3743+
if (returnedToken && Result.isFirstPPToken() && PP)
3744+
PP->setFirstPPToken(Result);
37353745
return returnedToken;
37363746
}
37373747

clang/lib/Parse/Parser.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,7 +2340,8 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() {
23402340

23412341
Parser::DeclGroupPtrTy
23422342
Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
2343-
SourceLocation StartLoc = Tok.getLocation();
2343+
Token Introducer = Tok;
2344+
SourceLocation StartLoc = Introducer.getLocation();
23442345

23452346
Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export)
23462347
? Sema::ModuleDeclKind::Interface
@@ -2359,7 +2360,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
23592360
// Parse a global-module-fragment, if present.
23602361
if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) {
23612362
SourceLocation SemiLoc = ConsumeToken();
2362-
if (ImportState != Sema::ModuleImportState::FirstDecl) {
2363+
if (!Introducer.isFirstPPToken()) {
23632364
Diag(StartLoc, diag::err_global_module_introducer_not_at_start)
23642365
<< SourceRange(StartLoc, SemiLoc);
23652366
return nullptr;
@@ -2416,7 +2417,7 @@ Parser::ParseModuleDecl(Sema::ModuleImportState &ImportState) {
24162417
ExpectAndConsumeSemi(diag::err_module_expected_semi);
24172418

24182419
return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition,
2419-
ImportState);
2420+
ImportState, Introducer.isFirstPPToken());
24202421
}
24212422

24222423
Decl *Parser::ParseModuleImport(SourceLocation AtLoc,

clang/lib/Sema/SemaModule.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,11 +258,11 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II,
258258
Sema::DeclGroupPtrTy
259259
Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
260260
ModuleDeclKind MDK, ModuleIdPath Path,
261-
ModuleIdPath Partition, ModuleImportState &ImportState) {
261+
ModuleIdPath Partition, ModuleImportState &ImportState,
262+
bool IntroducerIsFirstPPToken) {
262263
assert(getLangOpts().CPlusPlusModules &&
263264
"should only have module decl in standard C++ modules");
264265

265-
bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl;
266266
bool SeenGMF = ImportState == ModuleImportState::GlobalFragment;
267267
// If any of the steps here fail, we count that as invalidating C++20
268268
// module state;
@@ -328,12 +328,14 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc,
328328
SeenGMF == (bool)this->TheGlobalModuleFragment) &&
329329
"mismatched global module state");
330330

331-
// In C++20, the module-declaration must be the first declaration if there
332-
// is no global module fragment.
333-
if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !SeenGMF) {
331+
// In C++20, A module directive may only appear as the first preprocessing
332+
// tokens in a file (excluding the global module fragment.).
333+
if (getLangOpts().CPlusPlusModules && !IntroducerIsFirstPPToken && !SeenGMF) {
334334
Diag(ModuleLoc, diag::err_module_decl_not_at_start);
335335
SourceLocation BeginLoc =
336-
ModuleScopes.empty()
336+
PP.getFirstPPToken().getLocation().isValid()
337+
? PP.getFirstPPToken().getLocation()
338+
: ModuleScopes.empty()
337339
? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
338340
: ModuleScopes.back().BeginLoc;
339341
if (BeginLoc.isValid()) {
Lines changed: 107 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,128 @@
1-
// RUN: %clang_cc1 -std=c++2a -verify %s
2-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s
3-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s
4-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_PRIVATE_FRAG %s
5-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
6-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_PRIVATE_FRAG %s
7-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL %s
8-
// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG -DNO_MODULE_DECL -DNO_PRIVATE_FRAG %s
9-
// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s
10-
11-
#ifndef NO_GLOBAL_FRAG
12-
#ifdef EXPORT_FRAGS
13-
export // expected-error {{global module fragment cannot be exported}}
14-
#endif
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -std=c++2a -verify %t/M.cppm
5+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFrag.cppm
6+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDecl.cppm
7+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoPrivateFrag.cppm
8+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoModuleDeclAndNoPrivateFrag.cppm
9+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoPrivateFrag.cppm
10+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDecl.cppm
11+
// RUN: %clang_cc1 -std=c++2a -verify %t/NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm
12+
// RUN: %clang_cc1 -std=c++2a -verify %t/ExportFrags.cppm
13+
14+
//--- M.cppm
1515
module;
16-
#ifdef NO_MODULE_DECL
17-
// expected-error@-2 {{missing 'module' declaration at end of global module fragment introduced here}}
18-
#endif
19-
#endif
16+
extern int a; // #a1
17+
export module Foo;
18+
19+
int a; // expected-error {{declaration of 'a' in module Foo follows declaration in the global module}}
20+
// expected-note@#a1 {{previous decl}}
21+
extern int b;
22+
23+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
24+
module :private; // #priv-frag
25+
int b; // ok
26+
module :private; // expected-error {{private module fragment redefined}}
27+
// expected-note@#priv-frag {{previous definition is here}}
28+
29+
//--- NoGlobalFrag.cppm
30+
31+
extern int a; // #a1
32+
export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}}
33+
// expected-note@-2 {{add 'module;' to the start of the file to introduce a global module fragment}}
34+
35+
// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
36+
// expected-note@#a1 {{previous decl}}
37+
38+
int a; // #a2
39+
extern int b;
40+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
41+
module :private; // #priv-frag
42+
int b; // ok
43+
module :private; // expected-error {{private module fragment redefined}}
44+
// expected-note@#priv-frag {{previous definition is here}}
2045

46+
//--- NoModuleDecl.cppm
47+
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
2148
extern int a; // #a1
49+
int a; // #a2
50+
extern int b;
51+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
52+
module :private; // expected-error {{private module fragment declaration with no preceding module declaration}}
53+
int b; // ok
2254

23-
#ifndef NO_MODULE_DECL
55+
//--- NoPrivateFrag.cppm
56+
module;
57+
extern int a; // #a1
2458
export module Foo;
25-
#ifdef NO_GLOBAL_FRAG
26-
// expected-error@-2 {{module declaration must occur at the start of the translation unit}}
59+
60+
// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
61+
// expected-note@#a1 {{previous decl}}
62+
int a; // #a2
63+
extern int b;
64+
65+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
66+
int b; // ok
67+
68+
69+
//--- NoModuleDeclAndNoPrivateFrag.cppm
70+
module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}}
71+
extern int a; // #a1
72+
int a; // #a2
73+
extern int b;
74+
75+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
76+
77+
int b; // ok
78+
79+
//--- NoGlobalFragAndNoPrivateFrag.cppm
80+
extern int a; // #a1
81+
export module Foo; // expected-error {{module declaration must occur at the start of the translation unit}}
2782
// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}}
28-
#endif
2983

3084
// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
3185
// expected-note@#a1 {{previous decl}}
32-
#endif
3386

3487
int a; // #a2
3588
extern int b;
3689

3790
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
3891

39-
#ifndef NO_PRIVATE_FRAG
40-
#ifdef EXPORT_FRAGS
41-
export // expected-error {{private module fragment cannot be exported}}
42-
#endif
92+
int b; // ok
93+
94+
//--- NoGlobalFragAndNoModuleDecl.cppm
95+
extern int a; // #a1
96+
int a; // #a2
97+
extern int b;
98+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
4399
module :private; // #priv-frag
44-
#ifdef NO_MODULE_DECL
45-
// expected-error@-2 {{private module fragment declaration with no preceding module declaration}}
46-
#endif
47-
#endif
100+
// expected-error@-1 {{private module fragment declaration with no preceding module declaration}}
101+
int b; // ok
48102

103+
104+
//--- NoGlobalFragAndNoModuleDeclAndNoPrivateFrag.cppm
105+
extern int a; // #a1
106+
int a; // #a2
107+
extern int b;
108+
109+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
49110
int b; // ok
50111

112+
//--- ExportFrags.cppm
113+
export module; // expected-error {{global module fragment cannot be exported}}
114+
extern int a; // #a1
115+
export module Foo;
116+
// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}}
117+
// expected-note@#a1 {{previous decl}}
51118

52-
#ifndef NO_PRIVATE_FRAG
53-
#ifndef NO_MODULE_DECL
119+
int a; // #a2
120+
extern int b;
121+
122+
module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}}
123+
124+
module :private; // #priv-frag
125+
126+
int b; // ok
54127
module :private; // expected-error {{private module fragment redefined}}
55-
// expected-note@#priv-frag {{previous definition is here}}
56-
#endif
57-
#endif
128+
// expected-note@#priv-frag {{previous definition is here}}
Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -verify
2-
// RUN: %clang_cc1 -std=c++2a -DEXPORT %s -emit-module-interface -o %t.pcm
3-
// RUN: %clang_cc1 -std=c++2a -UEXPORT %s -verify -fmodule-file=M=%t.pcm
1+
// RUN: rm -rf %t
2+
// RUN: split-file %s %t
3+
4+
// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -verify
5+
// RUN: %clang_cc1 -std=c++2a %t/pmf_in_interface.cpp -emit-module-interface -o %t.pcm
6+
// RUN: %clang_cc1 -std=c++2a %t/pmf_in_implementation.cpp -verify -fmodule-file=M=%t.pcm
47

5-
#ifdef EXPORT
6-
// expected-no-diagnostics
7-
export
8-
#else
9-
// expected-note@+2 {{add 'export' here}}
10-
#endif
11-
module M;
128

13-
#ifndef EXPORT
14-
// expected-error@+2 {{private module fragment in module implementation unit}}
15-
#endif
9+
//--- pmf_in_interface.cpp
10+
// expected-no-diagnostics
11+
export module M;
1612
module :private;
13+
14+
//--- pmf_in_implementation.cpp
15+
module M; // expected-note {{add 'export' here}}
16+
module :private; // expected-error {{private module fragment in module implementation unit}}

0 commit comments

Comments
 (0)