Skip to content

Commit ffec266

Browse files
authored
[flang][runtime] Catch bad OPEN specifiers for unformatted files (#153707)
When an OPEN statement has specifiers that are allowed only for formatted files, detect an error when the file turns out to be unformatted. Fixes #153480.
1 parent c53792b commit ffec266

File tree

4 files changed

+40
-2
lines changed

4 files changed

+40
-2
lines changed

flang-rt/include/flang-rt/runtime/io-error.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ class IoErrorHandler : public Terminator {
3838
RT_API_ATTRS bool InError() const {
3939
return ioStat_ != IostatOk || pendingError_ != IostatOk;
4040
}
41+
RT_API_ATTRS bool HasErrorRecovery() const {
42+
return (flags_ & (hasIoStat | hasErr)) != 0;
43+
}
4144

4245
// For I/O statements that detect fatal errors in their
4346
// Begin...() API routines before it is known whether they

flang-rt/include/flang-rt/runtime/io-stmt.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,9 @@ class OpenStatementState : public ExternalIoStatementBase {
729729
RT_API_ATTRS void set_isUnformatted(bool yes = true) {
730730
isUnformatted_ = yes;
731731
} // FORM=
732+
RT_API_ATTRS void set_mustBeFormatted(bool yes = true) {
733+
mustBeFormatted_ = yes;
734+
}
732735

733736
RT_API_ATTRS void CompleteOperation();
734737
RT_API_ATTRS int EndIoStatement();
@@ -743,6 +746,7 @@ class OpenStatementState : public ExternalIoStatementBase {
743746
OwningPtr<char> path_;
744747
std::size_t pathLength_{};
745748
Fortran::common::optional<bool> isUnformatted_;
749+
Fortran::common::optional<bool> mustBeFormatted_;
746750
Fortran::common::optional<Access> access_;
747751
};
748752

flang-rt/lib/runtime/io-api.cpp

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,6 +528,9 @@ bool IODEF(SetAdvance)(Cookie cookie, const char *keyword, std::size_t length) {
528528

529529
bool IODEF(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
530530
IoStatementState &io{*cookie};
531+
if (auto *open{io.get_if<OpenStatementState>()}) {
532+
open->set_mustBeFormatted();
533+
}
531534
static const char *keywords[]{"NULL", "ZERO", nullptr};
532535
switch (IdentifyValue(keyword, length, keywords)) {
533536
case 0:
@@ -545,6 +548,9 @@ bool IODEF(SetBlank)(Cookie cookie, const char *keyword, std::size_t length) {
545548

546549
bool IODEF(SetDecimal)(Cookie cookie, const char *keyword, std::size_t length) {
547550
IoStatementState &io{*cookie};
551+
if (auto *open{io.get_if<OpenStatementState>()}) {
552+
open->set_mustBeFormatted();
553+
}
548554
static const char *keywords[]{"COMMA", "POINT", nullptr};
549555
switch (IdentifyValue(keyword, length, keywords)) {
550556
case 0:
@@ -562,6 +568,9 @@ bool IODEF(SetDecimal)(Cookie cookie, const char *keyword, std::size_t length) {
562568

563569
bool IODEF(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
564570
IoStatementState &io{*cookie};
571+
if (auto *open{io.get_if<OpenStatementState>()}) {
572+
open->set_mustBeFormatted();
573+
}
565574
static const char *keywords[]{"APOSTROPHE", "QUOTE", "NONE", nullptr};
566575
switch (IdentifyValue(keyword, length, keywords)) {
567576
case 0:
@@ -583,6 +592,9 @@ bool IODEF(SetDelim)(Cookie cookie, const char *keyword, std::size_t length) {
583592
bool IODEF(SetPad)(Cookie cookie, const char *keyword, std::size_t length) {
584593
IoStatementState &io{*cookie};
585594
IoErrorHandler &handler{io.GetIoErrorHandler()};
595+
if (auto *open{io.get_if<OpenStatementState>()}) {
596+
open->set_mustBeFormatted();
597+
}
586598
io.mutableModes().pad = YesOrNo(keyword, length, "PAD", handler);
587599
return !handler.InError();
588600
}
@@ -617,6 +629,9 @@ bool IODEF(SetRec)(Cookie cookie, std::int64_t rec) {
617629

618630
bool IODEF(SetRound)(Cookie cookie, const char *keyword, std::size_t length) {
619631
IoStatementState &io{*cookie};
632+
if (auto *open{io.get_if<OpenStatementState>()}) {
633+
open->set_mustBeFormatted();
634+
}
620635
static const char *keywords[]{"UP", "DOWN", "ZERO", "NEAREST", "COMPATIBLE",
621636
"PROCESSOR_DEFINED", nullptr};
622637
switch (IdentifyValue(keyword, length, keywords)) {
@@ -647,6 +662,9 @@ bool IODEF(SetRound)(Cookie cookie, const char *keyword, std::size_t length) {
647662

648663
bool IODEF(SetSign)(Cookie cookie, const char *keyword, std::size_t length) {
649664
IoStatementState &io{*cookie};
665+
if (auto *open{io.get_if<OpenStatementState>()}) {
666+
open->set_mustBeFormatted();
667+
}
650668
static const char *keywords[]{
651669
"PLUS", "SUPPRESS", "PROCESSOR_DEFINED", nullptr};
652670
switch (IdentifyValue(keyword, length, keywords)) {
@@ -784,6 +802,7 @@ bool IODEF(SetCarriagecontrol)(
784802
io.GetIoErrorHandler().Crash(
785803
"SetCarriageControl() called after GetNewUnit() for an OPEN statement");
786804
}
805+
open->set_mustBeFormatted();
787806
static const char *keywords[]{"LIST", "FORTRAN", "NONE", nullptr};
788807
switch (IdentifyValue(keyword, length, keywords)) {
789808
case 0:
@@ -840,6 +859,7 @@ bool IODEF(SetEncoding)(
840859
io.GetIoErrorHandler().Crash(
841860
"SetEncoding() called after GetNewUnit() for an OPEN statement");
842861
}
862+
open->set_mustBeFormatted();
843863
// Allow the encoding to be changed on an open unit -- it's
844864
// useful and safe.
845865
static const char *keywords[]{"UTF-8", "DEFAULT", nullptr};
@@ -872,10 +892,10 @@ bool IODEF(SetForm)(Cookie cookie, const char *keyword, std::size_t length) {
872892
}
873893
static const char *keywords[]{"FORMATTED", "UNFORMATTED", "BINARY", nullptr};
874894
switch (IdentifyValue(keyword, length, keywords)) {
875-
case 0:
895+
case 0: // FORM='FORMATTED'
876896
open->set_isUnformatted(false);
877897
break;
878-
case 1:
898+
case 1: // FORM='UNFORMATTED'
879899
open->set_isUnformatted(true);
880900
break;
881901
case 2: // legacy FORM='BINARY' means an unformatted stream

flang-rt/lib/runtime/io-stmt.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,17 @@ void OpenStatementState::CompleteOperation() {
352352
// Set default format (C.7.4 point 2).
353353
unit().isUnformatted = unit().access != Access::Sequential;
354354
}
355+
if (unit().isUnformatted.value_or(false) && mustBeFormatted_) {
356+
// This is an unformatted unit, but the OPEN statement contained at least
357+
// one specifier that is not permitted unless the unit is formatted
358+
// (e.g., BLANK=). Programs that want to detect this error (i.e., tests)
359+
// should be informed about it, but don't crash the program otherwise
360+
// since most other compilers let it slide.
361+
if (HasErrorRecovery()) {
362+
SignalError("FORM='UNFORMATTED' is not allowed with OPEN specifiers that "
363+
"apply only to formatted units");
364+
}
365+
}
355366
if (!wasExtant_ && InError()) {
356367
// Release the new unit on failure
357368
set_destroy();

0 commit comments

Comments
 (0)