diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp index ed5184b0aa13d..ec894ab8513d2 100644 --- a/flang/lib/Parser/prescan.cpp +++ b/flang/lib/Parser/prescan.cpp @@ -593,13 +593,13 @@ bool Prescanner::SkipToNextSignificantCharacter() { return false; } else { auto anyContinuationLine{false}; - bool mightNeedSpace{false}; + bool atNewline{false}; if (MustSkipToEndOfLine()) { SkipToEndOfLine(); } else { - mightNeedSpace = *at_ == '\n'; + atNewline = *at_ == '\n'; } - for (; Continuation(mightNeedSpace); mightNeedSpace = false) { + for (; Continuation(atNewline); atNewline = false) { anyContinuationLine = true; ++continuationLines_; if (MustSkipToEndOfLine()) { @@ -641,7 +641,7 @@ void Prescanner::SkipSpaces() { while (IsSpaceOrTab(at_)) { NextChar(); } - insertASpace_ = false; + brokenToken_ = false; } const char *Prescanner::SkipWhiteSpace(const char *p) { @@ -745,10 +745,7 @@ bool Prescanner::NextToken(TokenSequence &tokens) { } } } - if (insertASpace_) { - tokens.PutNextTokenChar(' ', spaceProvenance_); - insertASpace_ = false; - } + brokenToken_ = false; if (*at_ == '\n') { return false; } @@ -808,7 +805,7 @@ bool Prescanner::NextToken(TokenSequence &tokens) { bool anyDefined{false}; bool hadContinuation{false}; // Subtlety: When an identifier is split across continuation lines, - // its parts are kept as distinct pp-tokens if that macro replacement + // its parts are kept as distinct pp-tokens if macro replacement // should operate on them independently. This trick accommodates the // historic practice of using line continuation for token pasting after // replacement. @@ -822,6 +819,9 @@ bool Prescanner::NextToken(TokenSequence &tokens) { ++at_, ++column_; hadContinuation = SkipToNextSignificantCharacter(); if (hadContinuation && IsLegalIdentifierStart(*at_)) { + if (brokenToken_) { + break; + } // Continued identifier tokens.CloseToken(); ++parts; @@ -1348,7 +1348,7 @@ bool Prescanner::SkipCommentLine(bool afterAmpersand) { return false; } -const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { +const char *Prescanner::FixedFormContinuationLine(bool atNewline) { if (IsAtEnd()) { return nullptr; } @@ -1381,8 +1381,8 @@ const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { } const char *col6{nextLine_ + 5}; if (*col6 != '\n' && *col6 != '0' && !IsSpaceOrTab(col6)) { - if (mightNeedSpace && !IsSpace(nextLine_ + 6)) { - insertASpace_ = true; + if (atNewline && !IsSpace(nextLine_ + 6)) { + brokenToken_ = true; } return nextLine_ + 6; } @@ -1395,7 +1395,9 @@ const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) { nextLine_[4] == ' ' && IsCompilerDirectiveSentinel(&nextLine_[1], 1)) { if (const char *col6{nextLine_ + 5}; *col6 != '\n' && *col6 != '0' && !IsSpaceOrTab(col6)) { - insertASpace_ |= mightNeedSpace && !IsSpace(nextLine_ + 6); + if (atNewline && !IsSpace(nextLine_ + 6)) { + brokenToken_ = true; + } return nextLine_ + 6; } else { return nullptr; @@ -1464,7 +1466,7 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) { p = SkipWhiteSpace(p); if (*p == '&') { if (!ampersand) { - insertASpace_ = true; + brokenToken_ = true; } return p + 1; } else if (ampersand) { @@ -1494,7 +1496,7 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) { } else if (p > lineStart && IsSpaceOrTab(p - 1)) { --p; } else { - insertASpace_ = true; + brokenToken_ = true; } return p; } else { @@ -1502,14 +1504,14 @@ const char *Prescanner::FreeFormContinuationLine(bool ampersand) { } } -bool Prescanner::FixedFormContinuation(bool mightNeedSpace) { +bool Prescanner::FixedFormContinuation(bool atNewline) { // N.B. We accept '&' as a continuation indicator in fixed form, too, // but not in a character literal. if (*at_ == '&' && inCharLiteral_) { return false; } do { - if (const char *cont{FixedFormContinuationLine(mightNeedSpace)}) { + if (const char *cont{FixedFormContinuationLine(atNewline)}) { BeginSourceLine(cont); column_ = 7; NextLine(); diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h index ec4c53cf3e0f2..f650d548e6eff 100644 --- a/flang/lib/Parser/prescan.h +++ b/flang/lib/Parser/prescan.h @@ -203,10 +203,10 @@ class Prescanner { std::optional IsIncludeLine(const char *) const; void FortranInclude(const char *quote); const char *IsPreprocessorDirectiveLine(const char *) const; - const char *FixedFormContinuationLine(bool mightNeedSpace); + const char *FixedFormContinuationLine(bool atNewline); const char *FreeFormContinuationLine(bool ampersand); bool IsImplicitContinuation() const; - bool FixedFormContinuation(bool mightNeedSpace); + bool FixedFormContinuation(bool atNewline); bool FreeFormContinuation(); bool Continuation(bool mightNeedFixedFormSpace); std::optional IsFixedFormCompilerDirectiveLine( @@ -256,10 +256,15 @@ class Prescanner { bool continuationInCharLiteral_{false}; bool inPreprocessorDirective_{false}; - // In some edge cases of compiler directive continuation lines, it - // is necessary to treat the line break as a space character by - // setting this flag, which is cleared by EmitChar(). - bool insertASpace_{false}; + // True after processing a continuation that can't be allowed + // to appear in the middle of an identifier token, but is fixed form, + // or is free form and doesn't have a space character handy to use as + // a separator when: + // a) (standard) doesn't begin with a leading '&' on the continuation + // line, but has a non-blank in column 1, or + // b) (extension) does have a leading '&', but didn't have one + // on the continued line. + bool brokenToken_{false}; // When a free form continuation marker (&) appears at the end of a line // before a INCLUDE or #include, we delete it and omit the newline, so diff --git a/flang/test/Preprocessing/bug1077.F90 b/flang/test/Preprocessing/bug1077.F90 new file mode 100644 index 0000000000000..dd7391813a357 --- /dev/null +++ b/flang/test/Preprocessing/bug1077.F90 @@ -0,0 +1,7 @@ +!RUN: %flang -E %s 2>&1 | FileCheck %s +!CHECK: print *,((1)+(2)),4 +#define foo(x,y) ((x)+(y)) +print *,& +foo(1,2)& +,4 +end diff --git a/flang/test/Preprocessing/pp111.F90 b/flang/test/Preprocessing/pp111.F90 index 4da45ef35f5c0..bbf8709c3ab15 100644 --- a/flang/test/Preprocessing/pp111.F90 +++ b/flang/test/Preprocessing/pp111.F90 @@ -1,5 +1,5 @@ ! RUN: %flang -E %s 2>&1 | FileCheck %s -! CHECK: res = IFLM (666) +! CHECK: res = IFLM(666) ! FLM call name split across continuation, no leading &, with & ! comment integer function IFLM(x) integer :: x diff --git a/flang/test/Preprocessing/pp112.F90 b/flang/test/Preprocessing/pp112.F90 index 16705527f68c3..a5244410f31af 100644 --- a/flang/test/Preprocessing/pp112.F90 +++ b/flang/test/Preprocessing/pp112.F90 @@ -1,5 +1,5 @@ ! RUN: %flang -E %s 2>&1 | FileCheck %s -! CHECK: res = IFLM (666) +! CHECK: res = IFLM(666) ! ditto, but without & ! comment integer function IFLM(x) integer :: x diff --git a/flang/test/Preprocessing/pp115.F90 b/flang/test/Preprocessing/pp115.F90 index 4e4c621110ed8..eea42c53b936d 100644 --- a/flang/test/Preprocessing/pp115.F90 +++ b/flang/test/Preprocessing/pp115.F90 @@ -1,5 +1,5 @@ ! RUN: %flang -E %s 2>&1 | FileCheck %s -! CHECK: res = IFLM (666) +! CHECK: res = ((666)+111) ! ditto, with & ! comment, no leading & integer function IFLM(x) integer :: x diff --git a/flang/test/Preprocessing/pp116.F90 b/flang/test/Preprocessing/pp116.F90 index e35a13cbf6489..39edf95763eab 100644 --- a/flang/test/Preprocessing/pp116.F90 +++ b/flang/test/Preprocessing/pp116.F90 @@ -1,5 +1,5 @@ ! RUN: %flang -E %s 2>&1 | FileCheck %s -! CHECK: res = IFLM (666) +! CHECK: res = ((666)+111) ! FLM call split between name and (, no leading & integer function IFLM(x) integer :: x