[flang] Replay a FORMAT at the right position
authorpeter klausler <pklausler@nvidia.com>
Wed, 22 Jul 2020 00:10:14 +0000 (17:10 -0700)
committerpeter klausler <pklausler@nvidia.com>
Wed, 22 Jul 2020 01:59:49 +0000 (18:59 -0700)
When FORMAT control reaches the final parenthesis and data items
remain, we advance a record and revert to the beginning of the
FORMAT for further items.  But when the FORMAT contains any
nested parenthesized group of editing descriptors, possibly
repeated, reversion must be to the beginning of the last such
top-level parenthesized group, including its repetition count.

Reviewed By: sscalpone, PeteSteinfeld

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

flang/runtime/format-implementation.h
flang/unittests/Runtime/hello.cpp

index ce0e08b..a4453cd 100644 (file)
@@ -225,6 +225,7 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
   while (true) {
     std::optional<int> repeat;
     bool unlimited{false};
+    auto maybeReversionPoint{offset_};
     CharType ch{GetNextChar(context)};
     while (ch == ',' || ch == ':') {
       // Skip commas, and don't complain if they're missing; the format
@@ -254,6 +255,7 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
         return 0;
       }
       stack_[height_].start = offset_ - 1; // the '('
+      RUNTIME_CHECK(context, format_[stack_[height_].start] == '(');
       if (unlimited || height_ == 0) {
         stack_[height_].remaining = Iteration::unlimited;
         unlimitedLoopCheck = offset_ - 1;
@@ -265,6 +267,12 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
       } else {
         stack_[height_].remaining = 0;
       }
+      if (height_ == 1) {
+        // Subtle point (F'2018 13.4 para 9): tha last parenthesized group
+        // at height 1 becomes the restart point after control reaches the
+        // end of the format, including its repeat count.
+        stack_[0].start = maybeReversionPoint - 1;
+      }
       ++height_;
     } else if (height_ == 0) {
       context.SignalError(IostatErrorInFormat, "FORMAT lacks initial '('");
@@ -276,14 +284,15 @@ int FormatControl<CONTEXT>::CueUpNextDataEdit(Context &context, bool stop) {
         }
         context.AdvanceRecord(); // implied / before rightmost )
       }
+      auto restart{stack_[height_ - 1].start + 1};
       if (stack_[height_ - 1].remaining == Iteration::unlimited) {
-        offset_ = stack_[height_ - 1].start + 1;
+        offset_ = restart;
         if (offset_ == unlimitedLoopCheck) {
           context.SignalError(IostatErrorInFormat,
               "Unlimited repetition in FORMAT lacks data edit descriptors");
         }
       } else if (stack_[height_ - 1].remaining-- > 0) {
-        offset_ = stack_[height_ - 1].start + 1;
+        offset_ = restart;
       } else {
         --height_;
       }
@@ -396,7 +405,7 @@ DataEdit FormatControl<CONTEXT>::GetNextDataEdit(
     ++height_;
   }
   edit.repeat = 1;
-  if (height_ > 1) {
+  if (height_ > 1) { // Subtle: stack_[0].start doesn't necessarily point to '('
     int start{stack_[height_ - 1].start};
     if (format_[start] != '(') {
       if (stack_[height_ - 1].remaining > maxRepeat) {
index 71d4943..0543571 100644 (file)
@@ -38,16 +38,16 @@ static void hello() {
 }
 
 static void multiline() {
-  char buffer[4][32];
+  char buffer[5][32];
   StaticDescriptor<1> staticDescriptor[2];
   Descriptor &whole{staticDescriptor[0].descriptor()};
-  SubscriptValue extent[]{4};
+  SubscriptValue extent[]{5};
   whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent,
       CFI_attribute_pointer);
   whole.Dump();
   whole.Check();
   Descriptor &section{staticDescriptor[1].descriptor()};
-  SubscriptValue lowers[]{0}, uppers[]{3}, strides[]{1};
+  SubscriptValue lowers[]{0}, uppers[]{4}, strides[]{1};
   section.Establish(whole.type(), whole.ElementBytes(), nullptr, 1, extent,
       CFI_attribute_pointer);
   if (auto error{
@@ -57,12 +57,16 @@ static void multiline() {
   }
   section.Dump();
   section.Check();
-  const char *format{"('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,25X,'done')"};
+  const char *format{
+      "('?abcde,',T1,'>',T9,A,TL12,A,TR25,'<'//G0,17X,'abcd',1(2I4))"};
   auto cookie{IONAME(BeginInternalArrayFormattedOutput)(
       section, format, std::strlen(format))};
   IONAME(OutputAscii)(cookie, "WORLD", 5);
   IONAME(OutputAscii)(cookie, "HELLO", 5);
   IONAME(OutputInteger64)(cookie, 789);
+  for (int j{666}; j <= 999; j += 111) {
+    IONAME(OutputInteger64)(cookie, j);
+  }
   if (auto status{IONAME(EndIoStatement)(cookie)}) {
     Fail() << "multiline: '" << format << "' failed, status "
            << static_cast<int>(status) << '\n';
@@ -70,7 +74,8 @@ static void multiline() {
     test(format,
         ">HELLO, WORLD                  <"
         "                                "
-        "789                         done"
+        "789                 abcd 666 777"
+        " 888 999                        "
         "                                ",
         std::string{buffer[0], sizeof buffer});
   }