[flang-commits] [flang] 7cf198f - [flang][runtime] Don't do partial data transfer on short character read with ADVANCE='NO', PAD='NO'

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Mon Jul 17 10:04:27 PDT 2023


Author: Peter Klausler
Date: 2023-07-17T10:04:15-07:00
New Revision: 7cf198f7988bf94306d9a6cd26228b81f7c7ca7f

URL: https://github.com/llvm/llvm-project/commit/7cf198f7988bf94306d9a6cd26228b81f7c7ca7f
DIFF: https://github.com/llvm/llvm-project/commit/7cf198f7988bf94306d9a6cd26228b81f7c7ca7f.diff

LOG: [flang][runtime] Don't do partial data transfer on short character read with ADVANCE='NO', PAD='NO'

When a non-advancing formatted READ can't completely fill a CHARACTER
input item with data, and PAD='NO', don't modify the variable at all.
This matters when the error is recoverable.

Fixes https://github.com/llvm/llvm-project/issues/63772

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

Added: 
    

Modified: 
    flang/runtime/edit-input.cpp
    flang/runtime/io-stmt.cpp
    flang/runtime/io-stmt.h
    flang/unittests/Runtime/ExternalIOTest.cpp

Removed: 
    


################################################################################
diff  --git a/flang/runtime/edit-input.cpp b/flang/runtime/edit-input.cpp
index 7287d2e99979d9..98627c9dd82750 100644
--- a/flang/runtime/edit-input.cpp
+++ b/flang/runtime/edit-input.cpp
@@ -719,7 +719,7 @@ bool EditCharacterInput(
   }
   // When the field is wider than the variable, we drop the leading
   // characters.  When the variable is wider than the field, there can be
-  // trailing padding.
+  // trailing padding or an EOR condition.
   const char *input{nullptr};
   std::size_t ready{0};
   // Skip leading bytes.
@@ -729,11 +729,18 @@ bool EditCharacterInput(
   while (remaining > 0) {
     if (ready == 0) {
       ready = io.GetNextInputBytes(input);
-      if (ready == 0) {
-        if (io.CheckForEndOfRecord()) {
-          std::fill_n(x, length, ' '); // PAD='YES'
+      if (ready == 0 || (ready < remaining && edit.modes.nonAdvancing)) {
+        if (io.CheckForEndOfRecord(ready)) {
+          if (ready == 0) {
+            // PAD='YES' and no more data
+            std::fill_n(x, length, ' ');
+            return !io.GetIoErrorHandler().InError();
+          } else {
+            // Do partial read(s) then pad on last iteration
+          }
+        } else {
+          return !io.GetIoErrorHandler().InError();
         }
-        return !io.GetIoErrorHandler().InError();
       }
     }
     std::size_t chunk;

diff  --git a/flang/runtime/io-stmt.cpp b/flang/runtime/io-stmt.cpp
index 6935f1fdfa22cb..d18f81b93faf24 100644
--- a/flang/runtime/io-stmt.cpp
+++ b/flang/runtime/io-stmt.cpp
@@ -589,7 +589,7 @@ std::optional<char32_t> IoStatementState::NextInField(
       GotChar(byteCount);
       return next;
     }
-    if (CheckForEndOfRecord()) { // do padding
+    if (CheckForEndOfRecord(0)) { // do padding
       --*remaining;
       return std::optional<char32_t>{' '};
     }
@@ -597,11 +597,13 @@ std::optional<char32_t> IoStatementState::NextInField(
   return std::nullopt;
 }
 
-bool IoStatementState::CheckForEndOfRecord() {
+bool IoStatementState::CheckForEndOfRecord(std::size_t afterReading) {
   const ConnectionState &connection{GetConnectionState()};
   if (!connection.IsAtEOF()) {
     if (auto length{connection.EffectiveRecordLength()}) {
-      if (connection.positionInRecord >= *length) {
+      if (connection.positionInRecord +
+              static_cast<std::int64_t>(afterReading) >=
+          *length) {
         IoErrorHandler &handler{GetIoErrorHandler()};
         const auto &modes{mutableModes()};
         if (modes.nonAdvancing) {

diff  --git a/flang/runtime/io-stmt.h b/flang/runtime/io-stmt.h
index c1fdc29f8c255e..33653e69ad4ed6 100644
--- a/flang/runtime/io-stmt.h
+++ b/flang/runtime/io-stmt.h
@@ -169,7 +169,7 @@ class IoStatementState {
 
   // Detect and signal any end-of-record condition after input.
   // Returns true if at EOR and remaining input should be padded with blanks.
-  bool CheckForEndOfRecord();
+  bool CheckForEndOfRecord(std::size_t afterReading);
 
   // Skips spaces, advances records, and ignores NAMELIST comments
   std::optional<char32_t> GetNextNonBlank(std::size_t &byteCount) {

diff  --git a/flang/unittests/Runtime/ExternalIOTest.cpp b/flang/unittests/Runtime/ExternalIOTest.cpp
index 389abf1d0404a7..bc1fb7faa43149 100644
--- a/flang/unittests/Runtime/ExternalIOTest.cpp
+++ b/flang/unittests/Runtime/ExternalIOTest.cpp
@@ -525,35 +525,64 @@ TEST(ExternalIOTests, TestNonAvancingInput) {
   struct TestItems {
     std::string item;
     int expectedIoStat;
-    std::string expectedItemValue;
+    std::string expectedItemValue[2];
   };
   // Actual non advancing input IO test
   TestItems inputItems[]{
-      {std::string(4, '+'), IostatOk, "ABCD"},
-      {std::string(4, '+'), IostatOk, "EFGH"},
-      {std::string(4, '+'), IostatEor, "    "},
-      {std::string(2, '+'), IostatOk, "IJ"},
-      {std::string(8, '+'), IostatEor, "KLMNOP  "},
-      {std::string(10, '+'), IostatEor, "QRSTUVWX  "},
+      {std::string(4, '+'), IostatOk, "ABCD", "ABCD"},
+      {std::string(4, '+'), IostatOk, "EFGH", "EFGH"},
+      {std::string(4, '+'), IostatEor, "++++", "    "},
+      {std::string(2, '+'), IostatOk, "IJ", "IJ"},
+      {std::string(8, '+'), IostatEor, "++++++++", "KLMNOP  "},
+      {std::string(10, '+'), IostatEor, "++++++++++", "QRSTUVWX  "},
   };
 
+  // Test with PAD='NO'
   int j{0};
   for (auto &inputItem : inputItems) {
-    // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem
+    // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem
     io = IONAME(BeginExternalFormattedInput)(
         fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
     IONAME(EnableHandlers)(io, true, false, false, false, false);
     ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
+    ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j;
     bool result{
         IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
     ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
         << "InputAscii() " << j;
     ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
         << "EndIoStatement() for Read " << j;
-    ASSERT_EQ(inputItem.item, inputItem.expectedItemValue)
+    ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0])
+        << "Input-item value after non advancing read " << j;
+    j++;
+  }
+
+  // REWIND(UNIT=unit)
+  io = IONAME(BeginRewind)(unit, __FILE__, __LINE__);
+  ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)
+      << "EndIoStatement() for Rewind";
+
+  // Test again with PAD='YES'
+  j = 0;
+  for (auto &inputItem : inputItems) {
+    // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat)
+    // inputItem
+    io = IONAME(BeginExternalFormattedInput)(
+        fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__);
+    IONAME(EnableHandlers)(io, true, false, false, false, false);
+    ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j;
+    ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j;
+    bool result{
+        IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())};
+    ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk)
+        << "InputAscii() " << j;
+    ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat)
+        << "EndIoStatement() for Read " << j;
+    ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1])
         << "Input-item value after non advancing read " << j;
     j++;
   }
+
   // CLOSE(UNIT=unit)
   io = IONAME(BeginClose)(unit, __FILE__, __LINE__);
   ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk)


        


More information about the flang-commits mailing list