[flang-commits] [flang] [flang] Accept C-style comments in label fields (PR #207012)
Leandro Lupori via flang-commits
flang-commits at lists.llvm.org
Wed Jul 1 10:06:41 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/2] [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/2] 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;
More information about the flang-commits
mailing list