Skip to content

[flang][preprocessing] Mix preprocessing directives with free form li… #96244

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 24, 2024

Conversation

klausler
Copy link
Contributor

…ne continuation

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

Fixes #95476.

@klausler klausler requested a review from psteinfeld June 20, 2024 22:32
@llvmbot llvmbot added flang Flang issues not falling into any other category flang:parser labels Jun 20, 2024
@llvmbot
Copy link
Member

llvmbot commented Jun 20, 2024

@llvm/pr-subscribers-flang-driver

@llvm/pr-subscribers-flang-parser

Author: Peter Klausler (klausler)

Changes

…ne continuation

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

Fixes #95476.


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

4 Files Affected:

  • (modified) flang/include/flang/Parser/preprocessor.h (+1)
  • (modified) flang/lib/Parser/prescan.cpp (+23-17)
  • (modified) flang/lib/Parser/prescan.h (+1-1)
  • (added) flang/test/Preprocessing/cond-contin.F90 (+21)
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..452752cc89962 100644
--- a/flang/lib/Parser/prescan.cpp
+++ b/flang/lib/Parser/prescan.cpp
@@ -105,13 +105,14 @@ 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;
     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;
@@ -289,13 +290,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 +308,7 @@ void Prescanner::CheckAndEmitLine(
     omitNewline_ = false;
   } else {
     cooked_.Put('\n', newlineProvenance);
-    afterIncludeDirective_ = false;
+    afterPreprocessingDirective_ = false;
   }
 }
 
@@ -1069,6 +1071,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 +1093,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..919ea5b127d60
--- /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

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.

@foxtran
Copy link
Member

foxtran commented Jun 21, 2024

I still have some issues with preprocessor, even with commit 0511614.

@klausler
Copy link
Contributor Author

Examples would be useful.

@foxtran
Copy link
Member

foxtran commented Jun 21, 2024

The test is the following:
test.f:

      program main
#include "inc.f"
     $,.false.)
      end program main

inc.f:

      call t(

P.S. Ooops. It is invalid program, but the error messages did not say this :)

For valid program,
inc.f:

      call t(5.0

@foxtran
Copy link
Member

foxtran commented Jun 21, 2024

Another example with .f90:
test.f90:

        program main
#include "inc.f"
     &.44
      end program main

inc.f:

print *, 5.&

After running flang tests:

$ flang-new -fc1 -E test.f90
#line "./test.f90" 1
      program main
#line "./inc.f" 1
      print 5.end program main

while the expected output is:

$ flang-new -fc1 -E test.f90
#line "./test.f90" 1
      program main
#line "./inc.f" 1
      print *, 5.44
#line ??? ! I have no idea :)
      end program main

@foxtran
Copy link
Member

foxtran commented Jun 21, 2024

One more funny example with Fortran include statement:
test.f90:

      program main
#define ERROR 1
      include "inc.f"
     &.44
      end program main

inc.f:

#ifndef ERROR
print 5.&
#else
#error "Hi from LLVM"
#endif

gfortran -cpp -E produces:

# 1 "test.f90"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "test.f90"
      program main

      include "inc.f"
     &.44
      end program main

ifx -cpp -E produces (but, in general, it fails):

# 1 "test.f90"
      program main

      include "inc.f"
     &.44
      end program main

However, flang passes ERROR into Fortran-include statement that gives:

error: Could not scan test.f90
./inc.f:4:2: error: #error "Hi from LLVM"
  #error "Hi from LLVM"
   ^^^^^^^^^^^^^^^^^^^^
./test.f90:3:1: error: included here
        include "inc.f"
  ^^^^^^^^^^^^^^^^^^^^^

@foxtran
Copy link
Member

foxtran commented Jun 21, 2024

BTW, I would prefer to see preprocessed file and only after that error messages, if they are, when -E flag is passed.

@klausler
Copy link
Contributor Author

BTW, I would prefer to see preprocessed file and only after that error messages, if they are, when -E flag is passed.

That would be best filed as a separate bug against the driver; I don't work on that.

@klausler
Copy link
Contributor Author

Fortran's INCLUDE lines are handled by f18 in pretty much the same way as #include directives are, so that the included text has access to macro definitions in effect at the time of the inclusion. In other words, this is intentional behavior.

@klausler
Copy link
Contributor Author

Thanks for the additional two cases; the patch has been updated to cover them as well.

@@ -0,0 +1,6 @@
! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s
! CHECK: print *, 3.14159
Copy link
Member

Choose a reason for hiding this comment

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

It is improper result, since test has whitespace between . and & in flang/test/Preprocessing/inc-contin-2.h.
It should be:

! CHECK: print *, 3. 14159

And then, it must fail since whitespaces are not allowed in constants as it was in Fortran-77.

Copy link
Member

Choose a reason for hiding this comment

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

There is an example:
https://godbolt.org/z/Gx1brb7h6

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks; patch has been updated to test cases with and without the space.

…ne continuation

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

Fixes llvm#95476.
@foxtran
Copy link
Member

foxtran commented Jun 24, 2024

Thanks! Parser can read our source code :)

@klausler klausler merged commit 5d15f60 into llvm:main Jun 24, 2024
7 checks passed
@klausler klausler deleted the bug95476 branch June 24, 2024 17:18
AlexisPerry pushed a commit to llvm-project-tlp/llvm-project that referenced this pull request Jul 9, 2024
llvm#96244)

…ne continuation

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

Fixes llvm#95476.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
flang:driver 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] C preprocessor can not be inside of statement
4 participants