Skip to content

Conversation

@klausler
Copy link
Contributor

@klausler klausler commented Mar 7, 2025

Ignore any keyword macros with empty directives that might appear before a compiler directive.

Fixes #126459.

@llvmbot llvmbot added flang Flang issues not falling into any other category flang:parser labels Mar 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 7, 2025

@llvm/pr-subscribers-flang-parser

Author: Peter Klausler (klausler)

Changes

Ignore any keyword macros with empty directives that might appear before a compiler directive.

Fixes #126459.


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

5 Files Affected:

  • (modified) flang/include/flang/Parser/preprocessor.h (+1)
  • (modified) flang/lib/Parser/preprocessor.cpp (+9)
  • (modified) flang/lib/Parser/prescan.cpp (+32-10)
  • (modified) flang/lib/Parser/prescan.h (+1)
  • (added) flang/test/Preprocessing/bug126459.F90 (+5)
diff --git a/flang/include/flang/Parser/preprocessor.h b/flang/include/flang/Parser/preprocessor.h
index f8d3346065981..86528a7e68def 100644
--- a/flang/include/flang/Parser/preprocessor.h
+++ b/flang/include/flang/Parser/preprocessor.h
@@ -81,6 +81,7 @@ class Preprocessor {
   void Define(const std::string &macro, const std::string &value);
   void Undefine(std::string macro);
   bool IsNameDefined(const CharBlock &);
+  bool IsNameDefinedEmpty(const CharBlock &);
   bool IsFunctionLikeDefinition(const CharBlock &);
   bool AnyDefinitions() const { return !definitions_.empty(); }
   bool InConditional() const { return !ifStack_.empty(); }
diff --git a/flang/lib/Parser/preprocessor.cpp b/flang/lib/Parser/preprocessor.cpp
index 6c257f57c2d57..7e6a1c2ca6977 100644
--- a/flang/lib/Parser/preprocessor.cpp
+++ b/flang/lib/Parser/preprocessor.cpp
@@ -842,6 +842,15 @@ bool Preprocessor::IsNameDefined(const CharBlock &token) {
   return definitions_.find(token) != definitions_.end();
 }
 
+bool Preprocessor::IsNameDefinedEmpty(const CharBlock &token) {
+  if (auto it{definitions_.find(token)}; it != definitions_.end()) {
+    const Definition &def{it->second};
+    return !def.isFunctionLike() && def.replacement().SizeInChars() == 0;
+  } else {
+    return false;
+  }
+}
+
 bool Preprocessor::IsFunctionLikeDefinition(const CharBlock &token) {
   auto it{definitions_.find(token)};
   return it != definitions_.end() && it->second.isFunctionLike();
diff --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index f479cc51871ef..0b4c9d9f7378d 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -145,10 +145,8 @@ void Prescanner::Statement() {
     if (inFixedForm_) {
       CHECK(IsFixedFormCommentChar(*at_));
     } else {
-      while (int n{IsSpaceOrTab(at_)}) {
-        at_ += n, ++column_;
-      }
-      CHECK(*at_ == '!');
+      at_ += line.payloadOffset;
+      column_ += line.payloadOffset;
     }
     std::optional<int> condOffset;
     if (directiveSentinel_[0] == '$' && directiveSentinel_[1] == '\0') {
@@ -597,6 +595,30 @@ const char *Prescanner::SkipWhiteSpace(const char *p) {
   return p;
 }
 
+const char *Prescanner::SkipWhiteSpaceIncludingEmptyMacros(
+    const char *p) const {
+  while (true) {
+    if (int n{IsSpaceOrTab(p)}) {
+      p += n;
+    } else if (preprocessor_.AnyDefinitions() && IsLegalIdentifierStart(*p)) {
+      // Skip keyword macros with empty definitions
+      const char *q{p + 1};
+      while (IsLegalInIdentifier(*q)) {
+        ++q;
+      }
+      if (preprocessor_.IsNameDefinedEmpty(
+              CharBlock{p, static_cast<std::size_t>(q - p)})) {
+        p = q;
+      } else {
+        break;
+      }
+    } else {
+      break;
+    }
+  }
+  return p;
+}
+
 const char *Prescanner::SkipWhiteSpaceAndCComments(const char *p) const {
   while (true) {
     if (int n{IsSpaceOrTab(p)}) {
@@ -1463,18 +1485,18 @@ Prescanner::IsFixedFormCompilerDirectiveLine(const char *start) const {
   *sp = '\0';
   if (const char *ss{IsCompilerDirectiveSentinel(
           sentinel, static_cast<std::size_t>(sp - sentinel))}) {
-    std::size_t payloadOffset = p - start;
-    return {LineClassification{
-        LineClassification::Kind::CompilerDirective, payloadOffset, ss}};
+    return {
+        LineClassification{LineClassification::Kind::CompilerDirective, 0, ss}};
   }
   return std::nullopt;
 }
 
 std::optional<Prescanner::LineClassification>
 Prescanner::IsFreeFormCompilerDirectiveLine(const char *start) const {
-  if (const char *p{SkipWhiteSpace(start)}; p && *p++ == '!') {
+  if (const char *p{SkipWhiteSpaceIncludingEmptyMacros(start)};
+      p && *p++ == '!') {
     if (auto maybePair{IsCompilerDirectiveSentinel(p)}) {
-      auto offset{static_cast<std::size_t>(maybePair->second - start)};
+      auto offset{static_cast<std::size_t>(p - start - 1)};
       return {LineClassification{LineClassification::Kind::CompilerDirective,
           offset, maybePair->first}};
     }
@@ -1529,7 +1551,7 @@ Prescanner::IsCompilerDirectiveSentinel(const char *p) const {
     if (int n{*p == '&' ? 1 : IsSpaceOrTab(p)}) {
       if (j > 0) {
         sentinel[j] = '\0';
-        p = SkipWhiteSpace(p + n);
+        p = SkipWhiteSpaceIncludingEmptyMacros(p + n);
         if (*p != '!') {
           if (const char *sp{IsCompilerDirectiveSentinel(sentinel, j)}) {
             return std::make_pair(sp, p);
diff --git a/flang/lib/Parser/prescan.h b/flang/lib/Parser/prescan.h
index e2440ad6fbc42..9cf1b389f5b19 100644
--- a/flang/lib/Parser/prescan.h
+++ b/flang/lib/Parser/prescan.h
@@ -182,6 +182,7 @@ class Prescanner {
   void SkipCComments();
   void SkipSpaces();
   static const char *SkipWhiteSpace(const char *);
+  const char *SkipWhiteSpaceIncludingEmptyMacros(const char *) const;
   const char *SkipWhiteSpaceAndCComments(const char *) const;
   const char *SkipCComment(const char *) const;
   bool NextToken(TokenSequence &);
diff --git a/flang/test/Preprocessing/bug126459.F90 b/flang/test/Preprocessing/bug126459.F90
new file mode 100644
index 0000000000000..fae8a07659f72
--- /dev/null
+++ b/flang/test/Preprocessing/bug126459.F90
@@ -0,0 +1,5 @@
+! RUN: %flang -E -fopenmp %s 2>&1 | FileCheck %s
+!CHECK: NDIR=0
+#define BLANKMACRO
+BLANKMACRO !$ NDIR=0
+end

Ignore any keyword macros with empty directives that might
appear before a compiler directive.

Fixes llvm#126459.
@klausler klausler merged commit c189852 into llvm:main Mar 10, 2025
11 checks passed
@klausler klausler deleted the bug126459 branch March 10, 2025 20:21
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] preprocessor issue with openmp conditional code

4 participants