Skip to content

Commit e286ecf

Browse files
authored
[flang] Disable Fortran free form line continuation in non-source lin… (#94663)
…e produced by keyword macro replacement When later initial keyword macro replacement will yield a line that is not Fortran source, don't interpret "&" as a Fortran source line continuation marker during tokenization of the line. Fixes #82579.
1 parent 1a0e67d commit e286ecf

File tree

5 files changed

+116
-34
lines changed

5 files changed

+116
-34
lines changed

flang/include/flang/Parser/token-sequence.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class TokenSequence {
124124
TokenSequence &RemoveRedundantBlanks(std::size_t firstChar = 0);
125125
TokenSequence &ClipComment(const Prescanner &, bool skipFirst = false);
126126
const TokenSequence &CheckBadFortranCharacters(
127-
Messages &, const Prescanner &) const;
127+
Messages &, const Prescanner &, bool allowAmpersand) const;
128128
const TokenSequence &CheckBadParentheses(Messages &) const;
129129
void Emit(CookedSource &) const;
130130
llvm::raw_ostream &Dump(llvm::raw_ostream &) const;

flang/lib/Parser/prescan.cpp

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,28 @@ void Prescanner::Statement() {
180180
}
181181
} else {
182182
SkipSpaces();
183+
// Check for a leading identifier that might be a keyword macro
184+
// that will expand to anything indicating a non-source line, like
185+
// a comment marker or directive sentinel. If so, disable line
186+
// continuation, so that NextToken() won't consume anything from
187+
// following lines.
188+
if (IsLegalIdentifierStart(*at_)) {
189+
CHECK(NextToken(tokens));
190+
CHECK(tokens.SizeInTokens() == 1);
191+
CharBlock id{tokens.TokenAt(0)};
192+
if (preprocessor_.IsNameDefined(id) &&
193+
!preprocessor_.IsFunctionLikeDefinition(id)) {
194+
if (auto replaced{preprocessor_.MacroReplacement(tokens, *this)}) {
195+
auto newLineClass{ClassifyLine(*replaced, GetCurrentProvenance())};
196+
disableSourceContinuation_ =
197+
newLineClass.kind != LineClassification::Kind::Source;
198+
if (newLineClass.kind ==
199+
LineClassification::Kind::CompilerDirective) {
200+
directiveSentinel_ = newLineClass.sentinel;
201+
}
202+
}
203+
}
204+
}
183205
}
184206
break;
185207
}
@@ -197,17 +219,13 @@ void Prescanner::Statement() {
197219
Provenance newlineProvenance{GetCurrentProvenance()};
198220
if (std::optional<TokenSequence> preprocessed{
199221
preprocessor_.MacroReplacement(tokens, *this)}) {
200-
// Reprocess the preprocessed line. Append a newline temporarily.
201-
preprocessed->PutNextTokenChar('\n', newlineProvenance);
202-
preprocessed->CloseToken();
203-
const char *ppd{preprocessed->ToCharBlock().begin()};
204-
LineClassification ppl{ClassifyLine(ppd)};
205-
preprocessed->pop_back(); // remove the newline
222+
// Reprocess the preprocessed line.
223+
LineClassification ppl{ClassifyLine(*preprocessed, newlineProvenance)};
206224
switch (ppl.kind) {
207225
case LineClassification::Kind::Comment:
208226
break;
209227
case LineClassification::Kind::IncludeLine:
210-
FortranInclude(ppd + ppl.payloadOffset);
228+
FortranInclude(preprocessed->TokenAt(0).begin() + ppl.payloadOffset);
211229
break;
212230
case LineClassification::Kind::ConditionalCompilationDirective:
213231
case LineClassification::Kind::IncludeDirective:
@@ -270,7 +288,8 @@ void Prescanner::Statement() {
270288

271289
void Prescanner::CheckAndEmitLine(
272290
TokenSequence &tokens, Provenance newlineProvenance) {
273-
tokens.CheckBadFortranCharacters(messages_, *this);
291+
tokens.CheckBadFortranCharacters(
292+
messages_, *this, disableSourceContinuation_);
274293
// Parenthesis nesting check does not apply while any #include is
275294
// active, nor on the lines before and after a top-level #include.
276295
// Applications play shenanigans with line continuation before and
@@ -1243,7 +1262,9 @@ bool Prescanner::IsImplicitContinuation() const {
12431262
}
12441263

12451264
bool Prescanner::Continuation(bool mightNeedFixedFormSpace) {
1246-
if (*at_ == '\n' || *at_ == '&') {
1265+
if (disableSourceContinuation_) {
1266+
return false;
1267+
} else if (*at_ == '\n' || *at_ == '&') {
12471268
if (inFixedForm_) {
12481269
return FixedFormContinuation(mightNeedFixedFormSpace);
12491270
} else {
@@ -1255,8 +1276,9 @@ bool Prescanner::Continuation(bool mightNeedFixedFormSpace) {
12551276
BeginSourceLine(nextLine_);
12561277
NextLine();
12571278
return true;
1279+
} else {
1280+
return false;
12581281
}
1259-
return false;
12601282
}
12611283

12621284
std::optional<Prescanner::LineClassification>
@@ -1418,6 +1440,17 @@ Prescanner::LineClassification Prescanner::ClassifyLine(
14181440
return {LineClassification::Kind::Source};
14191441
}
14201442

1443+
Prescanner::LineClassification Prescanner::ClassifyLine(
1444+
TokenSequence &tokens, Provenance newlineProvenance) const {
1445+
// Append a newline temporarily.
1446+
tokens.PutNextTokenChar('\n', newlineProvenance);
1447+
tokens.CloseToken();
1448+
const char *ppd{tokens.ToCharBlock().begin()};
1449+
LineClassification classification{ClassifyLine(ppd)};
1450+
tokens.pop_back(); // remove the newline
1451+
return classification;
1452+
}
1453+
14211454
void Prescanner::SourceFormChange(std::string &&dir) {
14221455
if (dir == "!dir$ free") {
14231456
inFixedForm_ = false;
@@ -1445,7 +1478,7 @@ bool Prescanner::CompilerDirectiveContinuation(
14451478
return true;
14461479
}
14471480
CHECK(origSentinel != nullptr);
1448-
directiveSentinel_ = origSentinel; // so IsDirective() is true
1481+
directiveSentinel_ = origSentinel; // so InCompilerDirective() is true
14491482
const char *nextContinuation{
14501483
followingLine.kind == LineClassification::Kind::CompilerDirective
14511484
? FreeFormContinuationLine(true)
@@ -1457,7 +1490,6 @@ bool Prescanner::CompilerDirectiveContinuation(
14571490
auto origNextLine{nextLine_};
14581491
BeginSourceLine(nextLine_);
14591492
NextLine();
1460-
TokenSequence followingTokens;
14611493
if (nextContinuation) {
14621494
// What follows is !DIR$ & xxx; skip over the & so that it
14631495
// doesn't cause a spurious continuation.
@@ -1467,6 +1499,7 @@ bool Prescanner::CompilerDirectiveContinuation(
14671499
// but might become a directive continuation afterwards.
14681500
SkipSpaces();
14691501
}
1502+
TokenSequence followingTokens;
14701503
while (NextToken(followingTokens)) {
14711504
}
14721505
if (auto followingPrepro{
@@ -1475,25 +1508,31 @@ bool Prescanner::CompilerDirectiveContinuation(
14751508
}
14761509
followingTokens.RemoveRedundantBlanks();
14771510
std::size_t startAt{0};
1478-
std::size_t keep{followingTokens.SizeInTokens()};
1511+
std::size_t following{followingTokens.SizeInTokens()};
14791512
bool ok{false};
14801513
if (nextContinuation) {
14811514
ok = true;
14821515
} else {
1483-
if (keep >= 3 && followingTokens.TokenAt(0) == "!" &&
1484-
followingTokens.TokenAt(2) == "&") {
1516+
startAt = 2;
1517+
if (startAt < following && followingTokens.TokenAt(0) == "!") {
14851518
CharBlock sentinel{followingTokens.TokenAt(1)};
14861519
if (!sentinel.empty() &&
14871520
std::memcmp(sentinel.begin(), origSentinel, sentinel.size()) == 0) {
1488-
startAt = 3;
1489-
keep -= 3;
14901521
ok = true;
1522+
while (
1523+
startAt < following && followingTokens.TokenAt(startAt).IsBlank()) {
1524+
++startAt;
1525+
}
1526+
if (startAt < following && followingTokens.TokenAt(startAt) == "&") {
1527+
++startAt;
1528+
}
14911529
}
14921530
}
14931531
}
14941532
if (ok) {
14951533
tokens.pop_back(); // delete original '&'
1496-
tokens.Put(followingTokens, startAt, keep);
1534+
tokens.Put(followingTokens, startAt, following - startAt);
1535+
tokens.RemoveRedundantBlanks();
14971536
} else {
14981537
nextLine_ = origNextLine;
14991538
}

flang/lib/Parser/prescan.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class Prescanner {
9494
LineClassification(Kind k, std::size_t po = 0, const char *s = nullptr)
9595
: kind{k}, payloadOffset{po}, sentinel{s} {}
9696
LineClassification(LineClassification &&) = default;
97+
LineClassification &operator=(LineClassification &&) = default;
9798
Kind kind;
9899
std::size_t payloadOffset; // byte offset of content
99100
const char *sentinel; // if it's a compiler directive
@@ -117,6 +118,7 @@ class Prescanner {
117118
parenthesisNesting_ = 0;
118119
continuationLines_ = 0;
119120
isPossibleMacroCall_ = false;
121+
disableSourceContinuation_ = false;
120122
}
121123

122124
Provenance GetProvenance(const char *sourceChar) const {
@@ -192,6 +194,8 @@ class Prescanner {
192194
std::optional<LineClassification> IsFreeFormCompilerDirectiveLine(
193195
const char *) const;
194196
LineClassification ClassifyLine(const char *) const;
197+
LineClassification ClassifyLine(
198+
TokenSequence &, Provenance newlineProvenance) const;
195199
void SourceFormChange(std::string &&);
196200
bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
197201
bool SourceLineContinuation(TokenSequence &);
@@ -211,6 +215,7 @@ class Prescanner {
211215
int continuationLines_{0};
212216
bool isPossibleMacroCall_{false};
213217
bool afterIncludeDirective_{false};
218+
bool disableSourceContinuation_{false};
214219

215220
Provenance startProvenance_;
216221
const char *start_{nullptr}; // beginning of current source file content

flang/lib/Parser/token-sequence.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ ProvenanceRange TokenSequence::GetProvenanceRange() const {
347347
}
348348

349349
const TokenSequence &TokenSequence::CheckBadFortranCharacters(
350-
Messages &messages, const Prescanner &prescanner) const {
350+
Messages &messages, const Prescanner &prescanner,
351+
bool allowAmpersand) const {
351352
std::size_t tokens{SizeInTokens()};
352353
for (std::size_t j{0}; j < tokens; ++j) {
353354
CharBlock token{TokenAt(j)};
@@ -362,6 +363,8 @@ const TokenSequence &TokenSequence::CheckBadFortranCharacters(
362363
++j;
363364
continue;
364365
}
366+
} else if (ch == '&' && allowAmpersand) {
367+
continue;
365368
}
366369
if (ch < ' ' || ch >= '\x7f') {
367370
messages.Say(GetTokenProvenanceRange(j),
Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
1-
! RUN: %flang -E %s 2>&1 | FileCheck %s
1+
! RUN: %flang -fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
22

33
#define DIR_START !dir$
44
#define DIR_CONT !dir$&
55
#define FIRST(x) DIR_START x
66
#define NEXT(x) DIR_CONT x
77
#define AMPER &
8+
#define COMMENT !
9+
#define OMP_START !$omp
10+
#define OMP_CONT !$omp&
811

9-
subroutine s(x1, x2, x3, x4, x5, x6, x7)
12+
module m
13+
contains
14+
subroutine s(x1, x2, x3, x4, x5, x6, x7)
1015

1116
!dir$ ignore_tkr x1
1217

@@ -24,18 +29,48 @@ subroutine s(x1, x2, x3, x4, x5, x6, x7)
2429
FIRST(ignore_tkr &)
2530
NEXT(x6)
2631

27-
FIRST(ignore_tkr &)
28-
NEXT(x7 &)
29-
NEXT(x8)
32+
COMMENT blah &
33+
COMMENT & more
34+
stop 1
35+
36+
OMP_START parallel &
37+
OMP_START do &
38+
OMP_START reduction(+:x)
39+
do j1 = 1, n
40+
end do
41+
42+
OMP_START parallel &
43+
OMP_START & do &
44+
OMP_START & reduction(+:x)
45+
do j2 = 1, n
46+
end do
3047

48+
OMP_START parallel &
49+
OMP_CONT do &
50+
OMP_CONT reduction(+:x)
51+
do j3 = 1, n
52+
end do
53+
end
3154
end
3255

33-
!CHECK: subroutine s(x1, x2, x3, x4, x5, x6, x7)
34-
!CHECK: !dir$ ignore_tkr x1
35-
!CHECK: !dir$ ignore_tkr x2
36-
!CHECK: !dir$ ignore_tkr x3
37-
!CHECK: !dir$ ignore_tkr x4
38-
!CHECK: !dir$ ignore_tkr x5
39-
!CHECK: !dir$ ignore_tkr x6
40-
!CHECK: !dir$ ignore_tkr x7 x8
41-
!CHECK: end
56+
!CHECK: MODULE m
57+
!CHECK: CONTAINS
58+
!CHECK: SUBROUTINE s (x1, x2, x3, x4, x5, x6, x7)
59+
!CHECK: !DIR$ IGNORE_TKR x1
60+
!CHECK: !DIR$ IGNORE_TKR x2
61+
!CHECK: !DIR$ IGNORE_TKR x3
62+
!CHECK: !DIR$ IGNORE_TKR x4
63+
!CHECK: !DIR$ IGNORE_TKR x5
64+
!CHECK: !DIR$ IGNORE_TKR x6
65+
!CHECK: STOP 1_4
66+
!CHECK: !$OMP PARALLEL DO REDUCTION(+:x)
67+
!CHECK: DO j1=1_4,n
68+
!CHECK: END DO
69+
!CHECK: !$OMP PARALLEL DO REDUCTION(+:x)
70+
!CHECK: DO j2=1_4,n
71+
!CHECK: END DO
72+
!CHECK: !$OMP PARALLEL DO REDUCTION(+:x)
73+
!CHECK: DO j3=1_4,n
74+
!CHECK: END DO
75+
!CHECK: END SUBROUTINE
76+
!CHECK: END MODULE

0 commit comments

Comments
 (0)