diff --git a/flang/include/flang/Runtime/iostat.h b/flang/include/flang/Runtime/iostat.h --- a/flang/include/flang/Runtime/iostat.h +++ b/flang/include/flang/Runtime/iostat.h @@ -80,6 +80,7 @@ IostatBadWaitId, IostatTooManyAsyncOps, IostatBadBackspaceUnit, + IostatBadUnitNumber, }; const char *IostatErrorString(int); diff --git a/flang/runtime/io-api.cpp b/flang/runtime/io-api.cpp --- a/flang/runtime/io-api.cpp +++ b/flang/runtime/io-api.cpp @@ -147,6 +147,24 @@ format, formatLength, scratchArea, scratchBytes, sourceFile, sourceLine); } +static ExternalFileUnit *GetOrCreateUnit(int unitNumber, Direction direction, + std::optional isUnformatted, const Terminator &terminator, + Cookie &errorCookie) { + if (ExternalFileUnit * + unit{ExternalFileUnit::LookUpOrCreateAnonymous( + unitNumber, direction, isUnformatted, terminator)}) { + errorCookie = nullptr; + return unit; + } else { + errorCookie = &New{terminator}( + terminator.sourceFileName(), terminator.sourceLine(), unitNumber) + .release() + ->ioStatementState(); + errorCookie->GetIoErrorHandler().SetPendingError(IostatBadUnitNumber); + return nullptr; + } +} + template class STATE, typename... A> Cookie BeginExternalListIO( int unitNumber, const char *sourceFile, int sourceLine, A &&...xs) { @@ -154,16 +172,20 @@ if (unitNumber == DefaultUnit) { unitNumber = DIR == Direction::Input ? 5 : 6; } - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, DIR, false /*!unformatted*/, terminator)}; - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = false; + Cookie errorCookie{nullptr}; + ExternalFileUnit *unit{GetOrCreateUnit( + unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)}; + if (!unit) { + return errorCookie; + } + if (!unit->isUnformatted.has_value()) { + unit->isUnformatted = false; } Iostat iostat{IostatOk}; - if (*unit.isUnformatted) { + if (*unit->isUnformatted) { iostat = IostatFormattedIoOnUnformattedUnit; } - if (ChildIo * child{unit.GetChildIo()}) { + if (ChildIo * child{unit->GetChildIo()}) { if (iostat == IostatOk) { iostat = child->CheckFormattingAndDirection(false, DIR); } @@ -175,18 +197,18 @@ iostat, nullptr /* no unit */, sourceFile, sourceLine); } } else { - if (iostat == IostatOk && unit.access == Access::Direct) { + if (iostat == IostatOk && unit->access == Access::Direct) { iostat = IostatListIoOnDirectAccessUnit; } if (iostat == IostatOk) { - iostat = unit.SetDirection(DIR); + iostat = unit->SetDirection(DIR); } if (iostat == IostatOk) { - return &unit.BeginIoStatement>( - std::forward(xs)..., unit, sourceFile, sourceLine); + return &unit->BeginIoStatement>( + std::forward(xs)..., *unit, sourceFile, sourceLine); } else { - return &unit.BeginIoStatement( - iostat, &unit, sourceFile, sourceLine); + return &unit->BeginIoStatement( + iostat, unit, sourceFile, sourceLine); } } } @@ -210,16 +232,20 @@ if (unitNumber == DefaultUnit) { unitNumber = DIR == Direction::Input ? 5 : 6; } - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, DIR, false /*!unformatted*/, terminator)}; + Cookie errorCookie{nullptr}; + ExternalFileUnit *unit{GetOrCreateUnit( + unitNumber, DIR, false /*!unformatted*/, terminator, errorCookie)}; + if (!unit) { + return errorCookie; + } Iostat iostat{IostatOk}; - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = false; + if (!unit->isUnformatted.has_value()) { + unit->isUnformatted = false; } - if (*unit.isUnformatted) { + if (*unit->isUnformatted) { iostat = IostatFormattedIoOnUnformattedUnit; } - if (ChildIo * child{unit.GetChildIo()}) { + if (ChildIo * child{unit->GetChildIo()}) { if (iostat == IostatOk) { iostat = child->CheckFormattingAndDirection(false, DIR); } @@ -232,14 +258,14 @@ } } else { if (iostat == IostatOk) { - iostat = unit.SetDirection(DIR); + iostat = unit->SetDirection(DIR); } if (iostat == IostatOk) { - return &unit.BeginIoStatement>( - unit, format, formatLength, sourceFile, sourceLine); + return &unit->BeginIoStatement>( + *unit, format, formatLength, sourceFile, sourceLine); } else { - return &unit.BeginIoStatement( - iostat, &unit, sourceFile, sourceLine); + return &unit->BeginIoStatement( + iostat, unit, sourceFile, sourceLine); } } } @@ -262,16 +288,20 @@ Cookie BeginUnformattedIO( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, DIR, true /*unformatted*/, terminator)}; + Cookie errorCookie{nullptr}; + ExternalFileUnit *unit{GetOrCreateUnit( + unitNumber, DIR, true /*unformatted*/, terminator, errorCookie)}; + if (!unit) { + return errorCookie; + } Iostat iostat{IostatOk}; - if (!unit.isUnformatted.has_value()) { - unit.isUnformatted = true; + if (!unit->isUnformatted.has_value()) { + unit->isUnformatted = true; } - if (!*unit.isUnformatted) { + if (!*unit->isUnformatted) { iostat = IostatUnformattedIoOnFormattedUnit; } - if (ChildIo * child{unit.GetChildIo()}) { + if (ChildIo * child{unit->GetChildIo()}) { if (iostat == IostatOk) { iostat = child->CheckFormattingAndDirection(true, DIR); } @@ -284,24 +314,24 @@ } } else { if (iostat == IostatOk) { - iostat = unit.SetDirection(DIR); + iostat = unit->SetDirection(DIR); } if (iostat == IostatOk) { IoStatementState &io{ - unit.BeginIoStatement>( - unit, sourceFile, sourceLine)}; + unit->BeginIoStatement>( + *unit, sourceFile, sourceLine)}; if constexpr (DIR == Direction::Output) { - if (unit.access == Access::Sequential) { + if (unit->access == Access::Sequential) { // Create space for (sub)record header to be completed by // ExternalFileUnit::AdvanceRecord() - unit.recordLength.reset(); // in case of prior BACKSPACE + unit->recordLength.reset(); // in case of prior BACKSPACE io.Emit("\0\0\0\0", 4); // placeholder for record length header } } return &io; } else { - return &unit.BeginIoStatement( - iostat, &unit, sourceFile, sourceLine); + return &unit->BeginIoStatement( + iostat, unit, sourceFile, sourceLine); } } } @@ -320,12 +350,21 @@ Cookie IONAME(BeginOpenUnit)( // OPEN(without NEWUNIT=) ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { - bool wasExtant{false}; Terminator terminator{sourceFile, sourceLine}; - ExternalFileUnit &unit{ - ExternalFileUnit::LookUpOrCreate(unitNumber, terminator, wasExtant)}; - return &unit.BeginIoStatement( - unit, wasExtant, sourceFile, sourceLine); + bool wasExtant{false}; + if (ExternalFileUnit * + unit{ExternalFileUnit::LookUpOrCreate( + unitNumber, terminator, wasExtant)}) { + return &unit->BeginIoStatement( + *unit, wasExtant, sourceFile, sourceLine); + } else { + auto &io{ + New{terminator}(sourceFile, sourceLine, unitNumber) + .release() + ->ioStatementState()}; + io.GetIoErrorHandler().SetPendingError(IostatBadUnitNumber); + return &io; + } } Cookie IONAME(BeginOpenNewUnit)( // OPEN(NEWUNIT=j) @@ -411,19 +450,29 @@ Cookie IONAME(BeginEndfile)( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, Direction::Output, std::nullopt, terminator)}; - return &unit.BeginIoStatement( - unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); + Cookie errorCookie{nullptr}; + if (ExternalFileUnit * + unit{GetOrCreateUnit(unitNumber, Direction::Output, std::nullopt, + terminator, errorCookie)}) { + return &unit->BeginIoStatement( + *unit, ExternalMiscIoStatementState::Endfile, sourceFile, sourceLine); + } else { + return errorCookie; + } } Cookie IONAME(BeginRewind)( ExternalUnit unitNumber, const char *sourceFile, int sourceLine) { Terminator terminator{sourceFile, sourceLine}; - ExternalFileUnit &unit{ExternalFileUnit::LookUpOrCreateAnonymous( - unitNumber, Direction::Input, std::nullopt, terminator)}; - return &unit.BeginIoStatement( - unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); + Cookie errorCookie{nullptr}; + if (ExternalFileUnit * + unit{GetOrCreateUnit(unitNumber, Direction::Input, std::nullopt, + terminator, errorCookie)}) { + return &unit->BeginIoStatement( + *unit, ExternalMiscIoStatementState::Rewind, sourceFile, sourceLine); + } else { + return errorCookie; + } } Cookie IONAME(BeginInquireUnit)( diff --git a/flang/runtime/iostat.cpp b/flang/runtime/iostat.cpp --- a/flang/runtime/iostat.cpp +++ b/flang/runtime/iostat.cpp @@ -105,6 +105,8 @@ return "Too many asynchronous operations pending on unit"; case IostatBadBackspaceUnit: return "BACKSPACE on unconnected unit"; + case IostatBadUnitNumber: + return "Negative unit number is not allowed"; default: return nullptr; } diff --git a/flang/runtime/unit-map.h b/flang/runtime/unit-map.h --- a/flang/runtime/unit-map.h +++ b/flang/runtime/unit-map.h @@ -28,12 +28,16 @@ return Find(n); } - ExternalFileUnit &LookUpOrCreate( + ExternalFileUnit *LookUpOrCreate( int n, const Terminator &terminator, bool &wasExtant) { CriticalSection critical{lock_}; - auto *p{Find(n)}; - wasExtant = p != nullptr; - return p ? *p : Create(n, terminator); + if (auto *p{Find(n)}) { + wasExtant = true; + return p; + } else { + wasExtant = false; + return n >= 0 ? &Create(n, terminator) : nullptr; + } } // Unit look-up by name is needed for INQUIRE(FILE="...") diff --git a/flang/runtime/unit.h b/flang/runtime/unit.h --- a/flang/runtime/unit.h +++ b/flang/runtime/unit.h @@ -47,9 +47,9 @@ bool createdForInternalChildIo() const { return createdForInternalChildIo_; } static ExternalFileUnit *LookUp(int unit); - static ExternalFileUnit &LookUpOrCreate( + static ExternalFileUnit *LookUpOrCreate( int unit, const Terminator &, bool &wasExtant); - static ExternalFileUnit &LookUpOrCreateAnonymous(int unit, Direction, + static ExternalFileUnit *LookUpOrCreateAnonymous(int unit, Direction, std::optional isUnformatted, const Terminator &); static ExternalFileUnit *LookUp(const char *path, std::size_t pathLen); static ExternalFileUnit &CreateNew(int unit, const Terminator &); diff --git a/flang/runtime/unit.cpp b/flang/runtime/unit.cpp --- a/flang/runtime/unit.cpp +++ b/flang/runtime/unit.cpp @@ -43,23 +43,23 @@ return GetUnitMap().LookUp(unit); } -ExternalFileUnit &ExternalFileUnit::LookUpOrCreate( +ExternalFileUnit *ExternalFileUnit::LookUpOrCreate( int unit, const Terminator &terminator, bool &wasExtant) { return GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant); } -ExternalFileUnit &ExternalFileUnit::LookUpOrCreateAnonymous(int unit, +ExternalFileUnit *ExternalFileUnit::LookUpOrCreateAnonymous(int unit, Direction dir, std::optional isUnformatted, const Terminator &terminator) { bool exists{false}; - ExternalFileUnit &result{ + ExternalFileUnit *result{ GetUnitMap().LookUpOrCreate(unit, terminator, exists)}; - if (!exists) { + if (result && !exists) { IoErrorHandler handler{terminator}; - result.OpenAnonymousUnit( + result->OpenAnonymousUnit( dir == Direction::Input ? OpenStatus::Unknown : OpenStatus::Replace, Action::ReadWrite, Position::Rewind, Convert::Native, handler); - result.isUnformatted = isUnformatted; + result->isUnformatted = isUnformatted; } return result; } @@ -72,10 +72,10 @@ ExternalFileUnit &ExternalFileUnit::CreateNew( int unit, const Terminator &terminator) { bool wasExtant{false}; - ExternalFileUnit &result{ + ExternalFileUnit *result{ GetUnitMap().LookUpOrCreate(unit, terminator, wasExtant)}; - RUNTIME_CHECK(terminator, !wasExtant); - return result; + RUNTIME_CHECK(terminator, result && !wasExtant); + return *result; } ExternalFileUnit *ExternalFileUnit::LookUpForClose(int unit) { @@ -218,21 +218,22 @@ UnitMap *newUnitMap{New{terminator}().release()}; bool wasExtant{false}; - ExternalFileUnit &out{newUnitMap->LookUpOrCreate(6, terminator, wasExtant)}; + ExternalFileUnit &out{*newUnitMap->LookUpOrCreate(6, terminator, wasExtant)}; RUNTIME_CHECK(terminator, !wasExtant); out.Predefine(1); handler.SignalError(out.SetDirection(Direction::Output)); out.isUnformatted = false; defaultOutput = &out; - ExternalFileUnit &in{newUnitMap->LookUpOrCreate(5, terminator, wasExtant)}; + ExternalFileUnit &in{*newUnitMap->LookUpOrCreate(5, terminator, wasExtant)}; RUNTIME_CHECK(terminator, !wasExtant); in.Predefine(0); handler.SignalError(in.SetDirection(Direction::Input)); in.isUnformatted = false; defaultInput = ∈ - ExternalFileUnit &error{newUnitMap->LookUpOrCreate(0, terminator, wasExtant)}; + ExternalFileUnit &error{ + *newUnitMap->LookUpOrCreate(0, terminator, wasExtant)}; RUNTIME_CHECK(terminator, !wasExtant); error.Predefine(2); handler.SignalError(error.SetDirection(Direction::Output));