[flang] Fix minor nits with INCLUDE line recognition
authorPeter Klausler <pklausler@nvidia.com>
Mon, 17 Jul 2023 16:42:12 +0000 (09:42 -0700)
committerPeter Klausler <pklausler@nvidia.com>
Mon, 17 Jul 2023 19:58:26 +0000 (12:58 -0700)
Fix some problems with INCLUDE line recognition pointed out by some
recently-added tests to the LLVM test suite.

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

flang/docs/Extensions.md
flang/lib/Parser/prescan.cpp
flang/test/Parser/Inputs/include-file [new file with mode: 0644]
flang/test/Parser/include.f [new file with mode: 0644]

index bb9c0eb..5f82b3f 100644 (file)
@@ -285,6 +285,10 @@ end
   it's an error only if the resolution is ambiguous.
 * An entity may appear in a `DATA` statement before its explicit
   type declaration under `IMPLICIT NONE(TYPE)`.
+* INCLUDE lines can start in any column, can be preceded in
+  fixed form source by a '0' in column 6, can contain spaces
+  between the letters of the word INCLUDE, and can have a
+  numeric character literal kind prefix on the file name.
 
 ### Extensions supported when enabled by options
 
@@ -412,6 +416,9 @@ end
   This is especially desirable when two generics of the same
   name are combined due to USE association and the mixture may
   be inadvertent.
+* Since Fortran 90, INCLUDE lines have been allowed to have
+  a numeric kind parameter prefix on the file name.  No other
+  Fortran compiler supports them that I can find.
 
 ## Behavior in cases where the standard is ambiguous or indefinite
 
index 6444c87..09b31b1 100644 (file)
@@ -847,12 +847,25 @@ const char *Prescanner::IsFreeFormComment(const char *p) const {
 
 std::optional<std::size_t> Prescanner::IsIncludeLine(const char *start) const {
   const char *p{SkipWhiteSpace(start)};
-  for (char ch : "include"s) {
-    if (ToLowerCaseLetter(*p++) != ch) {
+  if (*p == '0' && inFixedForm_ && p == start + 5) {
+    // Accept "     0INCLUDE" in fixed form.
+    p = SkipWhiteSpace(p + 1);
+  }
+  for (const char *q{"include"}; *q; ++q) {
+    if (ToLowerCaseLetter(*p) != *q) {
       return std::nullopt;
     }
+    p = SkipWhiteSpace(p + 1);
+  }
+  if (IsDecimalDigit(*p)) { // accept & ignore a numeric kind prefix
+    for (p = SkipWhiteSpace(p + 1); IsDecimalDigit(*p);
+         p = SkipWhiteSpace(p + 1)) {
+    }
+    if (*p != '_') {
+      return std::nullopt;
+    }
+    p = SkipWhiteSpace(p + 1);
   }
-  p = SkipWhiteSpace(p);
   if (*p == '"' || *p == '\'') {
     return {p - start};
   }
@@ -1022,7 +1035,11 @@ const char *Prescanner::FixedFormContinuationLine(bool mightNeedSpace) {
         nextLine_[3] == ' ' && nextLine_[4] == ' ') {
       char col6{nextLine_[5]};
       if (col6 != '\n' && col6 != '\t' && col6 != ' ' && col6 != '0') {
-        return nextLine_ + 6;
+        if ((col6 == 'i' || col6 == 'I') && IsIncludeLine(nextLine_)) {
+          // It's An INCLUDE line, not a continuation
+        } else {
+          return nextLine_ + 6;
+        }
       }
     }
     if (IsImplicitContinuation()) {
diff --git a/flang/test/Parser/Inputs/include-file b/flang/test/Parser/Inputs/include-file
new file mode 100644 (file)
index 0000000..f2cb4f5
--- /dev/null
@@ -0,0 +1 @@
+      x = 1
diff --git a/flang/test/Parser/include.f b/flang/test/Parser/include.f
new file mode 100644 (file)
index 0000000..8a7fe3a
--- /dev/null
@@ -0,0 +1,68 @@
+! RUN: %flang_fc1 -E -I %S/Inputs %s 2>&1 | FileCheck %s
+      include 'include-file'
+      include "include-file"
+      include 1_'include-file'
+      include 1_"include-file"
+      i n c l u d e 'include-file'
+      INCLUDE 'include-file'
+      I N C L U D E 'include-file'
+include 'include-file'
+include "include-file"
+include 1_'include-file'
+include 1_"include-file"
+i n c l u d e 'include-file'
+INCLUDE 'include-file'
+I N C L U D E 'include-file'
+     0include 'include-file'
+      x = 2
+     include 'include-file'
+      print *, "
+     1include 'not-an-include'
+     2"
+cinclude 'not-an-include'
+*include 'not-an-include'
+!include 'not-an-include'
+c     include 'not-an-include'
+*     include 'not-an-include'
+!     include 'not-an-include'
+      end
+
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include.f" 17
+!CHECK:      x = 2
+!CHECK:#line "{{.*[/\\]}}include-file" 1
+!CHECK:      x = 1
+!CHECK:#line "{{.*[/\\]}}include.f" 19
+!CHECK:      print *, "                                                        &
+!CHECK:     &include 'not-an-include'                                          &
+!CHECK:     &"
+!CHECK:      end