diff --git a/flang/include/flang/FrontendTool/Utils.h b/flang/include/flang/FrontendTool/Utils.h --- a/flang/include/flang/FrontendTool/Utils.h +++ b/flang/include/flang/FrontendTool/Utils.h @@ -14,6 +14,8 @@ #ifndef LLVM_FLANG_FRONTENDTOOL_UTILS_H #define LLVM_FLANG_FRONTENDTOOL_UTILS_H +#include "llvm/ADT/StringRef.h" + namespace Fortran::frontend { class CompilerInstance; @@ -31,6 +33,14 @@ /// \return - True on success. bool ExecuteCompilerInvocation(CompilerInstance *flang); +/// \param suffix The file extension +/// \return True if the file extension should be processed as fixed form +bool isFixedFormSuffix(llvm::StringRef suffix); + +/// \param suffix The file extension +/// \return True if the file extension should be processed as free form +bool isFreeFormSuffix(llvm::StringRef suffix); + } // end namespace Fortran::frontend #endif // LLVM_FLANG_FRONTENDTOOL_UTILS_H diff --git a/flang/lib/Frontend/FrontendAction.cpp b/flang/lib/Frontend/FrontendAction.cpp --- a/flang/lib/Frontend/FrontendAction.cpp +++ b/flang/lib/Frontend/FrontendAction.cpp @@ -9,6 +9,7 @@ #include "flang/Frontend/FrontendAction.h" #include "flang/Frontend/CompilerInstance.h" #include "flang/Frontend/FrontendActions.h" +#include "flang/FrontendTool/Utils.h" #include "llvm/Support/Errc.h" using namespace Fortran::frontend; @@ -51,6 +52,12 @@ Fortran::parser::Options parserOptions = this->instance().invocation().fortranOpts(); + // Set the fixed form flag based on the file extension + auto pathDotIndex{currentInputPath.rfind(".")}; + if (pathDotIndex != std::string::npos) { + std::string pathSuffix{currentInputPath.substr(pathDotIndex + 1)}; + parserOptions.isFixedForm = isFixedFormSuffix(pathSuffix); + } // Prescan. In case of failure, report and return. ci.parsing().Prescan(currentInputPath, parserOptions); diff --git a/flang/lib/Frontend/FrontendOptions.cpp b/flang/lib/Frontend/FrontendOptions.cpp --- a/flang/lib/Frontend/FrontendOptions.cpp +++ b/flang/lib/Frontend/FrontendOptions.cpp @@ -7,15 +7,13 @@ //===----------------------------------------------------------------------===// #include "flang/Frontend/FrontendOptions.h" -#include "llvm/ADT/StringSwitch.h" +#include "flang/FrontendTool/Utils.h" using namespace Fortran::frontend; InputKind FrontendOptions::GetInputKindForExtension(llvm::StringRef extension) { - return llvm::StringSwitch(extension) - // TODO: Should match the list in flang/test/lit.cfg.py - // FIXME: Currently this API allows at most 9 items per case. - .Cases("f", "F", "f77", "f90", "F90", "f95", "F95", "ff95", "f18", "F18", - Language::Fortran) - .Default(Language::Unknown); + if (isFixedFormSuffix(extension) || isFreeFormSuffix(extension)) { + return Language::Fortran; + } + return Language::Unknown; } diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -84,4 +84,19 @@ return success; } +bool isFixedFormSuffix(llvm::StringRef suffix) { + // Note: Keep this list in-sync with flang/test/lit.cfg.py + return suffix == "f" || suffix == "F" || suffix == "ff" || suffix == "for" || + suffix == "FOR" || suffix == "fpp" || suffix == "FPP"; +} + +bool isFreeFormSuffix(llvm::StringRef suffix) { + // Note: Keep this list in-sync with flang/test/lit.cfg.py + // TODO: Add Cuda Fortan files (i.e. `*.cuf` and `*.CUF`). + return suffix == "f77" || suffix == "f90" || suffix == "F90" || + suffix == "ff90" || suffix == "f95" || suffix == "F95" || + suffix == "ff95" || suffix == "f03" || suffix == "F03" || + suffix == "f08" || suffix == "F08" || suffix == "f18" || suffix == "F18"; +} + } // namespace Fortran::frontend diff --git a/flang/test/Flang-Driver/Inputs/fixed-form-test.f b/flang/test/Flang-Driver/Inputs/fixed-form-test.f new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/Inputs/fixed-form-test.f @@ -0,0 +1,2 @@ + program FixedForm + end diff --git a/flang/test/Flang-Driver/Inputs/free-form-test.f90 b/flang/test/Flang-Driver/Inputs/free-form-test.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/Inputs/free-form-test.f90 @@ -0,0 +1,2 @@ +program FreeForm +end diff --git a/flang/test/Flang-Driver/fixed-free-detection.f90 b/flang/test/Flang-Driver/fixed-free-detection.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/fixed-free-detection.f90 @@ -0,0 +1,40 @@ +! Ensure the driver correctly switches between fixed and free form based on the file extension. +! This test exploits the fact that the preprocessor treats white-spaces differently for free +! and fixed form input files. + +! REQUIRES: new-flang-driver + +!-------------------------- +! FLANG DRIVER (flang-new) +!-------------------------- +! RUN: %flang-new -E %S/Inputs/free-form-test.f90 2>&1 | FileCheck %s --check-prefix=FREEFORM +! RUN: %flang-new -E %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=FIXEDFORM +! RUN: %flang-new -E %S/Inputs/free-form-test.f90 %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=MULTIPLEFORMS + +!----------------------------------------- +! FRONTEND FLANG DRIVER (flang-new -fc1) +!----------------------------------------- +! RUN: %flang-new -fc1 -E %S/Inputs/free-form-test.f90 2>&1 | FileCheck %s --check-prefix=FREEFORM +! RUN: %flang-new -fc1 -E %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=FIXEDFORM +! RUN: %flang-new -fc1 -E %S/Inputs/free-form-test.f90 %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=MULTIPLEFORMS + +!------------------------------------- +! EXPECTED OUTPUT FOR A FREE FORM FILE +!------------------------------------- +! FREEFORM:program freeform +! FREEFORM-NOT:programfixedform + +!--------------------------------------- +! EXPECTED OUTPUT FOR A FIXED FORM FILE +!--------------------------------------- +! FIXEDFORM:programfixedform +! FIXEDFORM-NOT:program freeform + +!------------------------------------------------ +! EXPECTED OUTPUT FOR 2 FILES OF DIFFERENT FORMS +!------------------------------------------------ +! MULTIPLEFORMS:program freeform +! MULTIPLEFORMS-NOT:programfixedform +! MULTIPLEFORMS-NEXT:end +! MULTIPLEFORMS-NEXT:programfixedform +! MULTIPLEFORMS-NOT:program freeform diff --git a/flang/test/lit.cfg.py b/flang/test/lit.cfg.py --- a/flang/test/lit.cfg.py +++ b/flang/test/lit.cfg.py @@ -26,8 +26,8 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = ['.c', '.cpp', '.f', '.F', '.ff', '.FOR', '.for', '.f77', '.f90', '.F90', - '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf', - '.CUF', '.f18', '.F18', '.fir'] + '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf' + '.CUF', '.f18', '.F18', '.fir', '.f03', '.F03', '.f08', '.F08'] config.substitutions.append(('%PATH%', config.environment['PATH'])) diff --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp --- a/flang/unittests/Frontend/FrontendActionTest.cpp +++ b/flang/unittests/Frontend/FrontendActionTest.cpp @@ -40,7 +40,7 @@ // Generate a unique test file name. const testing::TestInfo *const test_info = testing::UnitTest::GetInstance()->current_test_info(); - inputFileName_ = std::string(test_info->name()) + "_test-file.f"; + inputFileName_ = std::string(test_info->name()) + "_test-file.f90"; // Create the input file stream. Note that this stream is populated // separately in every test (i.e. the input is test specific).