[flang][runtime] EOF is recoverable only with END= or IOSTAT=
authorPeter Klausler <pklausler@nvidia.com>
Thu, 9 Mar 2023 00:09:01 +0000 (16:09 -0800)
committerPeter Klausler <pklausler@nvidia.com>
Fri, 10 Mar 2023 18:35:18 +0000 (10:35 -0800)
The runtime's I/O error handler was not crashing the program on an
end-of-file condition that arises for a data transfer statement with
at least one of ERR= or IOMSG= and none of END= or IOSTAT= control
items.  This turns out to be incorrect (per subclause 12.11);
an EOF is recoverable only for END= and/or IOSTAT=, and an
non-advancing end-of-record is recoverable only for EOR= and/or IOSTAT=.

Differential Revision: https://reviews.llvm.org/D145754

flang/runtime/io-error.cpp

index e333f6d..56e4b24 100644 (file)
 namespace Fortran::runtime::io {
 
 void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
-  if (iostatOrErrno == IostatEnd && (flags_ & hasEnd)) {
-    if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
-      ioStat_ = IostatEnd;
+  // Note that IOMSG= alone without IOSTAT=/END=/EOR=/ERR= does not suffice
+  // for error recovery (see F'2018 subclause 12.11).
+  switch (iostatOrErrno) {
+  case IostatOk:
+    return;
+  case IostatEnd:
+    if (flags_ & (hasIoStat | hasEnd)) {
+      if (ioStat_ == IostatOk || ioStat_ < IostatEnd) {
+        ioStat_ = IostatEnd;
+      }
+      return;
     }
-  } else if (iostatOrErrno == IostatEor && (flags_ & hasEor)) {
-    if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
-      ioStat_ = IostatEor; // least priority
+    break;
+  case IostatEor:
+    if (flags_ & (hasIoStat | hasEor)) {
+      if (ioStat_ == IostatOk || ioStat_ < IostatEor) {
+        ioStat_ = IostatEor; // least priority
+      }
+      return;
     }
-  } else if (iostatOrErrno != IostatOk) {
-    if (flags_ & (hasIoStat | hasIoMsg | hasErr)) {
+    break;
+  default:
+    if (flags_ & (hasIoStat | hasErr)) {
       if (ioStat_ <= 0) {
         ioStat_ = iostatOrErrno; // priority over END=/EOR=
         if (msg && (flags_ & hasIoMsg)) {
@@ -39,17 +52,21 @@ void IoErrorHandler::SignalError(int iostatOrErrno, const char *msg, ...) {
           va_end(ap);
         }
       }
-    } else if (msg) {
-      va_list ap;
-      va_start(ap, msg);
-      CrashArgs(msg, ap);
-      va_end(ap);
-    } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
-      Crash(errstr);
-    } else {
-      Crash("I/O error (errno=%d): %s", iostatOrErrno,
-          std::strerror(iostatOrErrno));
+      return;
     }
+    break;
+  }
+  // I/O error not caught!
+  if (msg) {
+    va_list ap;
+    va_start(ap, msg);
+    CrashArgs(msg, ap);
+    va_end(ap);
+  } else if (const char *errstr{IostatErrorString(iostatOrErrno)}) {
+    Crash(errstr);
+  } else {
+    Crash("I/O error (errno=%d): %s", iostatOrErrno,
+        std::strerror(iostatOrErrno));
   }
 }