[flang-commits] [flang] [flang] Accept C-style comments in label fields (PR #207012)
Leandro Lupori via flang-commits
flang-commits at lists.llvm.org
Fri Jul 3 07:01:24 PDT 2026
https://github.com/luporl updated https://github.com/llvm/llvm-project/pull/207012
>From e2d19de0f968244c32510b5801a383d880225f75 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Mon, 29 Jun 2026 19:35:59 -0300
Subject: [PATCH 1/4] [flang] Accept C-style comments in label fields
The main goal of this patch is to make multi-language header files
easier to write and use in fixed format sources. For this reason, and
for simplicity, C-style comments after labels, directives and OpenMP
conditional compilation sentinels are not supported.
Fixes #127426
---
flang/lib/Parser/prescan.cpp | 123 ++++++++++++++-------
flang/test/Preprocessing/fixed-c-comment.F | 62 +++++++++++
2 files changed, 144 insertions(+), 41 deletions(-)
create mode 100644 flang/test/Preprocessing/fixed-c-comment.F
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 0aed9545f6dbc..3fd28208719a2 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -71,6 +71,15 @@ static inline constexpr bool IsFixedFormCommentChar(char ch) {
return ch == '!' || ch == '*' || ch == 'C' || ch == 'c';
}
+static bool HasTabInLabelField(const char *col1) {
+ for (int i{0}; i < 6; ++i) {
+ if (col1[i] == '\t') {
+ return true;
+ }
+ }
+ return false;
+}
+
static void NormalizeCompilerDirectiveCommentMarker(TokenSequence &dir) {
char *p{dir.GetMutableCharData()};
char *limit{p + dir.SizeInChars()};
@@ -432,6 +441,30 @@ void Prescanner::LabelField(TokenSequence &token) {
int colOffset{column_ - 1};
const char *start{at_};
std::optional<int> badColumn;
+
+ // Skip C-style comments.
+ const char *p{SkipWhiteSpace(start)};
+ long spaces{HasTabInLabelField(start - colOffset) ? 6 : p - start};
+ if (spaces < 6 && IsCComment(p)) {
+ at_ += spaces;
+ column_ += spaces;
+ if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) {
+ Say(LanguageFeature::ClassicCComments, GetCurrentProvenance(),
+ "nonstandard usage: C-style comment"_port_en_US);
+ }
+ SkipCComments();
+ // Fix `column_`, which may be incorrect after multi-line comments.
+ p = at_ - 1;
+ while (p > start && *p != '\n') {
+ --p;
+ }
+ if (*p == '\n') {
+ column_ = at_ - p;
+ }
+ colOffset = column_ - 1;
+ start = at_;
+ }
+
for (; *at_ != '\n' && column_ <= 6; ++at_) {
if (*at_ == '\t') {
++at_;
@@ -716,42 +749,42 @@ const char *Prescanner::SkipCComment(const char *p) const {
bool Prescanner::NextToken(TokenSequence &tokens) {
CHECK(at_ >= start_ && at_ < limit_);
- if (InFixedFormSource() && !preprocessingOnly_) {
+ bool compilingFixedForm{InFixedFormSource() && !preprocessingOnly_};
+ if (compilingFixedForm) {
SkipSpaces();
- } else {
- if (*at_ == '/' && IsCComment(at_)) {
- // Recognize and skip over classic C style /*comments*/ when
- // outside a character literal.
- if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) {
- Say(LanguageFeature::ClassicCComments, GetCurrentProvenance(),
- "nonstandard usage: C-style comment"_port_en_US);
- }
- SkipCComments();
- }
- if (IsSpaceOrTab(at_)) {
- // Compress free-form white space into a single space character.
- const auto theSpace{at_};
- char previous{at_ <= start_ ? ' ' : at_[-1]};
- NextChar();
- SkipSpaces();
- if (*at_ == '\n' && !omitNewline_) {
- // Discard white space at the end of a line.
- } else if (!inPreprocessorDirective_ &&
- (previous == '(' || *at_ == '(' || *at_ == ')')) {
- // Discard white space before/after '(' and before ')', unless in a
- // preprocessor directive. This helps yield space-free contiguous
- // names for generic interfaces like OPERATOR( + ) and
- // READ ( UNFORMATTED ), without misinterpreting #define f (notAnArg).
- // This has the effect of silently ignoring the illegal spaces in
- // the array constructor ( /1,2/ ) but that seems benign; it's
- // hard to avoid that while still removing spaces from OPERATOR( / )
- // and OPERATOR( // ).
- } else {
- // Preserve the squashed white space as a single space character.
- tokens.PutNextTokenChar(' ', GetProvenance(theSpace));
- tokens.CloseToken();
- return true;
- }
+ }
+ if (*at_ == '/' && IsCComment(at_)) {
+ // Recognize and skip over classic C style /*comments*/ when
+ // outside a character literal.
+ if (features_.ShouldWarn(LanguageFeature::ClassicCComments)) {
+ Say(LanguageFeature::ClassicCComments, GetCurrentProvenance(),
+ "nonstandard usage: C-style comment"_port_en_US);
+ }
+ SkipCComments();
+ }
+ if (!compilingFixedForm && IsSpaceOrTab(at_)) {
+ // Compress free-form white space into a single space character.
+ const auto theSpace{at_};
+ char previous{at_ <= start_ ? ' ' : at_[-1]};
+ NextChar();
+ SkipSpaces();
+ if (*at_ == '\n' && !omitNewline_) {
+ // Discard white space at the end of a line.
+ } else if (!inPreprocessorDirective_ &&
+ (previous == '(' || *at_ == '(' || *at_ == ')')) {
+ // Discard white space before/after '(' and before ')', unless in a
+ // preprocessor directive. This helps yield space-free contiguous
+ // names for generic interfaces like OPERATOR( + ) and
+ // READ ( UNFORMATTED ), without misinterpreting #define f (notAnArg).
+ // This has the effect of silently ignoring the illegal spaces in
+ // the array constructor ( /1,2/ ) but that seems benign; it's
+ // hard to avoid that while still removing spaces from OPERATOR( / )
+ // and OPERATOR( // ).
+ } else {
+ // Preserve the squashed white space as a single space character.
+ tokens.PutNextTokenChar(' ', GetProvenance(theSpace));
+ tokens.CloseToken();
+ return true;
}
}
brokenToken_ = false;
@@ -1366,12 +1399,19 @@ const char *Prescanner::FixedFormContinuationLine(bool atNewline) {
}
tabInCurrentLine_ = false;
char col1{*nextLine_};
+ int trailingSpaces{0};
+ for (int i{4}; i > 0 && nextLine_[i] == ' '; --i) {
+ ++trailingSpaces;
+ }
+ bool CCommentAndSpaces{!HasTabInLabelField(nextLine_) &&
+ SkipCComment(SkipWhiteSpace(nextLine_)) - nextLine_ + trailingSpaces ==
+ 5};
bool canBeNonDirectiveContinuation{
- (col1 == ' ' ||
- ((col1 == 'D' || col1 == 'd') &&
- features_.IsEnabled(LanguageFeature::OldDebugLines))) &&
- nextLine_[1] == ' ' && nextLine_[2] == ' ' && nextLine_[3] == ' ' &&
- nextLine_[4] == ' '};
+ ((col1 == ' ' ||
+ ((col1 == 'D' || col1 == 'd') &&
+ features_.IsEnabled(LanguageFeature::OldDebugLines))) &&
+ trailingSpaces == 4) ||
+ CCommentAndSpaces};
if (InCompilerDirective() && !(InConditionalLine() && !preprocessingOnly_)) {
// !$ under -E is not continued, but deferred to later compilation
if (IsFixedFormCommentChar(col1) &&
@@ -1435,7 +1475,8 @@ const char *Prescanner::FixedFormContinuationLine(bool atNewline) {
}
if (canBeNonDirectiveContinuation) {
const char *col6{nextLine_ + 5};
- if (*col6 != '\n' && *col6 != '0' && !IsSpaceOrTab(col6)) {
+ if (*col6 != '\n' && *col6 != '0' && !IsSpaceOrTab(col6) &&
+ !IsCComment(col6)) {
if ((*col6 == 'i' || *col6 == 'I') && IsIncludeLine(nextLine_)) {
// It's an INCLUDE line, not a continuation
} else {
diff --git a/flang/test/Preprocessing/fixed-c-comment.F b/flang/test/Preprocessing/fixed-c-comment.F
new file mode 100644
index 0000000000000..82db6f7b43fa0
--- /dev/null
+++ b/flang/test/Preprocessing/fixed-c-comment.F
@@ -0,0 +1,62 @@
+! RUN: %flang_fc1 -fsyntax-only %s 2>&1
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+! RUN: not %flang_fc1 -pedantic -Werror -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix=ERROR
+
+ integer :: i
+
+! CHECK-NOT: C comment
+! CHECK: i = 1
+! ERROR: portability: nonstandard usage: C-style comment
+/* Old style C comments
+ *comments
+ */
+#define VAL 1
+
+! ERROR: portability: nonstandard usage: C-style comment
+/* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ /* Single-line C comment */
+ i = VAL
+
+! CHECK-NOT: /*
+! CHECK-NOT: Multi
+! CHECK-NOT: C comment
+! CHECK-NOT: */
+! CHECK: i = 2
+/*
+ * Multi-line C comment
+ * comment
+ */
+ /* Multi-line
+C comment */
+ i = 2
+
+! CHECK: i = 3
+! CHECK: i = 4
+! CHECK: 9 i = 5+ 6
+/* C comment + statement */ i = 3
+ /* C comment + statement */ i = 4
+/**/90i = 5
+/**/ + + 6
+
+! CHECK-NOT: /*
+! CHECK-NOT: C comment
+! CHECK-NOT: */
+! CHECK: 100 i = 7
+/* Multi-line C comment + statement
+*/100 i = 7
+
+! CHECK: i = 8
+! CHECK-NOT: C comment
+ i = 8 /* C comment after statement */
+
+! CHECK: print
+! CHECK-NOT: C comment
+ print *, "TAB"
+ /* C comment after tab */
+ end
>From 6d8e25f5dea220daad9a05c68847af6d413871ba Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Wed, 1 Jul 2026 17:05:34 +0000
Subject: [PATCH 2/4] Fix Windows build
---
flang/lib/Parser/prescan.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 3fd28208719a2..f8c14749ea48a 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -444,7 +444,8 @@ void Prescanner::LabelField(TokenSequence &token) {
// Skip C-style comments.
const char *p{SkipWhiteSpace(start)};
- long spaces{HasTabInLabelField(start - colOffset) ? 6 : p - start};
+ int spaces{
+ HasTabInLabelField(start - colOffset) ? 6 : static_cast<int>(p - start)};
if (spaces < 6 && IsCComment(p)) {
at_ += spaces;
column_ += spaces;
>From 12a7a1001b8a1b18a07730c1204b078641d56845 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Thu, 2 Jul 2026 17:40:31 -0300
Subject: [PATCH 3/4] Address review's comments
---
flang/lib/Parser/prescan.cpp | 8 ++++----
flang/test/Preprocessing/fixed-c-comment.F | 8 +++++++-
2 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index f8c14749ea48a..c0536f27ba2b6 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -72,7 +72,7 @@ static inline constexpr bool IsFixedFormCommentChar(char ch) {
}
static bool HasTabInLabelField(const char *col1) {
- for (int i{0}; i < 6; ++i) {
+ for (int i{0}; i < 6 && col1[i] != '\n'; ++i) {
if (col1[i] == '\t') {
return true;
}
@@ -1404,9 +1404,9 @@ const char *Prescanner::FixedFormContinuationLine(bool atNewline) {
for (int i{4}; i > 0 && nextLine_[i] == ' '; --i) {
++trailingSpaces;
}
- bool CCommentAndSpaces{!HasTabInLabelField(nextLine_) &&
- SkipCComment(SkipWhiteSpace(nextLine_)) - nextLine_ + trailingSpaces ==
- 5};
+ const char *afterCComment{SkipCComment(SkipWhiteSpace(nextLine_))};
+ bool CCommentAndSpaces{!HasTabInLabelField(nextLine_) && afterCComment &&
+ afterCComment - nextLine_ + trailingSpaces == 5};
bool canBeNonDirectiveContinuation{
((col1 == ' ' ||
((col1 == 'D' || col1 == 'd') &&
diff --git a/flang/test/Preprocessing/fixed-c-comment.F b/flang/test/Preprocessing/fixed-c-comment.F
index 82db6f7b43fa0..1d00528804e7e 100644
--- a/flang/test/Preprocessing/fixed-c-comment.F
+++ b/flang/test/Preprocessing/fixed-c-comment.F
@@ -1,6 +1,7 @@
! RUN: %flang_fc1 -fsyntax-only %s 2>&1
! RUN: %flang -E %s 2>&1 | FileCheck %s
-! RUN: not %flang_fc1 -pedantic -Werror -fdebug-dump-parse-tree-no-sema %s 2>&1 | FileCheck %s --check-prefix=ERROR
+! RUN: not %flang_fc1 -pedantic -Werror -fdebug-dump-parse-tree-no-sema \
+! RUN: -DTEST_ERRORS=1 %s 2>&1 | FileCheck %s --check-prefix=ERROR
integer :: i
@@ -59,4 +60,9 @@
! CHECK-NOT: C comment
print *, "TAB"
/* C comment after tab */
+
+! ERROR: error: Character in fixed-form label field must be a digit
+#if TEST_ERRORS
+/* Not-terminated C comment
+#endif
end
>From 83bed456fd0c2c612359f0721f5434259531a0c3 Mon Sep 17 00:00:00 2001
From: Leandro Lupori <leandro.lupori at linaro.org>
Date: Fri, 3 Jul 2026 10:52:18 -0300
Subject: [PATCH 4/4] Fix regression and improve efficiency
---
flang/lib/Parser/prescan.cpp | 8 +++++---
flang/test/Preprocessing/fixed-c-comment.F | 5 +++++
2 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index c0536f27ba2b6..238b50d3a4de0 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -1404,15 +1404,17 @@ const char *Prescanner::FixedFormContinuationLine(bool atNewline) {
for (int i{4}; i > 0 && nextLine_[i] == ' '; --i) {
++trailingSpaces;
}
- const char *afterCComment{SkipCComment(SkipWhiteSpace(nextLine_))};
- bool CCommentAndSpaces{!HasTabInLabelField(nextLine_) && afterCComment &&
+ const char *afterWhiteSpace{SkipWhiteSpace(nextLine_)};
+ const char *afterCComment{
+ IsCComment(afterWhiteSpace) ? SkipCComment(afterWhiteSpace) : nullptr};
+ bool cCommentAndSpaces{afterCComment && !HasTabInLabelField(nextLine_) &&
afterCComment - nextLine_ + trailingSpaces == 5};
bool canBeNonDirectiveContinuation{
((col1 == ' ' ||
((col1 == 'D' || col1 == 'd') &&
features_.IsEnabled(LanguageFeature::OldDebugLines))) &&
trailingSpaces == 4) ||
- CCommentAndSpaces};
+ cCommentAndSpaces};
if (InCompilerDirective() && !(InConditionalLine() && !preprocessingOnly_)) {
// !$ under -E is not continued, but deferred to later compilation
if (IsFixedFormCommentChar(col1) &&
diff --git a/flang/test/Preprocessing/fixed-c-comment.F b/flang/test/Preprocessing/fixed-c-comment.F
index 1d00528804e7e..97ad59dd54546 100644
--- a/flang/test/Preprocessing/fixed-c-comment.F
+++ b/flang/test/Preprocessing/fixed-c-comment.F
@@ -56,6 +56,11 @@
! CHECK-NOT: C comment
i = 8 /* C comment after statement */
+! CHECK: i = 9
+! CHECK-NOT: 8
+ i = 9
+* */ 88
+
! CHECK: print
! CHECK-NOT: C comment
print *, "TAB"
More information about the flang-commits
mailing list