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