Skip to content

Commit a6e77fd

Browse files
authored
[flang][runtime] Make defined formatted I/O process format elementally (#74150)
The present implementation of defined formatted I/O is incorrect for arrays in the data item list; it assumes that a DT defined format descriptor (or list-directed/namelist instance) applies to all of the elements in the array. The loop over the elements in the array is within the DefinedFormattedIo() template function that handles defined formatted I/O, not around its calls. This causes only one format list edit descriptor to be used for the whole array, which is of course wrong. Invert this arrangment by performing the per-element looping in at the top level in FormattedDerivedTypeIo() instead. Defined unformatted I/O remains as it was.
1 parent 54397f9 commit a6e77fd

File tree

2 files changed

+74
-45
lines changed

2 files changed

+74
-45
lines changed

flang/runtime/descriptor-io.cpp

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ namespace Fortran::runtime::io::descr {
1414
// Defined formatted I/O (maybe)
1515
std::optional<bool> DefinedFormattedIo(IoStatementState &io,
1616
const Descriptor &descriptor, const typeInfo::DerivedType &derived,
17-
const typeInfo::SpecialBinding &special) {
17+
const typeInfo::SpecialBinding &special,
18+
const SubscriptValue subscripts[]) {
1819
std::optional<DataEdit> peek{io.GetNextDataEdit(0 /*to peek at it*/)};
1920
if (peek &&
2021
(peek->descriptor == DataEdit::DefinedDerivedType ||
@@ -61,9 +62,6 @@ std::optional<bool> DefinedFormattedIo(IoStatementState &io,
6162
// I/O subroutine reads counts towards READ(SIZE=).
6263
startPos = io.InquirePos();
6364
}
64-
std::size_t numElements{descriptor.Elements()};
65-
SubscriptValue subscripts[maxRank];
66-
descriptor.GetLowerBounds(subscripts);
6765
if (special.IsArgDescriptor(0)) {
6866
// "dtv" argument is "class(t)", pass a descriptor
6967
auto *p{special.GetProc<void (*)(const Descriptor &, int &, char *,
@@ -72,25 +70,15 @@ std::optional<bool> DefinedFormattedIo(IoStatementState &io,
7270
Descriptor &elementDesc{elementStatDesc.descriptor()};
7371
elementDesc.Establish(
7472
derived, nullptr, 0, nullptr, CFI_attribute_pointer);
75-
for (; numElements-- > 0; descriptor.IncrementSubscripts(subscripts)) {
76-
elementDesc.set_base_addr(descriptor.Element<char>(subscripts));
77-
p(elementDesc, unit, ioType, vListDesc, ioStat, ioMsg, ioTypeLen,
78-
sizeof ioMsg);
79-
if (ioStat != IostatOk) {
80-
break;
81-
}
82-
}
73+
elementDesc.set_base_addr(descriptor.Element<char>(subscripts));
74+
p(elementDesc, unit, ioType, vListDesc, ioStat, ioMsg, ioTypeLen,
75+
sizeof ioMsg);
8376
} else {
8477
// "dtv" argument is "type(t)", pass a raw pointer
8578
auto *p{special.GetProc<void (*)(const void *, int &, char *,
8679
const Descriptor &, int &, char *, std::size_t, std::size_t)>()};
87-
for (; numElements-- > 0; descriptor.IncrementSubscripts(subscripts)) {
88-
p(descriptor.Element<char>(subscripts), unit, ioType, vListDesc, ioStat,
89-
ioMsg, ioTypeLen, sizeof ioMsg);
90-
if (ioStat != IostatOk) {
91-
break;
92-
}
93-
}
80+
p(descriptor.Element<char>(subscripts), unit, ioType, vListDesc, ioStat,
81+
ioMsg, ioTypeLen, sizeof ioMsg);
9482
}
9583
handler.Forward(ioStat, ioMsg, sizeof ioMsg);
9684
external->PopChildIo(child);

flang/runtime/descriptor-io.h

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,33 @@ static bool DefaultComponentIO(IoStatementState &io,
268268
}
269269

270270
template <Direction DIR>
271-
static bool DefaultComponentwiseIO(IoStatementState &io,
271+
static bool DefaultComponentwiseFormattedIO(IoStatementState &io,
272+
const Descriptor &descriptor, const typeInfo::DerivedType &type,
273+
const NonTbpDefinedIoTable *table, const SubscriptValue subscripts[]) {
274+
IoErrorHandler &handler{io.GetIoErrorHandler()};
275+
const Descriptor &compArray{type.component()};
276+
RUNTIME_CHECK(handler, compArray.rank() == 1);
277+
std::size_t numComponents{compArray.Elements()};
278+
SubscriptValue at[maxRank];
279+
compArray.GetLowerBounds(at);
280+
for (std::size_t k{0}; k < numComponents;
281+
++k, compArray.IncrementSubscripts(at)) {
282+
const typeInfo::Component &component{
283+
*compArray.Element<typeInfo::Component>(at)};
284+
if (!DefaultComponentIO<DIR>(
285+
io, component, descriptor, subscripts, handler, table)) {
286+
// Return true for NAMELIST input if any component appeared.
287+
auto *listInput{
288+
io.get_if<ListDirectedStatementState<Direction::Input>>()};
289+
return DIR == Direction::Input && k > 0 && listInput &&
290+
listInput->inNamelistSequence();
291+
}
292+
}
293+
return true;
294+
}
295+
296+
template <Direction DIR>
297+
static bool DefaultComponentwiseUnformattedIO(IoStatementState &io,
272298
const Descriptor &descriptor, const typeInfo::DerivedType &type,
273299
const NonTbpDefinedIoTable *table) {
274300
IoErrorHandler &handler{io.GetIoErrorHandler()};
@@ -288,19 +314,16 @@ static bool DefaultComponentwiseIO(IoStatementState &io,
288314
*compArray.Element<typeInfo::Component>(at)};
289315
if (!DefaultComponentIO<DIR>(
290316
io, component, descriptor, subscripts, handler, table)) {
291-
// Truncated nonempty namelist input sequence?
292-
auto *listInput{
293-
io.get_if<ListDirectedStatementState<Direction::Input>>()};
294-
return DIR == Direction::Input && (j > 0 || k > 0) && listInput &&
295-
listInput->inNamelistSequence();
317+
return false;
296318
}
297319
}
298320
}
299321
return true;
300322
}
301323

302324
std::optional<bool> DefinedFormattedIo(IoStatementState &, const Descriptor &,
303-
const typeInfo::DerivedType &, const typeInfo::SpecialBinding &);
325+
const typeInfo::DerivedType &, const typeInfo::SpecialBinding &,
326+
const SubscriptValue[]);
304327

305328
template <Direction DIR>
306329
static bool FormattedDerivedTypeIO(IoStatementState &io,
@@ -311,37 +334,54 @@ static bool FormattedDerivedTypeIO(IoStatementState &io,
311334
RUNTIME_CHECK(handler, addendum != nullptr);
312335
const typeInfo::DerivedType *type{addendum->derivedType()};
313336
RUNTIME_CHECK(handler, type != nullptr);
337+
std::optional<typeInfo::SpecialBinding> nonTbpSpecial;
338+
const typeInfo::SpecialBinding *special{nullptr};
314339
if (table) {
315340
if (const auto *definedIo{table->Find(*type,
316341
DIR == Direction::Input ? common::DefinedIo::ReadFormatted
317342
: common::DefinedIo::WriteFormatted)}) {
318343
if (definedIo->subroutine) {
319-
typeInfo::SpecialBinding special{DIR == Direction::Input
344+
nonTbpSpecial.emplace(DIR == Direction::Input
320345
? typeInfo::SpecialBinding::Which::ReadFormatted
321346
: typeInfo::SpecialBinding::Which::WriteFormatted,
322347
definedIo->subroutine, definedIo->isDtvArgPolymorphic, false,
323-
false};
324-
if (std::optional<bool> wasDefined{
325-
DefinedFormattedIo(io, descriptor, *type, special)}) {
326-
return *wasDefined;
327-
}
328-
} else {
329-
return DefaultComponentwiseIO<DIR>(io, descriptor, *type, table);
348+
false);
349+
special = &*nonTbpSpecial;
330350
}
331351
}
332352
}
333-
if (const typeInfo::SpecialBinding *
334-
special{type->FindSpecialBinding(DIR == Direction::Input
335-
? typeInfo::SpecialBinding::Which::ReadFormatted
336-
: typeInfo::SpecialBinding::Which::WriteFormatted)}) {
337-
if (!table || !table->ignoreNonTbpEntries || special->isTypeBound()) {
338-
if (std::optional<bool> wasDefined{
339-
DefinedFormattedIo(io, descriptor, *type, *special)}) {
340-
return *wasDefined; // defined I/O was applied
353+
if (!special) {
354+
if (const typeInfo::SpecialBinding *
355+
binding{type->FindSpecialBinding(DIR == Direction::Input
356+
? typeInfo::SpecialBinding::Which::ReadFormatted
357+
: typeInfo::SpecialBinding::Which::WriteFormatted)}) {
358+
if (!table || !table->ignoreNonTbpEntries || binding->isTypeBound()) {
359+
special = binding;
341360
}
342361
}
343362
}
344-
return DefaultComponentwiseIO<DIR>(io, descriptor, *type, table);
363+
SubscriptValue subscripts[maxRank];
364+
descriptor.GetLowerBounds(subscripts);
365+
std::size_t numElements{descriptor.Elements()};
366+
for (std::size_t j{0}; j < numElements;
367+
++j, descriptor.IncrementSubscripts(subscripts)) {
368+
std::optional<bool> result;
369+
if (special) {
370+
result = DefinedFormattedIo(io, descriptor, *type, *special, subscripts);
371+
}
372+
if (!result) {
373+
result = DefaultComponentwiseFormattedIO<DIR>(
374+
io, descriptor, *type, table, subscripts);
375+
}
376+
if (!result.value()) {
377+
// Return true for NAMELIST input if we got anything.
378+
auto *listInput{
379+
io.get_if<ListDirectedStatementState<Direction::Input>>()};
380+
return DIR == Direction::Input && j > 0 && listInput &&
381+
listInput->inNamelistSequence();
382+
}
383+
}
384+
return true;
345385
}
346386

347387
bool DefinedUnformattedIo(IoStatementState &, const Descriptor &,
@@ -371,7 +411,8 @@ static bool UnformattedDescriptorIO(IoStatementState &io,
371411
return *wasDefined;
372412
}
373413
} else {
374-
return DefaultComponentwiseIO<DIR>(io, descriptor, *type, table);
414+
return DefaultComponentwiseUnformattedIO<DIR>(
415+
io, descriptor, *type, table);
375416
}
376417
}
377418
}
@@ -388,7 +429,7 @@ static bool UnformattedDescriptorIO(IoStatementState &io,
388429
// TODO: If no component at any level has defined READ or WRITE
389430
// (as appropriate), the elements are contiguous, and no byte swapping
390431
// is active, do a block transfer via the code below.
391-
return DefaultComponentwiseIO<DIR>(io, descriptor, *type, table);
432+
return DefaultComponentwiseUnformattedIO<DIR>(io, descriptor, *type, table);
392433
} else {
393434
// intrinsic type unformatted I/O
394435
auto *externalUnf{io.get_if<ExternalUnformattedIoStatementState<DIR>>()};

0 commit comments

Comments
 (0)