Skip to content

Commit f706411

Browse files
authored
[flang][preprocessor] Handle compiler directives with continuations a… (#70128)
…fter macro expansion When compiler directives (!$omp) and/or their continuations (!$omp &) are produced by macro expansion, handle those continuations. Also allow a continuation marker (&) to appear in a macro actual argument.
1 parent ad81051 commit f706411

File tree

5 files changed

+168
-10
lines changed

5 files changed

+168
-10
lines changed

flang/docs/Preprocessing.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,5 +228,5 @@ E . . E E . pp125.F90 #DEFINE works in free form
228228
. . E . E E pp127.F90 FLM call with closing ')' on next line (not a continuation)
229229
E . E . E E pp128.F90 FLM call with '(' on next line (not a continuation)
230230
. . N . . N pp129.F90 #define KWM !, then KWM works as comment line initiator
231-
E . E . . E pp130.F90 #define KWM &, use for continuation w/o pasting (ifort and nag seem to continue #define)
231+
. . E . . E pp130.F90 #define KWM &, use for continuation w/o pasting (ifort and nag seem to continue #define)
232232
```

flang/lib/Parser/prescan.cpp

Lines changed: 122 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ void Prescanner::Statement() {
213213
if (preprocessed->HasRedundantBlanks()) {
214214
preprocessed->RemoveRedundantBlanks();
215215
}
216+
while (CompilerDirectiveContinuation(*preprocessed, ppl.sentinel)) {
217+
newlineProvenance = GetCurrentProvenance();
218+
}
216219
NormalizeCompilerDirectiveCommentMarker(*preprocessed);
217220
preprocessed->ToLowerCase();
218221
SourceFormChange(preprocessed->ToString());
@@ -227,6 +230,9 @@ void Prescanner::Statement() {
227230
preprocessed->RemoveBlanks(/*after column*/ 6);
228231
}
229232
} else {
233+
while (SourceLineContinuation(*preprocessed)) {
234+
newlineProvenance = GetCurrentProvenance();
235+
}
230236
if (preprocessed->HasRedundantBlanks()) {
231237
preprocessed->RemoveRedundantBlanks();
232238
}
@@ -239,12 +245,17 @@ void Prescanner::Statement() {
239245
break;
240246
}
241247
} else {
242-
tokens.ToLowerCase();
243248
if (line.kind == LineClassification::Kind::CompilerDirective) {
249+
while (CompilerDirectiveContinuation(tokens, line.sentinel)) {
250+
newlineProvenance = GetCurrentProvenance();
251+
}
252+
tokens.ToLowerCase();
244253
SourceFormChange(tokens.ToString());
245-
}
246-
if (inFixedForm_ && line.kind == LineClassification::Kind::Source) {
247-
EnforceStupidEndStatementRules(tokens);
254+
} else { // Kind::Source
255+
tokens.ToLowerCase();
256+
if (inFixedForm_) {
257+
EnforceStupidEndStatementRules(tokens);
258+
}
248259
}
249260
tokens.CheckBadFortranCharacters(messages_)
250261
.CheckBadParentheses(messages_)
@@ -1132,8 +1143,10 @@ bool Prescanner::FreeFormContinuation() {
11321143
if (*p != '\n') {
11331144
if (inCharLiteral_) {
11341145
return false;
1135-
} else if (*p != '!' &&
1136-
features_.ShouldWarn(LanguageFeature::CruftAfterAmpersand)) {
1146+
} else if (*p == '!') { // & ! comment - ok
1147+
} else if (ampersand && isPossibleMacroCall_ && (*p == ',' || *p == ')')) {
1148+
return false; // allow & at end of a macro argument
1149+
} else if (features_.ShouldWarn(LanguageFeature::CruftAfterAmpersand)) {
11371150
Say(GetProvenance(p), "missing ! before comment after &"_warn_en_US);
11381151
}
11391152
}
@@ -1318,4 +1331,107 @@ void Prescanner::SourceFormChange(std::string &&dir) {
13181331
inFixedForm_ = true;
13191332
}
13201333
}
1334+
1335+
// Acquire and append compiler directive continuation lines to
1336+
// the tokens that constitute a compiler directive, even when those
1337+
// directive continuation lines are the result of macro expansion.
1338+
// (Not used when neither the original compiler directive line nor
1339+
// the directive continuation line result from preprocessing; regular
1340+
// line continuation during tokenization handles that normal case.)
1341+
bool Prescanner::CompilerDirectiveContinuation(
1342+
TokenSequence &tokens, const char *origSentinel) {
1343+
if (inFixedForm_ || tokens.empty() ||
1344+
tokens.TokenAt(tokens.SizeInTokens() - 1) != "&") {
1345+
return false;
1346+
}
1347+
LineClassification followingLine{ClassifyLine(nextLine_)};
1348+
if (followingLine.kind == LineClassification::Kind::Comment) {
1349+
nextLine_ += followingLine.payloadOffset; // advance to '!' or newline
1350+
NextLine();
1351+
return true;
1352+
}
1353+
CHECK(origSentinel != nullptr);
1354+
directiveSentinel_ = origSentinel; // so IsDirective() is true
1355+
const char *nextContinuation{
1356+
followingLine.kind == LineClassification::Kind::CompilerDirective
1357+
? FreeFormContinuationLine(true)
1358+
: nullptr};
1359+
if (!nextContinuation &&
1360+
followingLine.kind != LineClassification::Kind::Source) {
1361+
return false;
1362+
}
1363+
auto origNextLine{nextLine_};
1364+
BeginSourceLine(nextLine_);
1365+
NextLine();
1366+
TokenSequence followingTokens;
1367+
if (nextContinuation) {
1368+
// What follows is !DIR$ & xxx; skip over the & so that it
1369+
// doesn't cause a spurious continuation.
1370+
at_ = nextContinuation;
1371+
} else {
1372+
// What follows looks like a source line before macro expansion,
1373+
// but might become a directive continuation afterwards.
1374+
SkipSpaces();
1375+
}
1376+
while (NextToken(followingTokens)) {
1377+
}
1378+
if (auto followingPrepro{
1379+
preprocessor_.MacroReplacement(followingTokens, *this)}) {
1380+
followingTokens = std::move(*followingPrepro);
1381+
}
1382+
followingTokens.RemoveRedundantBlanks();
1383+
std::size_t startAt{0};
1384+
std::size_t keep{followingTokens.SizeInTokens()};
1385+
bool ok{false};
1386+
if (nextContinuation) {
1387+
ok = true;
1388+
} else {
1389+
if (keep >= 3 && followingTokens.TokenAt(0) == "!" &&
1390+
followingTokens.TokenAt(2) == "&") {
1391+
CharBlock sentinel{followingTokens.TokenAt(1)};
1392+
if (!sentinel.empty() &&
1393+
std::memcmp(sentinel.begin(), origSentinel, sentinel.size()) == 0) {
1394+
startAt = 3;
1395+
keep -= 3;
1396+
ok = true;
1397+
}
1398+
}
1399+
}
1400+
if (ok) {
1401+
tokens.pop_back(); // delete original '&'
1402+
tokens.Put(followingTokens, startAt, keep);
1403+
} else {
1404+
nextLine_ = origNextLine;
1405+
}
1406+
return ok;
1407+
}
1408+
1409+
// Similar, but for source line continuation after macro replacement.
1410+
bool Prescanner::SourceLineContinuation(TokenSequence &tokens) {
1411+
if (!inFixedForm_ && !tokens.empty() &&
1412+
tokens.TokenAt(tokens.SizeInTokens() - 1) == "&") {
1413+
LineClassification followingLine{ClassifyLine(nextLine_)};
1414+
if (followingLine.kind == LineClassification::Kind::Comment) {
1415+
nextLine_ += followingLine.payloadOffset; // advance to '!' or newline
1416+
NextLine();
1417+
return true;
1418+
} else if (const char *nextContinuation{FreeFormContinuationLine(true)}) {
1419+
BeginSourceLine(nextLine_);
1420+
NextLine();
1421+
TokenSequence followingTokens;
1422+
at_ = nextContinuation;
1423+
while (NextToken(followingTokens)) {
1424+
}
1425+
if (auto followingPrepro{
1426+
preprocessor_.MacroReplacement(followingTokens, *this)}) {
1427+
followingTokens = std::move(*followingPrepro);
1428+
}
1429+
followingTokens.RemoveRedundantBlanks();
1430+
tokens.pop_back(); // delete original '&'
1431+
tokens.Put(followingTokens);
1432+
return true;
1433+
}
1434+
}
1435+
return false;
1436+
}
13211437
} // namespace Fortran::parser

flang/lib/Parser/prescan.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ class Prescanner {
186186
const char *) const;
187187
LineClassification ClassifyLine(const char *) const;
188188
void SourceFormChange(std::string &&);
189+
bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
190+
bool SourceLineContinuation(TokenSequence &);
189191

190192
Messages &messages_;
191193
CookedSource &cooked_;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
3+
#define DIR_START !dir$
4+
#define DIR_CONT !dir$&
5+
#define FIRST(x) DIR_START x
6+
#define NEXT(x) DIR_CONT x
7+
#define AMPER &
8+
9+
subroutine s(x1, x2, x3, x4, x5, x6, x7)
10+
11+
!dir$ ignore_tkr x1
12+
13+
!dir$ ignore_tkr &
14+
!dir$& x2
15+
16+
DIR_START ignore_tkr x3
17+
18+
!dir$ ignore_tkr AMPER
19+
DIR_CONT x4
20+
21+
FIRST(ignore_tkr &)
22+
!dir$& x5
23+
24+
FIRST(ignore_tkr &)
25+
NEXT(x6)
26+
27+
FIRST(ignore_tkr &)
28+
NEXT(x7 &)
29+
NEXT(x8)
30+
31+
end
32+
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

flang/test/Preprocessing/pp130.F90

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
! RUN: not %flang -E %s 2>&1 | FileCheck %s
2-
! CHECK: error: bad character ('&') in Fortran token
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
! CHECK: j = j + 111
33
! #define KWM &, use for continuation w/o pasting (ifort and nag seem to continue #define)
44
#define KWM &
5-
65
integer :: j
76
j = 666
87
j = j + KWM

0 commit comments

Comments
 (0)