diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4222,8 +4222,6 @@ // Generic gfortran options. def A_DASH : Joined<["-"], "A-">, Group; -def cpp : Flag<["-"], "cpp">, Group; -def nocpp : Flag<["-"], "nocpp">, Group; def static_libgfortran : Flag<["-"], "static-libgfortran">, Group; // "f" options with values for gfortran. @@ -4303,6 +4301,10 @@ //===----------------------------------------------------------------------===// let Flags = [FC1Option, FlangOption, FlangOnlyOption] in { +def cpp : Flag<["-"], "cpp">, Group, + HelpText<"Enable predefined and command line preprocessor macros">; +def nocpp : Flag<["-"], "nocpp">, Group, + HelpText<"Disable predefined and command line preprocessor macros">; def module_dir : Separate<["-"], "module-dir">, MetaVarName<"">, HelpText<"Put MODULE files in ">, DocBrief<[{This option specifies where to put .mod files for compiled modules. diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -37,7 +37,8 @@ void Flang::AddPreprocessingOptions(const ArgList &Args, ArgStringList &CmdArgs) const { - Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I}); + Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I, + options::OPT_cpp, options::OPT_nocpp}); } void Flang::AddOtherOptions(const ArgList &Args, ArgStringList &CmdArgs) const { diff --git a/flang/include/flang/Frontend/CompilerInvocation.h b/flang/include/flang/Frontend/CompilerInvocation.h --- a/flang/include/flang/Frontend/CompilerInvocation.h +++ b/flang/include/flang/Frontend/CompilerInvocation.h @@ -136,6 +136,10 @@ /// Set the default predefinitions. void setDefaultPredefinitions(); + /// Collect the macro definitions from preprocessorOpts_ and prepare them for + /// the parser (i.e. copy into parserOpts_) + void collectMacroDefinitions(); + /// Set the Fortran options to user-specified values. /// These values are found in the preprocessor options. void setFortranOpts(); diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -79,6 +79,10 @@ /// \return True if the file extension should be processed as free form bool isFreeFormSuffix(llvm::StringRef suffix); +/// \param suffix The file extension +/// \return True if the file should be preprocessed +bool mustBePreprocessed(llvm::StringRef suffix); + enum class Language : uint8_t { Unknown, @@ -137,6 +141,12 @@ /// stdin this is never modified. bool isFixedForm_ = false; + /// Must this file be preprocessed? Note that in Flang the preprocessor is + /// always run. This flag is used to control whether macro standard + /// predefinitions are added or not. In practice, this is sufficient to + /// implement gfortran`s logic controlled with `-cpp/-nocpp` flags. + unsigned mustBePreprocessed_ : 1; + public: FrontendInputFile() = default; FrontendInputFile(llvm::StringRef file, InputKind kind) @@ -147,7 +157,9 @@ auto pathDotIndex{file.rfind(".")}; std::string pathSuffix{file.substr(pathDotIndex + 1)}; isFixedForm_ = isFixedFormSuffix(pathSuffix); + mustBePreprocessed_ = mustBePreprocessed(pathSuffix); } + FrontendInputFile(const llvm::MemoryBuffer *buffer, InputKind kind) : buffer_(buffer), kind_(kind) {} @@ -157,6 +169,7 @@ bool IsFile() const { return !IsBuffer(); } bool IsBuffer() const { return buffer_ != nullptr; } bool IsFixedForm() const { return isFixedForm_; } + bool MustBePreprocessed() const { return mustBePreprocessed_; } llvm::StringRef file() const { assert(IsFile()); diff --git a/flang/include/flang/Frontend/PreprocessorOptions.h b/flang/include/flang/Frontend/PreprocessorOptions.h --- a/flang/include/flang/Frontend/PreprocessorOptions.h +++ b/flang/include/flang/Frontend/PreprocessorOptions.h @@ -19,6 +19,15 @@ namespace Fortran::frontend { +/// Communicates whether to include/exclude standard macro predefinitions +enum class StdMacroPredefs : uint8_t { + /// Use the file extension to decide + Unknown, + + Include, + Exclude +}; + /// This class is used for passing the various options used /// in preprocessor initialization to the parser options. class PreprocessorOptions { @@ -32,6 +41,8 @@ // Search directories specified by the user with -fintrinsic-modules-path std::vector searchDirectoriesFromIntrModPath; + StdMacroPredefs predefs_ = StdMacroPredefs::Unknown; + public: PreprocessorOptions() {} diff --git a/flang/lib/Frontend/CompilerInstance.cpp b/flang/lib/Frontend/CompilerInstance.cpp --- a/flang/lib/Frontend/CompilerInstance.cpp +++ b/flang/lib/Frontend/CompilerInstance.cpp @@ -142,7 +142,6 @@ // Set some sane defaults for the frontend. invoc.SetDefaultFortranOpts(); - invoc.setDefaultPredefinitions(); // Update the fortran options based on user-based input. invoc.setFortranOpts(); // Set the encoding to read all input files in based on user input. diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -323,6 +323,14 @@ for (const auto *currentArg : args.filtered(clang::driver::options::OPT_fintrinsic_modules_path)) opts.searchDirectoriesFromIntrModPath.emplace_back(currentArg->getValue()); + + // -cpp/-nocpp + if (const auto *currentArg = args.getLastArg( + clang::driver::options::OPT_cpp, clang::driver::options::OPT_nocpp)) + opts.predefs_ = + (currentArg->getOption().matches(clang::driver::options::OPT_cpp)) + ? StdMacroPredefs::Include + : StdMacroPredefs::Exclude; } /// Parses all semantic related arguments and populates the variables @@ -449,13 +457,9 @@ return success; } -/// Collect the macro definitions provided by the given preprocessor -/// options into the parser options. -/// -/// \param [in] ppOpts The preprocessor options -/// \param [out] opts The fortran options -static void collectMacroDefinitions( - const PreprocessorOptions &ppOpts, Fortran::parser::Options &opts) { +void CompilerInvocation::collectMacroDefinitions() { + auto &ppOpts = this->preprocessorOpts(); + for (unsigned i = 0, n = ppOpts.macros.size(); i != n; ++i) { llvm::StringRef macro = ppOpts.macros[i].first; bool isUndef = ppOpts.macros[i].second; @@ -466,7 +470,7 @@ // For an #undef'd macro, we only care about the name. if (isUndef) { - opts.predefinitions.emplace_back( + parserOpts_.predefinitions.emplace_back( macroName.str(), std::optional{}); continue; } @@ -479,7 +483,7 @@ llvm::StringRef::size_type End = macroBody.find_first_of("\n\r"); macroBody = macroBody.substr(0, End); } - opts.predefinitions.emplace_back( + parserOpts_.predefinitions.emplace_back( macroName, std::optional(macroBody.str())); } } @@ -535,8 +539,6 @@ fortranOptions.features = frontendOptions.features_; fortranOptions.encoding = frontendOptions.encoding_; - collectMacroDefinitions(preprocessorOptions, fortranOptions); - // Adding search directories specified by -I fortranOptions.searchDirectories.insert( fortranOptions.searchDirectories.end(), 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 @@ -67,6 +67,25 @@ return false; } + auto &invoc = ci.invocation(); + + // Include command-line and predefined preprocessor macros. Use either: + // * `-cpp/-nocpp`, or + // * the file extension (if the user didn't express any preference) + // to decide whether to include them or not. + if ((invoc.preprocessorOpts().predefs_ == StdMacroPredefs::Include) || + (invoc.preprocessorOpts().predefs_ == StdMacroPredefs::Unknown && + currentInput().MustBePreprocessed())) { + invoc.setDefaultPredefinitions(); + invoc.collectMacroDefinitions(); + } + + // Decide between fixed and free form (if the user didn't express any + // preference, use the file extension to decide) + if (invoc.frontendOpts().fortranForm_ == FortranForm::Unknown) { + invoc.fortranOpts().isFixedForm = currentInput().IsFixedForm(); + } + if (!BeginSourceFileAction(ci)) { BeginSourceFileCleanUp(*this, ci); return false; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -10,6 +10,7 @@ #include "flang/Common/default-kinds.h" #include "flang/Frontend/CompilerInstance.h" #include "flang/Frontend/FrontendOptions.h" +#include "flang/Frontend/PreprocessorOptions.h" #include "flang/Lower/PFTBuilder.h" #include "flang/Parser/dump-parse-tree.h" #include "flang/Parser/parsing.h" @@ -45,21 +46,9 @@ bool PrescanAction::BeginSourceFileAction(CompilerInstance &c1) { CompilerInstance &ci = this->instance(); - std::string currentInputPath{GetCurrentFileOrBufferName()}; - Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); - if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { - // Switch between fixed and free form format based on the input file - // extension. - // - // Ideally we should have all Fortran options set before entering this - // method (i.e. before processing any specific input files). However, we - // can't decide between fixed and free form based on the file extension - // earlier than this. - parserOptions.isFixedForm = currentInput().IsFixedForm(); - } // Prescan. In case of failure, report and return. ci.parsing().Prescan(currentInputPath, parserOptions); @@ -78,22 +67,9 @@ bool PrescanAndSemaAction::BeginSourceFileAction(CompilerInstance &c1) { CompilerInstance &ci = this->instance(); - std::string currentInputPath{GetCurrentFileOrBufferName()}; - Fortran::parser::Options parserOptions = ci.invocation().fortranOpts(); - if (ci.invocation().frontendOpts().fortranForm_ == FortranForm::Unknown) { - // Switch between fixed and free form format based on the input file - // extension. - // - // Ideally we should have all Fortran options set before entering this - // method (i.e. before processing any specific input files). However, we - // can't decide between fixed and free form based on the file extension - // earlier than this. - parserOptions.isFixedForm = currentInput().IsFixedForm(); - } - // 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 @@ -26,6 +26,12 @@ suffix == "f08" || suffix == "F08" || suffix == "f18" || suffix == "F18"; } +bool Fortran::frontend::mustBePreprocessed(llvm::StringRef suffix) { + return suffix == "F" || suffix == "FOR" || suffix == "fpp" || + suffix == "FPP" || suffix == "F90" || suffix == "F95" || + suffix == "F03" || suffix == "F08" || suffix == "F18"; +} + // TODO: This is a copy of `asFortran` from f18.cpp and is added here for // compatiblity. It doesn't really belong here, but I couldn't find a better // place. We should decide whether to add it to the Evaluate or Parse/Unparse diff --git a/flang/test/Driver/cpp-nocpp-command-line-macro.f90 b/flang/test/Driver/cpp-nocpp-command-line-macro.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/cpp-nocpp-command-line-macro.f90 @@ -0,0 +1,22 @@ +!----------- +! RUN lines +!----------- +! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED +! RUN: %flang_fc1 -E -cpp -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED +! RUN: %flang_fc1 -E -nocpp -DX=A %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED + +!----------------- +! EXPECTED OUTPUT +!----------------- +! UNDEFINED:program b +! UNDEFINED-NOT:program a + +! DEFINED:program a +! DEFINED-NOT:program b + +#ifdef X +program X +#else +program B +#endif +end diff --git a/flang/test/Driver/cpp-nocpp-predefined-macro.F90 b/flang/test/Driver/cpp-nocpp-predefined-macro.F90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/cpp-nocpp-predefined-macro.F90 @@ -0,0 +1,18 @@ +!----------- +! RUN lines +!----------- +! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=DEFINED +! RUN: %flang_fc1 -E -cpp %s 2>&1 | FileCheck %s --check-prefix=DEFINED +! RUN: %flang_fc1 -E -nocpp %s 2>&1 | FileCheck %s --check-prefix=NOT_DEFINED + +!----------------- +! EXPECTED OUTPUT +!----------------- +! DEFINED: flang = 1 +! DEFINED-NEXT: flang_major = {{[1-9][0-9]*$}} + +! NOT_DEFINED: flang = __flang__ +! NOT_DEFINED-NEXT: flang_major = __flang_major__ + +integer, parameter :: flang = __flang__ +integer, parameter :: flang_major = __flang_major__ diff --git a/flang/test/Driver/driver-help-hidden.f90 b/flang/test/Driver/driver-help-hidden.f90 --- a/flang/test/Driver/driver-help-hidden.f90 +++ b/flang/test/Driver/driver-help-hidden.f90 @@ -19,6 +19,7 @@ ! CHECK-EMPTY: ! CHECK-NEXT:OPTIONS: ! CHECK-NEXT: -### Print (but do not run) the commands to run for this compilation +! CHECK-NEXT: -cpp Enable predefined and command line preprocessor macros ! CHECK-NEXT: -c Only run preprocess, compile, and assemble steps ! CHECK-NEXT: -D = Define to (or 1 if omitted) ! CHECK-NEXT: -E Only run the preprocessor @@ -46,6 +47,7 @@ ! CHECK-NEXT: -help Display available options ! CHECK-NEXT: -I Add directory to the end of the list of include search paths ! CHECK-NEXT: -module-dir Put MODULE files in +! CHECK-NEXT: -nocpp Disable predefined and command line preprocessor macros ! CHECK-NEXT: -o Write output to ! CHECK-NEXT: -pedantic Warn on language extensions ! CHECK-NEXT: -std= Language standard to compile for diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -19,6 +19,7 @@ ! HELP-EMPTY: ! HELP-NEXT:OPTIONS: ! HELP-NEXT: -### Print (but do not run) the commands to run for this compilation +! HELP-NEXT: -cpp Enable predefined and command line preprocessor macros ! HELP-NEXT: -c Only run preprocess, compile, and assemble steps ! HELP-NEXT: -D = Define to (or 1 if omitted) ! HELP-NEXT: -E Only run the preprocessor @@ -46,6 +47,7 @@ ! HELP-NEXT: -help Display available options ! HELP-NEXT: -I Add directory to the end of the list of include search paths ! HELP-NEXT: -module-dir Put MODULE files in +! HELP-NEXT: -nocpp Disable predefined and command line preprocessor macros ! HELP-NEXT: -o Write output to ! HELP-NEXT: -pedantic Warn on language extensions ! HELP-NEXT: -std= Language standard to compile for @@ -59,6 +61,7 @@ ! HELP-FC1:USAGE: flang-new ! HELP-FC1-EMPTY: ! HELP-FC1-NEXT:OPTIONS: +! HELP-FC1-NEXT: -cpp Enable predefined and command line preprocessor macros ! HELP-FC1-NEXT: -D = Define to (or 1 if omitted) ! HELP-FC1-NEXT: -emit-obj Emit native object files ! HELP-FC1-NEXT: -E Only run the preprocessor @@ -97,6 +100,7 @@ ! HELP-FC1-NEXT: -help Display available options ! HELP-FC1-NEXT: -I Add directory to the end of the list of include search paths ! HELP-FC1-NEXT: -module-dir Put MODULE files in +! HELP-FC1-NEXT: -nocpp Disable predefined and command line preprocessor macros ! HELP-FC1-NEXT: -o Write output to ! HELP-FC1-NEXT: -pedantic Warn on language extensions ! HELP-FC1-NEXT: -std= Language standard to compile for diff --git a/flang/test/Driver/input-from-stdin.f90 b/flang/test/Driver/input-from-stdin.f90 --- a/flang/test/Driver/input-from-stdin.f90 +++ b/flang/test/Driver/input-from-stdin.f90 @@ -6,24 +6,26 @@ ! FLANG DRIVER (flang) !-------------------------- ! Input type is implicit -! RUN: cat %s | %flang -E - | FileCheck %s --check-prefix=PP-NOT-DEFINED -! RUN: cat %s | %flang -DNEW -E - | FileCheck %s --check-prefix=PP-DEFINED +! 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 -nocpp - | FileCheck %s --check-prefix=PP-NOT-DEFINED ! Input type is explicit -! RUN: cat %s | %flang -E -x f95-cpp-input - | FileCheck %s --check-prefix=PP-NOT-DEFINED -! RUN: cat %s | %flang -DNEW -E -x f95-cpp-input - | FileCheck %s --check-prefix=PP-DEFINED +! RUN: cat %s | %flang -E -cpp -x f95-cpp-input - | FileCheck %s --check-prefix=PP-NOT-DEFINED +! RUN: cat %s | %flang -DNEW -E -cpp -x f95-cpp-input - | FileCheck %s --check-prefix=PP-DEFINED !--------------------------------------- ! FLANG FRONTEND DRIVER (flang -fc1) !--------------------------------------- ! Test `-E`: for the corresponding frontend actions the driver relies on the prescanner API to handle file I/O -! RUN: cat %s | %flang -fc1 -E | FileCheck %s --check-prefix=PP-NOT-DEFINED -! RUN: cat %s | %flang -fc1 -DNEW -E | FileCheck %s --check-prefix=PP-DEFINED +! RUN: cat %s | %flang -fc1 -E -cpp | FileCheck %s --check-prefix=PP-NOT-DEFINED +! RUN: cat %s | %flang -fc1 -DNEW -E -cpp | FileCheck %s --check-prefix=PP-DEFINED ! Test `-test-io`: for the corresponding frontend action (`InputOutputTestAction`) the driver handles the file I/O on its own ! the corresponding action (`PrintPreprocessedAction`) -! RUN: cat %s | %flang -fc1 -test-io | FileCheck %s --check-prefix=IO --match-full-lines -! RUN: cat %s | %flang -fc1 -DNEW -test-io | FileCheck %s --check-prefix=IO --match-full-lines +! RUN: cat %s | %flang -fc1 -test-io -cpp | FileCheck %s --check-prefix=IO --match-full-lines +! RUN: cat %s | %flang -fc1 -DNEW -cpp -test-io | FileCheck %s --check-prefix=IO --match-full-lines !------------------------- ! EXPECTED OUTPUT for `-E` diff --git a/flang/test/Driver/macro-def-undef.f90 b/flang/test/Driver/macro-def-undef.F90 rename from flang/test/Driver/macro-def-undef.f90 rename to flang/test/Driver/macro-def-undef.F90 --- a/flang/test/Driver/macro-def-undef.f90 +++ b/flang/test/Driver/macro-def-undef.F90 @@ -1,20 +1,18 @@ ! Ensure arguments -D and -U work as expected. -! REQUIRES: new-flang-driver - !-------------------------- ! FLANG DRIVER (flang-new) !-------------------------- -! RUN: %flang-new -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED -! RUN: %flang-new -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED -! RUN: %flang-new -E -DX=A -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED +! RUN: %flang -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED +! RUN: %flang -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED +! RUN: %flang -E -DX=A -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED !----------------------------------------- ! FRONTEND FLANG DRIVER (flang-new -fc1) !----------------------------------------- -! RUN: %flang-new -fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED -! RUN: %flang-new -fc1 -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED -! RUN: %flang-new -fc1 -E -DX -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED +! RUN: %flang_fc1 -E %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED +! RUN: %flang_fc1 -E -DX=A %s 2>&1 | FileCheck %s --check-prefix=DEFINED +! RUN: %flang_fc1 -E -DX -UX %s 2>&1 | FileCheck %s --check-prefix=UNDEFINED !-------------------------------------------- ! EXPECTED OUTPUT FOR AN UNDEFINED MACRO @@ -35,4 +33,4 @@ #else program B #endif -end \ No newline at end of file +end diff --git a/flang/test/Driver/macro-multiline.f90 b/flang/test/Driver/macro-multiline.F90 rename from flang/test/Driver/macro-multiline.f90 rename to flang/test/Driver/macro-multiline.F90 diff --git a/flang/test/Driver/predefined-macros-compiler-version.f90 b/flang/test/Driver/predefined-macros-compiler-version.F90 rename from flang/test/Driver/predefined-macros-compiler-version.f90 rename to flang/test/Driver/predefined-macros-compiler-version.F90