Skip to content

Commit 7d469c7

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 <yronglin777@gmail.com>
1 parent 329dfa1 commit 7d469c7

File tree

30 files changed

+713
-304
lines changed

30 files changed

+713
-304
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: 17 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+
std::optional<Token> FirstPPToken;
355+
353356
/// A position within a C++20 import-seq.
354357
class StdCXXImportSeq {
355358
public:
@@ -1766,6 +1769,20 @@ class Preprocessor {
17661769
std::optional<LexEmbedParametersResult> LexEmbedParameters(Token &Current,
17671770
bool ForHasEmbed);
17681771

1772+
/// Whether the preprocessor already seen the first pp-token in main file.
1773+
bool hasSeenMainFileFirstPPToken() const { return FirstPPToken.has_value(); }
1774+
1775+
/// Record first pp-token and check if it has a Token::FirstPPToken flag.
1776+
void HandleMainFileFirstPPToken(const Token &Tok) {
1777+
if (!hasSeenMainFileFirstPPToken() && Tok.isFirstPPToken() &&
1778+
SourceMgr.isWrittenInMainFile(Tok.getLocation()))
1779+
FirstPPToken = Tok;
1780+
}
1781+
1782+
Token getMainFileFirstPPToken() const {
1783+
assert(FirstPPToken && "First main file pp-token doesn't exists");
1784+
return *FirstPPToken;
1785+
}
17691786
bool LexAfterModuleImport(Token &Result);
17701787
void CollectPpImportSuffix(SmallVectorImpl<Token> &Toks);
17711788

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: 13 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,22 @@ 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->hasSeenMainFileFirstPPToken())
3745+
PP->HandleMainFileFirstPPToken(Result);
37353746
return returnedToken;
37363747
}
37373748

@@ -4535,6 +4546,8 @@ const char *Lexer::convertDependencyDirectiveToken(
45354546
Result.setFlag((Token::TokenFlags)DDTok.Flags);
45364547
Result.setLength(DDTok.Length);
45374548
BufferPtr = TokPtr + DDTok.Length;
4549+
if (PP && !PP->hasSeenMainFileFirstPPToken() && Result.isFirstPPToken())
4550+
PP->HandleMainFileFirstPPToken(Result);
45384551
return TokPtr;
45394552
}
45404553

clang/lib/Lex/PPDirectives.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,9 @@ void Preprocessor::HandleDirective(Token &Result) {
12421242
// pp-directive.
12431243
bool ReadAnyTokensBeforeDirective =CurPPLexer->MIOpt.getHasReadAnyTokensVal();
12441244

1245+
if (!hasSeenMainFileFirstPPToken())
1246+
HandleMainFileFirstPPToken(Result);
1247+
12451248
// Save the '#' token in case we need to return it later.
12461249
Token SavedHash = Result;
12471250

clang/lib/Lex/PPMacroExpansion.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier,
469469
// to disable the optimization in this case.
470470
if (CurPPLexer) CurPPLexer->MIOpt.ExpandedMacro();
471471

472+
if (!hasSeenMainFileFirstPPToken())
473+
HandleMainFileFirstPPToken(Identifier);
474+
472475
// If this is a builtin macro, like __LINE__ or _Pragma, handle it specially.
473476
if (MI->isBuiltinMacro()) {
474477
if (Callbacks)

clang/lib/Lex/Preprocessor.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,8 @@ void Preprocessor::DumpToken(const Token &Tok, bool DumpFlags) const {
247247
llvm::errs() << " [LeadingSpace]";
248248
if (Tok.isExpandDisabled())
249249
llvm::errs() << " [ExpandDisabled]";
250+
if (Tok.isFirstPPToken())
251+
llvm::errs() << " [First pp-token]";
250252
if (Tok.needsCleaning()) {
251253
const char *Start = SourceMgr.getCharacterData(Tok.getLocation());
252254
llvm::errs() << " [UnClean='" << StringRef(Start, Tok.getLength())

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: 6 additions & 9 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,14 +328,11 @@ 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);
335-
SourceLocation BeginLoc =
336-
ModuleScopes.empty()
337-
? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID())
338-
: ModuleScopes.back().BeginLoc;
335+
SourceLocation BeginLoc = PP.getMainFileFirstPPToken().getLocation();
339336
if (BeginLoc.isValid()) {
340337
Diag(BeginLoc, diag::note_global_module_introducer_missing)
341338
<< FixItHint::CreateInsertion(BeginLoc, "module;\n");
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}}

0 commit comments

Comments
 (0)