[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