Skip to content

Commit df61398

Browse files
committed
[flang] Disable Fortran free form line continuation in non-source line 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 ab33fa5 commit df61398

File tree

5 files changed

+59
-12
lines changed

5 files changed

+59
-12
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: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,24 @@ 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+
disableSourceContinuation_ =
196+
ClassifyLine(*replaced, GetCurrentProvenance()).kind !=
197+
LineClassification::Kind::Source;
198+
}
199+
}
200+
}
183201
}
184202
break;
185203
}
@@ -197,17 +215,13 @@ void Prescanner::Statement() {
197215
Provenance newlineProvenance{GetCurrentProvenance()};
198216
if (std::optional<TokenSequence> preprocessed{
199217
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
218+
// Reprocess the preprocessed line.
219+
LineClassification ppl{ClassifyLine(*preprocessed, newlineProvenance)};
206220
switch (ppl.kind) {
207221
case LineClassification::Kind::Comment:
208222
break;
209223
case LineClassification::Kind::IncludeLine:
210-
FortranInclude(ppd + ppl.payloadOffset);
224+
FortranInclude(preprocessed->TokenAt(0).begin() + ppl.payloadOffset);
211225
break;
212226
case LineClassification::Kind::ConditionalCompilationDirective:
213227
case LineClassification::Kind::IncludeDirective:
@@ -270,7 +284,8 @@ void Prescanner::Statement() {
270284

271285
void Prescanner::CheckAndEmitLine(
272286
TokenSequence &tokens, Provenance newlineProvenance) {
273-
tokens.CheckBadFortranCharacters(messages_, *this);
287+
tokens.CheckBadFortranCharacters(
288+
messages_, *this, disableSourceContinuation_);
274289
// Parenthesis nesting check does not apply while any #include is
275290
// active, nor on the lines before and after a top-level #include.
276291
// Applications play shenanigans with line continuation before and
@@ -1243,7 +1258,9 @@ bool Prescanner::IsImplicitContinuation() const {
12431258
}
12441259

12451260
bool Prescanner::Continuation(bool mightNeedFixedFormSpace) {
1246-
if (*at_ == '\n' || *at_ == '&') {
1261+
if (disableSourceContinuation_) {
1262+
return false;
1263+
} else if (*at_ == '\n' || *at_ == '&') {
12471264
if (inFixedForm_) {
12481265
return FixedFormContinuation(mightNeedFixedFormSpace);
12491266
} else {
@@ -1255,8 +1272,9 @@ bool Prescanner::Continuation(bool mightNeedFixedFormSpace) {
12551272
BeginSourceLine(nextLine_);
12561273
NextLine();
12571274
return true;
1275+
} else {
1276+
return false;
12581277
}
1259-
return false;
12601278
}
12611279

12621280
std::optional<Prescanner::LineClassification>
@@ -1418,6 +1436,17 @@ Prescanner::LineClassification Prescanner::ClassifyLine(
14181436
return {LineClassification::Kind::Source};
14191437
}
14201438

1439+
Prescanner::LineClassification Prescanner::ClassifyLine(
1440+
TokenSequence &tokens, Provenance newlineProvenance) const {
1441+
// Append a newline temporarily.
1442+
tokens.PutNextTokenChar('\n', newlineProvenance);
1443+
tokens.CloseToken();
1444+
const char *ppd{tokens.ToCharBlock().begin()};
1445+
LineClassification classification{ClassifyLine(ppd)};
1446+
tokens.pop_back(); // remove the newline
1447+
return classification;
1448+
}
1449+
14211450
void Prescanner::SourceFormChange(std::string &&dir) {
14221451
if (dir == "!dir$ free") {
14231452
inFixedForm_ = false;

flang/lib/Parser/prescan.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ class Prescanner {
117117
parenthesisNesting_ = 0;
118118
continuationLines_ = 0;
119119
isPossibleMacroCall_ = false;
120+
disableSourceContinuation_ = false;
120121
}
121122

122123
Provenance GetProvenance(const char *sourceChar) const {
@@ -192,6 +193,8 @@ class Prescanner {
192193
std::optional<LineClassification> IsFreeFormCompilerDirectiveLine(
193194
const char *) const;
194195
LineClassification ClassifyLine(const char *) const;
196+
LineClassification ClassifyLine(
197+
TokenSequence &, Provenance newlineProvenance) const;
195198
void SourceFormChange(std::string &&);
196199
bool CompilerDirectiveContinuation(TokenSequence &, const char *sentinel);
197200
bool SourceLineContinuation(TokenSequence &);
@@ -211,6 +214,7 @@ class Prescanner {
211214
int continuationLines_{0};
212215
bool isPossibleMacroCall_{false};
213216
bool afterIncludeDirective_{false};
217+
bool disableSourceContinuation_{false};
214218

215219
Provenance startProvenance_;
216220
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),

flang/test/Preprocessing/directive-contin-with-pp.F90

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#define FIRST(x) DIR_START x
66
#define NEXT(x) DIR_CONT x
77
#define AMPER &
8+
#define COMMENT !
89

910
subroutine s(x1, x2, x3, x4, x5, x6, x7)
1011

@@ -28,6 +29,14 @@ subroutine s(x1, x2, x3, x4, x5, x6, x7)
2829
NEXT(x7 &)
2930
NEXT(x8)
3031

32+
COMMENT blah &
33+
COMMENT & more
34+
stop 1
35+
36+
DIR_START blah &
37+
DIR_START & more
38+
stop 2
39+
3140
end
3241

3342
!CHECK: subroutine s(x1, x2, x3, x4, x5, x6, x7)
@@ -38,4 +47,6 @@ subroutine s(x1, x2, x3, x4, x5, x6, x7)
3847
!CHECK: !dir$ ignore_tkr x5
3948
!CHECK: !dir$ ignore_tkr x6
4049
!CHECK: !dir$ ignore_tkr x7 x8
50+
!CHECK: stop 1
51+
!CHECK: stop 2
4152
!CHECK: end

0 commit comments

Comments
 (0)