[flang-commits] [flang] [flang] Fix continued literals incorrect padding with -ffixed-line-length=none (PR #225051)

Mattéo Rizza Murgier via flang-commits flang-commits at lists.llvm.org
Mon Sep 21 02:37:55 PDT 2026


https://github.com/matteo-rm created https://github.com/llvm/llvm-project/pull/225051

`-ffixed-line-length=none` and `=0` were implemented as a column limit of 1000000. This led to `PadOutCharacterLiteral` inserting a million blank characters when the flag was used. This PR fixes this by making the `fixedFormColumns` param an optional type. No padding being inserted is coherent with `gfortran`'s behavior when the equivalent flag is passed.

Examples of Fujitsu test suite tests that trigger this behavior: `0001_0073`, `0131_0048`, `0131_0052`, `0161_0003`.

NOTE: This also makes it so lines over one million characters don't get silently truncated.

>From 0e784ed92b81710cae4348435c87f58549cc81d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matt=C3=A9o=20Rizza=20Murgier?=
 <matteo.rizza-murgier at sipearl.com>
Date: Fri, 18 Sep 2026 17:59:01 +0200
Subject: [PATCH] [flang] Fix continued literals incorrect padding with
 -ffixed-line-length=none

---
 .../include/flang/Frontend/FrontendOptions.h  |  3 ++-
 flang/include/flang/Parser/options.h          |  2 +-
 flang/lib/Frontend/CompilerInvocation.cpp     |  2 +-
 flang/lib/Parser/prescan.cpp                  | 19 +++++++++++--------
 flang/lib/Parser/prescan.h                    |  7 +++++--
 ...fixed-line-length-none-char-continuation.f | 11 +++++++++++
 6 files changed, 31 insertions(+), 13 deletions(-)
 create mode 100644 flang/test/Parser/fixed-line-length-none-char-continuation.f

diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h
index b0e4918d6b4a07..abc008bfbe3391 100644
--- a/flang/include/flang/Frontend/FrontendOptions.h
+++ b/flang/include/flang/Frontend/FrontendOptions.h
@@ -20,6 +20,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include <cstdint>
+#include <optional>
 #include <string>
 
 namespace Fortran::frontend {
@@ -290,7 +291,7 @@ struct FrontendOptions {
 
   // The column after which characters are ignored in fixed form lines in the
   // source file.
-  int fixedFormColumns = 72;
+  std::optional<int> fixedFormColumns = 72;
 
   /// The input kind, either specified via -x argument or deduced from the input
   /// file name.
diff --git a/flang/include/flang/Parser/options.h b/flang/include/flang/Parser/options.h
index e65f253748d26f..5f24b89c230357 100644
--- a/flang/include/flang/Parser/options.h
+++ b/flang/include/flang/Parser/options.h
@@ -24,7 +24,7 @@ struct Options {
   using Predefinition = std::pair<std::string, std::optional<std::string>>;
 
   bool isFixedForm{false};
-  int fixedFormColumns{72};
+  std::optional<int> fixedFormColumns{72};
   common::LanguageFeatureControl features;
   std::vector<std::string> searchDirectories;
   std::vector<std::string> intrinsicModuleDirectories;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 85e5f477dcb8c7..9ee4142de77105 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -872,7 +872,7 @@ static bool parseFrontendArgs(FrontendOptions &opts, llvm::opt::ArgList &args,
       diags.Report(clang::diag::err_drv_negative_columns)
           << arg->getOption().getName() << arg->getValue();
     } else if (columns == 0) {
-      opts.fixedFormColumns = 1000000;
+      opts.fixedFormColumns = std::nullopt;
     } else if (columns < 7) {
       diags.Report(clang::diag::err_drv_small_columns)
           << arg->getOption().getName() << arg->getValue() << "7";
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index db4b50a4920028..11d3527e076382 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -572,7 +572,8 @@ void Prescanner::SkipToEndOfLine() {
 }
 
 bool Prescanner::MustSkipToEndOfLine() const {
-  if (inFixedForm_ && column_ > fixedFormColumnLimit_ && !tabInCurrentLine_) {
+  if (inFixedForm_ && IsPastFixedFormColumnLimit(column_) &&
+      !tabInCurrentLine_) {
     return true; // skip over ignored columns in right margin (73:80)
   } else if (*at_ == '!' && !inCharLiteral_ &&
       (!inFixedForm_ || tabInCurrentLine_ || column_ != 6)) {
@@ -880,12 +881,13 @@ bool Prescanner::NextToken(TokenSequence &tokens) {
           !preprocessingOnly_ && IsSpaceOrTab(at_)) {
         const char *probe{at_};
         int col{column_};
-        while (col <= fixedFormColumnLimit_ && IsSpaceOrTab(probe)) {
+        while (!IsPastFixedFormColumnLimit(col) && IsSpaceOrTab(probe)) {
           probe += IsSpaceOrTab(probe);
           ++col;
         }
-        if (col > fixedFormColumnLimit_ || *probe == '\n' || *probe == '\r' ||
-            (*probe == '!' && col <= fixedFormColumnLimit_)) {
+        if (IsPastFixedFormColumnLimit(col) || *probe == '\n' ||
+            *probe == '\r' ||
+            (*probe == '!' && !IsPastFixedFormColumnLimit(col))) {
           SkipSpaces();
           hadContinuation = SkipToNextSignificantCharacter();
         }
@@ -1204,8 +1206,9 @@ void Prescanner::Hollerith(
 // In fixed form, source card images must be processed as if they were at
 // least 72 columns wide, at least in character literal contexts.
 bool Prescanner::PadOutCharacterLiteral(TokenSequence &tokens) {
-  while (inFixedForm_ && !tabInCurrentLine_ && at_[1] == '\n') {
-    if (column_ < fixedFormColumnLimit_) {
+  while (inFixedForm_ && fixedFormColumnLimit_ && !tabInCurrentLine_ &&
+      at_[1] == '\n') {
+    if (column_ < *fixedFormColumnLimit_) {
       tokens.PutNextTokenChar(' ', spaceProvenance_);
       ++column_;
       return true;
@@ -1254,7 +1257,7 @@ bool Prescanner::IsFixedFormCommentLine(const char *start) const {
       break;
     }
   }
-  if (!anyTabs && p >= start + fixedFormColumnLimit_) {
+  if (!anyTabs && IsPastFixedFormColumnLimit(p - start + 1)) {
     return true;
   }
   if (*p == '!' && !inCharLiteral_ && (anyTabs || p != start + 5)) {
@@ -1755,7 +1758,7 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
       (features_.IsEnabled(LanguageFeature::OpenMP) &&
           std::strcmp(sentinel, "$omp") == 0)};
   if (isOpenMPSentinelScan) {
-    for (; column <= fixedFormColumnLimit_; ++column, ++p) {
+    for (; !IsPastFixedFormColumnLimit(column); ++column, ++p) {
       if (IsSpaceOrTab(p)) {
       } else if (*p == '!') {
         return std::nullopt; // sentinel + blanks + ! is a comment, not a
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index 656b3c98b6c739..1c57cfa2c6fe58 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -64,7 +64,7 @@ class Prescanner {
     encoding_ = code;
     return *this;
   }
-  Prescanner &set_fixedFormColumnLimit(int limit) {
+  Prescanner &set_fixedFormColumnLimit(std::optional<int> limit) {
     fixedFormColumnLimit_ = limit;
     return *this;
   }
@@ -208,6 +208,9 @@ class Prescanner {
             std::strcmp(directiveSentinel_, "$omx") == 0 ||
             std::strcmp(directiveSentinel_, "$ompx") == 0);
   }
+  bool IsPastFixedFormColumnLimit(int column) const {
+    return fixedFormColumnLimit_ && column > *fixedFormColumnLimit_;
+  }
   bool InFixedFormSource() const {
     return inFixedForm_ && !inPreprocessorDirective_ && !InCompilerDirective();
   }
@@ -280,7 +283,7 @@ class Prescanner {
   bool isNestedInIncludeDirective_{false};
   bool backslashFreeFormContinuation_{false};
   bool inFixedForm_{false};
-  int fixedFormColumnLimit_{72};
+  std::optional<int> fixedFormColumnLimit_{72};
   Encoding encoding_{Encoding::UTF_8};
   int parenthesisNesting_{0};
   int prescannerNesting_{0};
diff --git a/flang/test/Parser/fixed-line-length-none-char-continuation.f b/flang/test/Parser/fixed-line-length-none-char-continuation.f
new file mode 100644
index 00000000000000..17444cec18a8b7
--- /dev/null
+++ b/flang/test/Parser/fixed-line-length-none-char-continuation.f
@@ -0,0 +1,11 @@
+! RUN: %flang_fc1 -fdebug-unparse %s | FileCheck %s --check-prefix=DEFAULT
+! RUN: %flang_fc1 -fdebug-unparse -ffixed-line-length=132 %s | FileCheck %s --check-prefix=LEN132
+! RUN: %flang_fc1 -fdebug-unparse -ffixed-line-length=none %s | FileCheck %s --check-prefix=NONE
+! RUN: %flang_fc1 -fdebug-unparse -ffixed-line-length=0 %s | FileCheck %s --check-prefix=NONE
+      character*4 s
+      s = 'ab
+     +cd'
+      end
+! DEFAULT: s="ab{{( {59})}}cd"
+! LEN132: s="ab{{( {119})}}cd"
+! NONE: s="abcd"



More information about the flang-commits mailing list