[flang-commits] [flang] 1bef59c - [flang][preprocessor] Further macro replacement of continued identifiers (#134302)

via flang-commits flang-commits at lists.llvm.org
Fri Apr 4 08:44:25 PDT 2025


Author: Peter Klausler
Date: 2025-04-04T08:44:22-07:00
New Revision: 1bef59c9db07840609c919fa95127decbfc3f55d

URL: https://github.com/llvm/llvm-project/commit/1bef59c9db07840609c919fa95127decbfc3f55d
DIFF: https://github.com/llvm/llvm-project/commit/1bef59c9db07840609c919fa95127decbfc3f55d.diff

LOG: [flang][preprocessor] Further macro replacement of continued identifiers (#134302)

The preprocessor can perform macro replacement within identifiers when
they are split up with Fortran line continuation, but is failing to do
macro replacement on a continued identifier when none of its parts are
replaced.

Added: 
    flang/test/Preprocessing/pp047.F
    flang/test/Preprocessing/pp135.F90

Modified: 
    flang/lib/Parser/prescan.cpp

Removed: 
    


################################################################################
diff  --git a/flang/lib/Parser/prescan.cpp b/flang/lib/Parser/prescan.cpp
index 809b728c47ffe..755cb18cb8caf 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -749,35 +749,48 @@ bool Prescanner::NextToken(TokenSequence &tokens) {
     }
     preventHollerith_ = false;
   } else if (IsLegalInIdentifier(*at_)) {
-    int parts{1};
-    const char *afterLast{nullptr};
+    std::size_t parts{1};
+    bool anyDefined{false};
+    bool hadContinuation{false};
+    // Subtlety: When an identifier is split across continuation lines,
+    // its parts are kept as distinct pp-tokens if that macro replacement
+    // should operate on them independently.  This trick accommodates the
+    // historic practice of using line continuation for token pasting after
+    // replacement.
+    // In free form, the macro to be replaced must have been preceded
+    // by '&' and followed by either '&' or, if last, the end of a line.
+    //   call &                call foo&        call foo&
+    //     &MACRO&      OR       &MACRO&   OR     &MACRO
+    //     &foo(...)             &(...)
     do {
       EmitChar(tokens, *at_);
       ++at_, ++column_;
-      afterLast = at_;
-      if (SkipToNextSignificantCharacter() && IsLegalIdentifierStart(*at_)) {
+      hadContinuation = SkipToNextSignificantCharacter();
+      if (hadContinuation && IsLegalIdentifierStart(*at_)) {
+        // Continued identifier
         tokens.CloseToken();
         ++parts;
+        if (!anyDefined &&
+            (parts > 2 || inFixedForm_ ||
+                (start > start_ && start[-1] == '&')) &&
+            preprocessor_.IsNameDefined(
+                tokens.TokenAt(tokens.SizeInTokens() - 1))) {
+          anyDefined = true;
+        }
       }
     } while (IsLegalInIdentifier(*at_));
-    if (parts >= 3) {
-      // Subtlety: When an identifier is split across three or more continuation
-      // lines (or two continuation lines, immediately preceded or followed
-      // by '&' free form continuation line markers, its parts are kept as
-      // distinct pp-tokens so that macro replacement operates on them
-      // independently.  This trick accommodates the historic practice of
-      // using line continuation for token pasting after replacement.
-    } else if (parts == 2) {
-      if (afterLast && afterLast < limit_) {
-        afterLast = SkipWhiteSpace(afterLast);
-      }
-      if ((start > start_ && start[-1] == '&') ||
-          (afterLast && afterLast < limit_ &&
-              (*afterLast == '&' || *afterLast == '\n'))) {
-        // call &                call foo&        call foo&
-        //   &MACRO&      OR       &MACRO&   OR     &MACRO
-        //   &foo(...)             &(...)
-      } else {
+    if (!anyDefined && parts > 1) {
+      tokens.CloseToken();
+      char after{*SkipWhiteSpace(at_)};
+      anyDefined = (hadContinuation || after == '\n' || after == '&') &&
+          preprocessor_.IsNameDefined(
+              tokens.TokenAt(tokens.SizeInTokens() - 1));
+      tokens.ReopenLastToken();
+    }
+    if (!anyDefined) {
+      // If no part was a defined macro, combine the parts into one so that
+      // the combination itself can be subject to macro replacement.
+      while (parts-- > 1) {
         tokens.ReopenLastToken();
       }
     }

diff  --git a/flang/test/Preprocessing/pp047.F b/flang/test/Preprocessing/pp047.F
new file mode 100644
index 0000000000000..1d4f9f848e58a
--- /dev/null
+++ b/flang/test/Preprocessing/pp047.F
@@ -0,0 +1,25 @@
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+#define FOO BAR
+#define FO BA
+#define OO AR
+! CHECK: print *,BAR, 1
+      print *,
+     +FOO
+     +, 1
+      print *,
+! CHECK: print *,FAR, 2
+     +F
+     +OO
+     +, 2
+! CHECK: print *,BAO, 3
+      print *,
+     +FO
+     +O
+     +, 3
+! CHECK: print *,BAR, 4
+      print *,
+     +F
+     +O
+     +O
+     +, 4
+      end

diff  --git a/flang/test/Preprocessing/pp135.F90 b/flang/test/Preprocessing/pp135.F90
new file mode 100644
index 0000000000000..2905a8cec5d93
--- /dev/null
+++ b/flang/test/Preprocessing/pp135.F90
@@ -0,0 +1,25 @@
+! RUN: %flang -E %s 2>&1 | FileCheck %s
+#define FOO BAR
+#define FO BA
+#define OO AR
+! CHECK: print *, BAR, 1
+print *, &
+  &FOO&
+  &, 1
+! CHECK: print *, FAR, 2
+print *, &
+  &F&
+  &OO&
+  &, 2
+! CHECK: print *, BAO, 3
+print *, &
+  &FO&
+  &O&
+  &, 3
+! CHECK: print *, BAR, 4
+print *, &
+  &F&
+  &O&
+  &O&
+  &, 4
+end


        


More information about the flang-commits mailing list