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