Skip to content

Commit b8452db

Browse files
committed
[flang] Support NAMELIST input of short arrays
NAMELIST array input does not need to fully define an array. If another input item begins after at least one element, it ends input into the array and the remaining items are not modified. The tricky part of supporting this feature is that it's not always easy to determine whether the next non-blank thing in the input is a value or the next item's name, esp. in the case of logical data where T and F can be names. E.g., &group logicalArray = t f f t = 1 / should read three elements into "logicalArray" and then read an integer or real variable named "t". So the I/O runtime has to do some look-ahead to determine whether the next thing in the input is a name followed by '=', '(', or '%'. Since the '=' may be on a later record, possibly with intervening NAMELIST comments, the runtime has to support a general form of saving and restoring its current position. The infrastructure in the I/O runtime already has to support repositioning for list-directed repetition, even on non-positionable input sources like terminals and sockets; this patch adds an internal RAII API to make it easier to save a position and then do arbitrary look-ahead. Differential Revision: https://reviews.llvm.org/D112245
1 parent 236197e commit b8452db

File tree

8 files changed

+130
-14
lines changed

8 files changed

+130
-14
lines changed

flang/runtime/connection.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,32 @@ struct ConnectionState : public ConnectionAttributes {
6666
// Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT
6767
MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP
6868
};
69+
70+
// Utility class for capturing and restoring a position in an input stream.
71+
class SavedPosition {
72+
public:
73+
explicit SavedPosition(ConnectionState &c)
74+
: connection_{c}, positionInRecord_{c.positionInRecord},
75+
furthestPositionInRecord_{c.furthestPositionInRecord},
76+
leftTabLimit_{c.leftTabLimit}, previousResumptionRecordNumber_{
77+
c.resumptionRecordNumber} {
78+
c.resumptionRecordNumber = c.currentRecordNumber;
79+
}
80+
~SavedPosition() {
81+
connection_.currentRecordNumber = *connection_.resumptionRecordNumber;
82+
connection_.resumptionRecordNumber = previousResumptionRecordNumber_;
83+
connection_.leftTabLimit = leftTabLimit_;
84+
connection_.furthestPositionInRecord = furthestPositionInRecord_;
85+
connection_.positionInRecord = positionInRecord_;
86+
}
87+
88+
private:
89+
ConnectionState &connection_;
90+
std::int64_t positionInRecord_;
91+
std::int64_t furthestPositionInRecord_;
92+
std::optional<std::int64_t> leftTabLimit_;
93+
std::optional<std::int64_t> previousResumptionRecordNumber_;
94+
};
95+
6996
} // namespace Fortran::runtime::io
7097
#endif // FORTRAN_RUNTIME_IO_CONNECTION_H_

flang/runtime/descriptor-io.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ inline bool FormattedIntegerIO(
4949
SubscriptValue subscripts[maxRank];
5050
descriptor.GetLowerBounds(subscripts);
5151
using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
52+
bool anyInput{false};
5253
for (std::size_t j{0}; j < numElements; ++j) {
5354
if (auto edit{io.GetNextDataEdit()}) {
5455
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
@@ -57,8 +58,10 @@ inline bool FormattedIntegerIO(
5758
return false;
5859
}
5960
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
60-
if (!EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
61-
return false;
61+
if (EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
62+
anyInput = true;
63+
} else {
64+
return anyInput && edit->IsNamelist();
6265
}
6366
}
6467
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
@@ -79,6 +82,7 @@ inline bool FormattedRealIO(
7982
SubscriptValue subscripts[maxRank];
8083
descriptor.GetLowerBounds(subscripts);
8184
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
85+
bool anyInput{false};
8286
for (std::size_t j{0}; j < numElements; ++j) {
8387
if (auto edit{io.GetNextDataEdit()}) {
8488
RawType &x{ExtractElement<RawType>(io, descriptor, subscripts)};
@@ -87,8 +91,10 @@ inline bool FormattedRealIO(
8791
return false;
8892
}
8993
} else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
90-
if (!EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
91-
return false;
94+
if (EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
95+
anyInput = true;
96+
} else {
97+
return anyInput && edit->IsNamelist();
9298
}
9399
}
94100
if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
@@ -111,6 +117,7 @@ inline bool FormattedComplexIO(
111117
bool isListOutput{
112118
io.get_if<ListDirectedStatementState<Direction::Output>>() != nullptr};
113119
using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
120+
bool anyInput{false};
114121
for (std::size_t j{0}; j < numElements; ++j) {
115122
RawType *x{&ExtractElement<RawType>(io, descriptor, subscripts)};
116123
if (isListOutput) {
@@ -132,9 +139,11 @@ inline bool FormattedComplexIO(
132139
}
133140
} else if (edit->descriptor == DataEdit::ListDirectedNullValue) {
134141
break;
135-
} else if (!EditRealInput<KIND>(
142+
} else if (EditRealInput<KIND>(
136143
io, *edit, reinterpret_cast<void *>(x))) {
137-
return false;
144+
anyInput = true;
145+
} else {
146+
return anyInput && edit->IsNamelist();
138147
}
139148
}
140149
}
@@ -154,6 +163,7 @@ inline bool FormattedCharacterIO(
154163
descriptor.GetLowerBounds(subscripts);
155164
std::size_t length{descriptor.ElementBytes() / sizeof(A)};
156165
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
166+
bool anyInput{false};
157167
for (std::size_t j{0}; j < numElements; ++j) {
158168
A *x{&ExtractElement<A>(io, descriptor, subscripts)};
159169
if (listOutput) {
@@ -167,8 +177,10 @@ inline bool FormattedCharacterIO(
167177
}
168178
} else {
169179
if (edit->descriptor != DataEdit::ListDirectedNullValue) {
170-
if (!EditDefaultCharacterInput(io, *edit, x, length)) {
171-
return false;
180+
if (EditDefaultCharacterInput(io, *edit, x, length)) {
181+
anyInput = true;
182+
} else {
183+
return anyInput && edit->IsNamelist();
172184
}
173185
}
174186
}
@@ -191,6 +203,7 @@ inline bool FormattedLogicalIO(
191203
descriptor.GetLowerBounds(subscripts);
192204
auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
193205
using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
206+
bool anyInput{false};
194207
for (std::size_t j{0}; j < numElements; ++j) {
195208
IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
196209
if (listOutput) {
@@ -207,8 +220,9 @@ inline bool FormattedLogicalIO(
207220
bool truth{};
208221
if (EditLogicalInput(io, *edit, truth)) {
209222
x = truth;
223+
anyInput = true;
210224
} else {
211-
return false;
225+
return anyInput && edit->IsNamelist();
212226
}
213227
}
214228
}

flang/runtime/edit-input.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "edit-input.h"
10+
#include "namelist.h"
1011
#include "flang/Common/real.h"
1112
#include "flang/Common/uint128.h"
1213
#include <algorithm>
@@ -69,6 +70,10 @@ bool EditIntegerInput(
6970
RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
7071
switch (edit.descriptor) {
7172
case DataEdit::ListDirected:
73+
if (IsNamelistName(io)) {
74+
return false;
75+
}
76+
break;
7277
case 'G':
7378
case 'I':
7479
break;
@@ -298,6 +303,10 @@ bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
298303
constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
299304
switch (edit.descriptor) {
300305
case DataEdit::ListDirected:
306+
if (IsNamelistName(io)) {
307+
return false;
308+
}
309+
return EditCommonRealInput<KIND>(io, edit, n);
301310
case DataEdit::ListDirectedRealPart:
302311
case DataEdit::ListDirectedImaginaryPart:
303312
case 'F':
@@ -326,6 +335,10 @@ bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
326335
bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) {
327336
switch (edit.descriptor) {
328337
case DataEdit::ListDirected:
338+
if (IsNamelistName(io)) {
339+
return false;
340+
}
341+
break;
329342
case 'L':
330343
case 'G':
331344
break;
@@ -407,6 +420,9 @@ static bool EditListDirectedDefaultCharacterInput(
407420
io.HandleRelativePosition(1);
408421
return EditDelimitedCharacterInput(io, x, length, *ch);
409422
}
423+
if (IsNamelistName(io)) {
424+
return false;
425+
}
410426
// Undelimited list-directed character input: stop at a value separator
411427
// or the end of the current record.
412428
std::optional<int> remaining{length};

flang/runtime/format.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ struct DataEdit {
5151
return descriptor == ListDirected || descriptor == ListDirectedRealPart ||
5252
descriptor == ListDirectedImaginaryPart;
5353
}
54+
constexpr bool IsNamelist() const {
55+
return IsListDirected() && modes.inNamelist;
56+
}
5457

5558
static constexpr char DefinedDerivedType{'d'}; // DT user-defined derived type
5659

flang/runtime/io-stmt.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ class ListDirectedStatementState<Direction::Input>
229229
std::optional<DataEdit> GetNextDataEdit(
230230
IoStatementState &, int maxRepeat = 1);
231231

232-
// Each NAMELIST input item is a distinct "list-directed"
233-
// input statement. This member function resets this state
234-
// so that repetition and null values work correctly for each
235-
// successive NAMELIST input item.
232+
// Each NAMELIST input item is treated like a distinct list-directed
233+
// input statement. This member function resets some state so that
234+
// repetition and null values work correctly for each successive
235+
// NAMELIST input item.
236236
void ResetForNextNamelistItem() {
237237
remaining_ = 0;
238238
eatComma_ = false;

flang/runtime/namelist.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
333333
return false;
334334
}
335335
io.HandleRelativePosition(1);
336-
// Read the values into the descriptor
336+
// Read the values into the descriptor. An array can be short.
337337
listInput->ResetForNextNamelistItem();
338338
if (!descr::DescriptorIO<Direction::Input>(io, *useDescriptor)) {
339339
return false;
@@ -352,4 +352,25 @@ bool IONAME(InputNamelist)(Cookie cookie, const NamelistGroup &group) {
352352
return true;
353353
}
354354

355+
bool IsNamelistName(IoStatementState &io) {
356+
if (io.get_if<ListDirectedStatementState<Direction::Input>>()) {
357+
ConnectionState &connection{io.GetConnectionState()};
358+
if (connection.modes.inNamelist) {
359+
SavedPosition savedPosition{connection};
360+
if (auto ch{io.GetNextNonBlank()}) {
361+
if (IsLegalIdStart(*ch)) {
362+
do {
363+
io.HandleRelativePosition(1);
364+
ch = io.GetCurrentChar();
365+
} while (ch && IsLegalIdChar(*ch));
366+
ch = io.GetNextNonBlank();
367+
// TODO: how to deal with NaN(...) ambiguity?
368+
return ch && (ch == '=' || ch == '(' || ch == '%');
369+
}
370+
}
371+
}
372+
}
373+
return false;
374+
}
375+
355376
} // namespace Fortran::runtime::io

flang/runtime/namelist.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
namespace Fortran::runtime {
1717
class Descriptor;
18+
class IoStatementState;
1819
} // namespace Fortran::runtime
1920

2021
namespace Fortran::runtime::io {
@@ -33,5 +34,11 @@ class NamelistGroup {
3334
std::size_t items;
3435
const Item *item; // in original declaration order
3536
};
37+
38+
// Look ahead on input for an identifier followed by a '=', '(', or '%'
39+
// character; for use in disambiguating a name-like value (e.g. F or T) from a
40+
// NAMELIST group item name. Always false when not reading a NAMELIST.
41+
bool IsNamelistName(IoStatementState &);
42+
3643
} // namespace Fortran::runtime::io
3744
#endif // FORTRAN_RUNTIME_NAMELIST_H_

flang/unittests/Runtime/Namelist.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,4 +161,32 @@ TEST(NamelistTests, Subscripts) {
161161
EXPECT_EQ(got, expect);
162162
}
163163

164+
TEST(NamelistTests, ShortArrayInput) {
165+
OwningPtr<Descriptor> aDesc{
166+
MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(
167+
std::vector<int>{2}, std::vector<int>(2, -1))};
168+
OwningPtr<Descriptor> bDesc{
169+
MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(
170+
std::vector<int>{2}, std::vector<int>(2, -2))};
171+
const NamelistGroup::Item items[]{{"a", *aDesc}, {"b", *bDesc}};
172+
const NamelistGroup group{"nl", 2, items};
173+
// Two 12-character lines of internal input
174+
static char t1[]{"&nl a = 1 b "
175+
" = 2 / "};
176+
StaticDescriptor<1, true> statDesc;
177+
Descriptor &internalDesc{statDesc.descriptor()};
178+
SubscriptValue shape{2};
179+
internalDesc.Establish(1, 12, t1, 1, &shape, CFI_attribute_pointer);
180+
auto inCookie{IONAME(BeginInternalArrayListInput)(
181+
internalDesc, nullptr, 0, __FILE__, __LINE__)};
182+
ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));
183+
auto inStatus{IONAME(EndIoStatement)(inCookie)};
184+
ASSERT_EQ(inStatus, 0) << "Failed namelist input subscripts, status "
185+
<< static_cast<int>(inStatus);
186+
EXPECT_EQ(*aDesc->ZeroBasedIndexedElement<int>(0), 1);
187+
EXPECT_EQ(*aDesc->ZeroBasedIndexedElement<int>(1), -1);
188+
EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(0), 2);
189+
EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(1), -2);
190+
}
191+
164192
// TODO: Internal NAMELIST error tests

0 commit comments

Comments
 (0)