Skip to content

Commit 9d2c06b

Browse files
committed
[flang] Don't create bogus tokens from token pasting (##)
When blank tokens arise from macro replacement in token sequences with token pasting (##), the preprocessor is producing some bogus tokens (e.g., "name(") that can lead to subtle bugs later when macro names are not recognized as such. The fix is to not paste tokens together when the result would not be a valid Fortran or C token in the preprocessing context.
1 parent 2910c24 commit 9d2c06b

File tree

7 files changed

+61
-12
lines changed

7 files changed

+61
-12
lines changed

flang/include/flang/Parser/char-block.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ class CharBlock {
4646
constexpr const char &operator[](std::size_t j) const {
4747
return interval_.start()[j];
4848
}
49+
constexpr const char &front() const { return (*this)[0]; }
50+
constexpr const char &back() const { return (*this)[size() - 1]; }
4951

5052
bool Contains(const CharBlock &that) const {
5153
return interval_.Contains(that.interval_);

flang/lib/Parser/preprocessor.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -156,23 +156,50 @@ static TokenSequence TokenPasting(TokenSequence &&text) {
156156
}
157157
TokenSequence result;
158158
std::size_t tokens{text.SizeInTokens()};
159-
bool pasting{false};
159+
std::optional<CharBlock> before; // last non-blank token before ##
160160
for (std::size_t j{0}; j < tokens; ++j) {
161-
if (IsTokenPasting(text.TokenAt(j))) {
162-
if (!pasting) {
161+
CharBlock after{text.TokenAt(j)};
162+
if (!before) {
163+
if (IsTokenPasting(after)) {
163164
while (!result.empty() &&
164165
result.TokenAt(result.SizeInTokens() - 1).IsBlank()) {
165166
result.pop_back();
166167
}
167168
if (!result.empty()) {
168-
result.ReopenLastToken();
169-
pasting = true;
169+
before = result.TokenAt(result.SizeInTokens() - 1);
170170
}
171+
} else {
172+
result.AppendRange(text, j, 1);
173+
}
174+
} else if (after.IsBlank() || IsTokenPasting(after)) {
175+
// drop it
176+
} else { // pasting before ## after
177+
bool doPaste{false};
178+
char last{before->back()};
179+
char first{after.front()};
180+
// Apply basic sanity checking to pasting so avoid constructing a bogus
181+
// token that might cause macro replacement to fail, like "macro(".
182+
if (IsLegalInIdentifier(last) && IsLegalInIdentifier(first)) {
183+
doPaste = true;
184+
} else if (IsDecimalDigit(first) &&
185+
(last == '.' || last == '+' || last == '-')) {
186+
doPaste = true; // 1. ## 0, - ## 1
187+
} else if (before->size() == 1 && after.size() == 1) {
188+
if (first == last &&
189+
(last == '<' || last == '>' || last == '*' || last == '/' ||
190+
last == '=' || last == '&' || last == '|' || last == ':')) {
191+
// Fortran **, //, ==, ::
192+
// C <<, >>, &&, || for use in #if expressions
193+
doPaste = true;
194+
} else if (first == '=' && (last == '!' || last == '/')) {
195+
doPaste = true; // != and /=
196+
}
197+
}
198+
if (doPaste) {
199+
result.ReopenLastToken();
171200
}
172-
} else if (pasting && text.TokenAt(j).IsBlank()) {
173-
} else {
174201
result.AppendRange(text, j, 1);
175-
pasting = false;
202+
before.reset();
176203
}
177204
}
178205
return result;

flang/lib/Parser/unparse.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ class UnparseVisitor {
778778
}
779779
void Unparse(const SubstringInquiry &x) {
780780
Walk(x.v);
781-
Put(x.source.end()[-1] == 'n' ? "%LEN" : "%KIND");
781+
Put(x.source.back() == 'n' ? "%LEN" : "%KIND");
782782
}
783783
void Unparse(const SubstringRange &x) { // R910
784784
Walk(x.t, ":");

flang/lib/Semantics/expression.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(
12691269
MaybeExpr ExpressionAnalyzer::Analyze(const parser::SubstringInquiry &x) {
12701270
if (MaybeExpr substring{Analyze(x.v)}) {
12711271
CHECK(x.source.size() >= 8);
1272-
int nameLen{x.source.end()[-1] == 'n' ? 3 /*LEN*/ : 4 /*KIND*/};
1272+
int nameLen{x.source.back() == 'n' ? 3 /*LEN*/ : 4 /*KIND*/};
12731273
parser::CharBlock name{
12741274
x.source.end() - nameLen, static_cast<std::size_t>(nameLen)};
12751275
CHECK(name == "len" || name == "kind");

flang/lib/Semantics/resolve-names.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2139,7 +2139,7 @@ bool ImplicitRules::isImplicitNoneExternal() const {
21392139

21402140
const DeclTypeSpec *ImplicitRules::GetType(
21412141
SourceName name, bool respectImplicitNoneType) const {
2142-
char ch{name.begin()[0]};
2142+
char ch{name.front()};
21432143
if (isImplicitNoneType_ && respectImplicitNoneType) {
21442144
return nullptr;
21452145
} else if (auto it{map_.find(ch)}; it != map_.end()) {

flang/lib/Semantics/tools.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1683,7 +1683,7 @@ std::forward_list<std::string> GetOperatorNames(
16831683
std::forward_list<std::string> GetAllNames(
16841684
const SemanticsContext &context, const SourceName &name) {
16851685
std::string str{name.ToString()};
1686-
if (!name.empty() && name.end()[-1] == ')' &&
1686+
if (!name.empty() && name.back() == ')' &&
16871687
name.ToString().rfind("operator(", 0) == 0) {
16881688
for (int i{0}; i != common::LogicalOperator_enumSize; ++i) {
16891689
auto names{GetOperatorNames(context, common::LogicalOperator{i})};

flang/test/Preprocessing/bug1126.F90

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
! RUN: %flang -E %s 2>&1 | FileCheck %s
2+
#define STRINGIFY(x) #x
3+
#define TOSTRING(x) STRINGIFY(x)
4+
#define PREFIX(x) prefix ## x
5+
#define NAME(x) PREFIX(foo ## x)
6+
#define AUGMENT(x) NAME(x ## suffix)
7+
8+
! CHECK: subroutine prefixfoosuffix()
9+
! CHECK: print *, "prefixfoosuffix"
10+
! CHECK: end subroutine prefixfoosuffix
11+
subroutine AUGMENT()()
12+
print *, TOSTRING(AUGMENT())
13+
end subroutine AUGMENT()
14+
15+
! CHECK: subroutine prefixfoobarsuffix()
16+
! CHECK: print *, "prefixfoobarsuffix"
17+
! CHECK: end subroutine prefixfoobarsuffix
18+
subroutine AUGMENT(bar)()
19+
print *, TOSTRING(AUGMENT(bar))
20+
end subroutine AUGMENT(bar)

0 commit comments

Comments
 (0)