diff --git a/flang/include/flang/Parser/char-block.h b/flang/include/flang/Parser/char-block.h index 38f4f7b82e1ea..1365a920c70fd 100644 --- a/flang/include/flang/Parser/char-block.h +++ b/flang/include/flang/Parser/char-block.h @@ -46,6 +46,8 @@ class CharBlock { constexpr const char &operator[](std::size_t j) const { return interval_.start()[j]; } + constexpr const char &front() const { return (*this)[0]; } + constexpr const char &back() const { return (*this)[size() - 1]; } bool Contains(const CharBlock &that) const { return interval_.Contains(that.interval_); diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp index 8ef9810463d5a..0aadc41934f3c 100644 --- a/flang/lib/Parser/preprocessor.cpp +++ b/flang/lib/Parser/preprocessor.cpp @@ -156,23 +156,50 @@ static TokenSequence TokenPasting(TokenSequence &&text) { } TokenSequence result; std::size_t tokens{text.SizeInTokens()}; - bool pasting{false}; + std::optional before; // last non-blank token before ## for (std::size_t j{0}; j < tokens; ++j) { - if (IsTokenPasting(text.TokenAt(j))) { - if (!pasting) { + CharBlock after{text.TokenAt(j)}; + if (!before) { + if (IsTokenPasting(after)) { while (!result.empty() && result.TokenAt(result.SizeInTokens() - 1).IsBlank()) { result.pop_back(); } if (!result.empty()) { - result.ReopenLastToken(); - pasting = true; + before = result.TokenAt(result.SizeInTokens() - 1); } + } else { + result.AppendRange(text, j, 1); + } + } else if (after.IsBlank() || IsTokenPasting(after)) { + // drop it + } else { // pasting before ## after + bool doPaste{false}; + char last{before->back()}; + char first{after.front()}; + // Apply basic sanity checking to pasting so avoid constructing a bogus + // token that might cause macro replacement to fail, like "macro(". + if (IsLegalInIdentifier(last) && IsLegalInIdentifier(first)) { + doPaste = true; + } else if (IsDecimalDigit(first) && + (last == '.' || last == '+' || last == '-')) { + doPaste = true; // 1. ## 0, - ## 1 + } else if (before->size() == 1 && after.size() == 1) { + if (first == last && + (last == '<' || last == '>' || last == '*' || last == '/' || + last == '=' || last == '&' || last == '|' || last == ':')) { + // Fortran **, //, ==, :: + // C <<, >>, &&, || for use in #if expressions + doPaste = true; + } else if (first == '=' && (last == '!' || last == '/')) { + doPaste = true; // != and /= + } + } + if (doPaste) { + result.ReopenLastToken(); } - } else if (pasting && text.TokenAt(j).IsBlank()) { - } else { result.AppendRange(text, j, 1); - pasting = false; + before.reset(); } } return result; diff --git a/flang/lib/Parser/unparse.cpp b/flang/lib/Parser/unparse.cpp index dcd1ac165adc1..980c420ad4148 100644 --- a/flang/lib/Parser/unparse.cpp +++ b/flang/lib/Parser/unparse.cpp @@ -778,7 +778,7 @@ class UnparseVisitor { } void Unparse(const SubstringInquiry &x) { Walk(x.v); - Put(x.source.end()[-1] == 'n' ? "%LEN" : "%KIND"); + Put(x.source.back() == 'n' ? "%LEN" : "%KIND"); } void Unparse(const SubstringRange &x) { // R910 Walk(x.t, ":"); diff --git a/flang/lib/Semantics/expression.cpp b/flang/lib/Semantics/expression.cpp index f4af738284ed7..53ec3827893d0 100644 --- a/flang/lib/Semantics/expression.cpp +++ b/flang/lib/Semantics/expression.cpp @@ -1269,7 +1269,7 @@ MaybeExpr ExpressionAnalyzer::Analyze( MaybeExpr ExpressionAnalyzer::Analyze(const parser::SubstringInquiry &x) { if (MaybeExpr substring{Analyze(x.v)}) { CHECK(x.source.size() >= 8); - int nameLen{x.source.end()[-1] == 'n' ? 3 /*LEN*/ : 4 /*KIND*/}; + int nameLen{x.source.back() == 'n' ? 3 /*LEN*/ : 4 /*KIND*/}; parser::CharBlock name{ x.source.end() - nameLen, static_cast(nameLen)}; CHECK(name == "len" || name == "kind"); diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp index d0336c9cb661d..96faa5fd954cd 100644 --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -2139,7 +2139,7 @@ bool ImplicitRules::isImplicitNoneExternal() const { const DeclTypeSpec *ImplicitRules::GetType( SourceName name, bool respectImplicitNoneType) const { - char ch{name.begin()[0]}; + char ch{name.front()}; if (isImplicitNoneType_ && respectImplicitNoneType) { return nullptr; } else if (auto it{map_.find(ch)}; it != map_.end()) { diff --git a/flang/lib/Semantics/tools.cpp b/flang/lib/Semantics/tools.cpp index d053179448c00..bdca51ea0a0dd 100644 --- a/flang/lib/Semantics/tools.cpp +++ b/flang/lib/Semantics/tools.cpp @@ -1683,7 +1683,7 @@ std::forward_list GetOperatorNames( std::forward_list GetAllNames( const SemanticsContext &context, const SourceName &name) { std::string str{name.ToString()}; - if (!name.empty() && name.end()[-1] == ')' && + if (!name.empty() && name.back() == ')' && name.ToString().rfind("operator(", 0) == 0) { for (int i{0}; i != common::LogicalOperator_enumSize; ++i) { auto names{GetOperatorNames(context, common::LogicalOperator{i})}; diff --git a/flang/test/Preprocessing/bug1126.F90 b/flang/test/Preprocessing/bug1126.F90 new file mode 100644 index 0000000000000..ae5bb5633581d --- /dev/null +++ b/flang/test/Preprocessing/bug1126.F90 @@ -0,0 +1,20 @@ +! RUN: %flang -E %s 2>&1 | FileCheck %s +#define STRINGIFY(x) #x +#define TOSTRING(x) STRINGIFY(x) +#define PREFIX(x) prefix ## x +#define NAME(x) PREFIX(foo ## x) +#define AUGMENT(x) NAME(x ## suffix) + +! CHECK: subroutine prefixfoosuffix() +! CHECK: print *, "prefixfoosuffix" +! CHECK: end subroutine prefixfoosuffix +subroutine AUGMENT()() + print *, TOSTRING(AUGMENT()) +end subroutine AUGMENT() + +! CHECK: subroutine prefixfoobarsuffix() +! CHECK: print *, "prefixfoobarsuffix" +! CHECK: end subroutine prefixfoobarsuffix +subroutine AUGMENT(bar)() + print *, TOSTRING(AUGMENT(bar)) +end subroutine AUGMENT(bar)