[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