1//===-- runtime/descriptor-io.h ---------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef FORTRAN_RUNTIME_DESCRIPTOR_IO_H_
10#define FORTRAN_RUNTIME_DESCRIPTOR_IO_H_
11
12// Implementation of I/O data list item transfers based on descriptors.
13// (All I/O items come through here so that the code is exercised for test;
14// some scalar I/O data transfer APIs could be changed to bypass their use
15// of descriptors in the future for better efficiency.)
16
17#include "edit-input.h"
18#include "edit-output.h"
19#include "io-stmt.h"
20#include "namelist.h"
21#include "terminator.h"
22#include "type-info.h"
23#include "unit.h"
24#include "flang/Common/uint128.h"
25#include "flang/Runtime/cpp-type.h"
26#include "flang/Runtime/descriptor.h"
27
28namespace Fortran::runtime::io::descr {
29template <typename A>
30inline A &ExtractElement(IoStatementState &io, const Descriptor &descriptor,
31 const SubscriptValue subscripts[]) {
32 A *p{descriptor.Element<A>(subscripts)};
33 if (!p) {
34 io.GetIoErrorHandler().Crash("Bad address for I/O item -- null base "
35 "address or subscripts out of range");
36 }
37 return *p;
38}
39
40// Per-category descriptor-based I/O templates
41
42// TODO (perhaps as a nontrivial but small starter project): implement
43// automatic repetition counts, like "10*3.14159", for list-directed and
44// NAMELIST array output.
45
46template <int KIND, Direction DIR>
47inline bool FormattedIntegerIO(
48 IoStatementState &io, const Descriptor &descriptor) {
49 std::size_t numElements{descriptor.Elements()};
50 SubscriptValue subscripts[maxRank];
51 descriptor.GetLowerBounds(subscripts);
52 using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
53 bool anyInput{false};
54 for (std::size_t j{0}; j < numElements; ++j) {
55 if (auto edit{io.GetNextDataEdit()}) {
56 IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
57 if constexpr (DIR == Direction::Output) {
58 if (!EditIntegerOutput<KIND>(io, *edit, x)) {
59 return false;
60 }
61 } else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
62 if (EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
63 anyInput = true;
64 } else {
65 return anyInput && edit->IsNamelist();
66 }
67 }
68 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
69 io.GetIoErrorHandler().Crash(
70 "FormattedIntegerIO: subscripts out of bounds");
71 }
72 } else {
73 return false;
74 }
75 }
76 return true;
77}
78
79template <int KIND, Direction DIR>
80inline bool FormattedRealIO(
81 IoStatementState &io, const Descriptor &descriptor) {
82 std::size_t numElements{descriptor.Elements()};
83 SubscriptValue subscripts[maxRank];
84 descriptor.GetLowerBounds(subscripts);
85 using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
86 bool anyInput{false};
87 for (std::size_t j{0}; j < numElements; ++j) {
88 if (auto edit{io.GetNextDataEdit()}) {
89 RawType &x{ExtractElement<RawType>(io, descriptor, subscripts)};
90 if constexpr (DIR == Direction::Output) {
91 if (!RealOutputEditing<KIND>{io, x}.Edit(*edit)) {
92 return false;
93 }
94 } else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
95 if (EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
96 anyInput = true;
97 } else {
98 return anyInput && edit->IsNamelist();
99 }
100 }
101 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
102 io.GetIoErrorHandler().Crash(
103 "FormattedRealIO: subscripts out of bounds");
104 }
105 } else {
106 return false;
107 }
108 }
109 return true;
110}
111
112template <int KIND, Direction DIR>
113inline bool FormattedComplexIO(
114 IoStatementState &io, const Descriptor &descriptor) {
115 std::size_t numElements{descriptor.Elements()};
116 SubscriptValue subscripts[maxRank];
117 descriptor.GetLowerBounds(subscripts);
118 bool isListOutput{
119 io.get_if<ListDirectedStatementState<Direction::Output>>() != nullptr};
120 using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
121 bool anyInput{false};
122 for (std::size_t j{0}; j < numElements; ++j) {
123 RawType *x{&ExtractElement<RawType>(io, descriptor, subscripts)};
124 if (isListOutput) {
125 DataEdit rEdit, iEdit;
126 rEdit.descriptor = DataEdit::ListDirectedRealPart;
127 iEdit.descriptor = DataEdit::ListDirectedImaginaryPart;
128 rEdit.modes = iEdit.modes = io.mutableModes();
129 if (!RealOutputEditing<KIND>{io, x[0]}.Edit(rEdit) ||
130 !RealOutputEditing<KIND>{io, x[1]}.Edit(iEdit)) {
131 return false;
132 }
133 } else {
134 for (int k{0}; k < 2; ++k, ++x) {
135 auto edit{io.GetNextDataEdit()};
136 if (!edit) {
137 return false;
138 } else if constexpr (DIR == Direction::Output) {
139 if (!RealOutputEditing<KIND>{io, *x}.Edit(*edit)) {
140 return false;
141 }
142 } else if (edit->descriptor == DataEdit::ListDirectedNullValue) {
143 break;
144 } else if (EditRealInput<KIND>(
145 io, *edit, reinterpret_cast<void *>(x))) {
146 anyInput = true;
147 } else {
148 return anyInput && edit->IsNamelist();
149 }
150 }
151 }
152 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
153 io.GetIoErrorHandler().Crash(
154 "FormattedComplexIO: subscripts out of bounds");
155 }
156 }
157 return true;
158}
159
160template <typename A, Direction DIR>
161inline bool FormattedCharacterIO(
162 IoStatementState &io, const Descriptor &descriptor) {
163 std::size_t numElements{descriptor.Elements()};
164 SubscriptValue subscripts[maxRank];
165 descriptor.GetLowerBounds(subscripts);
166 std::size_t length{descriptor.ElementBytes() / sizeof(A)};
167 auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
168 bool anyInput{false};
169 for (std::size_t j{0}; j < numElements; ++j) {
170 A *x{&ExtractElement<A>(io, descriptor, subscripts)};
171 if (listOutput) {
172 if (!ListDirectedCharacterOutput(io, *listOutput, x, length)) {
173 return false;
174 }
175 } else if (auto edit{io.GetNextDataEdit()}) {
176 if constexpr (DIR == Direction::Output) {
177 if (!EditCharacterOutput(io, *edit, x, length)) {
178 return false;
179 }
180 } else { // input
181 if (edit->descriptor != DataEdit::ListDirectedNullValue) {
182 if (EditCharacterInput(io, *edit, x, length)) {
183 anyInput = true;
184 } else {
185 return anyInput && edit->IsNamelist();
186 }
187 }
188 }
189 } else {
190 return false;
191 }
192 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
193 io.GetIoErrorHandler().Crash(
194 "FormattedCharacterIO: subscripts out of bounds");
195 }
196 }
197 return true;
198}
199
200template <int KIND, Direction DIR>
201inline bool FormattedLogicalIO(
202 IoStatementState &io, const Descriptor &descriptor) {
203 std::size_t numElements{descriptor.Elements()};
204 SubscriptValue subscripts[maxRank];
205 descriptor.GetLowerBounds(subscripts);
206 auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
207 using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
208 bool anyInput{false};
209 for (std::size_t j{0}; j < numElements; ++j) {
210 IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
211 if (listOutput) {
212 if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) {
213 return false;
214 }
215 } else if (auto edit{io.GetNextDataEdit()}) {
216 if constexpr (DIR == Direction::Output) {
217 if (!EditLogicalOutput(io, *edit, x != 0)) {
218 return false;
219 }
220 } else {
221 if (edit->descriptor != DataEdit::ListDirectedNullValue) {
222 bool truth{};
223 if (EditLogicalInput(io, *edit, truth)) {
224 x = truth;
225 anyInput = true;
226 } else {
227 return anyInput && edit->IsNamelist();
228 }
229 }
230 }
231 } else {
232 return false;
233 }
234 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
235 io.GetIoErrorHandler().Crash(
236 "FormattedLogicalIO: subscripts out of bounds");
237 }
238 }
239 return true;
240}
241
242template <Direction DIR>
243static bool DescriptorIO(IoStatementState &, const Descriptor &,
244 const NonTbpDefinedIoTable * = nullptr);
245
246// For intrinsic (not defined) derived type I/O, formatted & unformatted
247template <Direction DIR>
248static bool DefaultComponentIO(IoStatementState &io,
249 const typeInfo::Component &component, const Descriptor &origDescriptor,
250 const SubscriptValue origSubscripts[], Terminator &terminator,
251 const NonTbpDefinedIoTable *table) {
252 if (component.genre() == typeInfo::Component::Genre::Data) {
253 // Create a descriptor for the component
254 StaticDescriptor<maxRank, true, 16 /*?*/> statDesc;
255 Descriptor &desc{statDesc.descriptor()};
256 component.CreatePointerDescriptor(
257 desc, origDescriptor, terminator, origSubscripts);
258 return DescriptorIO<DIR>(io, desc, table);
259 } else {
260 // Component is itself a descriptor
261 char *pointer{
262 origDescriptor.Element<char>(origSubscripts) + component.offset()};
263 RUNTIME_CHECK(
264 terminator, component.genre() == typeInfo::Component::Genre::Automatic);
265 const Descriptor &compDesc{*reinterpret_cast<const Descriptor *>(pointer)};
266 return DescriptorIO<DIR>(io, compDesc, table);
267 }
268}
269
270template <Direction DIR>
271static 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
296template <Direction DIR>
297static bool DefaultComponentwiseUnformattedIO(IoStatementState &io,
298 const Descriptor &descriptor, const typeInfo::DerivedType &type,
299 const NonTbpDefinedIoTable *table) {
300 IoErrorHandler &handler{io.GetIoErrorHandler()};
301 const Descriptor &compArray{type.component()};
302 RUNTIME_CHECK(handler, compArray.rank() == 1);
303 std::size_t numComponents{compArray.Elements()};
304 std::size_t numElements{descriptor.Elements()};
305 SubscriptValue subscripts[maxRank];
306 descriptor.GetLowerBounds(subscripts);
307 for (std::size_t j{0}; j < numElements;
308 ++j, descriptor.IncrementSubscripts(subscripts)) {
309 SubscriptValue at[maxRank];
310 compArray.GetLowerBounds(at);
311 for (std::size_t k{0}; k < numComponents;
312 ++k, compArray.IncrementSubscripts(at)) {
313 const typeInfo::Component &component{
314 *compArray.Element<typeInfo::Component>(at)};
315 if (!DefaultComponentIO<DIR>(
316 io, component, descriptor, subscripts, handler, table)) {
317 return false;
318 }
319 }
320 }
321 return true;
322}
323
324std::optional<bool> DefinedFormattedIo(IoStatementState &, const Descriptor &,
325 const typeInfo::DerivedType &, const typeInfo::SpecialBinding &,
326 const SubscriptValue[]);
327
328template <Direction DIR>
329static bool FormattedDerivedTypeIO(IoStatementState &io,
330 const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {
331 IoErrorHandler &handler{io.GetIoErrorHandler()};
332 // Derived type information must be present for formatted I/O.
333 const DescriptorAddendum *addendum{descriptor.Addendum()};
334 RUNTIME_CHECK(handler, addendum != nullptr);
335 const typeInfo::DerivedType *type{addendum->derivedType()};
336 RUNTIME_CHECK(handler, type != nullptr);
337 std::optional<typeInfo::SpecialBinding> nonTbpSpecial;
338 const typeInfo::SpecialBinding *special{nullptr};
339 if (table) {
340 if (const auto *definedIo{table->Find(*type,
341 DIR == Direction::Input ? common::DefinedIo::ReadFormatted
342 : common::DefinedIo::WriteFormatted)}) {
343 if (definedIo->subroutine) {
344 nonTbpSpecial.emplace(DIR == Direction::Input
345 ? typeInfo::SpecialBinding::Which::ReadFormatted
346 : typeInfo::SpecialBinding::Which::WriteFormatted,
347 definedIo->subroutine, definedIo->isDtvArgPolymorphic, false,
348 false);
349 special = &*nonTbpSpecial;
350 }
351 }
352 }
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;
360 }
361 }
362 }
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;
385}
386
387bool DefinedUnformattedIo(IoStatementState &, const Descriptor &,
388 const typeInfo::DerivedType &, const typeInfo::SpecialBinding &);
389
390// Unformatted I/O
391template <Direction DIR>
392static bool UnformattedDescriptorIO(IoStatementState &io,
393 const Descriptor &descriptor, const NonTbpDefinedIoTable *table = nullptr) {
394 IoErrorHandler &handler{io.GetIoErrorHandler()};
395 const DescriptorAddendum *addendum{descriptor.Addendum()};
396 if (const typeInfo::DerivedType *
397 type{addendum ? addendum->derivedType() : nullptr}) {
398 // derived type unformatted I/O
399 if (table) {
400 if (const auto *definedIo{table->Find(*type,
401 DIR == Direction::Input ? common::DefinedIo::ReadUnformatted
402 : common::DefinedIo::WriteUnformatted)}) {
403 if (definedIo->subroutine) {
404 typeInfo::SpecialBinding special{DIR == Direction::Input
405 ? typeInfo::SpecialBinding::Which::ReadUnformatted
406 : typeInfo::SpecialBinding::Which::WriteUnformatted,
407 definedIo->subroutine, definedIo->isDtvArgPolymorphic, false,
408 false};
409 if (std::optional<bool> wasDefined{
410 DefinedUnformattedIo(io, descriptor, *type, special)}) {
411 return *wasDefined;
412 }
413 } else {
414 return DefaultComponentwiseUnformattedIO<DIR>(
415 io, descriptor, *type, table);
416 }
417 }
418 }
419 if (const typeInfo::SpecialBinding *
420 special{type->FindSpecialBinding(DIR == Direction::Input
421 ? typeInfo::SpecialBinding::Which::ReadUnformatted
422 : typeInfo::SpecialBinding::Which::WriteUnformatted)}) {
423 if (!table || !table->ignoreNonTbpEntries || special->isTypeBound()) {
424 // defined derived type unformatted I/O
425 return DefinedUnformattedIo(io, descriptor, *type, *special);
426 }
427 }
428 // Default derived type unformatted I/O
429 // TODO: If no component at any level has defined READ or WRITE
430 // (as appropriate), the elements are contiguous, and no byte swapping
431 // is active, do a block transfer via the code below.
432 return DefaultComponentwiseUnformattedIO<DIR>(io, descriptor, *type, table);
433 } else {
434 // intrinsic type unformatted I/O
435 auto *externalUnf{io.get_if<ExternalUnformattedIoStatementState<DIR>>()};
436 auto *childUnf{io.get_if<ChildUnformattedIoStatementState<DIR>>()};
437 auto *inq{
438 DIR == Direction::Output ? io.get_if<InquireIOLengthState>() : nullptr};
439 RUNTIME_CHECK(handler, externalUnf || childUnf || inq);
440 std::size_t elementBytes{descriptor.ElementBytes()};
441 std::size_t numElements{descriptor.Elements()};
442 std::size_t swappingBytes{elementBytes};
443 if (auto maybeCatAndKind{descriptor.type().GetCategoryAndKind()}) {
444 // Byte swapping units can be smaller than elements, namely
445 // for COMPLEX and CHARACTER.
446 if (maybeCatAndKind->first == TypeCategory::Character) {
447 // swap each character position independently
448 swappingBytes = maybeCatAndKind->second; // kind
449 } else if (maybeCatAndKind->first == TypeCategory::Complex) {
450 // swap real and imaginary components independently
451 swappingBytes /= 2;
452 }
453 }
454 SubscriptValue subscripts[maxRank];
455 descriptor.GetLowerBounds(subscripts);
456 using CharType =
457 std::conditional_t<DIR == Direction::Output, const char, char>;
458 auto Transfer{[=](CharType &x, std::size_t totalBytes) -> bool {
459 if constexpr (DIR == Direction::Output) {
460 return externalUnf ? externalUnf->Emit(&x, totalBytes, swappingBytes)
461 : childUnf ? childUnf->Emit(&x, totalBytes, swappingBytes)
462 : inq->Emit(&x, bytes: totalBytes, elementBytes: swappingBytes);
463 } else {
464 return externalUnf ? externalUnf->Receive(&x, totalBytes, swappingBytes)
465 : childUnf->Receive(&x, totalBytes, swappingBytes);
466 }
467 }};
468 bool swapEndianness{externalUnf && externalUnf->unit().swapEndianness()};
469 if (!swapEndianness &&
470 descriptor.IsContiguous()) { // contiguous unformatted I/O
471 char &x{ExtractElement<char>(io, descriptor, subscripts)};
472 return Transfer(x, numElements * elementBytes);
473 } else { // non-contiguous or byte-swapped intrinsic type unformatted I/O
474 for (std::size_t j{0}; j < numElements; ++j) {
475 char &x{ExtractElement<char>(io, descriptor, subscripts)};
476 if (!Transfer(x, elementBytes)) {
477 return false;
478 }
479 if (!descriptor.IncrementSubscripts(subscripts) &&
480 j + 1 < numElements) {
481 handler.Crash("DescriptorIO: subscripts out of bounds");
482 }
483 }
484 return true;
485 }
486 }
487}
488
489template <Direction DIR>
490static bool DescriptorIO(IoStatementState &io, const Descriptor &descriptor,
491 const NonTbpDefinedIoTable *table) {
492 IoErrorHandler &handler{io.GetIoErrorHandler()};
493 if (handler.InError()) {
494 return false;
495 }
496 if (!io.get_if<IoDirectionState<DIR>>()) {
497 io.GetIoErrorHandler().Crash(
498 "DescriptorIO() called for wrong I/O direction");
499 return false;
500 }
501 if constexpr (DIR == Direction::Input) {
502 if (!io.BeginReadingRecord()) {
503 return false;
504 }
505 }
506 if (!io.get_if<FormattedIoStatementState<DIR>>()) {
507 return UnformattedDescriptorIO<DIR>(io, descriptor, table);
508 }
509 if (auto catAndKind{descriptor.type().GetCategoryAndKind()}) {
510 TypeCategory cat{catAndKind->first};
511 int kind{catAndKind->second};
512 switch (cat) {
513 case TypeCategory::Integer:
514 switch (kind) {
515 case 1:
516 return FormattedIntegerIO<1, DIR>(io, descriptor);
517 case 2:
518 return FormattedIntegerIO<2, DIR>(io, descriptor);
519 case 4:
520 return FormattedIntegerIO<4, DIR>(io, descriptor);
521 case 8:
522 return FormattedIntegerIO<8, DIR>(io, descriptor);
523 case 16:
524 return FormattedIntegerIO<16, DIR>(io, descriptor);
525 default:
526 handler.Crash(
527 "not yet implemented: INTEGER(KIND=%d) in formatted IO", kind);
528 return false;
529 }
530 case TypeCategory::Real:
531 switch (kind) {
532 case 2:
533 return FormattedRealIO<2, DIR>(io, descriptor);
534 case 3:
535 return FormattedRealIO<3, DIR>(io, descriptor);
536 case 4:
537 return FormattedRealIO<4, DIR>(io, descriptor);
538 case 8:
539 return FormattedRealIO<8, DIR>(io, descriptor);
540 case 10:
541 return FormattedRealIO<10, DIR>(io, descriptor);
542 // TODO: case double/double
543 case 16:
544 return FormattedRealIO<16, DIR>(io, descriptor);
545 default:
546 handler.Crash(
547 "not yet implemented: REAL(KIND=%d) in formatted IO", kind);
548 return false;
549 }
550 case TypeCategory::Complex:
551 switch (kind) {
552 case 2:
553 return FormattedComplexIO<2, DIR>(io, descriptor);
554 case 3:
555 return FormattedComplexIO<3, DIR>(io, descriptor);
556 case 4:
557 return FormattedComplexIO<4, DIR>(io, descriptor);
558 case 8:
559 return FormattedComplexIO<8, DIR>(io, descriptor);
560 case 10:
561 return FormattedComplexIO<10, DIR>(io, descriptor);
562 // TODO: case double/double
563 case 16:
564 return FormattedComplexIO<16, DIR>(io, descriptor);
565 default:
566 handler.Crash(
567 "not yet implemented: COMPLEX(KIND=%d) in formatted IO", kind);
568 return false;
569 }
570 case TypeCategory::Character:
571 switch (kind) {
572 case 1:
573 return FormattedCharacterIO<char, DIR>(io, descriptor);
574 case 2:
575 return FormattedCharacterIO<char16_t, DIR>(io, descriptor);
576 case 4:
577 return FormattedCharacterIO<char32_t, DIR>(io, descriptor);
578 default:
579 handler.Crash(
580 "not yet implemented: CHARACTER(KIND=%d) in formatted IO", kind);
581 return false;
582 }
583 case TypeCategory::Logical:
584 switch (kind) {
585 case 1:
586 return FormattedLogicalIO<1, DIR>(io, descriptor);
587 case 2:
588 return FormattedLogicalIO<2, DIR>(io, descriptor);
589 case 4:
590 return FormattedLogicalIO<4, DIR>(io, descriptor);
591 case 8:
592 return FormattedLogicalIO<8, DIR>(io, descriptor);
593 default:
594 handler.Crash(
595 "not yet implemented: LOGICAL(KIND=%d) in formatted IO", kind);
596 return false;
597 }
598 case TypeCategory::Derived:
599 return FormattedDerivedTypeIO<DIR>(io, descriptor, table);
600 }
601 }
602 handler.Crash("DescriptorIO: bad type code (%d) in descriptor",
603 static_cast<int>(descriptor.type().raw()));
604 return false;
605}
606} // namespace Fortran::runtime::io::descr
607#endif // FORTRAN_RUNTIME_DESCRIPTOR_IO_H_
608

source code of flang/runtime/descriptor-io.h