xref: /llvm-project/flang/runtime/io-stmt.h (revision c893e3d02d1f7b67880090485a030b79741bba1c)
1 //===-- runtime/io-stmt.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 // Representations of the state of an I/O statement in progress
10 
11 #ifndef FORTRAN_RUNTIME_IO_STMT_H_
12 #define FORTRAN_RUNTIME_IO_STMT_H_
13 
14 #include "connection.h"
15 #include "file.h"
16 #include "format.h"
17 #include "internal-unit.h"
18 #include "io-error.h"
19 #include "flang/Common/optional.h"
20 #include "flang/Common/reference-wrapper.h"
21 #include "flang/Common/visit.h"
22 #include "flang/Runtime/descriptor.h"
23 #include "flang/Runtime/io-api.h"
24 #include <flang/Common/variant.h>
25 #include <functional>
26 #include <type_traits>
27 
28 namespace Fortran::runtime::io {
29 
30 class ExternalFileUnit;
31 class ChildIo;
32 
33 class OpenStatementState;
34 class InquireUnitState;
35 class InquireNoUnitState;
36 class InquireUnconnectedFileState;
37 class InquireIOLengthState;
38 class ExternalMiscIoStatementState;
39 class CloseStatementState;
40 class NoopStatementState; // CLOSE or FLUSH on unknown unit
41 class ErroneousIoStatementState;
42 
43 template <Direction, typename CHAR = char>
44 class InternalFormattedIoStatementState;
45 template <Direction> class InternalListIoStatementState;
46 template <Direction, typename CHAR = char>
47 class ExternalFormattedIoStatementState;
48 template <Direction> class ExternalListIoStatementState;
49 template <Direction> class ExternalUnformattedIoStatementState;
50 template <Direction, typename CHAR = char> class ChildFormattedIoStatementState;
51 template <Direction> class ChildListIoStatementState;
52 template <Direction> class ChildUnformattedIoStatementState;
53 
54 struct InputStatementState {};
55 struct OutputStatementState {};
56 template <Direction D>
57 using IoDirectionState = std::conditional_t<D == Direction::Input,
58     InputStatementState, OutputStatementState>;
59 
60 // Common state for all kinds of formatted I/O
61 template <Direction D> class FormattedIoStatementState {};
62 template <> class FormattedIoStatementState<Direction::Input> {
63 public:
64   RT_API_ATTRS std::size_t GetEditDescriptorChars() const;
65   RT_API_ATTRS void GotChar(int);
66 
67 private:
68   // Account of characters read for edit descriptors (i.e., formatted I/O
69   // with a FORMAT, not list-directed or NAMELIST), not including padding.
70   std::size_t chars_{0}; // for READ(SIZE=)
71 };
72 
73 // The Cookie type in the I/O API is a pointer (for C) to this class.
74 class IoStatementState {
75 public:
76   template <typename A> explicit RT_API_ATTRS IoStatementState(A &x) : u_{x} {}
77 
78   // These member functions each project themselves into the active alternative.
79   // They're used by per-data-item routines in the I/O API (e.g., OutputReal64)
80   // to interact with the state of the I/O statement in progress.
81   // This design avoids virtual member functions and function pointers,
82   // which may not have good support in some runtime environments.
83 
84   // CompleteOperation() is the last opportunity to raise an I/O error.
85   // It is called by EndIoStatement(), but it can be invoked earlier to
86   // catch errors for (e.g.) GetIoMsg() and GetNewUnit().  If called
87   // more than once, it is a no-op.
88   RT_API_ATTRS void CompleteOperation();
89   // Completes an I/O statement and reclaims storage.
90   RT_API_ATTRS int EndIoStatement();
91 
92   RT_API_ATTRS bool Emit(
93       const char *, std::size_t bytes, std::size_t elementBytes = 0);
94   RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
95   RT_API_ATTRS std::size_t GetNextInputBytes(const char *&);
96   RT_API_ATTRS std::size_t ViewBytesInRecord(const char *&, bool forward) const;
97   RT_API_ATTRS bool AdvanceRecord(int = 1);
98   RT_API_ATTRS void BackspaceRecord();
99   RT_API_ATTRS void HandleRelativePosition(std::int64_t byteOffset);
100   RT_API_ATTRS void HandleAbsolutePosition(
101       std::int64_t byteOffset); // for r* in list I/O
102   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
103       int maxRepeat = 1);
104   RT_API_ATTRS ExternalFileUnit *
105   GetExternalFileUnit() const; // null if internal unit
106   RT_API_ATTRS bool BeginReadingRecord();
107   RT_API_ATTRS void FinishReadingRecord();
108   RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t);
109   RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &);
110   RT_API_ATTRS bool Inquire(
111       InquiryKeywordHash, std::int64_t, bool &); // PENDING=
112   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &);
113   RT_API_ATTRS std::int64_t InquirePos();
114   RT_API_ATTRS void GotChar(signed int = 1); // for READ(SIZE=); can be <0
115 
116   RT_API_ATTRS MutableModes &mutableModes();
117   RT_API_ATTRS ConnectionState &GetConnectionState();
118   RT_API_ATTRS IoErrorHandler &GetIoErrorHandler() const;
119 
120   // N.B.: this also works with base classes
121   template <typename A> RT_API_ATTRS A *get_if() const {
122     return common::visit(
123         [](auto &x) -> A * {
124           if constexpr (std::is_convertible_v<decltype(x.get()), A &>) {
125             return &x.get();
126           }
127           return nullptr;
128         },
129         u_);
130   }
131 
132   // Vacant after the end of the current record
133   RT_API_ATTRS Fortran::common::optional<char32_t> GetCurrentChar(
134       std::size_t &byteCount);
135 
136   // The result of CueUpInput() and the "remaining" arguments to SkipSpaces()
137   // and NextInField() are always in units of bytes, not characters; the
138   // distinction matters for internal input from CHARACTER(KIND=2 and 4).
139 
140   // For fixed-width fields, return the number of remaining bytes.
141   // Skip over leading blanks.
142   RT_API_ATTRS Fortran::common::optional<int> CueUpInput(const DataEdit &edit) {
143     Fortran::common::optional<int> remaining;
144     if (edit.IsListDirected()) {
145       std::size_t byteCount{0};
146       GetNextNonBlank(byteCount);
147     } else {
148       if (edit.width.value_or(0) > 0) {
149         remaining = *edit.width;
150         if (int bytesPerChar{GetConnectionState().internalIoCharKind};
151             bytesPerChar > 1) {
152           *remaining *= bytesPerChar;
153         }
154       }
155       SkipSpaces(remaining);
156     }
157     return remaining;
158   }
159 
160   RT_API_ATTRS Fortran::common::optional<char32_t> SkipSpaces(
161       Fortran::common::optional<int> &remaining) {
162     while (!remaining || *remaining > 0) {
163       std::size_t byteCount{0};
164       if (auto ch{GetCurrentChar(byteCount)}) {
165         if (*ch != ' ' && *ch != '\t') {
166           return ch;
167         }
168         if (remaining) {
169           if (static_cast<std::size_t>(*remaining) < byteCount) {
170             break;
171           }
172           GotChar(byteCount);
173           *remaining -= byteCount;
174         }
175         HandleRelativePosition(byteCount);
176       } else {
177         break;
178       }
179     }
180     return Fortran::common::nullopt;
181   }
182 
183   // Acquires the next input character, respecting any applicable field width
184   // or separator character.
185   RT_API_ATTRS Fortran::common::optional<char32_t> NextInField(
186       Fortran::common::optional<int> &remaining, const DataEdit &);
187 
188   // Detect and signal any end-of-record condition after input.
189   // Returns true if at EOR and remaining input should be padded with blanks.
190   RT_API_ATTRS bool CheckForEndOfRecord(std::size_t afterReading);
191 
192   // Skips spaces, advances records, and ignores NAMELIST comments
193   RT_API_ATTRS Fortran::common::optional<char32_t> GetNextNonBlank(
194       std::size_t &byteCount) {
195     auto ch{GetCurrentChar(byteCount)};
196     bool inNamelist{mutableModes().inNamelist};
197     while (!ch || *ch == ' ' || *ch == '\t' || *ch == '\n' ||
198         (inNamelist && *ch == '!')) {
199       if (ch && (*ch == ' ' || *ch == '\t' || *ch == '\n')) {
200         HandleRelativePosition(byteCount);
201       } else if (!AdvanceRecord()) {
202         return Fortran::common::nullopt;
203       }
204       ch = GetCurrentChar(byteCount);
205     }
206     return ch;
207   }
208 
209   template <Direction D>
210   RT_API_ATTRS bool CheckFormattedStmtType(const char *name) {
211     if (get_if<FormattedIoStatementState<D>>()) {
212       return true;
213     } else {
214       auto &handler{GetIoErrorHandler()};
215       if (!handler.InError()) {
216         handler.Crash("%s called for I/O statement that is not formatted %s",
217             name, D == Direction::Output ? "output" : "input");
218       }
219       return false;
220     }
221   }
222 
223 private:
224   std::variant<Fortran::common::reference_wrapper<OpenStatementState>,
225       Fortran::common::reference_wrapper<CloseStatementState>,
226       Fortran::common::reference_wrapper<NoopStatementState>,
227       Fortran::common::reference_wrapper<
228           InternalFormattedIoStatementState<Direction::Output>>,
229       Fortran::common::reference_wrapper<
230           InternalFormattedIoStatementState<Direction::Input>>,
231       Fortran::common::reference_wrapper<
232           InternalListIoStatementState<Direction::Output>>,
233       Fortran::common::reference_wrapper<
234           InternalListIoStatementState<Direction::Input>>,
235       Fortran::common::reference_wrapper<
236           ExternalFormattedIoStatementState<Direction::Output>>,
237       Fortran::common::reference_wrapper<
238           ExternalFormattedIoStatementState<Direction::Input>>,
239       Fortran::common::reference_wrapper<
240           ExternalListIoStatementState<Direction::Output>>,
241       Fortran::common::reference_wrapper<
242           ExternalListIoStatementState<Direction::Input>>,
243       Fortran::common::reference_wrapper<
244           ExternalUnformattedIoStatementState<Direction::Output>>,
245       Fortran::common::reference_wrapper<
246           ExternalUnformattedIoStatementState<Direction::Input>>,
247       Fortran::common::reference_wrapper<
248           ChildFormattedIoStatementState<Direction::Output>>,
249       Fortran::common::reference_wrapper<
250           ChildFormattedIoStatementState<Direction::Input>>,
251       Fortran::common::reference_wrapper<
252           ChildListIoStatementState<Direction::Output>>,
253       Fortran::common::reference_wrapper<
254           ChildListIoStatementState<Direction::Input>>,
255       Fortran::common::reference_wrapper<
256           ChildUnformattedIoStatementState<Direction::Output>>,
257       Fortran::common::reference_wrapper<
258           ChildUnformattedIoStatementState<Direction::Input>>,
259       Fortran::common::reference_wrapper<InquireUnitState>,
260       Fortran::common::reference_wrapper<InquireNoUnitState>,
261       Fortran::common::reference_wrapper<InquireUnconnectedFileState>,
262       Fortran::common::reference_wrapper<InquireIOLengthState>,
263       Fortran::common::reference_wrapper<ExternalMiscIoStatementState>,
264       Fortran::common::reference_wrapper<ErroneousIoStatementState>>
265       u_;
266 };
267 
268 // Base class for all per-I/O statement state classes.
269 class IoStatementBase : public IoErrorHandler {
270 public:
271   using IoErrorHandler::IoErrorHandler;
272 
273   RT_API_ATTRS bool completedOperation() const { return completedOperation_; }
274 
275   RT_API_ATTRS void CompleteOperation() { completedOperation_ = true; }
276   RT_API_ATTRS int EndIoStatement() { return GetIoStat(); }
277 
278   // These are default no-op backstops that can be overridden by descendants.
279   RT_API_ATTRS bool Emit(
280       const char *, std::size_t bytes, std::size_t elementBytes = 0);
281   RT_API_ATTRS bool Receive(
282       char *, std::size_t bytes, std::size_t elementBytes = 0);
283   RT_API_ATTRS std::size_t GetNextInputBytes(const char *&);
284   RT_API_ATTRS std::size_t ViewBytesInRecord(const char *&, bool forward) const;
285   RT_API_ATTRS bool AdvanceRecord(int);
286   RT_API_ATTRS void BackspaceRecord();
287   RT_API_ATTRS void HandleRelativePosition(std::int64_t);
288   RT_API_ATTRS void HandleAbsolutePosition(std::int64_t);
289   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
290       IoStatementState &, int maxRepeat = 1);
291   RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const;
292   RT_API_ATTRS bool BeginReadingRecord();
293   RT_API_ATTRS void FinishReadingRecord();
294   RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t);
295   RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &);
296   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
297   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &);
298   RT_API_ATTRS std::int64_t InquirePos();
299 
300   RT_API_ATTRS void BadInquiryKeywordHashCrash(InquiryKeywordHash);
301 
302   RT_API_ATTRS void ReportUnsupportedChildIo() const {
303     Crash("not yet implemented: child IO");
304   }
305 
306 protected:
307   bool completedOperation_{false};
308 };
309 
310 // Common state for list-directed & NAMELIST I/O, both internal & external
311 template <Direction> class ListDirectedStatementState;
312 template <>
313 class ListDirectedStatementState<Direction::Output>
314     : public FormattedIoStatementState<Direction::Output> {
315 public:
316   RT_API_ATTRS bool EmitLeadingSpaceOrAdvance(
317       IoStatementState &, std::size_t = 1, bool isCharacter = false);
318   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
319       IoStatementState &, int maxRepeat = 1);
320   RT_API_ATTRS bool lastWasUndelimitedCharacter() const {
321     return lastWasUndelimitedCharacter_;
322   }
323   RT_API_ATTRS void set_lastWasUndelimitedCharacter(bool yes = true) {
324     lastWasUndelimitedCharacter_ = yes;
325   }
326 
327 private:
328   bool lastWasUndelimitedCharacter_{false};
329 };
330 template <>
331 class ListDirectedStatementState<Direction::Input>
332     : public FormattedIoStatementState<Direction::Input> {
333 public:
334   RT_API_ATTRS bool inNamelistSequence() const { return inNamelistSequence_; }
335   RT_API_ATTRS int EndIoStatement();
336 
337   // Skips value separators, handles repetition and null values.
338   // Vacant when '/' appears; present with descriptor == ListDirectedNullValue
339   // when a null value appears.
340   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
341       IoStatementState &, int maxRepeat = 1);
342 
343   // Each NAMELIST input item is treated like a distinct list-directed
344   // input statement.  This member function resets some state so that
345   // repetition and null values work correctly for each successive
346   // NAMELIST input item.
347   RT_API_ATTRS void ResetForNextNamelistItem(bool inNamelistSequence) {
348     remaining_ = 0;
349     if (repeatPosition_) {
350       repeatPosition_->Cancel();
351     }
352     eatComma_ = false;
353     realPart_ = imaginaryPart_ = false;
354     inNamelistSequence_ = inNamelistSequence;
355   }
356 
357 private:
358   int remaining_{0}; // for "r*" repetition
359   Fortran::common::optional<SavedPosition> repeatPosition_;
360   bool eatComma_{false}; // consume comma after previously read item
361   bool hitSlash_{false}; // once '/' is seen, nullify further items
362   bool realPart_{false};
363   bool imaginaryPart_{false};
364   bool inNamelistSequence_{false};
365 };
366 
367 template <Direction DIR>
368 class InternalIoStatementState : public IoStatementBase,
369                                  public IoDirectionState<DIR> {
370 public:
371   using Buffer =
372       std::conditional_t<DIR == Direction::Input, const char *, char *>;
373   RT_API_ATTRS InternalIoStatementState(Buffer, std::size_t,
374       const char *sourceFile = nullptr, int sourceLine = 0);
375   RT_API_ATTRS InternalIoStatementState(
376       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
377   RT_API_ATTRS int EndIoStatement();
378 
379   RT_API_ATTRS bool Emit(
380       const char *data, std::size_t bytes, std::size_t elementBytes = 0);
381   RT_API_ATTRS std::size_t GetNextInputBytes(const char *&);
382   RT_API_ATTRS bool AdvanceRecord(int = 1);
383   RT_API_ATTRS void BackspaceRecord();
384   RT_API_ATTRS ConnectionState &GetConnectionState() { return unit_; }
385   RT_API_ATTRS MutableModes &mutableModes() { return unit_.modes; }
386   RT_API_ATTRS void HandleRelativePosition(std::int64_t);
387   RT_API_ATTRS void HandleAbsolutePosition(std::int64_t);
388   RT_API_ATTRS std::int64_t InquirePos();
389 
390 protected:
391   bool free_{true};
392   InternalDescriptorUnit<DIR> unit_;
393 };
394 
395 template <Direction DIR, typename CHAR>
396 class InternalFormattedIoStatementState
397     : public InternalIoStatementState<DIR>,
398       public FormattedIoStatementState<DIR> {
399 public:
400   using CharType = CHAR;
401   using typename InternalIoStatementState<DIR>::Buffer;
402   RT_API_ATTRS InternalFormattedIoStatementState(Buffer internal,
403       std::size_t internalLength, const CharType *format,
404       std::size_t formatLength, const Descriptor *formatDescriptor = nullptr,
405       const char *sourceFile = nullptr, int sourceLine = 0);
406   RT_API_ATTRS InternalFormattedIoStatementState(const Descriptor &,
407       const CharType *format, std::size_t formatLength,
408       const Descriptor *formatDescriptor = nullptr,
409       const char *sourceFile = nullptr, int sourceLine = 0);
410   RT_API_ATTRS IoStatementState &ioStatementState() {
411     return ioStatementState_;
412   }
413   RT_API_ATTRS void CompleteOperation();
414   RT_API_ATTRS int EndIoStatement();
415   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
416       IoStatementState &, int maxRepeat = 1) {
417     return format_.GetNextDataEdit(*this, maxRepeat);
418   }
419 
420 private:
421   IoStatementState ioStatementState_; // points to *this
422   using InternalIoStatementState<DIR>::unit_;
423   // format_ *must* be last; it may be partial someday
424   FormatControl<InternalFormattedIoStatementState> format_;
425 };
426 
427 template <Direction DIR>
428 class InternalListIoStatementState : public InternalIoStatementState<DIR>,
429                                      public ListDirectedStatementState<DIR> {
430 public:
431   using typename InternalIoStatementState<DIR>::Buffer;
432   RT_API_ATTRS InternalListIoStatementState(Buffer internal,
433       std::size_t internalLength, const char *sourceFile = nullptr,
434       int sourceLine = 0);
435   RT_API_ATTRS InternalListIoStatementState(
436       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
437   RT_API_ATTRS IoStatementState &ioStatementState() {
438     return ioStatementState_;
439   }
440   using ListDirectedStatementState<DIR>::GetNextDataEdit;
441   RT_API_ATTRS void CompleteOperation();
442   RT_API_ATTRS int EndIoStatement();
443 
444 private:
445   IoStatementState ioStatementState_; // points to *this
446   using InternalIoStatementState<DIR>::unit_;
447 };
448 
449 class ExternalIoStatementBase : public IoStatementBase {
450 public:
451   RT_API_ATTRS ExternalIoStatementBase(
452       ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0);
453   RT_API_ATTRS ExternalFileUnit &unit() { return unit_; }
454   RT_API_ATTRS const ExternalFileUnit &unit() const { return unit_; }
455   RT_API_ATTRS MutableModes &mutableModes();
456   RT_API_ATTRS ConnectionState &GetConnectionState();
457   RT_API_ATTRS int asynchronousID() const { return asynchronousID_; }
458   RT_API_ATTRS void set_destroy(bool yes = true) { destroy_ = yes; }
459   RT_API_ATTRS int EndIoStatement();
460   RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const { return &unit_; }
461   RT_API_ATTRS void SetAsynchronous();
462   RT_API_ATTRS std::int64_t InquirePos();
463 
464 private:
465   ExternalFileUnit &unit_;
466   int asynchronousID_{-1};
467   bool destroy_{false};
468 };
469 
470 template <Direction DIR>
471 class ExternalIoStatementState : public ExternalIoStatementBase,
472                                  public IoDirectionState<DIR> {
473 public:
474   RT_API_ATTRS ExternalIoStatementState(
475       ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0);
476   RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
477   RT_API_ATTRS void CompleteOperation();
478   RT_API_ATTRS int EndIoStatement();
479   RT_API_ATTRS bool Emit(
480       const char *, std::size_t bytes, std::size_t elementBytes = 0);
481   RT_API_ATTRS std::size_t GetNextInputBytes(const char *&);
482   RT_API_ATTRS std::size_t ViewBytesInRecord(const char *&, bool forward) const;
483   RT_API_ATTRS bool AdvanceRecord(int = 1);
484   RT_API_ATTRS void BackspaceRecord();
485   RT_API_ATTRS void HandleRelativePosition(std::int64_t);
486   RT_API_ATTRS void HandleAbsolutePosition(std::int64_t);
487   RT_API_ATTRS bool BeginReadingRecord();
488   RT_API_ATTRS void FinishReadingRecord();
489 
490 private:
491   // These are forked from ConnectionState's modes at the beginning
492   // of each formatted I/O statement so they may be overridden by control
493   // edit descriptors during the statement.
494   MutableModes mutableModes_;
495 };
496 
497 template <Direction DIR, typename CHAR>
498 class ExternalFormattedIoStatementState
499     : public ExternalIoStatementState<DIR>,
500       public FormattedIoStatementState<DIR> {
501 public:
502   using CharType = CHAR;
503   RT_API_ATTRS ExternalFormattedIoStatementState(ExternalFileUnit &,
504       const CharType *format, std::size_t formatLength,
505       const Descriptor *formatDescriptor = nullptr,
506       const char *sourceFile = nullptr, int sourceLine = 0);
507   RT_API_ATTRS void CompleteOperation();
508   RT_API_ATTRS int EndIoStatement();
509   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
510       IoStatementState &, int maxRepeat = 1) {
511     return format_.GetNextDataEdit(*this, maxRepeat);
512   }
513 
514 private:
515   FormatControl<ExternalFormattedIoStatementState> format_;
516 };
517 
518 template <Direction DIR>
519 class ExternalListIoStatementState : public ExternalIoStatementState<DIR>,
520                                      public ListDirectedStatementState<DIR> {
521 public:
522   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
523   using ListDirectedStatementState<DIR>::GetNextDataEdit;
524   RT_API_ATTRS int EndIoStatement();
525 };
526 
527 template <Direction DIR>
528 class ExternalUnformattedIoStatementState
529     : public ExternalIoStatementState<DIR> {
530 public:
531   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
532   RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
533 };
534 
535 template <Direction DIR>
536 class ChildIoStatementState : public IoStatementBase,
537                               public IoDirectionState<DIR> {
538 public:
539   RT_API_ATTRS ChildIoStatementState(
540       ChildIo &, const char *sourceFile = nullptr, int sourceLine = 0);
541   RT_API_ATTRS ChildIo &child() { return child_; }
542   RT_API_ATTRS MutableModes &mutableModes();
543   RT_API_ATTRS ConnectionState &GetConnectionState();
544   RT_API_ATTRS ExternalFileUnit *GetExternalFileUnit() const;
545   RT_API_ATTRS int EndIoStatement();
546   RT_API_ATTRS bool Emit(
547       const char *, std::size_t bytes, std::size_t elementBytes = 0);
548   RT_API_ATTRS std::size_t GetNextInputBytes(const char *&);
549   RT_API_ATTRS std::size_t ViewBytesInRecord(const char *&, bool forward) const;
550   RT_API_ATTRS void HandleRelativePosition(std::int64_t);
551   RT_API_ATTRS void HandleAbsolutePosition(std::int64_t);
552 
553 private:
554   ChildIo &child_;
555 };
556 
557 template <Direction DIR, typename CHAR>
558 class ChildFormattedIoStatementState : public ChildIoStatementState<DIR>,
559                                        public FormattedIoStatementState<DIR> {
560 public:
561   using CharType = CHAR;
562   RT_API_ATTRS ChildFormattedIoStatementState(ChildIo &, const CharType *format,
563       std::size_t formatLength, const Descriptor *formatDescriptor = nullptr,
564       const char *sourceFile = nullptr, int sourceLine = 0);
565   RT_API_ATTRS MutableModes &mutableModes() { return mutableModes_; }
566   RT_API_ATTRS void CompleteOperation();
567   RT_API_ATTRS int EndIoStatement();
568   RT_API_ATTRS bool AdvanceRecord(int = 1);
569   RT_API_ATTRS Fortran::common::optional<DataEdit> GetNextDataEdit(
570       IoStatementState &, int maxRepeat = 1) {
571     return format_.GetNextDataEdit(*this, maxRepeat);
572   }
573 
574 private:
575   MutableModes mutableModes_;
576   FormatControl<ChildFormattedIoStatementState> format_;
577 };
578 
579 template <Direction DIR>
580 class ChildListIoStatementState : public ChildIoStatementState<DIR>,
581                                   public ListDirectedStatementState<DIR> {
582 public:
583   using ChildIoStatementState<DIR>::ChildIoStatementState;
584   using ListDirectedStatementState<DIR>::GetNextDataEdit;
585   RT_API_ATTRS int EndIoStatement();
586 };
587 
588 template <Direction DIR>
589 class ChildUnformattedIoStatementState : public ChildIoStatementState<DIR> {
590 public:
591   using ChildIoStatementState<DIR>::ChildIoStatementState;
592   RT_API_ATTRS bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
593 };
594 
595 // OPEN
596 class OpenStatementState : public ExternalIoStatementBase {
597 public:
598   RT_API_ATTRS OpenStatementState(ExternalFileUnit &unit, bool wasExtant,
599       bool isNewUnit, const char *sourceFile = nullptr, int sourceLine = 0)
600       : ExternalIoStatementBase{unit, sourceFile, sourceLine},
601         wasExtant_{wasExtant}, isNewUnit_{isNewUnit} {}
602   RT_API_ATTRS bool wasExtant() const { return wasExtant_; }
603   RT_API_ATTRS void set_status(OpenStatus status) {
604     status_ = status;
605   } // STATUS=
606   RT_API_ATTRS void set_path(const char *, std::size_t); // FILE=
607   RT_API_ATTRS void set_position(Position position) {
608     position_ = position;
609   } // POSITION=
610   RT_API_ATTRS void set_action(Action action) { action_ = action; } // ACTION=
611   RT_API_ATTRS void set_convert(Convert convert) {
612     convert_ = convert;
613   } // CONVERT=
614   RT_API_ATTRS void set_access(Access access) { access_ = access; } // ACCESS=
615   RT_API_ATTRS void set_isUnformatted(bool yes = true) {
616     isUnformatted_ = yes;
617   } // FORM=
618 
619   RT_API_ATTRS void CompleteOperation();
620   RT_API_ATTRS int EndIoStatement();
621 
622 private:
623   bool wasExtant_;
624   bool isNewUnit_;
625   Fortran::common::optional<OpenStatus> status_;
626   Fortran::common::optional<Position> position_;
627   Fortran::common::optional<Action> action_;
628   Convert convert_{Convert::Unknown};
629   OwningPtr<char> path_;
630   std::size_t pathLength_;
631   Fortran::common::optional<bool> isUnformatted_;
632   Fortran::common::optional<Access> access_;
633 };
634 
635 class CloseStatementState : public ExternalIoStatementBase {
636 public:
637   RT_API_ATTRS CloseStatementState(ExternalFileUnit &unit,
638       const char *sourceFile = nullptr, int sourceLine = 0)
639       : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
640   RT_API_ATTRS void set_status(CloseStatus status) { status_ = status; }
641   RT_API_ATTRS int EndIoStatement();
642 
643 private:
644   CloseStatus status_{CloseStatus::Keep};
645 };
646 
647 // For CLOSE(bad unit), WAIT(bad unit, ID=nonzero), INQUIRE(unconnected unit),
648 // and recoverable BACKSPACE(bad unit)
649 class NoUnitIoStatementState : public IoStatementBase {
650 public:
651   RT_API_ATTRS IoStatementState &ioStatementState() {
652     return ioStatementState_;
653   }
654   RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; }
655   RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; }
656   RT_API_ATTRS int badUnitNumber() const { return badUnitNumber_; }
657   RT_API_ATTRS void CompleteOperation();
658   RT_API_ATTRS int EndIoStatement();
659 
660 protected:
661   template <typename A>
662   RT_API_ATTRS NoUnitIoStatementState(A &stmt, const char *sourceFile = nullptr,
663       int sourceLine = 0, int badUnitNumber = -1)
664       : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt},
665         badUnitNumber_{badUnitNumber} {}
666 
667 private:
668   IoStatementState ioStatementState_; // points to *this
669   ConnectionState connection_;
670   int badUnitNumber_;
671 };
672 
673 class NoopStatementState : public NoUnitIoStatementState {
674 public:
675   RT_API_ATTRS NoopStatementState(
676       const char *sourceFile = nullptr, int sourceLine = 0, int unitNumber = -1)
677       : NoUnitIoStatementState{*this, sourceFile, sourceLine, unitNumber} {}
678   RT_API_ATTRS void set_status(CloseStatus) {} // discards
679 };
680 
681 extern template class InternalIoStatementState<Direction::Output>;
682 extern template class InternalIoStatementState<Direction::Input>;
683 extern template class InternalFormattedIoStatementState<Direction::Output>;
684 extern template class InternalFormattedIoStatementState<Direction::Input>;
685 extern template class InternalListIoStatementState<Direction::Output>;
686 extern template class InternalListIoStatementState<Direction::Input>;
687 extern template class ExternalIoStatementState<Direction::Output>;
688 extern template class ExternalIoStatementState<Direction::Input>;
689 extern template class ExternalFormattedIoStatementState<Direction::Output>;
690 extern template class ExternalFormattedIoStatementState<Direction::Input>;
691 extern template class ExternalListIoStatementState<Direction::Output>;
692 extern template class ExternalListIoStatementState<Direction::Input>;
693 extern template class ExternalUnformattedIoStatementState<Direction::Output>;
694 extern template class ExternalUnformattedIoStatementState<Direction::Input>;
695 extern template class ChildIoStatementState<Direction::Output>;
696 extern template class ChildIoStatementState<Direction::Input>;
697 extern template class ChildFormattedIoStatementState<Direction::Output>;
698 extern template class ChildFormattedIoStatementState<Direction::Input>;
699 extern template class ChildListIoStatementState<Direction::Output>;
700 extern template class ChildListIoStatementState<Direction::Input>;
701 extern template class ChildUnformattedIoStatementState<Direction::Output>;
702 extern template class ChildUnformattedIoStatementState<Direction::Input>;
703 
704 extern template class FormatControl<
705     InternalFormattedIoStatementState<Direction::Output>>;
706 extern template class FormatControl<
707     InternalFormattedIoStatementState<Direction::Input>>;
708 extern template class FormatControl<
709     ExternalFormattedIoStatementState<Direction::Output>>;
710 extern template class FormatControl<
711     ExternalFormattedIoStatementState<Direction::Input>>;
712 extern template class FormatControl<
713     ChildFormattedIoStatementState<Direction::Output>>;
714 extern template class FormatControl<
715     ChildFormattedIoStatementState<Direction::Input>>;
716 
717 class InquireUnitState : public ExternalIoStatementBase {
718 public:
719   RT_API_ATTRS InquireUnitState(ExternalFileUnit &unit,
720       const char *sourceFile = nullptr, int sourceLine = 0);
721   RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t);
722   RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &);
723   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
724   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &);
725 };
726 
727 class InquireNoUnitState : public NoUnitIoStatementState {
728 public:
729   RT_API_ATTRS InquireNoUnitState(const char *sourceFile = nullptr,
730       int sourceLine = 0, int badUnitNumber = -1);
731   RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t);
732   RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &);
733   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
734   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &);
735 };
736 
737 class InquireUnconnectedFileState : public NoUnitIoStatementState {
738 public:
739   RT_API_ATTRS InquireUnconnectedFileState(OwningPtr<char> &&path,
740       const char *sourceFile = nullptr, int sourceLine = 0);
741   RT_API_ATTRS bool Inquire(InquiryKeywordHash, char *, std::size_t);
742   RT_API_ATTRS bool Inquire(InquiryKeywordHash, bool &);
743   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
744   RT_API_ATTRS bool Inquire(InquiryKeywordHash, std::int64_t &);
745 
746 private:
747   OwningPtr<char> path_; // trimmed and NUL terminated
748 };
749 
750 class InquireIOLengthState : public NoUnitIoStatementState,
751                              public OutputStatementState {
752 public:
753   RT_API_ATTRS InquireIOLengthState(
754       const char *sourceFile = nullptr, int sourceLine = 0);
755   RT_API_ATTRS std::size_t bytes() const { return bytes_; }
756   RT_API_ATTRS bool Emit(
757       const char *, std::size_t bytes, std::size_t elementBytes = 0);
758 
759 private:
760   std::size_t bytes_{0};
761 };
762 
763 class ExternalMiscIoStatementState : public ExternalIoStatementBase {
764 public:
765   enum Which { Flush, Backspace, Endfile, Rewind, Wait };
766   RT_API_ATTRS ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
767       const char *sourceFile = nullptr, int sourceLine = 0)
768       : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
769   RT_API_ATTRS void CompleteOperation();
770   RT_API_ATTRS int EndIoStatement();
771 
772 private:
773   Which which_;
774 };
775 
776 class ErroneousIoStatementState : public IoStatementBase {
777 public:
778   explicit RT_API_ATTRS ErroneousIoStatementState(Iostat iostat,
779       ExternalFileUnit *unit = nullptr, const char *sourceFile = nullptr,
780       int sourceLine = 0)
781       : IoStatementBase{sourceFile, sourceLine}, unit_{unit} {
782     SetPendingError(iostat);
783   }
784   RT_API_ATTRS int EndIoStatement();
785   RT_API_ATTRS ConnectionState &GetConnectionState() { return connection_; }
786   RT_API_ATTRS MutableModes &mutableModes() { return connection_.modes; }
787 
788 private:
789   ConnectionState connection_;
790   ExternalFileUnit *unit_{nullptr};
791 };
792 
793 } // namespace Fortran::runtime::io
794 #endif // FORTRAN_RUNTIME_IO_STMT_H_
795