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 @@ -4129,7 +4129,6 @@ def fcheck_EQ : Joined<["-"], "fcheck=">, Group; def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group; def fconvert_EQ : Joined<["-"], "fconvert=">, Group; -def ffixed_line_length_VALUE : Joined<["-"], "ffixed-line-length-">, Group; def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group; def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group; def finit_character_EQ : Joined<["-"], "finit-character=">, Group; @@ -4163,8 +4162,6 @@ defm dump_parse_tree : BooleanFFlag<"dump-parse-tree">, Group; defm external_blas : BooleanFFlag<"external-blas">, Group; defm f2c : BooleanFFlag<"f2c">, Group; -defm fixed_form : BooleanFFlag<"fixed-form">, Group; -defm free_form : BooleanFFlag<"free-form">, Group; defm frontend_optimize : BooleanFFlag<"frontend-optimize">, Group; defm implicit_none : BooleanFFlag<"implicit-none">, Group; defm init_local_zero : BooleanFFlag<"init-local-zero">, Group; @@ -4207,6 +4204,20 @@ def test_io : Flag<["-"], "test-io">, Flags<[HelpHidden, FlangOption, FC1Option, FlangOnlyOption]>, Group, HelpText<"Run the InputOuputTest action. Use for development and testing only.">; +let Flags = [FC1Option, FlangOption, FlangOnlyOption] in { + +def ffixed_form : Flag<["-"], "ffixed-form">, Group, + HelpText<"Process source files in fixed form">; +def ffree_form : Flag<["-"], "ffree-form">, Group, + HelpText<"Process source files in free form">; +def ffixed_line_length_EQ : Joined<["-"], "ffixed-line-length=">, Group, + HelpText<"Use as character line width in fixed mode">, + DocBrief<[{Set column after which characters are ignored in typical fixed-form lines in the source +file}]>; +def ffixed_line_length_VALUE : Joined<["-"], "ffixed-line-length-">, Group, Alias; + +} + //===----------------------------------------------------------------------===// // CC1 Options //===----------------------------------------------------------------------===// diff --git a/clang/lib/Driver/ToolChains/Flang.h b/clang/lib/Driver/ToolChains/Flang.h --- a/clang/lib/Driver/ToolChains/Flang.h +++ b/clang/lib/Driver/ToolChains/Flang.h @@ -24,6 +24,14 @@ /// Flang compiler tool. class LLVM_LIBRARY_VISIBILITY Flang : public Tool { private: + /// Extract fortran dialect options from the driver arguments and add them to + /// the list of arguments for the generated command/job. + /// + /// \param [in] Args The list of input driver arguments + /// \param [out] CmdArgs The list of output command arguments + void AddFortranDialectOptions(const llvm::opt::ArgList &Args, + llvm::opt::ArgStringList &CmdArgs) const; + /// Extract preprocessing options from the driver arguments and add them to /// the preprocessor command arguments. /// 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 @@ -19,6 +19,12 @@ using namespace clang; using namespace llvm::opt; +void Flang::AddFortranDialectOptions(const ArgList &Args, + ArgStringList &CmdArgs) const { + Args.AddAllArgs(CmdArgs, {options::OPT_ffixed_form, options::OPT_ffree_form, + options::OPT_ffixed_line_length_EQ}); +} + void Flang::AddPreprocessingOptions(const ArgList &Args, ArgStringList &CmdArgs) const { Args.AddAllArgs(CmdArgs, {options::OPT_D, options::OPT_U, options::OPT_I}); @@ -79,6 +85,8 @@ if (types::getPreprocessedType(InputType) != types::TY_INVALID) AddPreprocessingOptions(Args, CmdArgs); + AddFortranDialectOptions(Args, CmdArgs); + if (Output.isFilename()) { CmdArgs.push_back("-o"); CmdArgs.push_back(Output.getFilename()); 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 @@ -74,6 +74,18 @@ Fortran, }; +// Source file layout +enum class FortranForm { + /// The user has not specified a form. Base the form off the file extension. + Unknown, + + /// -ffree-form + FixedForm, + + /// -ffixed-form + FreeForm +}; + /// The kind of a file that we've been handed as an input. class InputKind { private: @@ -159,6 +171,13 @@ /// The frontend action to perform. frontend::ActionKind programAction_; + // The form to process files in, if specified. + FortranForm fortranForm_ = FortranForm::Unknown; + + // The column after which characters are ignored in fixed form lines in the + // source file. + int fixedFormColumns_ = 72; + public: FrontendOptions() : showHelp_(false), showVersion_(false) {} 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 @@ -148,12 +148,14 @@ // Run the frontend action `act` for every input file. for (const FrontendInputFile &fif : frontendOpts().inputs_) { if (act.BeginSourceFile(*this, fif)) { - // Switch between fixed and free form format based on the input file - // extension. Ideally we should have all Fortran options set before - // entering this loop (i.e. processing any input files). However, we - // can't decide between fixed and free form based on the file extension - // earlier than this. - invoc.fortranOpts().isFixedForm = fif.IsFixedForm(); + if (invoc.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 loop (i.e. processing any input files). However, we + // can't decide between fixed and free form based on the file extension + // earlier than this. + invoc.fortranOpts().isFixedForm = fif.IsFixedForm(); + } if (llvm::Error err = act.Execute()) { consumeError(std::move(err)); } 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 @@ -162,6 +162,40 @@ opts.inputs_.emplace_back(std::move(inputs[i]), ik); } + + // Set fortranForm_ based on options -ffree-form and -ffixed-form. + if (const auto *arg = args.getLastArg(clang::driver::options::OPT_ffixed_form, + clang::driver::options::OPT_ffree_form)) { + opts.fortranForm_ = + arg->getOption().matches(clang::driver::options::OPT_ffixed_form) + ? FortranForm::FixedForm + : FortranForm::FreeForm; + } + + // Set fixedFormColumns_ based on -ffixed-line-length= + if (const auto *arg = + args.getLastArg(clang::driver::options::OPT_ffixed_line_length_EQ)) { + llvm::StringRef argValue = llvm::StringRef(arg->getValue()); + std::int64_t columns = -1; + if (argValue == "none") { + columns = 0; + } else if (argValue.getAsInteger(/*Radix=*/10, columns)) { + columns = -1; + } + if (columns < 0) { + diags.Report(clang::diag::err_drv_invalid_value_with_suggestion) + << arg->getOption().getName() << arg->getValue() + << "value must be 'none' or a non-negative integer"; + } else if (columns == 0) { + opts.fixedFormColumns_ = 1000000; + } else if (columns < 7) { + diags.Report(clang::diag::err_drv_invalid_value_with_suggestion) + << arg->getOption().getName() << arg->getValue() + << "value must be at least seven"; + } else { + opts.fixedFormColumns_ = columns; + } + } return dashX; } @@ -278,8 +312,15 @@ void CompilerInvocation::setFortranOpts() { auto &fortranOptions = fortranOpts(); + const auto &frontendOptions = frontendOpts(); const auto &preprocessorOptions = preprocessorOpts(); + if (frontendOptions.fortranForm_ != FortranForm::Unknown) { + fortranOptions.isFixedForm = + frontendOptions.fortranForm_ == FortranForm::FixedForm; + } + fortranOptions.fixedFormColumns = frontendOptions.fixedFormColumns_; + collectMacroDefinitions(preprocessorOptions, fortranOptions); fortranOptions.searchDirectories.insert( diff --git a/flang/test/Flang-Driver/Inputs/fixed-form-test.f b/flang/test/Flang-Driver/Inputs/fixed-form-test.f --- a/flang/test/Flang-Driver/Inputs/fixed-form-test.f +++ b/flang/test/Flang-Driver/Inputs/fixed-form-test.f @@ -1,2 +1,3 @@ program FixedForm +c end end diff --git a/flang/test/Flang-Driver/Inputs/fixed-line-length-test.f b/flang/test/Flang-Driver/Inputs/fixed-line-length-test.f new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/Inputs/fixed-line-length-test.f @@ -0,0 +1,3 @@ +! The length of the line below is exactly 73 characters + program aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + end diff --git a/flang/test/Flang-Driver/driver-help-hidden.f90 b/flang/test/Flang-Driver/driver-help-hidden.f90 --- a/flang/test/Flang-Driver/driver-help-hidden.f90 +++ b/flang/test/Flang-Driver/driver-help-hidden.f90 @@ -23,6 +23,10 @@ ! CHECK-NEXT: -D = Define to (or 1 if omitted) ! CHECK-NEXT: -E Only run the preprocessor ! CHECK-NEXT: -fcolor-diagnostics Enable colors in diagnostics +! CHECK-NEXT: -ffixed-form Process source files in fixed form +! CHECK-NEXT: -ffixed-line-length= +! CHECK-NEXT: Use as character line width in fixed mode +! CHECK-NEXT: -ffree-form Process source files in free form ! CHECK-NEXT: -fno-color-diagnostics Disable colors in diagnostics ! CHECK-NEXT: -help Display available options ! CHECK-NEXT: -I Add directory to the end of the list of include search paths diff --git a/flang/test/Flang-Driver/driver-help.f90 b/flang/test/Flang-Driver/driver-help.f90 --- a/flang/test/Flang-Driver/driver-help.f90 +++ b/flang/test/Flang-Driver/driver-help.f90 @@ -23,6 +23,10 @@ ! HELP-NEXT: -D = Define to (or 1 if omitted) ! HELP-NEXT: -E Only run the preprocessor ! HELP-NEXT: -fcolor-diagnostics Enable colors in diagnostics +! HELP-NEXT: -ffixed-form Process source files in fixed form +! HELP-NEXT: -ffixed-line-length= +! HELP-NEXT: Use as character line width in fixed mode +! HELP-NEXT: -ffree-form Process source files in free form ! HELP-NEXT: -fno-color-diagnostics Disable colors in diagnostics ! HELP-NEXT: -help Display available options ! HELP-NEXT: -I Add directory to the end of the list of include search paths @@ -39,6 +43,10 @@ ! 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 +! HELP-FC1-NEXT: -ffixed-form Process source files in fixed form +! HELP-FC1-NEXT: -ffixed-line-length= +! HELP-FC1-NEXT: Use as character line width in fixed mode +! HELP-FC1-NEXT: -ffree-form Process source files in free form ! 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: -o Write output to diff --git a/flang/test/Flang-Driver/fixed-free-flag.f90 b/flang/test/Flang-Driver/fixed-free-flag.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/fixed-free-flag.f90 @@ -0,0 +1,25 @@ +! Ensure arguments -ffree-form and -ffixed-form work as expected. + +! REQUIRES: new-flang-driver + +!-------------------------- +! FLANG DRIVER (flang-new) +!-------------------------- +! RUN: not %flang-new -fsyntax-only -ffree-form %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=FREEFORM +! RUN: %flang-new -fsyntax-only -ffixed-form %S/Inputs/free-form-test.f90 2>&1 | FileCheck %s --check-prefix=FIXEDFORM + +!---------------------------------------- +! FRONTEND FLANG DRIVER (flang-new -fc1) +!---------------------------------------- +! RUN: not %flang-new -fc1 -fsyntax-only -ffree-form %S/Inputs/fixed-form-test.f 2>&1 | FileCheck %s --check-prefix=FREEFORM +! RUN: %flang-new -fc1 -fsyntax-only -ffixed-form %S/Inputs/free-form-test.f90 2>&1 | FileCheck %s --check-prefix=FIXEDFORM + +!------------------------------------ +! EXPECTED OUTPUT FOR FREE FORM MODE +!------------------------------------ +! FREEFORM:error: Could not parse + +!------------------------------------- +! EXPECTED OUTPUT FOR FIXED FORM MODE +!------------------------------------- +! FIXEDFORM:free-form-test.f90:1:1: Character in fixed-form label field must be a digit diff --git a/flang/test/Flang-Driver/fixed-line-length.f90 b/flang/test/Flang-Driver/fixed-line-length.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Flang-Driver/fixed-line-length.f90 @@ -0,0 +1,56 @@ +! Ensure argument -ffixed-line-length=n works as expected. + +! REQUIRES: new-flang-driver + +!-------------------------- +! FLANG DRIVER (flang-new) +!-------------------------- +! RUN: %flang-new -E %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=DEFAULTLENGTH +! RUN: not %flang-new -E -ffixed-line-length=-2 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=NEGATIVELENGTH +! RUN: not %flang-new -E -ffixed-line-length=3 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=INVALIDLENGTH +! RUN: %flang-new -E -ffixed-line-length=none %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=UNLIMITEDLENGTH +! RUN: %flang-new -E -ffixed-line-length=0 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=UNLIMITEDLENGTH +! RUN: %flang-new -E -ffixed-line-length=13 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=LENGTH13 + +!---------------------------------------- +! FRONTEND FLANG DRIVER (flang-new -fc1) +!---------------------------------------- +! RUN: %flang-new -fc1 -E %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=DEFAULTLENGTH +! RUN: not %flang-new -fc1 -E -ffixed-line-length=-2 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=NEGATIVELENGTH +! RUN: not %flang-new -fc1 -E -ffixed-line-length=3 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=INVALIDLENGTH +! RUN: %flang-new -fc1 -E -ffixed-line-length=none %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=UNLIMITEDLENGTH +! RUN: %flang-new -fc1 -E -ffixed-line-length=0 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=UNLIMITEDLENGTH +! RUN: %flang-new -fc1 -E -ffixed-line-length=13 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=LENGTH13 + +!------------------------------------- +! COMMAND ALIAS -ffixed-line-length-n +!------------------------------------- +! RUN: %flang-new -E -ffixed-line-length-13 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=LENGTH13 +! RUN: %flang-new -fc1 -E -ffixed-line-length-13 %S/Inputs/fixed-line-length-test.f 2>&1 | FileCheck %s --check-prefix=LENGTH13 + +!------------------------------------- +! EXPECTED OUTPUT WITH DEFAULT LENGTH +!------------------------------------- +! The line should be trimmed to 72 characters when reading based on the default value of fixed line length. +! DEFAULTLENGTH: program{{(a{58})}} + +!----------------------------------------- +! EXPECTED OUTPUT WITH A NEGATIVE LENGTH +!----------------------------------------- +! NEGATIVELENGTH: invalid value '-2' in 'ffixed-line-length=','value must be 'none' or a non-negative integer' + +!----------------------------------------- +! EXPECTED OUTPUT WITH LENGTH LESS THAN 7 +!----------------------------------------- +! INVALIDLENGTH: invalid value '3' in 'ffixed-line-length=','value must be at least seven' + +!--------------------------------------- +! EXPECTED OUTPUT WITH UNLIMITED LENGTH +!--------------------------------------- +! The line should not be trimmed and so 73 characters (including spaces) should be read. +! UNLIMITEDLENGTH: program{{(a{59})}} + +!-------------------------------- +! EXPECTED OUTPUT WITH LENGTH 13 +!-------------------------------- +! LENGTH13: program