[Support] Remove unused includes (NFC) (#116752)
[llvm-project.git] / flang / runtime / descriptor-io.h
blobff5f683c6da52f14097d9471e68bbb1020f654a9
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 //===----------------------------------------------------------------------===//
9 #ifndef FORTRAN_RUNTIME_DESCRIPTOR_IO_H_
10 #define FORTRAN_RUNTIME_DESCRIPTOR_IO_H_
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.)
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/optional.h"
25 #include "flang/Common/uint128.h"
26 #include "flang/Runtime/cpp-type.h"
27 #include "flang/Runtime/descriptor.h"
29 namespace Fortran::runtime::io::descr {
30 template <typename A>
31 inline RT_API_ATTRS A &ExtractElement(IoStatementState &io,
32 const Descriptor &descriptor, const SubscriptValue subscripts[]) {
33 A *p{descriptor.Element<A>(subscripts)};
34 if (!p) {
35 io.GetIoErrorHandler().Crash("Bad address for I/O item -- null base "
36 "address or subscripts out of range");
38 return *p;
41 // Per-category descriptor-based I/O templates
43 // TODO (perhaps as a nontrivial but small starter project): implement
44 // automatic repetition counts, like "10*3.14159", for list-directed and
45 // NAMELIST array output.
47 template <int KIND, Direction DIR>
48 inline RT_API_ATTRS bool FormattedIntegerIO(
49 IoStatementState &io, const Descriptor &descriptor) {
50 std::size_t numElements{descriptor.Elements()};
51 SubscriptValue subscripts[maxRank];
52 descriptor.GetLowerBounds(subscripts);
53 using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
54 bool anyInput{false};
55 for (std::size_t j{0}; j < numElements; ++j) {
56 if (auto edit{io.GetNextDataEdit()}) {
57 IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
58 if constexpr (DIR == Direction::Output) {
59 if (!EditIntegerOutput<KIND>(io, *edit, x)) {
60 return false;
62 } else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
63 if (EditIntegerInput(io, *edit, reinterpret_cast<void *>(&x), KIND)) {
64 anyInput = true;
65 } else {
66 return anyInput && edit->IsNamelist();
69 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
70 io.GetIoErrorHandler().Crash(
71 "FormattedIntegerIO: subscripts out of bounds");
73 } else {
74 return false;
77 return true;
80 template <int KIND, Direction DIR>
81 inline RT_API_ATTRS bool FormattedRealIO(
82 IoStatementState &io, const Descriptor &descriptor) {
83 std::size_t numElements{descriptor.Elements()};
84 SubscriptValue subscripts[maxRank];
85 descriptor.GetLowerBounds(subscripts);
86 using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
87 bool anyInput{false};
88 for (std::size_t j{0}; j < numElements; ++j) {
89 if (auto edit{io.GetNextDataEdit()}) {
90 RawType &x{ExtractElement<RawType>(io, descriptor, subscripts)};
91 if constexpr (DIR == Direction::Output) {
92 if (!RealOutputEditing<KIND>{io, x}.Edit(*edit)) {
93 return false;
95 } else if (edit->descriptor != DataEdit::ListDirectedNullValue) {
96 if (EditRealInput<KIND>(io, *edit, reinterpret_cast<void *>(&x))) {
97 anyInput = true;
98 } else {
99 return anyInput && edit->IsNamelist();
102 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
103 io.GetIoErrorHandler().Crash(
104 "FormattedRealIO: subscripts out of bounds");
106 } else {
107 return false;
110 return true;
113 template <int KIND, Direction DIR>
114 inline RT_API_ATTRS bool FormattedComplexIO(
115 IoStatementState &io, const Descriptor &descriptor) {
116 std::size_t numElements{descriptor.Elements()};
117 SubscriptValue subscripts[maxRank];
118 descriptor.GetLowerBounds(subscripts);
119 bool isListOutput{
120 io.get_if<ListDirectedStatementState<Direction::Output>>() != nullptr};
121 using RawType = typename RealOutputEditing<KIND>::BinaryFloatingPoint;
122 bool anyInput{false};
123 for (std::size_t j{0}; j < numElements; ++j) {
124 RawType *x{&ExtractElement<RawType>(io, descriptor, subscripts)};
125 if (isListOutput) {
126 DataEdit rEdit, iEdit;
127 rEdit.descriptor = DataEdit::ListDirectedRealPart;
128 iEdit.descriptor = DataEdit::ListDirectedImaginaryPart;
129 rEdit.modes = iEdit.modes = io.mutableModes();
130 if (!RealOutputEditing<KIND>{io, x[0]}.Edit(rEdit) ||
131 !RealOutputEditing<KIND>{io, x[1]}.Edit(iEdit)) {
132 return false;
134 } else {
135 for (int k{0}; k < 2; ++k, ++x) {
136 auto edit{io.GetNextDataEdit()};
137 if (!edit) {
138 return false;
139 } else if constexpr (DIR == Direction::Output) {
140 if (!RealOutputEditing<KIND>{io, *x}.Edit(*edit)) {
141 return false;
143 } else if (edit->descriptor == DataEdit::ListDirectedNullValue) {
144 break;
145 } else if (EditRealInput<KIND>(
146 io, *edit, reinterpret_cast<void *>(x))) {
147 anyInput = true;
148 } else {
149 return anyInput && edit->IsNamelist();
153 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
154 io.GetIoErrorHandler().Crash(
155 "FormattedComplexIO: subscripts out of bounds");
158 return true;
161 template <typename A, Direction DIR>
162 inline RT_API_ATTRS bool FormattedCharacterIO(
163 IoStatementState &io, const Descriptor &descriptor) {
164 std::size_t numElements{descriptor.Elements()};
165 SubscriptValue subscripts[maxRank];
166 descriptor.GetLowerBounds(subscripts);
167 std::size_t length{descriptor.ElementBytes() / sizeof(A)};
168 auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
169 bool anyInput{false};
170 for (std::size_t j{0}; j < numElements; ++j) {
171 A *x{&ExtractElement<A>(io, descriptor, subscripts)};
172 if (listOutput) {
173 if (!ListDirectedCharacterOutput(io, *listOutput, x, length)) {
174 return false;
176 } else if (auto edit{io.GetNextDataEdit()}) {
177 if constexpr (DIR == Direction::Output) {
178 if (!EditCharacterOutput(io, *edit, x, length)) {
179 return false;
181 } else { // input
182 if (edit->descriptor != DataEdit::ListDirectedNullValue) {
183 if (EditCharacterInput(io, *edit, x, length)) {
184 anyInput = true;
185 } else {
186 return anyInput && edit->IsNamelist();
190 } else {
191 return false;
193 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
194 io.GetIoErrorHandler().Crash(
195 "FormattedCharacterIO: subscripts out of bounds");
198 return true;
201 template <int KIND, Direction DIR>
202 inline RT_API_ATTRS bool FormattedLogicalIO(
203 IoStatementState &io, const Descriptor &descriptor) {
204 std::size_t numElements{descriptor.Elements()};
205 SubscriptValue subscripts[maxRank];
206 descriptor.GetLowerBounds(subscripts);
207 auto *listOutput{io.get_if<ListDirectedStatementState<Direction::Output>>()};
208 using IntType = CppTypeFor<TypeCategory::Integer, KIND>;
209 bool anyInput{false};
210 for (std::size_t j{0}; j < numElements; ++j) {
211 IntType &x{ExtractElement<IntType>(io, descriptor, subscripts)};
212 if (listOutput) {
213 if (!ListDirectedLogicalOutput(io, *listOutput, x != 0)) {
214 return false;
216 } else if (auto edit{io.GetNextDataEdit()}) {
217 if constexpr (DIR == Direction::Output) {
218 if (!EditLogicalOutput(io, *edit, x != 0)) {
219 return false;
221 } else {
222 if (edit->descriptor != DataEdit::ListDirectedNullValue) {
223 bool truth{};
224 if (EditLogicalInput(io, *edit, truth)) {
225 x = truth;
226 anyInput = true;
227 } else {
228 return anyInput && edit->IsNamelist();
232 } else {
233 return false;
235 if (!descriptor.IncrementSubscripts(subscripts) && j + 1 < numElements) {
236 io.GetIoErrorHandler().Crash(
237 "FormattedLogicalIO: subscripts out of bounds");
240 return true;
243 template <Direction DIR>
244 static RT_API_ATTRS bool DescriptorIO(IoStatementState &, const Descriptor &,
245 const NonTbpDefinedIoTable * = nullptr);
247 // For intrinsic (not defined) derived type I/O, formatted & unformatted
248 template <Direction DIR>
249 static RT_API_ATTRS bool DefaultComponentIO(IoStatementState &io,
250 const typeInfo::Component &component, const Descriptor &origDescriptor,
251 const SubscriptValue origSubscripts[], Terminator &terminator,
252 const NonTbpDefinedIoTable *table) {
253 #if !defined(RT_DEVICE_AVOID_RECURSION)
254 if (component.genre() == typeInfo::Component::Genre::Data) {
255 // Create a descriptor for the component
256 StaticDescriptor<maxRank, true, 16 /*?*/> statDesc;
257 Descriptor &desc{statDesc.descriptor()};
258 component.CreatePointerDescriptor(
259 desc, origDescriptor, terminator, origSubscripts);
260 return DescriptorIO<DIR>(io, desc, table);
261 } else {
262 // Component is itself a descriptor
263 char *pointer{
264 origDescriptor.Element<char>(origSubscripts) + component.offset()};
265 RUNTIME_CHECK(
266 terminator, component.genre() == typeInfo::Component::Genre::Automatic);
267 const Descriptor &compDesc{*reinterpret_cast<const Descriptor *>(pointer)};
268 return DescriptorIO<DIR>(io, compDesc, table);
270 #else
271 terminator.Crash("not yet implemented: component IO");
272 #endif
275 template <Direction DIR>
276 static RT_API_ATTRS bool DefaultComponentwiseFormattedIO(IoStatementState &io,
277 const Descriptor &descriptor, const typeInfo::DerivedType &type,
278 const NonTbpDefinedIoTable *table, const SubscriptValue subscripts[]) {
279 IoErrorHandler &handler{io.GetIoErrorHandler()};
280 const Descriptor &compArray{type.component()};
281 RUNTIME_CHECK(handler, compArray.rank() == 1);
282 std::size_t numComponents{compArray.Elements()};
283 SubscriptValue at[maxRank];
284 compArray.GetLowerBounds(at);
285 for (std::size_t k{0}; k < numComponents;
286 ++k, compArray.IncrementSubscripts(at)) {
287 const typeInfo::Component &component{
288 *compArray.Element<typeInfo::Component>(at)};
289 if (!DefaultComponentIO<DIR>(
290 io, component, descriptor, subscripts, handler, table)) {
291 // Return true for NAMELIST input if any component appeared.
292 auto *listInput{
293 io.get_if<ListDirectedStatementState<Direction::Input>>()};
294 return DIR == Direction::Input && k > 0 && listInput &&
295 listInput->inNamelistSequence();
298 return true;
301 template <Direction DIR>
302 static RT_API_ATTRS bool DefaultComponentwiseUnformattedIO(IoStatementState &io,
303 const Descriptor &descriptor, const typeInfo::DerivedType &type,
304 const NonTbpDefinedIoTable *table) {
305 IoErrorHandler &handler{io.GetIoErrorHandler()};
306 const Descriptor &compArray{type.component()};
307 RUNTIME_CHECK(handler, compArray.rank() == 1);
308 std::size_t numComponents{compArray.Elements()};
309 std::size_t numElements{descriptor.Elements()};
310 SubscriptValue subscripts[maxRank];
311 descriptor.GetLowerBounds(subscripts);
312 for (std::size_t j{0}; j < numElements;
313 ++j, descriptor.IncrementSubscripts(subscripts)) {
314 SubscriptValue at[maxRank];
315 compArray.GetLowerBounds(at);
316 for (std::size_t k{0}; k < numComponents;
317 ++k, compArray.IncrementSubscripts(at)) {
318 const typeInfo::Component &component{
319 *compArray.Element<typeInfo::Component>(at)};
320 if (!DefaultComponentIO<DIR>(
321 io, component, descriptor, subscripts, handler, table)) {
322 return false;
326 return true;
329 RT_API_ATTRS Fortran::common::optional<bool> DefinedFormattedIo(
330 IoStatementState &, const Descriptor &, const typeInfo::DerivedType &,
331 const typeInfo::SpecialBinding &, const SubscriptValue[]);
333 template <Direction DIR>
334 static RT_API_ATTRS bool FormattedDerivedTypeIO(IoStatementState &io,
335 const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {
336 IoErrorHandler &handler{io.GetIoErrorHandler()};
337 // Derived type information must be present for formatted I/O.
338 const DescriptorAddendum *addendum{descriptor.Addendum()};
339 RUNTIME_CHECK(handler, addendum != nullptr);
340 const typeInfo::DerivedType *type{addendum->derivedType()};
341 RUNTIME_CHECK(handler, type != nullptr);
342 Fortran::common::optional<typeInfo::SpecialBinding> nonTbpSpecial;
343 const typeInfo::SpecialBinding *special{nullptr};
344 if (table) {
345 if (const auto *definedIo{table->Find(*type,
346 DIR == Direction::Input ? common::DefinedIo::ReadFormatted
347 : common::DefinedIo::WriteFormatted)}) {
348 if (definedIo->subroutine) {
349 nonTbpSpecial.emplace(DIR == Direction::Input
350 ? typeInfo::SpecialBinding::Which::ReadFormatted
351 : typeInfo::SpecialBinding::Which::WriteFormatted,
352 definedIo->subroutine, definedIo->isDtvArgPolymorphic, false,
353 false);
354 special = &*nonTbpSpecial;
358 if (!special) {
359 if (const typeInfo::SpecialBinding *
360 binding{type->FindSpecialBinding(DIR == Direction::Input
361 ? typeInfo::SpecialBinding::Which::ReadFormatted
362 : typeInfo::SpecialBinding::Which::WriteFormatted)}) {
363 if (!table || !table->ignoreNonTbpEntries || binding->isTypeBound()) {
364 special = binding;
368 SubscriptValue subscripts[maxRank];
369 descriptor.GetLowerBounds(subscripts);
370 std::size_t numElements{descriptor.Elements()};
371 for (std::size_t j{0}; j < numElements;
372 ++j, descriptor.IncrementSubscripts(subscripts)) {
373 Fortran::common::optional<bool> result;
374 if (special) {
375 result = DefinedFormattedIo(io, descriptor, *type, *special, subscripts);
377 if (!result) {
378 result = DefaultComponentwiseFormattedIO<DIR>(
379 io, descriptor, *type, table, subscripts);
381 if (!result.value()) {
382 // Return true for NAMELIST input if we got anything.
383 auto *listInput{
384 io.get_if<ListDirectedStatementState<Direction::Input>>()};
385 return DIR == Direction::Input && j > 0 && listInput &&
386 listInput->inNamelistSequence();
389 return true;
392 RT_API_ATTRS bool DefinedUnformattedIo(IoStatementState &, const Descriptor &,
393 const typeInfo::DerivedType &, const typeInfo::SpecialBinding &);
395 // Unformatted I/O
396 template <Direction DIR>
397 static RT_API_ATTRS bool UnformattedDescriptorIO(IoStatementState &io,
398 const Descriptor &descriptor, const NonTbpDefinedIoTable *table = nullptr) {
399 IoErrorHandler &handler{io.GetIoErrorHandler()};
400 const DescriptorAddendum *addendum{descriptor.Addendum()};
401 if (const typeInfo::DerivedType *
402 type{addendum ? addendum->derivedType() : nullptr}) {
403 // derived type unformatted I/O
404 if (table) {
405 if (const auto *definedIo{table->Find(*type,
406 DIR == Direction::Input ? common::DefinedIo::ReadUnformatted
407 : common::DefinedIo::WriteUnformatted)}) {
408 if (definedIo->subroutine) {
409 typeInfo::SpecialBinding special{DIR == Direction::Input
410 ? typeInfo::SpecialBinding::Which::ReadUnformatted
411 : typeInfo::SpecialBinding::Which::WriteUnformatted,
412 definedIo->subroutine, definedIo->isDtvArgPolymorphic, false,
413 false};
414 if (Fortran::common::optional<bool> wasDefined{
415 DefinedUnformattedIo(io, descriptor, *type, special)}) {
416 return *wasDefined;
418 } else {
419 return DefaultComponentwiseUnformattedIO<DIR>(
420 io, descriptor, *type, table);
424 if (const typeInfo::SpecialBinding *
425 special{type->FindSpecialBinding(DIR == Direction::Input
426 ? typeInfo::SpecialBinding::Which::ReadUnformatted
427 : typeInfo::SpecialBinding::Which::WriteUnformatted)}) {
428 if (!table || !table->ignoreNonTbpEntries || special->isTypeBound()) {
429 // defined derived type unformatted I/O
430 return DefinedUnformattedIo(io, descriptor, *type, *special);
433 // Default derived type unformatted I/O
434 // TODO: If no component at any level has defined READ or WRITE
435 // (as appropriate), the elements are contiguous, and no byte swapping
436 // is active, do a block transfer via the code below.
437 return DefaultComponentwiseUnformattedIO<DIR>(io, descriptor, *type, table);
438 } else {
439 // intrinsic type unformatted I/O
440 auto *externalUnf{io.get_if<ExternalUnformattedIoStatementState<DIR>>()};
441 auto *childUnf{io.get_if<ChildUnformattedIoStatementState<DIR>>()};
442 auto *inq{
443 DIR == Direction::Output ? io.get_if<InquireIOLengthState>() : nullptr};
444 RUNTIME_CHECK(handler, externalUnf || childUnf || inq);
445 std::size_t elementBytes{descriptor.ElementBytes()};
446 std::size_t numElements{descriptor.Elements()};
447 std::size_t swappingBytes{elementBytes};
448 if (auto maybeCatAndKind{descriptor.type().GetCategoryAndKind()}) {
449 // Byte swapping units can be smaller than elements, namely
450 // for COMPLEX and CHARACTER.
451 if (maybeCatAndKind->first == TypeCategory::Character) {
452 // swap each character position independently
453 swappingBytes = maybeCatAndKind->second; // kind
454 } else if (maybeCatAndKind->first == TypeCategory::Complex) {
455 // swap real and imaginary components independently
456 swappingBytes /= 2;
459 SubscriptValue subscripts[maxRank];
460 descriptor.GetLowerBounds(subscripts);
461 using CharType =
462 std::conditional_t<DIR == Direction::Output, const char, char>;
463 auto Transfer{[=](CharType &x, std::size_t totalBytes) -> bool {
464 if constexpr (DIR == Direction::Output) {
465 return externalUnf ? externalUnf->Emit(&x, totalBytes, swappingBytes)
466 : childUnf ? childUnf->Emit(&x, totalBytes, swappingBytes)
467 : inq->Emit(&x, totalBytes, swappingBytes);
468 } else {
469 return externalUnf ? externalUnf->Receive(&x, totalBytes, swappingBytes)
470 : childUnf->Receive(&x, totalBytes, swappingBytes);
473 bool swapEndianness{externalUnf && externalUnf->unit().swapEndianness()};
474 if (!swapEndianness &&
475 descriptor.IsContiguous()) { // contiguous unformatted I/O
476 char &x{ExtractElement<char>(io, descriptor, subscripts)};
477 return Transfer(x, numElements * elementBytes);
478 } else { // non-contiguous or byte-swapped intrinsic type unformatted I/O
479 for (std::size_t j{0}; j < numElements; ++j) {
480 char &x{ExtractElement<char>(io, descriptor, subscripts)};
481 if (!Transfer(x, elementBytes)) {
482 return false;
484 if (!descriptor.IncrementSubscripts(subscripts) &&
485 j + 1 < numElements) {
486 handler.Crash("DescriptorIO: subscripts out of bounds");
489 return true;
494 template <Direction DIR>
495 static RT_API_ATTRS bool DescriptorIO(IoStatementState &io,
496 const Descriptor &descriptor, const NonTbpDefinedIoTable *table) {
497 IoErrorHandler &handler{io.GetIoErrorHandler()};
498 if (handler.InError()) {
499 return false;
501 if (!io.get_if<IoDirectionState<DIR>>()) {
502 handler.Crash("DescriptorIO() called for wrong I/O direction");
503 return false;
505 if constexpr (DIR == Direction::Input) {
506 if (!io.BeginReadingRecord()) {
507 return false;
510 if (!io.get_if<FormattedIoStatementState<DIR>>()) {
511 return UnformattedDescriptorIO<DIR>(io, descriptor, table);
513 if (auto catAndKind{descriptor.type().GetCategoryAndKind()}) {
514 TypeCategory cat{catAndKind->first};
515 int kind{catAndKind->second};
516 switch (cat) {
517 case TypeCategory::Integer:
518 switch (kind) {
519 case 1:
520 return FormattedIntegerIO<1, DIR>(io, descriptor);
521 case 2:
522 return FormattedIntegerIO<2, DIR>(io, descriptor);
523 case 4:
524 return FormattedIntegerIO<4, DIR>(io, descriptor);
525 case 8:
526 return FormattedIntegerIO<8, DIR>(io, descriptor);
527 case 16:
528 return FormattedIntegerIO<16, DIR>(io, descriptor);
529 default:
530 handler.Crash(
531 "not yet implemented: INTEGER(KIND=%d) in formatted IO", kind);
532 return false;
534 case TypeCategory::Real:
535 switch (kind) {
536 case 2:
537 return FormattedRealIO<2, DIR>(io, descriptor);
538 case 3:
539 return FormattedRealIO<3, DIR>(io, descriptor);
540 case 4:
541 return FormattedRealIO<4, DIR>(io, descriptor);
542 case 8:
543 return FormattedRealIO<8, DIR>(io, descriptor);
544 case 10:
545 return FormattedRealIO<10, DIR>(io, descriptor);
546 // TODO: case double/double
547 case 16:
548 return FormattedRealIO<16, DIR>(io, descriptor);
549 default:
550 handler.Crash(
551 "not yet implemented: REAL(KIND=%d) in formatted IO", kind);
552 return false;
554 case TypeCategory::Complex:
555 switch (kind) {
556 case 2:
557 return FormattedComplexIO<2, DIR>(io, descriptor);
558 case 3:
559 return FormattedComplexIO<3, DIR>(io, descriptor);
560 case 4:
561 return FormattedComplexIO<4, DIR>(io, descriptor);
562 case 8:
563 return FormattedComplexIO<8, DIR>(io, descriptor);
564 case 10:
565 return FormattedComplexIO<10, DIR>(io, descriptor);
566 // TODO: case double/double
567 case 16:
568 return FormattedComplexIO<16, DIR>(io, descriptor);
569 default:
570 handler.Crash(
571 "not yet implemented: COMPLEX(KIND=%d) in formatted IO", kind);
572 return false;
574 case TypeCategory::Character:
575 switch (kind) {
576 case 1:
577 return FormattedCharacterIO<char, DIR>(io, descriptor);
578 case 2:
579 return FormattedCharacterIO<char16_t, DIR>(io, descriptor);
580 case 4:
581 return FormattedCharacterIO<char32_t, DIR>(io, descriptor);
582 default:
583 handler.Crash(
584 "not yet implemented: CHARACTER(KIND=%d) in formatted IO", kind);
585 return false;
587 case TypeCategory::Logical:
588 switch (kind) {
589 case 1:
590 return FormattedLogicalIO<1, DIR>(io, descriptor);
591 case 2:
592 return FormattedLogicalIO<2, DIR>(io, descriptor);
593 case 4:
594 return FormattedLogicalIO<4, DIR>(io, descriptor);
595 case 8:
596 return FormattedLogicalIO<8, DIR>(io, descriptor);
597 default:
598 handler.Crash(
599 "not yet implemented: LOGICAL(KIND=%d) in formatted IO", kind);
600 return false;
602 case TypeCategory::Derived:
603 return FormattedDerivedTypeIO<DIR>(io, descriptor, table);
606 handler.Crash("DescriptorIO: bad type code (%d) in descriptor",
607 static_cast<int>(descriptor.type().raw()));
608 return false;
610 } // namespace Fortran::runtime::io::descr
611 #endif // FORTRAN_RUNTIME_DESCRIPTOR_IO_H_