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