[llvm] [flang][runtime] Handle spaces before ')' in alternative list-directe… (PR #149384)

Peter Klausler via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 17 12:15:48 PDT 2025


https://github.com/klausler created https://github.com/llvm/llvm-project/pull/149384

…d complex input

List-directed reads of complex values that can't go through the usual fast path (as in this bug's test case, which uses DECIMAL='COMMA') didn't skip spaces before the closing right parenthesis correctly.

Fixes https://github.com/llvm/llvm-project/issues/149164.

>From cccbaf01be9c26dfdaa59e50d8c101216483f043 Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Thu, 17 Jul 2025 12:11:35 -0700
Subject: [PATCH] [flang][runtime] Handle spaces before ')' in alternative
 list-directed complex input

List-directed reads of complex values that can't go through the
usual fast path (as in this bug's test case, which uses DECIMAL='COMMA')
didn't skip spaces before the closing right parenthesis correctly.

Fixes https://github.com/llvm/llvm-project/issues/149164.
---
 flang-rt/lib/runtime/edit-input.cpp           | 41 +++++--------------
 .../unittests/Runtime/NumericalFormatTest.cpp | 31 ++++++++++++++
 2 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/flang-rt/lib/runtime/edit-input.cpp b/flang-rt/lib/runtime/edit-input.cpp
index 0cc287aa3b47e..13557678f6057 100644
--- a/flang-rt/lib/runtime/edit-input.cpp
+++ b/flang-rt/lib/runtime/edit-input.cpp
@@ -19,16 +19,19 @@
 namespace Fortran::runtime::io {
 RT_OFFLOAD_API_GROUP_BEGIN
 
-// Checks that a list-directed input value has been entirely consumed and
-// doesn't contain unparsed characters before the next value separator.
+// Handle DC or DECIMAL='COMMA' and determine the active separator character
+static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
+  return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
+}
+
 static inline RT_API_ATTRS bool IsCharValueSeparator(
     const DataEdit &edit, char32_t ch) {
-  char32_t comma{
-      edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
-  return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
+  return ch == ' ' || ch == '\t' || ch == '/' || ch == GetSeparatorChar(edit) ||
       (edit.IsNamelist() && (ch == '&' || ch == '$'));
 }
 
+// Checks that a list-directed input value has been entirely consumed and
+// doesn't contain unparsed characters before the next value separator.
 static RT_API_ATTRS bool CheckCompleteListDirectedField(
     IoStatementState &io, const DataEdit &edit) {
   if (edit.IsListDirected()) {
@@ -54,10 +57,6 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
   }
 }
 
-static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
-  return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
-}
-
 template <int LOG2_BASE>
 static RT_API_ATTRS bool EditBOZInput(
     IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
@@ -518,7 +517,7 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput(
   // Consume the trailing ')' of a list-directed or NAMELIST complex
   // input value.
   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
-    if (next && (*next == ' ' || *next == '\t')) {
+    if (!next || *next == ' ' || *next == '\t') {
       io.SkipSpaces(remaining);
       next = io.NextInField(remaining, edit);
     }
@@ -1006,27 +1005,7 @@ static RT_API_ATTRS bool EditListDirectedCharacterInput(
   // Undelimited list-directed character input: stop at a value separator
   // or the end of the current record.
   while (auto ch{io.GetCurrentChar(byteCount)}) {
-    bool isSep{false};
-    switch (*ch) {
-    case ' ':
-    case '\t':
-    case '/':
-      isSep = true;
-      break;
-    case '&':
-    case '$':
-      isSep = edit.IsNamelist();
-      break;
-    case ',':
-      isSep = !(edit.modes.editingFlags & decimalComma);
-      break;
-    case ';':
-      isSep = !!(edit.modes.editingFlags & decimalComma);
-      break;
-    default:
-      break;
-    }
-    if (isSep) {
+    if (IsCharValueSeparator(edit, *ch)) {
       break;
     }
     if (length > 0) {
diff --git a/flang-rt/unittests/Runtime/NumericalFormatTest.cpp b/flang-rt/unittests/Runtime/NumericalFormatTest.cpp
index f1492d0e39fec..73245dca13bc0 100644
--- a/flang-rt/unittests/Runtime/NumericalFormatTest.cpp
+++ b/flang-rt/unittests/Runtime/NumericalFormatTest.cpp
@@ -213,6 +213,37 @@ TEST(IOApiTests, ListInputTest) {
       << "', but got '" << output << "'";
 }
 
+TEST(IOApiTests, ListInputComplexRegressionTest) {
+  static const char input[]{"(1,;2, );(3,;4,)"};
+  auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)};
+  static constexpr int numRealValues{4};
+  float z[numRealValues];
+  ASSERT_TRUE(IONAME(SetDecimal)(cookie, "COMMA", 5));
+  for (int j{0}; j < numRealValues; j += 2) {
+    ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j]))
+        << "InputComplex32 failed with value " << z[j];
+  }
+  auto status{IONAME(EndIoStatement)(cookie)};
+  ASSERT_EQ(status, 0) << "Failed complex list-directed input, status "
+                       << static_cast<int>(status);
+  static constexpr int bufferSize{18};
+  char output[bufferSize];
+  output[bufferSize - 1] = '\0';
+  cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1);
+  for (int j{0}; j < numRealValues; j += 2) {
+    ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1]))
+        << "OutputComplex32 failed when outputting value " << z[j] << ", "
+        << z[j + 1];
+  }
+  status = IONAME(EndIoStatement)(cookie);
+  ASSERT_EQ(status, 0) << "Failed complex list-directed output, status "
+                       << static_cast<int>(status);
+  static const char expect[bufferSize]{" (1.,2.) (3.,4.) "};
+  ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0)
+      << "Failed complex list-directed output, expected '" << expect
+      << "', but got '" << output << "'";
+}
+
 TEST(IOApiTests, DescriptorOutputTest) {
   static constexpr int bufferSize{10};
   char buffer[bufferSize];



More information about the llvm-commits mailing list