Skip to content

Commit 427aa37

Browse files
committed
[flang][runtime] Catch bad OPEN specifiers for unformatted files
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 2775c79 commit 427aa37

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)