Skip to content

[flang][preprocessor] Fixed-form continuation across preprocessing di… #95332

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 13, 2024

Conversation

klausler
Copy link
Contributor

…rective

Implement fixed-form line continuation when the continuation line is the result of text produced by an #include or other preprocessing directive. This accommodates the somewhat common practice of putting dummy or actual arguments into a header file and #including it into several code sites.

Fixes #78928.

…rective

Implement fixed-form line continuation when the continuation line is
the result of text produced by an #include or other preprocessing
directive.  This accommodates the somewhat common practice of putting
dummy or actual arguments into a header file and #including it into
several code sites.

Fixes llvm#78928.
@klausler klausler requested a review from psteinfeld June 12, 2024 23:45
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:parser labels Jun 12, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 12, 2024

@llvm/pr-subscribers-flang-parser

Author: Peter Klausler (klausler)

Changes

…rective

Implement fixed-form line continuation when the continuation line is the result of text produced by an #include or other preprocessing directive. This accommodates the somewhat common practice of putting dummy or actual arguments into a header file and #including it into several code sites.

Fixes #78928.


Full diff: https://github.com/llvm/llvm-project/pull/95332.diff

8 Files Affected:

  • (modified) flang/include/flang/Parser/provenance.h (+5)
  • (modified) flang/include/flang/Parser/token-sequence.h (+1)
  • (modified) flang/lib/Parser/prescan.cpp (+17-3)
  • (modified) flang/lib/Parser/prescan.h (+2)
  • (modified) flang/lib/Parser/provenance.cpp (+10)
  • (modified) flang/lib/Parser/token-sequence.cpp (+8-4)
  • (added) flang/test/Preprocessing/ff-args.h (+1)
  • (added) flang/test/Preprocessing/ff-include-args.F (+14)
diff --git a/flang/include/flang/Parser/provenance.h b/flang/include/flang/Parser/provenance.h
index 73d500f32831b..42c5b3de2cbe2 100644
--- a/flang/include/flang/Parser/provenance.h
+++ b/flang/include/flang/Parser/provenance.h
@@ -257,6 +257,10 @@ class CookedSource {
     provenanceMap_.Put(pm);
   }
 
+  void MarkPossibleFixedFormContinuation() {
+    possibleFixedFormContinuations_.push_back(BufferedBytes());
+  }
+
   std::size_t BufferedBytes() const;
   void Marshal(AllCookedSources &); // marshals text into one contiguous block
   void CompileProvenanceRangeToOffsetMappings(AllSources &);
@@ -269,6 +273,7 @@ class CookedSource {
   std::string data_; // all of it, prescanned and preprocessed
   OffsetToProvenanceMappings provenanceMap_;
   ProvenanceRangeToOffsetMappings invertedMap_;
+  std::list<std::size_t> possibleFixedFormContinuations_;
 };
 
 class AllCookedSources {
diff --git a/flang/include/flang/Parser/token-sequence.h b/flang/include/flang/Parser/token-sequence.h
index ee5f71edd03c8..1f82a3c1a203a 100644
--- a/flang/include/flang/Parser/token-sequence.h
+++ b/flang/include/flang/Parser/token-sequence.h
@@ -125,6 +125,7 @@ class TokenSequence {
   TokenSequence &ClipComment(const Prescanner &, bool skipFirst = false);
   const TokenSequence &CheckBadFortranCharacters(
       Messages &, const Prescanner &, bool allowAmpersand) const;
+  bool BadlyNestedParentheses() const;
   const TokenSequence &CheckBadParentheses(Messages &) const;
   void Emit(CookedSource &) const;
   llvm::raw_ostream &Dump(llvm::raw_ostream &) const;
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index e4801c36505b4..8efcd617cf0f9 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -295,8 +295,13 @@ void Prescanner::CheckAndEmitLine(
   // Applications play shenanigans with line continuation before and
   // after #include'd subprogram argument lists.
   if (!isNestedInIncludeDirective_ && !omitNewline_ &&
-      !afterIncludeDirective_) {
-    tokens.CheckBadParentheses(messages_);
+      !afterIncludeDirective_ && tokens.BadlyNestedParentheses()) {
+    if (inFixedForm_ && nextLine_ < limit_ &&
+        IsPreprocessorDirectiveLine(nextLine_)) {
+      // don't complain
+    } else {
+      tokens.CheckBadParentheses(messages_);
+    }
   }
   tokens.Emit(cooked_);
   if (omitNewline_) {
@@ -350,7 +355,16 @@ void Prescanner::LabelField(TokenSequence &token) {
     ++column_;
   }
   if (badColumn && !preprocessor_.IsNameDefined(token.CurrentOpenToken())) {
-    if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
+    if (prescannerNesting_ > 0 && *badColumn == 6 &&
+        cooked_.BufferedBytes() == firstCookedCharacterOffset_) {
+      // This is the first source line in #included text or conditional
+      // code under #if.
+      // 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
+      // CookedSource::Marshal().
+      cooked_.MarkPossibleFixedFormContinuation();
+    } else if (features_.ShouldWarn(common::UsageWarning::Scanning)) {
       Say(GetProvenance(start + *badColumn - 1),
           *badColumn == 6
               ? "Statement should not begin with a continuation line"_warn_en_US
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index cf64bdb02a9b7..b6f6d2ca439ee 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -247,6 +247,8 @@ class Prescanner {
   bool omitNewline_{false};
   bool skipLeadingAmpersand_{false};
 
+  const std::size_t firstCookedCharacterOffset_{cooked_.BufferedBytes()};
+
   const Provenance spaceProvenance_{
       allSources_.CompilerInsertionProvenance(' ')};
   const Provenance backslashProvenance_{
diff --git a/flang/lib/Parser/provenance.cpp b/flang/lib/Parser/provenance.cpp
index 55ef67fd6288d..6e2e7326e2167 100644
--- a/flang/lib/Parser/provenance.cpp
+++ b/flang/lib/Parser/provenance.cpp
@@ -513,6 +513,16 @@ void CookedSource::Marshal(AllCookedSources &allCookedSources) {
       "(after end of source)"));
   data_ = buffer_.Marshal();
   buffer_.clear();
+  for (std::size_t ffStart : possibleFixedFormContinuations_) {
+    if (ffStart > 0 && ffStart + 1 < data_.size() &&
+        data_[ffStart - 1] == '\n' && data_[ffStart] == ' ') {
+      // This fixed form include line is the first source line in an
+      // #include file (or after an empty one).  Connect it with the previous
+      // source line by deleting its terminal newline.
+      data_[ffStart - 1] = ' ';
+    }
+  }
+  possibleFixedFormContinuations_.clear();
   allCookedSources.Register(*this);
 }
 
diff --git a/flang/lib/Parser/token-sequence.cpp b/flang/lib/Parser/token-sequence.cpp
index 40560bbacb54f..133e60ba4f009 100644
--- a/flang/lib/Parser/token-sequence.cpp
+++ b/flang/lib/Parser/token-sequence.cpp
@@ -378,9 +378,7 @@ const TokenSequence &TokenSequence::CheckBadFortranCharacters(
   return *this;
 }
 
-const TokenSequence &TokenSequence::CheckBadParentheses(
-    Messages &messages) const {
-  // First, a quick pass with no allocation for the common case
+bool TokenSequence::BadlyNestedParentheses() const {
   int nesting{0};
   std::size_t tokens{SizeInTokens()};
   for (std::size_t j{0}; j < tokens; ++j) {
@@ -394,8 +392,14 @@ const TokenSequence &TokenSequence::CheckBadParentheses(
       }
     }
   }
-  if (nesting != 0) {
+  return nesting != 0;
+}
+
+const TokenSequence &TokenSequence::CheckBadParentheses(
+    Messages &messages) const {
+  if (BadlyNestedParentheses()) {
     // There's an error; diagnose it
+    std::size_t tokens{SizeInTokens()};
     std::vector<std::size_t> stack;
     for (std::size_t j{0}; j < tokens; ++j) {
       CharBlock token{TokenAt(j)};
diff --git a/flang/test/Preprocessing/ff-args.h b/flang/test/Preprocessing/ff-args.h
new file mode 100644
index 0000000000000..99562784006c9
--- /dev/null
+++ b/flang/test/Preprocessing/ff-args.h
@@ -0,0 +1 @@
+     +3.14159)
\ No newline at end of file
diff --git a/flang/test/Preprocessing/ff-include-args.F b/flang/test/Preprocessing/ff-include-args.F
new file mode 100644
index 0000000000000..81e4102598c2f
--- /dev/null
+++ b/flang/test/Preprocessing/ff-include-args.F
@@ -0,0 +1,14 @@
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+! CHECK: call foo (  3.14159)
+! CHECK: subroutine foo(test)
+      call foo (
+#include "ff-args.h"
+      end
+#define TEST
+      subroutine foo(
+#ifdef TEST
+     +test)
+#else
+     +)
+#endif
+      end

Copy link
Contributor

@psteinfeld psteinfeld left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All builds and tests correctly and looks good.

@klausler klausler merged commit 86bee81 into llvm:main Jun 13, 2024
10 checks passed
@klausler klausler deleted the fixed-include branch June 13, 2024 18:22
EthanLuisMcDonough pushed a commit to EthanLuisMcDonough/llvm-project that referenced this pull request Aug 13, 2024
llvm#95332)

…rective

Implement fixed-form line continuation when the continuation line is the
result of text produced by an #include or other preprocessing directive.
This accommodates the somewhat common practice of putting dummy or
actual arguments into a header file and #including it into several code
sites.

Fixes llvm#78928.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:parser flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[flang] included sources are parsing separately
3 participants