[flang][runtime] Resilient opening of anonymous unit (#93876)

When an I/O statement references a unit number that has not been
explicitly opened or predefined, the I/O runtime support library opens a
local "fort.N" file. If this fails, the program crashes, even when the
I/O statement has IOSTAT= or IOMSG= or ERR= control list items. Connect
the dots to enable resilience in these cases.
This commit is contained in:
Peter Klausler 2024-06-03 14:23:50 -07:00 committed by GitHub
parent ea2c88f512
commit a8f2d185b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 18 additions and 13 deletions

View File

@ -58,15 +58,13 @@ ExternalFileUnit *ExternalFileUnit::LookUpOrCreate(
ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
Direction dir, Fortran::common::optional<bool> isUnformatted,
const Terminator &terminator) {
// Make sure that the returned anonymous unit has been opened
IoErrorHandler &handler) {
// Make sure that the returned anonymous unit has been opened,
// not just created in the unitMap.
CriticalSection critical{createOpenLock};
bool exists{false};
ExternalFileUnit *result{
GetUnitMap().LookUpOrCreate(unit, terminator, exists)};
ExternalFileUnit *result{GetUnitMap().LookUpOrCreate(unit, handler, exists)};
if (result && !exists) {
IoErrorHandler handler{terminator};
result->OpenAnonymousUnit(
dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace,
Action::ReadWrite, Position::Rewind, Convert::Unknown, handler);
@ -143,6 +141,9 @@ bool ExternalFileUnit::OpenUnit(Fortran::common::optional<OpenStatus> status,
}
set_path(std::move(newPath), newPathLength);
Open(status.value_or(OpenStatus::Unknown), action, position, handler);
if (handler.InError()) {
return impliedClose;
}
auto totalBytes{knownSize()};
if (access == Access::Direct) {
if (!openRecl) {

View File

@ -131,17 +131,17 @@ void OpenFile::Open(OpenStatus status, Fortran::common::optional<Action> action,
}
RUNTIME_CHECK(handler, action.has_value());
pending_.reset();
if (position == Position::Append && !RawSeekToEnd()) {
if (fd_ >= 0 && position == Position::Append && !RawSeekToEnd()) {
handler.SignalError(IostatOpenBadAppend);
}
isTerminal_ = IsATerminal(fd_) == 1;
isTerminal_ = fd_ >= 0 && IsATerminal(fd_) == 1;
mayRead_ = *action != Action::Write;
mayWrite_ = *action != Action::Read;
if (status == OpenStatus::Old || status == OpenStatus::Unknown) {
knownSize_.reset();
#ifndef _WIN32
struct stat buf;
if (::fstat(fd_, &buf) == 0) {
if (fd_ >= 0 && ::fstat(fd_, &buf) == 0) {
mayPosition_ = S_ISREG(buf.st_mode);
knownSize_ = buf.st_size;
}

View File

@ -33,13 +33,17 @@ static inline RT_API_ATTRS Cookie NoopUnit(const Terminator &terminator,
static inline RT_API_ATTRS ExternalFileUnit *GetOrCreateUnit(int unitNumber,
Direction direction, Fortran::common::optional<bool> isUnformatted,
const Terminator &terminator, Cookie &errorCookie) {
IoErrorHandler handler{terminator};
handler.HasIoStat();
if (ExternalFileUnit *
unit{ExternalFileUnit::LookUpOrCreateAnonymous(
unitNumber, direction, isUnformatted, terminator)}) {
unitNumber, direction, isUnformatted, handler)}) {
errorCookie = nullptr;
return unit;
} else {
errorCookie = NoopUnit(terminator, unitNumber, IostatBadUnitNumber);
auto iostat{static_cast<enum Iostat>(handler.GetIoStat())};
errorCookie = NoopUnit(terminator, unitNumber,
iostat != IostatOk ? iostat : IostatBadUnitNumber);
return nullptr;
}
}

View File

@ -36,11 +36,11 @@ ExternalFileUnit *ExternalFileUnit::LookUpOrCreate(
ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit,
Direction direction, Fortran::common::optional<bool>,
const Terminator &terminator) {
IoErrorHandler &handler) {
if (direction != Direction::Output) {
terminator.Crash("ExternalFileUnit only supports output IO");
}
return New<ExternalFileUnit>{terminator}(unit).release();
return New<ExternalFileUnit>{handler}(unit).release();
}
ExternalFileUnit *ExternalFileUnit::LookUp(const char *, std::size_t) {

View File

@ -120,7 +120,7 @@ public:
int unit, const Terminator &, bool &wasExtant);
static RT_API_ATTRS ExternalFileUnit *LookUpOrCreateAnonymous(int unit,
Direction, Fortran::common::optional<bool> isUnformatted,
const Terminator &);
IoErrorHandler &);
static RT_API_ATTRS ExternalFileUnit *LookUp(
const char *path, std::size_t pathLen);
static RT_API_ATTRS ExternalFileUnit &CreateNew(int unit, const Terminator &);