llvm-project/flang/runtime/connection.h
Peter Klausler 9469dc38b0
[flang][runtime] Handle unconsumed repeated list-directed input items (#75400)
If list-directed input contains a repeated item ("20*123.0") that is not
fully consumed by the READ statement's data item list, the end of that
READ statement was repositioning the input to the repeated value
("123.0"), leading to later confusion. Cancel the input item repetition
during EndIoStatement() processing to prevent this misbehavior.

Fixes llvm-test-suite/Fortran/gfortran/regression/list_read_4.f90.
2023-12-26 15:44:31 -08:00

124 lines
4.7 KiB
C++

//===-- runtime/connection.h ------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// Fortran I/O connection state (abstracted over internal & external units)
#ifndef FORTRAN_RUNTIME_IO_CONNECTION_H_
#define FORTRAN_RUNTIME_IO_CONNECTION_H_
#include "format.h"
#include <cinttypes>
#include <optional>
namespace Fortran::runtime::io {
class IoStatementState;
enum class Direction { Output, Input };
enum class Access { Sequential, Direct, Stream };
// These characteristics of a connection are immutable after being
// established in an OPEN statement.
struct ConnectionAttributes {
Access access{Access::Sequential}; // ACCESS='SEQUENTIAL', 'DIRECT', 'STREAM'
std::optional<bool> isUnformatted; // FORM='UNFORMATTED' if true
bool isUTF8{false}; // ENCODING='UTF-8'
unsigned char internalIoCharKind{0}; // 0->external, 1/2/4->internal
std::optional<std::int64_t> openRecl; // RECL= on OPEN
bool IsRecordFile() const {
// Formatted stream files are viewed as having records, at least on input
return access != Access::Stream || !isUnformatted.value_or(true);
}
template <typename CHAR = char> constexpr bool useUTF8() const {
// For wide CHARACTER kinds, always use UTF-8 for formatted I/O.
// For single-byte CHARACTER, encode characters >= 0x80 with
// UTF-8 iff the mode is set.
return internalIoCharKind == 0 && (sizeof(CHAR) > 1 || isUTF8);
}
};
struct ConnectionState : public ConnectionAttributes {
bool IsAtEOF() const; // true when read has hit EOF or endfile record
bool IsAfterEndfile() const; // true after ENDFILE until repositioned
// All positions and measurements are always in units of bytes,
// not characters. Multi-byte character encodings are possible in
// both internal I/O (when the character kind of the variable is 2 or 4)
// and external formatted I/O (when the encoding is UTF-8).
std::size_t RemainingSpaceInRecord() const;
bool NeedAdvance(std::size_t) const;
void HandleAbsolutePosition(std::int64_t);
void HandleRelativePosition(std::int64_t);
void BeginRecord() {
positionInRecord = 0;
furthestPositionInRecord = 0;
unterminatedRecord = false;
}
std::optional<std::int64_t> EffectiveRecordLength() const {
// When an input record is longer than an explicit RECL= from OPEN
// it is effectively truncated on input.
return openRecl && recordLength && *openRecl < *recordLength ? openRecl
: recordLength;
}
std::optional<std::int64_t> recordLength;
std::int64_t currentRecordNumber{1}; // 1 is first
// positionInRecord is the 0-based bytes offset in the current record
// to/from which the next data transfer will occur. It can be past
// furthestPositionInRecord if moved by an X or T or TR control edit
// descriptor.
std::int64_t positionInRecord{0};
// furthestPositionInRecord is the 0-based byte offset of the greatest
// position in the current record to/from which any data transfer has
// occurred, plus one. It can be viewed as a count of bytes processed.
std::int64_t furthestPositionInRecord{0}; // max(position+bytes)
// Set at end of non-advancing I/O data transfer
std::optional<std::int64_t> leftTabLimit; // offset in current record
// currentRecordNumber value captured after ENDFILE/REWIND/BACKSPACE statement
// or an end-of-file READ condition on a sequential access file
std::optional<std::int64_t> endfileRecordNumber;
// Mutable modes set at OPEN() that can be overridden in READ/WRITE & FORMAT
MutableModes modes; // BLANK=, DECIMAL=, SIGN=, ROUND=, PAD=, DELIM=, kP
// Set when processing repeated items during list-directed & NAMELIST input
// in order to keep a span of records in frame on a non-positionable file,
// so that backspacing to the beginning of the repeated item doesn't require
// repositioning the external storage medium when that's impossible.
bool pinnedFrame{false};
// Set when the last record of a file is not properly terminated
// so that a non-advancing READ will not signal EOR.
bool unterminatedRecord{false};
};
// Utility class for capturing and restoring a position in an input stream.
class SavedPosition {
public:
explicit SavedPosition(IoStatementState &);
~SavedPosition();
void Cancel() { cancelled_ = true; }
private:
IoStatementState &io_;
ConnectionState saved_;
bool cancelled_{false};
};
} // namespace Fortran::runtime::io
#endif // FORTRAN_RUNTIME_IO_CONNECTION_H_