Skip to content

Commit c13f7e1

Browse files
authored
[flang][runtime] Allow already-documented missing 'w' on edit descriptor (llvm#72901)
As already documented in flang/docs/Extensions.md and implemented in the compilation-time format validator, we want to support at execution time the Intel (and presumably also Fujitsu) extension of allowing a missing width on an edit descriptor in a formatted I/O statement. Fixes llvm#72597.
1 parent 13386c6 commit c13f7e1

File tree

2 files changed

+39
-37
lines changed

2 files changed

+39
-37
lines changed

flang/runtime/format-implementation.h

Lines changed: 37 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -416,14 +416,16 @@ std::optional<DataEdit> FormatControl<CONTEXT>::GetNextDataEdit(
416416
int repeat{CueUpNextDataEdit(context)};
417417
auto start{offset_};
418418
DataEdit edit;
419+
edit.modes = context.mutableModes();
420+
// Handle repeated nonparenthesized edit descriptors
421+
edit.repeat = std::min(repeat, maxRepeat); // 0 if maxRepeat==0
422+
if (repeat > maxRepeat) {
423+
stack_[height_].start = start; // after repeat count
424+
stack_[height_].remaining = repeat - edit.repeat;
425+
++height_;
426+
}
419427
edit.descriptor = static_cast<char>(Capitalize(GetNextChar(context)));
420-
if (edit.descriptor == 'E') {
421-
if (auto next{static_cast<char>(Capitalize(PeekNext()))};
422-
next == 'N' || next == 'S' || next == 'X') {
423-
edit.variation = next;
424-
++offset_;
425-
}
426-
} else if (edit.descriptor == 'D' && Capitalize(PeekNext()) == 'T') {
428+
if (edit.descriptor == 'D' && Capitalize(PeekNext()) == 'T') {
427429
// DT['iotype'][(v_list)] defined I/O
428430
edit.descriptor = DataEdit::DefinedDerivedType;
429431
++offset_;
@@ -479,38 +481,37 @@ std::optional<DataEdit> FormatControl<CONTEXT>::GetNextDataEdit(
479481
return std::nullopt;
480482
}
481483
}
482-
}
483-
if (edit.descriptor == 'A' || edit.descriptor == 'L') {
484-
// width is optional for A[w] or L[w]
485-
auto ch{PeekNext()};
486-
if (ch >= '0' && ch <= '9') {
487-
edit.width = GetIntField(context);
488-
}
489-
} else if (edit.descriptor != DataEdit::DefinedDerivedType) {
490-
edit.width = GetIntField(context);
491-
}
492-
if constexpr (std::is_base_of_v<InputStatementState, CONTEXT>) {
493-
if (edit.width.value_or(-1) == 0) {
494-
ReportBadFormat(context, "Input field width is zero", start);
484+
} else { // not DT'iotype'
485+
if (edit.descriptor == 'E') {
486+
if (auto next{static_cast<char>(Capitalize(PeekNext()))};
487+
next == 'N' || next == 'S' || next == 'X') {
488+
edit.variation = next;
489+
++offset_;
490+
}
495491
}
496-
}
497-
if (edit.descriptor != DataEdit::DefinedDerivedType && PeekNext() == '.') {
498-
++offset_;
499-
edit.digits = GetIntField(context);
500-
CharType ch{PeekNext()};
501-
if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') {
502-
++offset_;
503-
edit.expoDigits = GetIntField(context);
492+
// Width is optional for A[w] in the standard and optional
493+
// for Lw in most compilers.
494+
// Intel & (presumably, from bug report) Fujitsu allow
495+
// a missing 'w' & 'd'/'m' for other edit descriptors -- but not
496+
// 'd'/'m' with a missing 'w' -- and so interpret "(E)" as "(E0)".
497+
if (CharType ch{PeekNext()}; (ch >= '0' && ch <= '9') || ch == '.') {
498+
edit.width = GetIntField(context);
499+
if constexpr (std::is_base_of_v<InputStatementState, CONTEXT>) {
500+
if (edit.width.value_or(-1) == 0) {
501+
ReportBadFormat(context, "Input field width is zero", start);
502+
}
503+
}
504+
if (PeekNext() == '.') {
505+
++offset_;
506+
edit.digits = GetIntField(context);
507+
if (CharType ch{PeekNext()};
508+
ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') {
509+
++offset_;
510+
edit.expoDigits = GetIntField(context);
511+
}
512+
}
504513
}
505514
}
506-
edit.modes = context.mutableModes();
507-
// Handle repeated nonparenthesized edit descriptors
508-
edit.repeat = std::min(repeat, maxRepeat); // 0 if maxRepeat==0
509-
if (repeat > maxRepeat) {
510-
stack_[height_].start = start; // after repeat count
511-
stack_[height_].remaining = repeat - edit.repeat;
512-
++height_;
513-
}
514515
return edit;
515516
}
516517

flang/unittests/Runtime/Format.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ TEST(FormatTests, FormatStringTraversal) {
111111
ResultsTy{"I4", "E10.1", "E10.1", "/", "I4", "E10.1", "E10.1", "/",
112112
"I4", "E10.1", "E10.1"},
113113
1},
114+
{1, "(F)", ResultsTy{"F"}, 1}, // missing 'w'
114115
};
115116

116117
for (const auto &[n, format, expect, repeat] : params) {
@@ -170,7 +171,7 @@ TEST(InvalidFormatFailure, MissingPrecision) {
170171
R"(Invalid FORMAT: integer expected at '\)')");
171172
}
172173

173-
TEST(InvalidFormatFailure, MissingFormatWidth) {
174+
TEST(InvalidFormatFailure, MissingFormatWidthWithDigits) {
174175
static constexpr const char *format{"(F.9)"};
175176
static constexpr int repeat{1};
176177

0 commit comments

Comments
 (0)