Skip to content

[flang] Align -x language modes with gfortran #130268

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 2 commits into from
Mar 12, 2025

Conversation

inaki-amatria
Copy link
Member

This PR addresses some of the issues described in #127617. Key changes:

  • Stop assuming fixed-form for -x f95 unless the input is a .i file. This change ensures compatibility with -save-temps workflows while preventing unintended fixed-form assumptions.
  • Ensure -x f95-cpp-input enables -cpp by default, aligning Flang's behavior with gfortran.

@inaki-amatria inaki-amatria self-assigned this Mar 7, 2025
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang Flang issues not falling into any other category labels Mar 7, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 7, 2025

@llvm/pr-subscribers-clang
@llvm/pr-subscribers-flang-driver

@llvm/pr-subscribers-clang-driver

Author: Iñaki Amatria Barral (inaki-amatria)

Changes

This PR addresses some of the issues described in #127617. Key changes:

  • Stop assuming fixed-form for -x f95 unless the input is a .i file. This change ensures compatibility with -save-temps workflows while preventing unintended fixed-form assumptions.
  • Ensure -x f95-cpp-input enables -cpp by default, aligning Flang's behavior with gfortran.

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

5 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+6-2)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+6)
  • (added) flang/test/Driver/dash-x-f95-cpp-input.f (+35)
  • (added) flang/test/Driver/dash-x-f95-do-not-assume-fixed-form.f90 (+12)
  • (modified) flang/test/Driver/input-from-stdin/input-from-stdin.f90 (+1-1)
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index d4fea633d0edf..f6b23cec9c2ee 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -817,8 +817,12 @@ void Flang::ConstructJob(Compilation &C, const JobAction &JA,
 
   // 'flang -E' always produces output that is suitable for use as fixed form
   // Fortran. However it is only valid free form source if the original is also
-  // free form.
-  if (InputType == types::TY_PP_Fortran &&
+  // free form. Ensure this logic does not incorrectly assume fixed-form for
+  // cases where it shouldn't, such as `flang -x f95 foo.f90`.
+  bool isAtemporaryPreprocessedFile =
+      llvm::sys::path::extension(Input.getFilename())
+          .ends_with(types::getTypeTempSuffix(InputType, /*CLStyle=*/false));
+  if (InputType == types::TY_PP_Fortran && isAtemporaryPreprocessedFile &&
       !Args.getLastArg(options::OPT_ffixed_form, options::OPT_ffree_form))
     CmdArgs.push_back("-ffixed-form");
 
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 8b07a50824899..1537122ddced5 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -863,6 +863,12 @@ static void parsePreprocessorArgs(Fortran::frontend::PreprocessorOptions &opts,
         (currentArg->getOption().matches(clang::driver::options::OPT_cpp))
             ? PPMacrosFlag::Include
             : PPMacrosFlag::Exclude;
+  // Enable -cpp based on -x unless explicitly disabled with -nocpp
+  if (opts.macrosFlag != PPMacrosFlag::Exclude)
+    if (const auto *dashX = args.getLastArg(clang::driver::options::OPT_x))
+      opts.macrosFlag = llvm::StringSwitch<PPMacrosFlag>(dashX->getValue())
+                            .Case("f95-cpp-input", PPMacrosFlag::Include)
+                            .Default(opts.macrosFlag);
 
   opts.noReformat = args.hasArg(clang::driver::options::OPT_fno_reformat);
   opts.preprocessIncludeLines =
diff --git a/flang/test/Driver/dash-x-f95-cpp-input.f b/flang/test/Driver/dash-x-f95-cpp-input.f
new file mode 100644
index 0000000000000..2f42dc9342842
--- /dev/null
+++ b/flang/test/Driver/dash-x-f95-cpp-input.f
@@ -0,0 +1,35 @@
+program main
+  print *, __FILE__, __LINE__
+end
+
+! This test verifies that `flang`'s `-x` options behave like `gfortran`'s.
+! Specifically:
+! - `-x f95` should process the file based on its extension unless overridden.
+! - `-x f95-cpp-input` should behave like `-x f95` but with preprocessing
+!   (`-cpp`) enabled unless overridden.
+
+! ---
+! Ensure the file is treated as fixed-form unless explicitly set otherwise
+! ---
+! RUN: not %flang -Werror -x f95 -cpp %s 2>&1 | FileCheck --check-prefix=SCAN-ERROR %s
+! RUN: not %flang -Werror -x f95-cpp-input %s 2>&1 | FileCheck --check-prefix=SCAN-ERROR %s
+
+! SCAN-ERROR: error
+
+! RUN: %flang -Werror -x f95 -cpp -ffree-form %s 2>&1 | FileCheck --check-prefix=NO-SCAN-ERROR --allow-empty %s
+! RUN: %flang -Werror -x f95-cpp-input -ffree-form %s 2>&1 | FileCheck --check-prefix=NO-SCAN-ERROR --allow-empty %s
+
+! NO-SCAN-ERROR-NOT: error
+
+! ---
+! Ensure `-cpp` is not enabled by default unless explicitly requested
+! ---
+! RUN: not %flang -Werror -x f95 -ffree-form %s 2>&1 | FileCheck --check-prefix=SEMA-ERROR %s
+! RUN: not %flang -Werror -x f95-cpp-input -nocpp -ffree-form %s 2>&1 | FileCheck --check-prefix=SEMA-ERROR %s
+
+! SEMA-ERROR: error
+
+! RUN: %flang -Werror -x f95 -cpp -ffree-form %s 2>&1 | FileCheck --check-prefix=NO-SEMA-ERROR --allow-empty %s
+! RUN: %flang -Werror -x f95-cpp-input -ffree-form %s 2>&1 | FileCheck --check-prefix=NO-SEMA-ERROR --allow-empty %s
+
+! NO-SEMA-ERROR-NOT: error
diff --git a/flang/test/Driver/dash-x-f95-do-not-assume-fixed-form.f90 b/flang/test/Driver/dash-x-f95-do-not-assume-fixed-form.f90
new file mode 100644
index 0000000000000..549286d7f2f3d
--- /dev/null
+++ b/flang/test/Driver/dash-x-f95-do-not-assume-fixed-form.f90
@@ -0,0 +1,12 @@
+! This test verifies that using `-x f95` does not cause the driver to assume
+! this file is in fixed-form.
+
+program main
+  print *, "Hello, World!"
+end
+
+! RUN: %flang -### -x f95 %s 2>&1 | FileCheck --check-prefix=PRINT-PHASES %s
+! PRINT-PHASES-NOT: -ffixed-form
+
+! RUN: %flang -Werror -x f95 %s 2>&1 | FileCheck --check-prefix=COMPILE --allow-empty %s
+! COMPILE-NOT: error
diff --git a/flang/test/Driver/input-from-stdin/input-from-stdin.f90 b/flang/test/Driver/input-from-stdin/input-from-stdin.f90
index 285f0751b35d8..1fcc0340a64ba 100644
--- a/flang/test/Driver/input-from-stdin/input-from-stdin.f90
+++ b/flang/test/Driver/input-from-stdin/input-from-stdin.f90
@@ -6,7 +6,7 @@
 ! Input type is implicit
 ! RUN: cat %s | %flang -E -cpp - | FileCheck %s --check-prefix=PP-NOT-DEFINED
 ! RUN: cat %s | %flang -DNEW -E -cpp - | FileCheck %s --check-prefix=PP-DEFINED
-! RUN: cat %s | %flang -DNEW -E - | FileCheck %s --check-prefix=PP-NOT-DEFINED
+! RUN: cat %s | %flang -DNEW -E - | FileCheck %s --check-prefix=PP-DEFINED
 ! RUN: cat %s | %flang -DNEW -E -nocpp - | FileCheck %s --check-prefix=PP-NOT-DEFINED
 
 ! Input type is explicit

Copy link
Contributor

@tarunprabhu tarunprabhu left a comment

Choose a reason for hiding this comment

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

Thanks for continuing to see this through

! RUN: %flang -### -x f95 %s 2>&1 | FileCheck --check-prefix=PRINT-PHASES %s
! PRINT-PHASES-NOT: -ffixed-form

! RUN: %flang -Werror -x f95 %s 2>&1 | FileCheck --check-prefix=COMPILE --allow-empty %s
Copy link
Contributor

Choose a reason for hiding this comment

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

With this test as written, a linker and the runtime libraries are both required. Is this necessary? Is the intention to check that the free-form code here is parsed correctly? If so, is -emit-llvm or -emit-mlir sufficient?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed. I added -fsyntax-only to ensure this executes flang only

! RUN: not %flang -Werror -x f95 -ffree-form %s 2>&1 | FileCheck --check-prefix=SEMA-ERROR %s
! RUN: not %flang -Werror -x f95-cpp-input -nocpp -ffree-form %s 2>&1 | FileCheck --check-prefix=SEMA-ERROR %s

! SEMA-ERROR: error
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this test pass if a different error occurs? Is it feasible to search for something more precise than just "error" - perhaps "semantics error? If so, we should do that for the other checks as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

I assumed the broader the check, the better. But if you believe checking for "Semantic errors" or "Scan errors" is a better alternative I can update the test. TYSM for taking the time to review this. I appreciate it :-)

Copy link
Contributor

Choose a reason for hiding this comment

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

It's always a balancing act.

In this case, you are checking that the compiler parses the file in a specific way. In that case, you expect to see a specific category of error, so you might as well check for that. That way, if a change elsewhere in the compiler results in a different error, the test failure will alert the developer. It may be that this "new" error is expected. Or, it could indicate that the change had an effect on a seemingly unrelated part of the code. In either case, it forces the developer to take a closer look.

That's just my opinion, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed. I replaced the ! CHECK: error pattern with specific checks!

@inaki-amatria inaki-amatria force-pushed the fix-dash-x-language-modes branch from 6e304d0 to eceaa34 Compare March 7, 2025 13:52
Copy link
Member

@DavidTruby DavidTruby left a comment

Choose a reason for hiding this comment

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

This LGTM now when @tarunprabhu is happy, thanks for seeing this through despite my lengthy comments!

This isn't to hold up this patch, but as a separate thought: I wonder if there's even any reason to have types::TY_PP_Fortran and types::TY_Fortran? Do we ever actually treat them differently..? It seems we always decide whether to preprocess or not based on other criteria

@inaki-amatria inaki-amatria force-pushed the fix-dash-x-language-modes branch from eceaa34 to 91f1dee Compare March 11, 2025 07:03
@inaki-amatria
Copy link
Member Author

This LGTM now when @tarunprabhu is happy, thanks for seeing this through despite my lengthy comments!

Thank you for the quick review, David! I also appreciate your patience with my lengthy comments. I was so caught up in the fixed-form assumption that I failed to realize that reverting the patch I had intended to undo in the previous PR would introduce a regression on your side. I truly appreciate your time and insights!

This isn't to hold up this patch, but as a separate thought: I wonder if there's even any reason to have types::TY_PP_Fortran and types::TY_Fortran? Do we ever actually treat them differently..? It seems we always decide whether to preprocess or not based on other criteria

As for your separate thought: I agree that this part of the code might benefit from a second look.

This change ensures that flang no longer assumes fixed-form when using
`-x f95`. The only case where fixed-form is still assumed is when
`-x f95` is used with `.i` files, unless explicitly overridden by the
user.
This change ensures that specifying `-x f95-cpp-input` automatically
enables `-cpp`, making `flang`'s behavior consistent with `gfortran`.

`flang/test/Driver/input-from-stdin/input-from-stdin.f90` changes
because the driver assumes `-x f95-cpp-input` for `<stdin>`. Therefore,
after this patch, Fortran source that comes from `<stdin>` will now be
preprocessed.
@inaki-amatria inaki-amatria force-pushed the fix-dash-x-language-modes branch from 91f1dee to 35ebfdf Compare March 12, 2025 12:25
Copy link
Contributor

@tarunprabhu tarunprabhu left a comment

Choose a reason for hiding this comment

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

Thanks for all the changes :-)

@inaki-amatria inaki-amatria merged commit bdbe8fa into llvm:main Mar 12, 2025
9 of 10 checks passed
@inaki-amatria inaki-amatria deleted the fix-dash-x-language-modes branch March 12, 2025 15:45
frederik-h pushed a commit to frederik-h/llvm-project that referenced this pull request Mar 18, 2025
This PR addresses some of the issues described in
llvm#127617. Key changes:

- Stop assuming fixed-form for `-x f95` unless the input is a `.i` file.
This change ensures compatibility with `-save-temps` workflows while
preventing unintended fixed-form assumptions.
- Ensure `-x f95-cpp-input` enables `-cpp` by default, aligning Flang's
behavior with `gfortran`.
Input.isFilename() &&
llvm::sys::path::extension(Input.getFilename())
.ends_with(types::getTypeTempSuffix(InputType, /*CLStyle=*/false));
if (InputType == types::TY_PP_Fortran && isAtemporaryPreprocessedFile &&
Copy link
Member

@DavidTruby DavidTruby Mar 31, 2025

Choose a reason for hiding this comment

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

I've only just spotted this, but this requirement that isAtemporaryPreprocessedFile appears to have changed the default for all files to -cpp instead of -nocpp. I don't think this patch intended that change?

E.g. files with the .f90 suffix are now treated as if -cpp were passed, which I don't think is the intended behaviour.

Copy link
Member

Choose a reason for hiding this comment

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

Sorry I thought it was this part but I actually think it is the other change 😓

if (opts.macrosFlag != PPMacrosFlag::Exclude)
if (const auto *dashX = args.getLastArg(clang::driver::options::OPT_x))
opts.macrosFlag = llvm::StringSwitch<PPMacrosFlag>(dashX->getValue())
.Case("f95-cpp-input", PPMacrosFlag::Include)
Copy link
Member

@DavidTruby DavidTruby Mar 31, 2025

Choose a reason for hiding this comment

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

I believe we concluded that f95-cpp-input should mean that it has already been preprocessed, and so does not need preprocessing again, and fixed that elsewhere. So this should be PPMacrosFlag::Exclude since -x f95-cpp-input is what we pass for .f90 files, and that's where the issue is coming from.

Edit: Ah, no, we agreed the opposite 😅 , and we are passing the wrong -x for .f90 files. Sorry, I find this all a bit confusing....

@DavidTruby
Copy link
Member

I've created a PR at #133775 cleaning up the meanings of the -x options in the compiler driver, which I believe resolves the above issue but leaves this fix in place. If you could check that it still works for your use case that'd be great! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants