Skip to content

Commit 680b8dd

Browse files
authored
[flang][runtime] Handle spaces before ')' in alternative list-directe… (#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 #149164.
1 parent 97a8476 commit 680b8dd

File tree

2 files changed

+41
-31
lines changed

2 files changed

+41
-31
lines changed

flang-rt/lib/runtime/edit-input.cpp

Lines changed: 10 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@
1919
namespace Fortran::runtime::io {
2020
RT_OFFLOAD_API_GROUP_BEGIN
2121

22-
// Checks that a list-directed input value has been entirely consumed and
23-
// doesn't contain unparsed characters before the next value separator.
22+
// Handle DC or DECIMAL='COMMA' and determine the active separator character
23+
static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
24+
return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
25+
}
26+
2427
static inline RT_API_ATTRS bool IsCharValueSeparator(
2528
const DataEdit &edit, char32_t ch) {
26-
char32_t comma{
27-
edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','}};
28-
return ch == ' ' || ch == '\t' || ch == comma || ch == '/' ||
29+
return ch == ' ' || ch == '\t' || ch == '/' || ch == GetSeparatorChar(edit) ||
2930
(edit.IsNamelist() && (ch == '&' || ch == '$'));
3031
}
3132

33+
// Checks that a list-directed input value has been entirely consumed and
34+
// doesn't contain unparsed characters before the next value separator.
3235
static RT_API_ATTRS bool CheckCompleteListDirectedField(
3336
IoStatementState &io, const DataEdit &edit) {
3437
if (edit.IsListDirected()) {
@@ -54,10 +57,6 @@ static RT_API_ATTRS bool CheckCompleteListDirectedField(
5457
}
5558
}
5659

57-
static inline RT_API_ATTRS char32_t GetSeparatorChar(const DataEdit &edit) {
58-
return edit.modes.editingFlags & decimalComma ? char32_t{';'} : char32_t{','};
59-
}
60-
6160
template <int LOG2_BASE>
6261
static RT_API_ATTRS bool EditBOZInput(
6362
IoStatementState &io, const DataEdit &edit, void *n, std::size_t bytes) {
@@ -518,7 +517,7 @@ static RT_API_ATTRS ScannedRealInput ScanRealInput(
518517
// Consume the trailing ')' of a list-directed or NAMELIST complex
519518
// input value.
520519
if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
521-
if (next && (*next == ' ' || *next == '\t')) {
520+
if (!next || *next == ' ' || *next == '\t') {
522521
io.SkipSpaces(remaining);
523522
next = io.NextInField(remaining, edit);
524523
}
@@ -1006,27 +1005,7 @@ static RT_API_ATTRS bool EditListDirectedCharacterInput(
10061005
// Undelimited list-directed character input: stop at a value separator
10071006
// or the end of the current record.
10081007
while (auto ch{io.GetCurrentChar(byteCount)}) {
1009-
bool isSep{false};
1010-
switch (*ch) {
1011-
case ' ':
1012-
case '\t':
1013-
case '/':
1014-
isSep = true;
1015-
break;
1016-
case '&':
1017-
case '$':
1018-
isSep = edit.IsNamelist();
1019-
break;
1020-
case ',':
1021-
isSep = !(edit.modes.editingFlags & decimalComma);
1022-
break;
1023-
case ';':
1024-
isSep = !!(edit.modes.editingFlags & decimalComma);
1025-
break;
1026-
default:
1027-
break;
1028-
}
1029-
if (isSep) {
1008+
if (IsCharValueSeparator(edit, *ch)) {
10301009
break;
10311010
}
10321011
if (length > 0) {

flang-rt/unittests/Runtime/NumericalFormatTest.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,37 @@ TEST(IOApiTests, ListInputTest) {
213213
<< "', but got '" << output << "'";
214214
}
215215

216+
TEST(IOApiTests, ListInputComplexRegressionTest) {
217+
static const char input[]{"(1,;2, );(3,;4,)"};
218+
auto cookie{IONAME(BeginInternalListInput)(input, sizeof input - 1)};
219+
static constexpr int numRealValues{4};
220+
float z[numRealValues];
221+
ASSERT_TRUE(IONAME(SetDecimal)(cookie, "COMMA", 5));
222+
for (int j{0}; j < numRealValues; j += 2) {
223+
ASSERT_TRUE(IONAME(InputComplex32)(cookie, &z[j]))
224+
<< "InputComplex32 failed with value " << z[j];
225+
}
226+
auto status{IONAME(EndIoStatement)(cookie)};
227+
ASSERT_EQ(status, 0) << "Failed complex list-directed input, status "
228+
<< static_cast<int>(status);
229+
static constexpr int bufferSize{18};
230+
char output[bufferSize];
231+
output[bufferSize - 1] = '\0';
232+
cookie = IONAME(BeginInternalListOutput)(output, bufferSize - 1);
233+
for (int j{0}; j < numRealValues; j += 2) {
234+
ASSERT_TRUE(IONAME(OutputComplex32)(cookie, z[j], z[j + 1]))
235+
<< "OutputComplex32 failed when outputting value " << z[j] << ", "
236+
<< z[j + 1];
237+
}
238+
status = IONAME(EndIoStatement)(cookie);
239+
ASSERT_EQ(status, 0) << "Failed complex list-directed output, status "
240+
<< static_cast<int>(status);
241+
static const char expect[bufferSize]{" (1.,2.) (3.,4.) "};
242+
ASSERT_EQ(std::strncmp(output, expect, bufferSize), 0)
243+
<< "Failed complex list-directed output, expected '" << expect
244+
<< "', but got '" << output << "'";
245+
}
246+
216247
TEST(IOApiTests, DescriptorOutputTest) {
217248
static constexpr int bufferSize{10};
218249
char buffer[bufferSize];

0 commit comments

Comments
 (0)