Skip to content

Commit 4299d9b

Browse files
authored
[flang] Accomodate historic preprocessing usage (#78868)
Some Fortran codes use line continuation as a form of token pasting; see #78797. This works in compilers that run a C-like preprocessor and then apply line continuation to its output; f18 implements line continuation during tokenization and preprocessing, but can still handle this case. In the rare case when an identifier is split across two or more continuation lines, this patch allows its parts to be distinct preprocessing tokens for the purpose of macro replacemnt. They (or their replacement texts) can be effectively rejoined later as a single identifier when the cooked character stream is tokenized in parsing. Fixes #78797.
1 parent 6ac392b commit 4299d9b

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

flang/lib/Parser/prescan.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,8 @@ void Prescanner::NextChar() {
438438
// character is reached; handles C-style comments in preprocessing
439439
// directives, Fortran ! comments, stuff after the right margin in
440440
// fixed form, and all forms of line continuation.
441-
void Prescanner::SkipToNextSignificantCharacter() {
441+
bool Prescanner::SkipToNextSignificantCharacter() {
442+
auto anyContinuationLine{false};
442443
if (inPreprocessorDirective_) {
443444
SkipCComments();
444445
} else {
@@ -449,6 +450,7 @@ void Prescanner::SkipToNextSignificantCharacter() {
449450
mightNeedSpace = *at_ == '\n';
450451
}
451452
for (; Continuation(mightNeedSpace); mightNeedSpace = false) {
453+
anyContinuationLine = true;
452454
++continuationLines_;
453455
if (MustSkipToEndOfLine()) {
454456
SkipToEndOfLine();
@@ -458,6 +460,7 @@ void Prescanner::SkipToNextSignificantCharacter() {
458460
tabInCurrentLine_ = true;
459461
}
460462
}
463+
return anyContinuationLine;
461464
}
462465

463466
void Prescanner::SkipCComments() {
@@ -625,7 +628,23 @@ bool Prescanner::NextToken(TokenSequence &tokens) {
625628
}
626629
preventHollerith_ = false;
627630
} else if (IsLegalInIdentifier(*at_)) {
628-
while (IsLegalInIdentifier(EmitCharAndAdvance(tokens, *at_))) {
631+
int parts{1};
632+
do {
633+
EmitChar(tokens, *at_);
634+
++at_, ++column_;
635+
if (SkipToNextSignificantCharacter() && IsLegalIdentifierStart(*at_)) {
636+
tokens.CloseToken();
637+
++parts;
638+
}
639+
} while (IsLegalInIdentifier(*at_));
640+
if (parts >= 3) {
641+
// Subtlety: When an identifier is split across three or more continuation
642+
// lines, its parts are kept as distinct pp-tokens so that macro
643+
// operates on them independently. This trick accommodates the historic
644+
// practice of using line continuation for token pasting after
645+
// replacement.
646+
} else if (parts == 2) {
647+
tokens.ReopenLastToken();
629648
}
630649
if (InFixedFormSource()) {
631650
SkipSpaces();

flang/lib/Parser/prescan.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,8 @@ class Prescanner {
159159
void SkipToEndOfLine();
160160
bool MustSkipToEndOfLine() const;
161161
void NextChar();
162-
void SkipToNextSignificantCharacter();
162+
// True when input flowed to a continuation line
163+
bool SkipToNextSignificantCharacter();
163164
void SkipCComments();
164165
void SkipSpaces();
165166
static const char *SkipWhiteSpace(const char *);

flang/test/Preprocessing/pp133.F90

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
! CHECK: print *, ADC
3+
#define B D
4+
implicit none
5+
real ADC
6+
print *, A&
7+
&B&
8+
&C
9+
end

0 commit comments

Comments
 (0)