[flang-commits] [flang] [flang][preprocessing] Mix preprocessing directives with free form li… (PR #96244)

Peter Klausler via flang-commits flang-commits at lists.llvm.org
Sun Jun 23 10:19:55 PDT 2024


https://github.com/klausler updated https://github.com/llvm/llvm-project/pull/96244

>From e4ef4335b41e95a6e70c21396d76e93ec4c16e4b Mon Sep 17 00:00:00 2001
From: Peter Klausler <pklausler at nvidia.com>
Date: Thu, 20 Jun 2024 15:28:08 -0700
Subject: [PATCH] [flang][preprocessing] Mix preprocessing directives with free
 form line continuation

Allow preprocessing directives to appear between a source line and
its continuation, including conditional compilation directives
(#if, #ifdef, &c.).

Fixes https://github.com/llvm/llvm-project/issues/95476.
---
 flang/include/flang/Parser/preprocessor.h     |  1 +
 flang/lib/Parser/prescan.cpp                  | 70 +++++++++++--------
 flang/lib/Parser/prescan.h                    |  2 +-
 flang/test/Preprocessing/cond-contin.F90      | 21 ++++++
 .../directive-contin-with-pp.F90              |  2 +-
 flang/test/Preprocessing/inc-contin-1.F       |  6 ++
 flang/test/Preprocessing/inc-contin-1.h       |  1 +
 flang/test/Preprocessing/inc-contin-2.F90     |  9 +++
 flang/test/Preprocessing/inc-contin-2a.h      |  1 +
 flang/test/Preprocessing/inc-contin-2b.h      |  1 +
 flang/test/Preprocessing/include-args.F90     |  2 +-
 .../unittests/Frontend/FrontendActionTest.cpp |  2 +-
 12 files changed, 84 insertions(+), 34 deletions(-)
 create mode 100644 flang/test/Preprocessing/cond-contin.F90
 create mode 100644 flang/test/Preprocessing/inc-contin-1.F
 create mode 100644 flang/test/Preprocessing/inc-contin-1.h
 create mode 100644 flang/test/Preprocessing/inc-contin-2.F90
 create mode 100644 flang/test/Preprocessing/inc-contin-2a.h
 create mode 100644 flang/test/Preprocessing/inc-contin-2b.h

diff --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h
index c3076435be5f0..57690dd226f62 100644
--- a/flang/include/flang/Parser/preprocessor.h
+++ b/flang/include/flang/Parser/preprocessor.h
@@ -82,6 +82,7 @@ class Preprocessor {
   bool IsNameDefined(const CharBlock &);
   bool IsFunctionLikeDefinition(const CharBlock &);
   bool AnyDefinitions() const { return !definitions_.empty(); }
+  bool InConditional() const { return !ifStack_.empty(); }
 
   // When called with partialFunctionLikeMacro not null, MacroReplacement()
   // and ReplaceMacros() handle an unclosed function-like macro reference
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 2a6ecfbb0830e..42aa829e0ed5b 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -105,13 +105,15 @@ void Prescanner::Statement() {
     NextLine();
     return;
   case LineClassification::Kind::ConditionalCompilationDirective:
-  case LineClassification::Kind::DefinitionDirective:
-  case LineClassification::Kind::PreprocessorDirective:
+  case LineClassification::Kind::IncludeDirective:
     preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
+    afterPreprocessingDirective_ = true;
+    skipLeadingAmpersand_ |= !inFixedForm_;
     return;
-  case LineClassification::Kind::IncludeDirective:
+  case LineClassification::Kind::PreprocessorDirective:
+  case LineClassification::Kind::DefinitionDirective:
     preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
-    afterIncludeDirective_ = true;
+    // Don't set afterPreprocessingDirective_
     return;
   case LineClassification::Kind::CompilerDirective: {
     directiveSentinel_ = line.sentinel;
@@ -171,15 +173,17 @@ void Prescanner::Statement() {
         NextChar();
       }
       LabelField(tokens);
-    } else if (skipLeadingAmpersand_) {
-      skipLeadingAmpersand_ = false;
-      const char *p{SkipWhiteSpace(at_)};
-      if (p < limit_ && *p == '&') {
-        column_ += ++p - at_;
-        at_ = p;
-      }
     } else {
-      SkipSpaces();
+      if (skipLeadingAmpersand_) {
+        skipLeadingAmpersand_ = false;
+        const char *p{SkipWhiteSpace(at_)};
+        if (p < limit_ && *p == '&') {
+          column_ += ++p - at_;
+          at_ = p;
+        }
+      } else {
+        SkipSpaces();
+      }
       // Check for a leading identifier that might be a keyword macro
       // that will expand to anything indicating a non-source line, like
       // a comment marker or directive sentinel.  If so, disable line
@@ -289,13 +293,14 @@ void Prescanner::CheckAndEmitLine(
   tokens.CheckBadFortranCharacters(
       messages_, *this, disableSourceContinuation_);
   // Parenthesis nesting check does not apply while any #include is
-  // active, nor on the lines before and after a top-level #include.
+  // active, nor on the lines before and after a top-level #include,
+  // nor before or after conditional source.
   // Applications play shenanigans with line continuation before and
-  // after #include'd subprogram argument lists.
+  // after #include'd subprogram argument lists and conditional source.
   if (!isNestedInIncludeDirective_ && !omitNewline_ &&
-      !afterIncludeDirective_ && tokens.BadlyNestedParentheses()) {
-    if (inFixedForm_ && nextLine_ < limit_ &&
-        IsPreprocessorDirectiveLine(nextLine_)) {
+      !afterPreprocessingDirective_ && tokens.BadlyNestedParentheses() &&
+      !preprocessor_.InConditional()) {
+    if (nextLine_ < limit_ && IsPreprocessorDirectiveLine(nextLine_)) {
       // don't complain
     } else {
       tokens.CheckBadParentheses(messages_);
@@ -306,7 +311,7 @@ void Prescanner::CheckAndEmitLine(
     omitNewline_ = false;
   } else {
     cooked_.Put('\n', newlineProvenance);
-    afterIncludeDirective_ = false;
+    afterPreprocessingDirective_ = false;
   }
 }
 
@@ -353,10 +358,11 @@ void Prescanner::LabelField(TokenSequence &token) {
     ++column_;
   }
   if (badColumn && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) {
-    if (prescannerNesting_ > 0 && *badColumn == 6 &&
-        cooked_.BufferedBytes() == firstCookedCharacterOffset_) {
-      // This is the first source line in #included text or conditional
-      // code under #if.
+    if ((prescannerNesting_ > 0 && *badColumn == 6 &&
+            cooked_.BufferedBytes() == firstCookedCharacterOffset_) ||
+        afterPreprocessingDirective_) {
+      // This is the first source line in #include'd text or conditional
+      // code under #if, or the first source line after such.
       // If it turns out that the preprocessed text begins with a
       // fixed form continuation line, the newline at the end
       // of the latest source line beforehand will be deleted in
@@ -599,7 +605,7 @@ bool Prescanner::NextToken(TokenSequence &tokens) {
       char previous{at_ <= start_ ? ' ' : at_[-1]};
       NextChar();
       SkipSpaces();
-      if (*at_ == '\n') {
+      if (*at_ == '\n' && !omitNewline_) {
         // Discard white space at the end of a line.
       } else if (!inPreprocessorDirective_ &&
           (previous == '(' || *at_ == '(' || *at_ == ')')) {
@@ -1069,6 +1075,17 @@ bool Prescanner::SkipCommentLine(bool afterAmpersand) {
     return true;
   } else if (inPreprocessorDirective_) {
     return false;
+  } else if (afterAmpersand &&
+      (lineClass.kind ==
+              LineClassification::Kind::ConditionalCompilationDirective ||
+          lineClass.kind == LineClassification::Kind::DefinitionDirective ||
+          lineClass.kind == LineClassification::Kind::PreprocessorDirective ||
+          lineClass.kind == LineClassification::Kind::IncludeDirective ||
+          lineClass.kind == LineClassification::Kind::IncludeLine)) {
+    SkipToEndOfLine();
+    omitNewline_ = true;
+    skipLeadingAmpersand_ = true;
+    return false;
   } else if (lineClass.kind ==
           LineClassification::Kind::ConditionalCompilationDirective ||
       lineClass.kind == LineClassification::Kind::PreprocessorDirective) {
@@ -1080,13 +1097,6 @@ bool Prescanner::SkipCommentLine(bool afterAmpersand) {
     // continued line).
     preprocessor_.Directive(TokenizePreprocessorDirective(), *this);
     return true;
-  } else if (afterAmpersand &&
-      (lineClass.kind == LineClassification::Kind::IncludeDirective ||
-          lineClass.kind == LineClassification::Kind::IncludeLine)) {
-    SkipToEndOfLine();
-    omitNewline_ = true;
-    skipLeadingAmpersand_ = true;
-    return false;
   } else {
     return false;
   }
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index b6f6d2ca439ee..421ba97d324c9 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -214,7 +214,7 @@ class Prescanner {
   int prescannerNesting_{0};
   int continuationLines_{0};
   bool isPossibleMacroCall_{false};
-  bool afterIncludeDirective_{false};
+  bool afterPreprocessingDirective_{false};
   bool disableSourceContinuation_{false};
 
   Provenance startProvenance_;
diff --git a/flang/test/Preprocessing/cond-contin.F90 b/flang/test/Preprocessing/cond-contin.F90
new file mode 100644
index 0000000000000..9221e34e013f4
--- /dev/null
+++ b/flang/test/Preprocessing/cond-contin.F90
@@ -0,0 +1,21 @@
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s
+! CHECK: subroutine test(ARG1, FA, FB, ARG2)
+! CHECK: end
+
+subroutine test( &
+ARG1, &
+! test
+#ifndef SWAP
+#define ARG1 FA
+#define ARG2 FB
+#else
+#define ARG1 FB
+#define ARG2 FA
+#endif
+ARG1, ARG2, &
+! test
+#undef ARG1
+#undef ARG2
+&ARG2)
+! comment
+end
diff --git a/flang/test/Preprocessing/directive-contin-with-pp.F90 b/flang/test/Preprocessing/directive-contin-with-pp.F90
index be8eb4d3c1cee..64f1dc43f72b4 100644
--- a/flang/test/Preprocessing/directive-contin-with-pp.F90
+++ b/flang/test/Preprocessing/directive-contin-with-pp.F90
@@ -1,4 +1,4 @@
-! RUN: %flang -fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
+! RUN: %flang_fc1 -fdebug-unparse -fopenmp %s 2>&1 | FileCheck %s
 
 #define DIR_START !dir$
 #define DIR_CONT !dir$&
diff --git a/flang/test/Preprocessing/inc-contin-1.F b/flang/test/Preprocessing/inc-contin-1.F
new file mode 100644
index 0000000000000..7a4e3a0cb0b59
--- /dev/null
+++ b/flang/test/Preprocessing/inc-contin-1.F
@@ -0,0 +1,6 @@
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s
+! CHECK: call t(1  ,.false.)
+      program main
+#include "inc-contin-1.h"
+     $,.false.)
+      end
diff --git a/flang/test/Preprocessing/inc-contin-1.h b/flang/test/Preprocessing/inc-contin-1.h
new file mode 100644
index 0000000000000..d4b6461e75274
--- /dev/null
+++ b/flang/test/Preprocessing/inc-contin-1.h
@@ -0,0 +1 @@
+      call t(1
diff --git a/flang/test/Preprocessing/inc-contin-2.F90 b/flang/test/Preprocessing/inc-contin-2.F90
new file mode 100644
index 0000000000000..3386ee0dfcddc
--- /dev/null
+++ b/flang/test/Preprocessing/inc-contin-2.F90
@@ -0,0 +1,9 @@
+! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s
+! CHECK: print *, 3.14159
+! CHECK: print *, 3. 14159
+      program main
+#include "inc-contin-2a.h"
+     &14159
+#include "inc-contin-2b.h"
+     &14159
+      end program main
diff --git a/flang/test/Preprocessing/inc-contin-2a.h b/flang/test/Preprocessing/inc-contin-2a.h
new file mode 100644
index 0000000000000..24a4fa4830fa3
--- /dev/null
+++ b/flang/test/Preprocessing/inc-contin-2a.h
@@ -0,0 +1 @@
+print *, 3.&
diff --git a/flang/test/Preprocessing/inc-contin-2b.h b/flang/test/Preprocessing/inc-contin-2b.h
new file mode 100644
index 0000000000000..b84a464af86e3
--- /dev/null
+++ b/flang/test/Preprocessing/inc-contin-2b.h
@@ -0,0 +1 @@
+print *, 3. &
diff --git a/flang/test/Preprocessing/include-args.F90 b/flang/test/Preprocessing/include-args.F90
index 011e4dba13e73..7c521db666ff9 100644
--- a/flang/test/Preprocessing/include-args.F90
+++ b/flang/test/Preprocessing/include-args.F90
@@ -1,5 +1,5 @@
 ! RUN: %flang -E %s 2>&1 | FileCheck %s
-! CHECK: call foo(3.14159)
+! CHECK: call foo(3.14159 )
 call foo (&
 #include "args.h"
 )
diff --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp
index 123f428cc8b40..bdf5a23fdbf6a 100644
--- a/flang/unittests/Frontend/FrontendActionTest.cpp
+++ b/flang/unittests/Frontend/FrontendActionTest.cpp
@@ -143,7 +143,7 @@ TEST_F(FrontendActionTest, PrintPreprocessedInput) {
   EXPECT_TRUE(success);
   EXPECT_TRUE(!outputFileBuffer.empty());
   EXPECT_TRUE(
-      llvm::StringRef(outputFileBuffer.data()).starts_with("program b\n"));
+      llvm::StringRef(outputFileBuffer.data()).starts_with(" program b\n"));
 }
 
 TEST_F(FrontendActionTest, ParseSyntaxOnly) {



More information about the flang-commits mailing list