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 @@ -4536,6 +4536,11 @@ def module_suffix : Separate<["-"], "module-suffix">, Group, MetaVarName<"">, HelpText<"Use as the suffix for module files (the default value is `.mod`)">; +def fanalyzed_objects_for_unparse : Flag<["-"], + "fanalyzed-objects-for-unparse">, Group; +def fno_analyzed_objects_for_unparse : Flag<["-"], + "fno-analyzed-objects-for-unparse">, Group, + HelpText<"Do not use the analyzed objects when unparsing">; } 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 @@ -75,11 +75,39 @@ bool warnAsErr_ = false; + /// This flag controls the unparsing and is used to decide whether to print out + /// the semantically analyzed version of an object or expression or the plain + /// version that does not include any information from semantic analysis. + bool useAnalyzedObjectsForUnparse_ = true; + // Fortran Dialect options Fortran::common::IntrinsicTypeDefaultKinds defaultKinds_; bool EnableConformanceChecks_ = false; + /// Used in e.g. unparsing to dump the analyzed rather than the original + /// parse-tree objects. + Fortran::parser::AnalyzedObjectsAsFortran AsFortran_{ + [](llvm::raw_ostream &o, const Fortran::evaluate::GenericExprWrapper &x) { + if (x.v) { + x.v->AsFortran(o); + } else { + o << "(bad expression)"; + } + }, + [](llvm::raw_ostream &o, + const Fortran::evaluate::GenericAssignmentWrapper &x) { + if (x.v) { + x.v->AsFortran(o); + } else { + o << "(bad assignment)"; + } + }, + [](llvm::raw_ostream &o, const Fortran::evaluate::ProcedureRef &x) { + x.AsFortran(o << "CALL "); + }, + }; + public: CompilerInvocation() = default; @@ -108,11 +136,21 @@ bool &warnAsErr() { return warnAsErr_; } const bool &warnAsErr() const { return warnAsErr_; } + bool &useAnalyzedObjectsForUnparse() { return useAnalyzedObjectsForUnparse_; } + const bool &useAnalyzedObjectsForUnparse() const { + return useAnalyzedObjectsForUnparse_; + } + bool &enableConformanceChecks() { return EnableConformanceChecks_; } const bool &enableConformanceChecks() const { return EnableConformanceChecks_; } + Fortran::parser::AnalyzedObjectsAsFortran &asFortran() { return AsFortran_; } + const Fortran::parser::AnalyzedObjectsAsFortran &asFortran() const { + return AsFortran_; + } + Fortran::common::IntrinsicTypeDefaultKinds &defaultKinds() { return defaultKinds_; } @@ -142,6 +180,10 @@ void SetWarnAsErr(bool flag) { warnAsErr_ = flag; } + void SetUseAnalyzedObjectsForUnparse(bool flag) { + useAnalyzedObjectsForUnparse_ = flag; + } + /// Set the Fortran options to predifined defaults. These defaults are /// consistend with f18/f18.cpp. // TODO: We should map frontendOpts_ to parserOpts_ instead. For that, we 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 @@ -404,6 +404,12 @@ res.SetModuleFileSuffix(moduleSuffix->getValue()); } + // -fno-analyzed-objects-for-unparse + if (args.hasArg( + clang::driver::options::OPT_fno_analyzed_objects_for_unparse)) { + res.SetUseAnalyzedObjectsForUnparse(false); + } + return diags.getNumErrors() == numErrorsBefore; } 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 @@ -242,28 +242,27 @@ } void DebugUnparseNoSemaAction::ExecuteAction() { + auto &invoc = this->instance().invocation(); auto &parseTree{instance().parsing().parseTree()}; - Fortran::parser::AnalyzedObjectsAsFortran asFortran = - Fortran::frontend::getBasicAsFortran(); - // TODO: Options should come from CompilerInvocation Unparse(llvm::outs(), *parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8, /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, - /*preStatement=*/nullptr, &asFortran); + /*preStatement=*/nullptr, + invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); } void DebugUnparseAction::ExecuteAction() { + auto &invoc = this->instance().invocation(); auto &parseTree{instance().parsing().parseTree()}; - Fortran::parser::AnalyzedObjectsAsFortran asFortran = - Fortran::frontend::getBasicAsFortran(); // TODO: Options should come from CompilerInvocation Unparse(llvm::outs(), *parseTree, /*encoding=*/Fortran::parser::Encoding::UTF_8, /*capitalizeKeywords=*/true, /*backslashEscapes=*/false, - /*preStatement=*/nullptr, &asFortran); + /*preStatement=*/nullptr, + invoc.useAnalyzedObjectsForUnparse() ? &invoc.asFortran() : nullptr); // Report fatal semantic errors reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), @@ -310,12 +309,11 @@ // Dump parse tree auto &parseTree{instance().parsing().parseTree()}; - Fortran::parser::AnalyzedObjectsAsFortran asFortran = - Fortran::frontend::getBasicAsFortran(); llvm::outs() << "========================"; llvm::outs() << " Flang: parse tree dump "; llvm::outs() << "========================\n"; - Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); + Fortran::parser::DumpTree( + llvm::outs(), parseTree, &ci.invocation().asFortran()); auto &semantics = this->semantics(); auto tables{Fortran::semantics::BuildRuntimeDerivedTypeTables( @@ -343,20 +341,19 @@ void DebugDumpParseTreeNoSemaAction::ExecuteAction() { auto &parseTree{instance().parsing().parseTree()}; - Fortran::parser::AnalyzedObjectsAsFortran asFortran = - Fortran::frontend::getBasicAsFortran(); // Dump parse tree - Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); + Fortran::parser::DumpTree( + llvm::outs(), parseTree, &this->instance().invocation().asFortran()); } void DebugDumpParseTreeAction::ExecuteAction() { auto &parseTree{instance().parsing().parseTree()}; - Fortran::parser::AnalyzedObjectsAsFortran asFortran = - Fortran::frontend::getBasicAsFortran(); // Dump parse tree - Fortran::parser::DumpTree(llvm::outs(), parseTree, &asFortran); + Fortran::parser::DumpTree( + llvm::outs(), parseTree, &this->instance().invocation().asFortran()); + // Report fatal semantic errors reportFatalSemanticErrors(semantics(), this->instance().diagnostics(), GetCurrentFileOrBufferName()); 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 @@ -32,34 +32,6 @@ 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 -// APIs or some dedicated utility library in the driver. -Fortran::parser::AnalyzedObjectsAsFortran -Fortran::frontend::getBasicAsFortran() { - return Fortran::parser::AnalyzedObjectsAsFortran{ - [](llvm::raw_ostream &o, const Fortran::evaluate::GenericExprWrapper &x) { - if (x.v) { - x.v->AsFortran(o); - } else { - o << "(bad expression)"; - } - }, - [](llvm::raw_ostream &o, - const Fortran::evaluate::GenericAssignmentWrapper &x) { - if (x.v) { - x.v->AsFortran(o); - } else { - o << "(bad assignment)"; - } - }, - [](llvm::raw_ostream &o, const Fortran::evaluate::ProcedureRef &x) { - x.AsFortran(o << "CALL "); - }, - }; -} - InputKind FrontendOptions::GetInputKindForExtension(llvm::StringRef extension) { if (isFixedFormSuffix(extension) || isFreeFormSuffix(extension)) { return Language::Fortran; 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 @@ -101,6 +101,8 @@ ! HELP-FC1-NEXT: Specify where to find the compiled intrinsic modules ! HELP-FC1-NEXT: -flarge-sizes Use INTEGER(KIND=8) for the result type in size-related intrinsics ! HELP-FC1-NEXT: -flogical-abbreviations Enable logical abbreviations +! HELP-FC1-NEXT: -fno-analyzed-objects-for-unparse +! HELP-FC1-NEXT: Do not use the analyzed objects when unparsing ! HELP-FC1-NEXT: -fopenacc Enable OpenACC ! HELP-FC1-NEXT: -fopenmp Parse OpenMP pragmas and generate parallel code. ! HELP-FC1-NEXT: -fxor-operator Enable .XOR. as a synonym of .NEQV. diff --git a/flang/test/Driver/unparse-use-analyzed.f95 b/flang/test/Driver/unparse-use-analyzed.f95 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/unparse-use-analyzed.f95 @@ -0,0 +1,31 @@ +! Tests `-fno-analyzed-exprs-as-fortran` frontend option + +!-------------------------- +! RUN lines +!-------------------------- +! RUN: %flang_fc1 -fdebug-unparse %s | FileCheck %s --check-prefix=DEFAULT +! RUN: %flang_fc1 -fdebug-unparse -fno-analyzed-objects-for-unparse %s | FileCheck %s --check-prefix=DISABLED + +!------------------------------------------------ +! EXPECTED OUTPUT: default - use analyzed objects +!------------------------------------------------ +! DEFAULT: PROGRAM test +! DEFAULT-NEXT: REAL, PARAMETER :: val = 3.43e2_4 +! DEFAULT-NEXT: PRINT *, 3.47e2_4 +! DEFAULT-NEXT: END PROGRAM + +!----------------------------------------------------------- +! EXPECTED OUTPUT: disabled - don't use the analyzed objects +!----------------------------------------------------------- +! DISABLED: PROGRAM test +! DISABLED-NEXT: REAL, PARAMETER :: val = 343.0 +! DISABLED-NEXT: PRINT *, val+4 +! DISABLED-NEXT: END PROGRAM + +!-------------------------- +! INPUT +!-------------------------- +program test + real, parameter :: val = 343.0 + print *, val + 4 +end program diff --git a/flang/tools/f18/f18.cpp b/flang/tools/f18/f18.cpp --- a/flang/tools/f18/f18.cpp +++ b/flang/tools/f18/f18.cpp @@ -105,7 +105,7 @@ bool debugModuleWriter{false}; bool defaultReal8{false}; bool measureTree{false}; - bool unparseTypedExprsToF18_FC{false}; + bool useAnalyzedObjectsForUnparse{true}; std::vector F18_FCArgs; const char *prefix{nullptr}; bool getDefinition{false}; @@ -322,7 +322,8 @@ Unparse(llvm::outs(), parseTree, driver.encoding, true /*capitalize*/, options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes), - nullptr /* action before each statement */, &asFortran); + nullptr /* action before each statement */, + driver.useAnalyzedObjectsForUnparse ? &asFortran : nullptr); return {}; } if (driver.dumpPreFirTree) { @@ -353,7 +354,7 @@ options.features.IsEnabled( Fortran::common::LanguageFeature::BackslashEscapes), nullptr /* action before each statement */, - driver.unparseTypedExprsToF18_FC ? &asFortran : nullptr); + driver.useAnalyzedObjectsForUnparse ? &asFortran : nullptr); } RunOtherCompiler(driver, tmpSourcePath.data(), relo.data()); @@ -578,8 +579,8 @@ } else if (arg == "-funparse-with-symbols" || arg == "-fdebug-unparse-with-symbols") { driver.dumpUnparseWithSymbols = true; - } else if (arg == "-funparse-typed-exprs-to-f18-fc") { - driver.unparseTypedExprsToF18_FC = true; + } else if (arg == "-fno-analyzed-objects-for-unparse") { + driver.useAnalyzedObjectsForUnparse = false; } else if (arg == "-fparse-only" || arg == "-fsyntax-only") { driver.syntaxOnly = true; } else if (arg == "-c") { diff --git a/flang/tools/f18/flang b/flang/tools/f18/flang --- a/flang/tools/f18/flang +++ b/flang/tools/f18/flang @@ -8,7 +8,7 @@ #===------------------------------------------------------------------------===# wd=$(cd $(dirname "$0")/.. && pwd) -opts="-module-suffix .f18.mod " +opts="-fno-analyzed-objects-for-unparse -module-suffix .f18.mod " if ! $wd/bin/f18 $opts "$@" then status=$? echo flang: in $PWD, f18 failed with exit status $status: $wd/bin/f18 $opts "$@" >&2