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 | |
28 | namespace Fortran::runtime::io::descr { |
29 | template <typename A> |
30 | inline A &(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 | |
46 | template <int KIND, Direction DIR> |
47 | inline 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 | |
79 | template <int KIND, Direction DIR> |
80 | inline 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 | |
112 | template <int KIND, Direction DIR> |
113 | inline 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 | |
160 | template <typename A, Direction DIR> |
161 | inline 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 | |
200 | template <int KIND, Direction DIR> |
201 | inline 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 | |
242 | template <Direction DIR> |
243 | static bool DescriptorIO(IoStatementState &, const Descriptor &, |
244 | const NonTbpDefinedIoTable * = nullptr); |
245 | |
246 | // For intrinsic (not defined) derived type I/O, formatted & unformatted |
247 | template <Direction DIR> |
248 | static 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 | |
270 | template <Direction DIR> |
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, |
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 | |
324 | std::optional<bool> DefinedFormattedIo(IoStatementState &, const Descriptor &, |
325 | const typeInfo::DerivedType &, const typeInfo::SpecialBinding &, |
326 | const SubscriptValue[]); |
327 | |
328 | template <Direction DIR> |
329 | static 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 | |
387 | bool DefinedUnformattedIo(IoStatementState &, const Descriptor &, |
388 | const typeInfo::DerivedType &, const typeInfo::SpecialBinding &); |
389 | |
390 | // Unformatted I/O |
391 | template <Direction DIR> |
392 | static 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 | |
489 | template <Direction DIR> |
490 | static 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 | |