• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "descriptor.h"
16 #include "file.h"
17 #include "format.h"
18 #include "internal-unit.h"
19 #include "io-api.h"
20 #include "io-error.h"
21 #include <functional>
22 #include <type_traits>
23 #include <variant>
24 
25 namespace Fortran::runtime::io {
26 
27 class ExternalFileUnit;
28 
29 class OpenStatementState;
30 class InquireUnitState;
31 class InquireNoUnitState;
32 class InquireUnconnectedFileState;
33 class InquireIOLengthState;
34 class ExternalMiscIoStatementState;
35 class CloseStatementState;
36 class NoopCloseStatementState;
37 
38 template <Direction, typename CHAR = char>
39 class InternalFormattedIoStatementState;
40 template <Direction, typename CHAR = char> class InternalListIoStatementState;
41 template <Direction, typename CHAR = char>
42 class ExternalFormattedIoStatementState;
43 template <Direction> class ExternalListIoStatementState;
44 template <Direction> class UnformattedIoStatementState;
45 
46 struct InputStatementState {};
47 struct OutputStatementState {};
48 template <Direction D>
49 using IoDirectionState = std::conditional_t<D == Direction::Input,
50     InputStatementState, OutputStatementState>;
51 struct FormattedIoStatementState {};
52 
53 // The Cookie type in the I/O API is a pointer (for C) to this class.
54 class IoStatementState {
55 public:
IoStatementState(A & x)56   template <typename A> explicit IoStatementState(A &x) : u_{x} {}
57 
58   // These member functions each project themselves into the active alternative.
59   // They're used by per-data-item routines in the I/O API (e.g., OutputReal64)
60   // to interact with the state of the I/O statement in progress.
61   // This design avoids virtual member functions and function pointers,
62   // which may not have good support in some runtime environments.
63   std::optional<DataEdit> GetNextDataEdit(int = 1);
64   bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
65   std::optional<char32_t> GetCurrentChar(); // vacant after end of record
66   bool AdvanceRecord(int = 1);
67   void BackspaceRecord();
68   void HandleRelativePosition(std::int64_t);
69   int EndIoStatement();
70   ConnectionState &GetConnectionState();
71   IoErrorHandler &GetIoErrorHandler() const;
72   ExternalFileUnit *GetExternalFileUnit() const; // null if internal unit
73   MutableModes &mutableModes();
74   void BeginReadingRecord();
75   void FinishReadingRecord();
76   bool Inquire(InquiryKeywordHash, char *, std::size_t);
77   bool Inquire(InquiryKeywordHash, bool &);
78   bool Inquire(InquiryKeywordHash, std::int64_t, bool &); // PENDING=
79   bool Inquire(InquiryKeywordHash, std::int64_t &);
80 
81   // N.B.: this also works with base classes
get_if()82   template <typename A> A *get_if() const {
83     return std::visit(
84         [](auto &x) -> A * {
85           if constexpr (std::is_convertible_v<decltype(x.get()), A &>) {
86             return &x.get();
87           }
88           return nullptr;
89         },
90         u_);
91   }
92 
93   bool EmitRepeated(char, std::size_t);
94   bool EmitField(const char *, std::size_t length, std::size_t width);
95 
96   std::optional<char32_t> SkipSpaces(std::optional<int> &remaining);
97   std::optional<char32_t> NextInField(std::optional<int> &remaining);
98   std::optional<char32_t> GetNextNonBlank(); // can advance record
99 
CheckFormattedStmtType(const char * name)100   template <Direction D> void CheckFormattedStmtType(const char *name) {
101     if (!get_if<FormattedIoStatementState>() ||
102         !get_if<IoDirectionState<D>>()) {
103       GetIoErrorHandler().Crash(
104           "%s called for I/O statement that is not formatted %s", name,
105           D == Direction::Output ? "output" : "input");
106     }
107   }
108 
109 private:
110   std::variant<std::reference_wrapper<OpenStatementState>,
111       std::reference_wrapper<CloseStatementState>,
112       std::reference_wrapper<NoopCloseStatementState>,
113       std::reference_wrapper<
114           InternalFormattedIoStatementState<Direction::Output>>,
115       std::reference_wrapper<
116           InternalFormattedIoStatementState<Direction::Input>>,
117       std::reference_wrapper<InternalListIoStatementState<Direction::Output>>,
118       std::reference_wrapper<InternalListIoStatementState<Direction::Input>>,
119       std::reference_wrapper<
120           ExternalFormattedIoStatementState<Direction::Output>>,
121       std::reference_wrapper<
122           ExternalFormattedIoStatementState<Direction::Input>>,
123       std::reference_wrapper<ExternalListIoStatementState<Direction::Output>>,
124       std::reference_wrapper<ExternalListIoStatementState<Direction::Input>>,
125       std::reference_wrapper<UnformattedIoStatementState<Direction::Output>>,
126       std::reference_wrapper<UnformattedIoStatementState<Direction::Input>>,
127       std::reference_wrapper<InquireUnitState>,
128       std::reference_wrapper<InquireNoUnitState>,
129       std::reference_wrapper<InquireUnconnectedFileState>,
130       std::reference_wrapper<InquireIOLengthState>,
131       std::reference_wrapper<ExternalMiscIoStatementState>>
132       u_;
133 };
134 
135 // Base class for all per-I/O statement state classes.
136 // Inherits IoErrorHandler from its base.
137 struct IoStatementBase : public DefaultFormatControlCallbacks {
138   using DefaultFormatControlCallbacks::DefaultFormatControlCallbacks;
139   int EndIoStatement();
140   std::optional<DataEdit> GetNextDataEdit(IoStatementState &, int = 1);
GetExternalFileUnitIoStatementBase141   ExternalFileUnit *GetExternalFileUnit() const { return nullptr; }
BeginReadingRecordIoStatementBase142   void BeginReadingRecord() {}
FinishReadingRecordIoStatementBase143   void FinishReadingRecord() {}
144   bool Inquire(InquiryKeywordHash, char *, std::size_t);
145   bool Inquire(InquiryKeywordHash, bool &);
146   bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
147   bool Inquire(InquiryKeywordHash, std::int64_t &);
148   void BadInquiryKeywordHashCrash(InquiryKeywordHash);
149 };
150 
151 // Common state for list-directed internal & external I/O
152 template <Direction> class ListDirectedStatementState;
153 template <>
154 class ListDirectedStatementState<Direction::Output>
155     : public FormattedIoStatementState {
156 public:
157   static std::size_t RemainingSpaceInRecord(const ConnectionState &);
158   bool NeedAdvance(const ConnectionState &, std::size_t) const;
159   bool EmitLeadingSpaceOrAdvance(
160       IoStatementState &, std::size_t, bool isCharacter = false);
161   std::optional<DataEdit> GetNextDataEdit(
162       IoStatementState &, int maxRepeat = 1);
163   bool lastWasUndelimitedCharacter{false};
164 };
165 template <>
166 class ListDirectedStatementState<Direction::Input>
167     : public FormattedIoStatementState {
168 public:
169   // Skips value separators, handles repetition and null values.
170   // Vacant when '/' appears; present with descriptor == ListDirectedNullValue
171   // when a null value appears.
172   std::optional<DataEdit> GetNextDataEdit(
173       IoStatementState &, int maxRepeat = 1);
174 
175 private:
176   int remaining_{0}; // for "r*" repetition
177   std::int64_t initialRecordNumber_;
178   std::int64_t initialPositionInRecord_;
179   bool isFirstItem_{true}; // leading separator implies null first item
180   bool hitSlash_{false}; // once '/' is seen, nullify further items
181   bool realPart_{false};
182   bool imaginaryPart_{false};
183 };
184 
185 template <Direction DIR, typename CHAR = char>
186 class InternalIoStatementState : public IoStatementBase,
187                                  public IoDirectionState<DIR> {
188 public:
189   using CharType = CHAR;
190   using Buffer =
191       std::conditional_t<DIR == Direction::Input, const CharType *, CharType *>;
192   InternalIoStatementState(Buffer, std::size_t,
193       const char *sourceFile = nullptr, int sourceLine = 0);
194   InternalIoStatementState(
195       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
196   int EndIoStatement();
197   bool Emit(const CharType *, std::size_t chars /* not necessarily bytes */,
198       std::size_t elementBytes = 0);
199   std::optional<char32_t> GetCurrentChar();
200   bool AdvanceRecord(int = 1);
201   void BackspaceRecord();
GetConnectionState()202   ConnectionState &GetConnectionState() { return unit_; }
mutableModes()203   MutableModes &mutableModes() { return unit_.modes; }
204   void HandleRelativePosition(std::int64_t);
205   void HandleAbsolutePosition(std::int64_t);
206 
207 protected:
208   bool free_{true};
209   InternalDescriptorUnit<DIR> unit_;
210 };
211 
212 template <Direction DIR, typename CHAR>
213 class InternalFormattedIoStatementState
214     : public InternalIoStatementState<DIR, CHAR>,
215       public FormattedIoStatementState {
216 public:
217   using CharType = CHAR;
218   using typename InternalIoStatementState<DIR, CharType>::Buffer;
219   InternalFormattedIoStatementState(Buffer internal, std::size_t internalLength,
220       const CharType *format, std::size_t formatLength,
221       const char *sourceFile = nullptr, int sourceLine = 0);
222   InternalFormattedIoStatementState(const Descriptor &, const CharType *format,
223       std::size_t formatLength, const char *sourceFile = nullptr,
224       int sourceLine = 0);
ioStatementState()225   IoStatementState &ioStatementState() { return ioStatementState_; }
226   int EndIoStatement();
227   std::optional<DataEdit> GetNextDataEdit(
228       IoStatementState &, int maxRepeat = 1) {
229     return format_.GetNextDataEdit(*this, maxRepeat);
230   }
231 
232 private:
233   IoStatementState ioStatementState_; // points to *this
234   using InternalIoStatementState<DIR, CharType>::unit_;
235   // format_ *must* be last; it may be partial someday
236   FormatControl<InternalFormattedIoStatementState> format_;
237 };
238 
239 template <Direction DIR, typename CHAR>
240 class InternalListIoStatementState : public InternalIoStatementState<DIR, CHAR>,
241                                      public ListDirectedStatementState<DIR> {
242 public:
243   using CharType = CHAR;
244   using typename InternalIoStatementState<DIR, CharType>::Buffer;
245   InternalListIoStatementState(Buffer internal, std::size_t internalLength,
246       const char *sourceFile = nullptr, int sourceLine = 0);
247   InternalListIoStatementState(
248       const Descriptor &, const char *sourceFile = nullptr, int sourceLine = 0);
ioStatementState()249   IoStatementState &ioStatementState() { return ioStatementState_; }
250   using ListDirectedStatementState<DIR>::GetNextDataEdit;
251 
252 private:
253   IoStatementState ioStatementState_; // points to *this
254   using InternalIoStatementState<DIR, CharType>::unit_;
255 };
256 
257 class ExternalIoStatementBase : public IoStatementBase {
258 public:
259   ExternalIoStatementBase(
260       ExternalFileUnit &, const char *sourceFile = nullptr, int sourceLine = 0);
unit()261   ExternalFileUnit &unit() { return unit_; }
262   MutableModes &mutableModes();
263   ConnectionState &GetConnectionState();
264   int EndIoStatement();
GetExternalFileUnit()265   ExternalFileUnit *GetExternalFileUnit() { return &unit_; }
266 
267 private:
268   ExternalFileUnit &unit_;
269 };
270 
271 template <Direction DIR>
272 class ExternalIoStatementState : public ExternalIoStatementBase,
273                                  public IoDirectionState<DIR> {
274 public:
275   using ExternalIoStatementBase::ExternalIoStatementBase;
276   int EndIoStatement();
277   bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
278   bool Emit(const char16_t *, std::size_t chars /* not bytes */);
279   bool Emit(const char32_t *, std::size_t chars /* not bytes */);
280   std::optional<char32_t> GetCurrentChar();
281   bool AdvanceRecord(int = 1);
282   void BackspaceRecord();
283   void HandleRelativePosition(std::int64_t);
284   void HandleAbsolutePosition(std::int64_t);
285   void BeginReadingRecord();
286   void FinishReadingRecord();
287 };
288 
289 template <Direction DIR, typename CHAR>
290 class ExternalFormattedIoStatementState : public ExternalIoStatementState<DIR>,
291                                           public FormattedIoStatementState {
292 public:
293   using CharType = CHAR;
294   ExternalFormattedIoStatementState(ExternalFileUnit &, const CharType *format,
295       std::size_t formatLength, const char *sourceFile = nullptr,
296       int sourceLine = 0);
mutableModes()297   MutableModes &mutableModes() { return mutableModes_; }
298   int EndIoStatement();
299   std::optional<DataEdit> GetNextDataEdit(
300       IoStatementState &, int maxRepeat = 1) {
301     return format_.GetNextDataEdit(*this, maxRepeat);
302   }
303 
304 private:
305   // These are forked from ConnectionState's modes at the beginning
306   // of each formatted I/O statement so they may be overridden by control
307   // edit descriptors during the statement.
308   MutableModes mutableModes_;
309   FormatControl<ExternalFormattedIoStatementState> format_;
310 };
311 
312 template <Direction DIR>
313 class ExternalListIoStatementState : public ExternalIoStatementState<DIR>,
314                                      public ListDirectedStatementState<DIR> {
315 public:
316   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
317   using ListDirectedStatementState<DIR>::GetNextDataEdit;
318 };
319 
320 template <Direction DIR>
321 class UnformattedIoStatementState : public ExternalIoStatementState<DIR> {
322 public:
323   using ExternalIoStatementState<DIR>::ExternalIoStatementState;
324   bool Receive(char *, std::size_t, std::size_t elementBytes = 0);
325   bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
326 };
327 
328 class OpenStatementState : public ExternalIoStatementBase {
329 public:
330   OpenStatementState(ExternalFileUnit &unit, bool wasExtant,
331       const char *sourceFile = nullptr, int sourceLine = 0)
332       : ExternalIoStatementBase{unit, sourceFile, sourceLine}, wasExtant_{
333                                                                    wasExtant} {}
wasExtant()334   bool wasExtant() const { return wasExtant_; }
set_status(OpenStatus status)335   void set_status(OpenStatus status) { status_ = status; } // STATUS=
336   void set_path(const char *, std::size_t); // FILE=
set_position(Position position)337   void set_position(Position position) { position_ = position; } // POSITION=
set_action(Action action)338   void set_action(Action action) { action_ = action; } // ACTION=
set_convert(Convert convert)339   void set_convert(Convert convert) { convert_ = convert; } // CONVERT=
set_access(Access access)340   void set_access(Access access) { access_ = access; } // ACCESS=
341   void set_isUnformatted(bool yes = true) { isUnformatted_ = yes; } // FORM=
342   int EndIoStatement();
343 
344 private:
345   bool wasExtant_;
346   std::optional<OpenStatus> status_;
347   Position position_{Position::AsIs};
348   std::optional<Action> action_;
349   Convert convert_{Convert::Native};
350   OwningPtr<char> path_;
351   std::size_t pathLength_;
352   std::optional<bool> isUnformatted_;
353   std::optional<Access> access_;
354 };
355 
356 class CloseStatementState : public ExternalIoStatementBase {
357 public:
358   CloseStatementState(ExternalFileUnit &unit, const char *sourceFile = nullptr,
359       int sourceLine = 0)
360       : ExternalIoStatementBase{unit, sourceFile, sourceLine} {}
set_status(CloseStatus status)361   void set_status(CloseStatus status) { status_ = status; }
362   int EndIoStatement();
363 
364 private:
365   CloseStatus status_{CloseStatus::Keep};
366 };
367 
368 // For CLOSE(bad unit) and INQUIRE(unconnected unit)
369 class NoUnitIoStatementState : public IoStatementBase {
370 public:
ioStatementState()371   IoStatementState &ioStatementState() { return ioStatementState_; }
mutableModes()372   MutableModes &mutableModes() { return connection_.modes; }
GetConnectionState()373   ConnectionState &GetConnectionState() { return connection_; }
374   int EndIoStatement();
375 
376 protected:
377   template <typename A>
NoUnitIoStatementState(const char * sourceFile,int sourceLine,A & stmt)378   NoUnitIoStatementState(const char *sourceFile, int sourceLine, A &stmt)
379       : IoStatementBase{sourceFile, sourceLine}, ioStatementState_{stmt} {}
380 
381 private:
382   IoStatementState ioStatementState_; // points to *this
383   ConnectionState connection_;
384 };
385 
386 class NoopCloseStatementState : public NoUnitIoStatementState {
387 public:
NoopCloseStatementState(const char * sourceFile,int sourceLine)388   NoopCloseStatementState(const char *sourceFile, int sourceLine)
389       : NoUnitIoStatementState{sourceFile, sourceLine, *this} {}
set_status(CloseStatus)390   void set_status(CloseStatus) {} // discards
391 };
392 
393 extern template class InternalIoStatementState<Direction::Output>;
394 extern template class InternalIoStatementState<Direction::Input>;
395 extern template class InternalFormattedIoStatementState<Direction::Output>;
396 extern template class InternalFormattedIoStatementState<Direction::Input>;
397 extern template class InternalListIoStatementState<Direction::Output>;
398 extern template class InternalListIoStatementState<Direction::Input>;
399 extern template class ExternalIoStatementState<Direction::Output>;
400 extern template class ExternalIoStatementState<Direction::Input>;
401 extern template class ExternalFormattedIoStatementState<Direction::Output>;
402 extern template class ExternalFormattedIoStatementState<Direction::Input>;
403 extern template class ExternalListIoStatementState<Direction::Output>;
404 extern template class ExternalListIoStatementState<Direction::Input>;
405 extern template class UnformattedIoStatementState<Direction::Output>;
406 extern template class UnformattedIoStatementState<Direction::Input>;
407 extern template class FormatControl<
408     InternalFormattedIoStatementState<Direction::Output>>;
409 extern template class FormatControl<
410     InternalFormattedIoStatementState<Direction::Input>>;
411 extern template class FormatControl<
412     ExternalFormattedIoStatementState<Direction::Output>>;
413 extern template class FormatControl<
414     ExternalFormattedIoStatementState<Direction::Input>>;
415 
416 class InquireUnitState : public ExternalIoStatementBase {
417 public:
418   InquireUnitState(ExternalFileUnit &unit, const char *sourceFile = nullptr,
419       int sourceLine = 0);
420   bool Inquire(InquiryKeywordHash, char *, std::size_t);
421   bool Inquire(InquiryKeywordHash, bool &);
422   bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
423   bool Inquire(InquiryKeywordHash, std::int64_t &);
424 };
425 
426 class InquireNoUnitState : public NoUnitIoStatementState {
427 public:
428   InquireNoUnitState(const char *sourceFile = nullptr, int sourceLine = 0);
429   bool Inquire(InquiryKeywordHash, char *, std::size_t);
430   bool Inquire(InquiryKeywordHash, bool &);
431   bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
432   bool Inquire(InquiryKeywordHash, std::int64_t &);
433 };
434 
435 class InquireUnconnectedFileState : public NoUnitIoStatementState {
436 public:
437   InquireUnconnectedFileState(OwningPtr<char> &&path,
438       const char *sourceFile = nullptr, int sourceLine = 0);
439   bool Inquire(InquiryKeywordHash, char *, std::size_t);
440   bool Inquire(InquiryKeywordHash, bool &);
441   bool Inquire(InquiryKeywordHash, std::int64_t, bool &);
442   bool Inquire(InquiryKeywordHash, std::int64_t &);
443 
444 private:
445   OwningPtr<char> path_; // trimmed and NUL terminated
446 };
447 
448 class InquireIOLengthState : public NoUnitIoStatementState,
449                              public OutputStatementState {
450 public:
451   InquireIOLengthState(const char *sourceFile = nullptr, int sourceLine = 0);
bytes()452   std::size_t bytes() const { return bytes_; }
453   bool Emit(const char *, std::size_t, std::size_t elementBytes = 0);
454 
455 private:
456   std::size_t bytes_{0};
457 };
458 
459 class ExternalMiscIoStatementState : public ExternalIoStatementBase {
460 public:
461   enum Which { Flush, Backspace, Endfile, Rewind };
462   ExternalMiscIoStatementState(ExternalFileUnit &unit, Which which,
463       const char *sourceFile = nullptr, int sourceLine = 0)
464       : ExternalIoStatementBase{unit, sourceFile, sourceLine}, which_{which} {}
465   int EndIoStatement();
466 
467 private:
468   Which which_;
469 };
470 
471 } // namespace Fortran::runtime::io
472 #endif // FORTRAN_RUNTIME_IO_STMT_H_
473