Skip to content

Commit 04eb93b

Browse files
committed
[flang] Fix repeated "DT" editing
User-defined derived type editing in formatted I/O wasn't working with repeat counts; e.g., "2DT(10)". The solution required some code to be moved from GetNextDataEdit() to CueUpNextDataEdit() so that a stack entry for a nonparenthesized repeated data edit descriptor would work correctly -- all other data edit descriptors are capable of dealing with repetition in their callees, so the bug hadn't been exposed before. Debugging this problem led to some improvements in error messages for bad format strings, and those changes have been retained; also, a dead member function was discovered and expunged. Differential Revision: https://reviews.llvm.org/D117904
1 parent e6cdef1 commit 04eb93b

File tree

4 files changed

+55
-101
lines changed

4 files changed

+55
-101
lines changed

flang/runtime/descriptor-io.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ std::optional<bool> DefinedFormattedIo(IoStatementState &io,
1919
peek->descriptor == DataEdit::ListDirected)) {
2020
// User-defined derived type formatting
2121
IoErrorHandler &handler{io.GetIoErrorHandler()};
22-
DataEdit edit{*io.GetNextDataEdit()}; // consume it this time
22+
DataEdit edit{*io.GetNextDataEdit(1)}; // now consume it; no repeats
2323
RUNTIME_CHECK(handler, edit.descriptor == peek->descriptor);
2424
char ioType[2 + edit.maxIoTypeChars];
2525
auto ioTypeLen{std::size_t{2} /*"DT"*/ + edit.ioTypeChars};

flang/runtime/format-implementation.h

Lines changed: 42 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -33,59 +33,6 @@ FormatControl<CONTEXT>::FormatControl(const Terminator &terminator,
3333
stack_[0].remaining = Iteration::unlimited; // 13.4(8)
3434
}
3535

36-
template <typename CONTEXT>
37-
int FormatControl<CONTEXT>::GetMaxParenthesisNesting(
38-
IoErrorHandler &handler, const CharType *format, std::size_t formatLength) {
39-
int maxNesting{0};
40-
int nesting{0};
41-
const CharType *end{format + formatLength};
42-
std::optional<CharType> quote;
43-
int repeat{0};
44-
for (const CharType *p{format}; p < end; ++p) {
45-
if (quote) {
46-
if (*p == *quote) {
47-
quote.reset();
48-
}
49-
} else if (*p >= '0' && *p <= '9') {
50-
repeat = 10 * repeat + *p - '0';
51-
} else if (*p != ' ') {
52-
switch (*p) {
53-
case '\'':
54-
case '"':
55-
quote = *p;
56-
break;
57-
case 'h':
58-
case 'H': // 9HHOLLERITH
59-
p += repeat;
60-
if (p >= end) {
61-
handler.SignalError(IostatErrorInFormat,
62-
"Hollerith (%dH) too long in FORMAT", repeat);
63-
return maxNesting;
64-
}
65-
break;
66-
case ' ':
67-
break;
68-
case '(':
69-
++nesting;
70-
maxNesting = std::max(nesting, maxNesting);
71-
break;
72-
case ')':
73-
nesting = std::max(nesting - 1, 0);
74-
break;
75-
}
76-
repeat = 0;
77-
}
78-
}
79-
if (quote) {
80-
handler.SignalError(
81-
IostatErrorInFormat, "Unbalanced quotation marks in FORMAT string");
82-
} else if (nesting) {
83-
handler.SignalError(
84-
IostatErrorInFormat, "Unbalanced parentheses in FORMAT string");
85-
}
86-
return maxNesting;
87-
}
88-
8936
template <typename CONTEXT>
9037
int FormatControl<CONTEXT>::GetIntField(
9138
IoErrorHandler &handler, CharType firstCh) {
@@ -98,7 +45,11 @@ int FormatControl<CONTEXT>::GetIntField(
9845
int result{0};
9946
bool negate{ch == '-'};
10047
if (negate || ch == '+') {
101-
firstCh = '\0';
48+
if (firstCh) {
49+
firstCh = '\0';
50+
} else {
51+
++offset_;
52+
}
10253
ch = PeekNext();
10354
}
10455
while (ch >= '0' && ch <= '9') {
@@ -222,6 +173,15 @@ static void HandleControl(CONTEXT &context, char ch, char next, int n) {
222173
template <typename CONTEXT>
223174
int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
224175
int unlimitedLoopCheck{-1};
176+
// Do repetitions remain on an unparenthesized data edit?
177+
while (height_ > 1 && format_[stack_[height_ - 1].start] != '(') {
178+
offset_ = stack_[height_ - 1].start;
179+
int repeat{stack_[height_ - 1].remaining};
180+
--height_;
181+
if (repeat > 0) {
182+
return repeat;
183+
}
184+
}
225185
while (true) {
226186
std::optional<int> repeat;
227187
bool unlimited{false};
@@ -242,16 +202,18 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
242202
unlimited = true;
243203
ch = GetNextChar(context);
244204
if (ch != '(') {
245-
context.SignalError(IostatErrorInFormat,
246-
"Invalid FORMAT: '*' may appear only before '('");
205+
ReportBadFormat(context,
206+
"Invalid FORMAT: '*' may appear only before '('",
207+
maybeReversionPoint);
247208
return 0;
248209
}
249210
}
250211
ch = Capitalize(ch);
251212
if (ch == '(') {
252213
if (height_ >= maxHeight_) {
253-
context.SignalError(IostatErrorInFormat,
254-
"FORMAT stack overflow: too many nested parentheses");
214+
ReportBadFormat(context,
215+
"FORMAT stack overflow: too many nested parentheses",
216+
maybeReversionPoint);
255217
return 0;
256218
}
257219
stack_[height_].start = offset_ - 1; // the '('
@@ -271,11 +233,11 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
271233
// Subtle point (F'2018 13.4 para 9): tha last parenthesized group
272234
// at height 1 becomes the restart point after control reaches the
273235
// end of the format, including its repeat count.
274-
stack_[0].start = maybeReversionPoint - 1;
236+
stack_[0].start = maybeReversionPoint;
275237
}
276238
++height_;
277239
} else if (height_ == 0) {
278-
context.SignalError(IostatErrorInFormat, "FORMAT lacks initial '('");
240+
ReportBadFormat(context, "FORMAT lacks initial '('", maybeReversionPoint);
279241
return 0;
280242
} else if (ch == ')') {
281243
if (height_ == 1) {
@@ -284,12 +246,16 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
284246
}
285247
context.AdvanceRecord(); // implied / before rightmost )
286248
}
287-
auto restart{stack_[height_ - 1].start + 1};
249+
auto restart{stack_[height_ - 1].start};
250+
if (format_[restart] == '(') {
251+
++restart;
252+
}
288253
if (stack_[height_ - 1].remaining == Iteration::unlimited) {
289254
offset_ = restart;
290255
if (offset_ == unlimitedLoopCheck) {
291-
context.SignalError(IostatErrorInFormat,
292-
"Unlimited repetition in FORMAT lacks data edit descriptors");
256+
ReportBadFormat(context,
257+
"Unlimited repetition in FORMAT lacks data edit descriptors",
258+
restart);
293259
}
294260
} else if (stack_[height_ - 1].remaining-- > 0) {
295261
offset_ = restart;
@@ -304,8 +270,9 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
304270
++offset_;
305271
}
306272
if (offset_ >= formatLength_) {
307-
context.SignalError(IostatErrorInFormat,
308-
"FORMAT missing closing quote on character literal");
273+
ReportBadFormat(context,
274+
"FORMAT missing closing quote on character literal",
275+
maybeReversionPoint);
309276
return 0;
310277
}
311278
++offset_;
@@ -322,8 +289,8 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
322289
} else if (ch == 'H') {
323290
// 9HHOLLERITH
324291
if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) {
325-
context.SignalError(
326-
IostatErrorInFormat, "Invalid width on Hollerith in FORMAT");
292+
ReportBadFormat(context, "Invalid width on Hollerith in FORMAT",
293+
maybeReversionPoint);
327294
return 0;
328295
}
329296
context.Emit(format_ + offset_, static_cast<std::size_t>(*repeat));
@@ -364,8 +331,8 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
364331
// TODO: any other raw characters?
365332
context.Emit(format_ + offset_ - 1, 1);
366333
} else {
367-
context.SignalError(IostatErrorInFormat,
368-
"Invalid character '%c' in FORMAT", static_cast<char>(ch));
334+
ReportBadFormat(
335+
context, "Invalid character in FORMAT", maybeReversionPoint);
369336
return 0;
370337
}
371338
}
@@ -410,11 +377,9 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
410377
}
411378
}
412379
if (!ok) {
413-
context.SignalError(
414-
IostatErrorInFormat, "Unclosed DT'iotype' in FORMAT");
380+
ReportBadFormat(context, "Unclosed DT'iotype' in FORMAT", start);
415381
} else if (tooLong) {
416-
context.SignalError(
417-
IostatErrorInFormat, "Excessive DT'iotype' in FORMAT");
382+
ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start);
418383
}
419384
}
420385
if (PeekNext() == '(') {
@@ -434,11 +399,9 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
434399
}
435400
}
436401
if (!ok) {
437-
context.SignalError(
438-
IostatErrorInFormat, "Unclosed DT(v_list) in FORMAT");
402+
ReportBadFormat(context, "Unclosed DT(v_list) in FORMAT", start);
439403
} else if (tooLong) {
440-
context.SignalError(
441-
IostatErrorInFormat, "Excessive DT(v_list) in FORMAT");
404+
ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start);
442405
}
443406
}
444407
}
@@ -460,27 +423,13 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
460423
}
461424
}
462425
edit.modes = context.mutableModes();
463-
464426
// Handle repeated nonparenthesized edit descriptors
427+
edit.repeat = std::min(repeat, maxRepeat); // 0 if maxRepeat==0
465428
if (repeat > maxRepeat) {
466429
stack_[height_].start = start; // after repeat count
467-
stack_[height_].remaining = repeat; // full count
430+
stack_[height_].remaining = repeat - edit.repeat;
468431
++height_;
469432
}
470-
edit.repeat = std::min(1, maxRepeat); // 0 if maxRepeat==0
471-
if (height_ > 1) { // Subtle: stack_[0].start doesn't necessarily point to '('
472-
int start{stack_[height_ - 1].start};
473-
if (format_[start] != '(') {
474-
if (stack_[height_ - 1].remaining > maxRepeat) {
475-
edit.repeat = maxRepeat;
476-
stack_[height_ - 1].remaining -= maxRepeat;
477-
offset_ = start; // repeat same edit descriptor next time
478-
} else {
479-
edit.repeat = stack_[height_ - 1].remaining;
480-
--height_;
481-
}
482-
}
483-
}
484433
return edit;
485434
}
486435

flang/runtime/format.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ template <typename CONTEXT> class FormatControl {
8686
FormatControl(const Terminator &, const CharType *format,
8787
std::size_t formatLength, int maxHeight = maxMaxHeight);
8888

89-
// Determines the max parenthesis nesting level by scanning and validating
90-
// the FORMAT string.
91-
static int GetMaxParenthesisNesting(
92-
IoErrorHandler &, const CharType *format, std::size_t formatLength);
93-
9489
// For attempting to allocate in a user-supplied stack area
9590
static std::size_t GetNeededSize(int maxHeight) {
9691
return sizeof(FormatControl) -
@@ -146,6 +141,15 @@ template <typename CONTEXT> class FormatControl {
146141
return ch >= 'a' && ch <= 'z' ? ch + 'A' - 'a' : ch;
147142
}
148143

144+
void ReportBadFormat(Context &context, const char *msg, int offset) const {
145+
if constexpr (std::is_same_v<CharType, char>) {
146+
context.SignalError(IostatErrorInFormat,
147+
"%s; at offset %d in format '%s'", msg, offset, format_);
148+
} else {
149+
context.SignalError(IostatErrorInFormat, "%s; at offset %d", msg, offset);
150+
}
151+
}
152+
149153
// Data members are arranged and typed so as to reduce size.
150154
// This structure may be allocated in stack space loaned by the
151155
// user program for internal I/O.

flang/runtime/io-stmt.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ class IoStatementState {
8787
void BackspaceRecord();
8888
void HandleRelativePosition(std::int64_t);
8989
void HandleAbsolutePosition(std::int64_t); // for r* in list I/O
90-
std::optional<DataEdit> GetNextDataEdit(int = 1);
90+
std::optional<DataEdit> GetNextDataEdit(int maxRepeat = 1);
9191
ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
9292
bool BeginReadingRecord();
9393
void FinishReadingRecord();
@@ -287,7 +287,8 @@ struct IoStatementBase : public IoErrorHandler {
287287
void BackspaceRecord();
288288
void HandleRelativePosition(std::int64_t);
289289
void HandleAbsolutePosition(std::int64_t);
290-
std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
290+
std::optional<DataEdit> GetNextDataEdit(
291+
IoStatementState &, int maxRepeat = 1);
291292
ExternalFileUnit *GetExternalFileUnit() const;
292293
bool BeginReadingRecord();
293294
void FinishReadingRecord();

0 commit comments

Comments
 (0)