From e668139fdcd3d4acc8ba7963e507ce2b63ea5f2f Mon Sep 17 00:00:00 2001 From: peter klausler Date: Tue, 17 Dec 2019 15:59:25 -0800 Subject: [PATCH] [flang] Fix unparsing of assignment representation and excess parentheses More fixes, and move prefix/infix/suffix strings into formatting.cc Original-commit: flang-compiler/f18@49d68700e0ab105459fb1e05bf60f8ec352a8aac Reviewed-on: https://github.com/flang-compiler/f18/pull/874 --- flang/lib/evaluate/expression.h | 39 ----- flang/lib/evaluate/formatting.cc | 242 +++++++++++++++++++----------- flang/lib/parser/unparse.cc | 2 + flang/lib/semantics/assignment.cc | 33 ++-- flang/lib/semantics/expression.cc | 29 ++-- flang/lib/semantics/expression.h | 5 +- flang/tools/f18/f18.cc | 3 + 7 files changed, 199 insertions(+), 154 deletions(-) diff --git a/flang/lib/evaluate/expression.h b/flang/lib/evaluate/expression.h index 7b3cb169da4d..34489d801fd8 100644 --- a/flang/lib/evaluate/expression.h +++ b/flang/lib/evaluate/expression.h @@ -192,12 +192,6 @@ public: std::ostream &AsFortran(std::ostream &) const; -protected: - // Overridable functions for AsFortran() - static const char *Prefix() { return ""; } - static const char *Infix() { return ""; } - static const char *Suffix() { return ""; } - private: Container operand_; }; @@ -232,8 +226,6 @@ struct Parentheses : public Operation, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Prefix() { return "("; } - static const char *Suffix() { return ")"; } }; template struct Negate : public Operation, A, A> { @@ -241,7 +233,6 @@ template struct Negate : public Operation, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Prefix() { return "-"; } }; template @@ -257,9 +248,6 @@ struct ComplexComponent ComplexComponent(bool isImaginary, Expr &&x) : Base{std::move(x)}, isImaginaryPart{isImaginary} {} - const char *Prefix() const { return isImaginaryPart ? "IMAG(" : "REAL("; } - const char *Suffix() const { return ")"; } - bool isImaginaryPart{true}; }; @@ -270,7 +258,6 @@ struct Not : public Operation, Type, using Operand = Result; using Base = Operation; using Base::Base; - static const char *Prefix() { return ".NOT."; } }; // Character lengths are determined by context in Fortran and do not @@ -286,9 +273,6 @@ struct SetLength using LengthOperand = SubscriptInteger; using Base = Operation; using Base::Base; - static const char *Prefix() { return "%SET_LENGTH("; } - static const char *Infix() { return ","; } - static const char *Suffix() { return ")"; } }; // Binary operations @@ -298,7 +282,6 @@ template struct Add : public Operation, A, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Infix() { return "+"; } }; template struct Subtract : public Operation, A, A, A> { @@ -306,7 +289,6 @@ template struct Subtract : public Operation, A, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Infix() { return "-"; } }; template struct Multiply : public Operation, A, A, A> { @@ -314,7 +296,6 @@ template struct Multiply : public Operation, A, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Infix() { return "*"; } }; template struct Divide : public Operation, A, A, A> { @@ -322,7 +303,6 @@ template struct Divide : public Operation, A, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Infix() { return "/"; } }; template struct Power : public Operation, A, A, A> { @@ -330,7 +310,6 @@ template struct Power : public Operation, A, A, A> { using Operand = A; using Base = Operation; using Base::Base; - static const char *Infix() { return "**"; } }; template @@ -340,7 +319,6 @@ struct RealToIntPower : public Operation, A, A, SomeInteger> { using BaseOperand = A; using ExponentOperand = SomeInteger; using Base::Base; - static const char *Infix() { return "**"; } }; template struct Extremum : public Operation, A, A, A> { @@ -352,13 +330,6 @@ template struct Extremum : public Operation, A, A, A> { : Base{x, y}, ordering{ord} {} Extremum(Ordering ord, Expr &&x, Expr &&y) : Base{std::move(x), std::move(y)}, ordering{ord} {} - - const char *Prefix() const { - return ordering == Ordering::Less ? "MIN(" : "MAX("; - } - static const char *Infix() { return ","; } - static const char *Suffix() { return ")"; } - Ordering ordering{Ordering::Greater}; }; @@ -371,9 +342,6 @@ struct ComplexConstructor using Operand = Type; using Base = Operation; using Base::Base; - static const char *Prefix() { return "("; } - static const char *Infix() { return ","; } - static const char *Suffix() { return ")"; } }; template @@ -385,7 +353,6 @@ struct Concat using Operand = Result; using Base = Operation; using Base::Base; - static const char *Infix() { return "//"; } }; template @@ -401,9 +368,6 @@ struct LogicalOperation : Base{x, y}, logicalOperator{opr} {} LogicalOperation(LogicalOperator opr, Expr &&x, Expr &&y) : Base{std::move(x), std::move(y)}, logicalOperator{opr} {} - - const char *Infix() const; - LogicalOperator logicalOperator; }; @@ -655,9 +619,6 @@ struct Relational : public Operation, LogicalResult, T, T> { : Base{a, b}, opr{r} {} Relational(RelationalOperator r, Expr &&a, Expr &&b) : Base{std::move(a), std::move(b)}, opr{r} {} - - const char *Infix() const; - RelationalOperator opr; }; diff --git a/flang/lib/evaluate/formatting.cc b/flang/lib/evaluate/formatting.cc index fc65ca7263c7..6cde8a940e5a 100644 --- a/flang/lib/evaluate/formatting.cc +++ b/flang/lib/evaluate/formatting.cc @@ -163,68 +163,72 @@ enum class Precedence { // in increasing order for sane comparisons Power, // **, which is right-associative unlike the other dyadic operators DefinedUnary, Parenthesize, // (x), (real, imaginary) - Constant, // parenthesize if negative integer/real operand - Primary, // don't parenthesize + Literal, + Top, }; -template constexpr Precedence ToPrecedence{Precedence::Primary}; - -template -constexpr Precedence ToPrecedence>{Precedence::Or}; -template -constexpr Precedence ToPrecedence>{Precedence::Not}; -template -constexpr Precedence ToPrecedence>{Precedence::Relational}; -template -constexpr Precedence ToPrecedence>{Precedence::Additive}; -template -constexpr Precedence ToPrecedence>{Precedence::Additive}; -template -constexpr Precedence ToPrecedence>{Precedence::Additive}; -template -constexpr Precedence ToPrecedence>{Precedence::Negate}; -template -constexpr Precedence ToPrecedence>{Precedence::Multiplicative}; -template -constexpr Precedence ToPrecedence>{Precedence::Multiplicative}; -template -constexpr Precedence ToPrecedence>{Precedence::Power}; -template -constexpr Precedence ToPrecedence>{Precedence::Power}; -template -constexpr Precedence ToPrecedence>{Precedence::Constant}; -template -constexpr Precedence ToPrecedence>{Precedence::Constant}; -template -constexpr Precedence ToPrecedence>{Precedence::Parenthesize}; -template -constexpr Precedence ToPrecedence>{ - Precedence::Parenthesize}; - -template -static constexpr Precedence GetPrecedence(const Expr &expr) { - return std::visit( - [](const auto &x) { - static constexpr Precedence prec{ - ToPrecedence>}; - if constexpr (prec == Precedence::Or) { - // Distinguish the four logical binary operations. - switch (x.logicalOperator) { - SWITCH_COVERS_ALL_CASES - case LogicalOperator::And: return Precedence::And; - case LogicalOperator::Or: return Precedence::Or; - case LogicalOperator::Not: return Precedence::Not; - case LogicalOperator::Eqv: - case LogicalOperator::Neqv: return Precedence::Equivalence; - } - } - return prec; - }, - expr.u); +template constexpr Precedence ToPrecedence(const A &) { + return Precedence::Top; } -template -static constexpr Precedence GetPrecedence(const Expr> &expr) { - return std::visit([](const auto &x) { return GetPrecedence(x); }, expr.u); +template +static Precedence ToPrecedence(const LogicalOperation &x) { + switch (x.logicalOperator) { + SWITCH_COVERS_ALL_CASES + case LogicalOperator::And: return Precedence::And; + case LogicalOperator::Or: return Precedence::Or; + case LogicalOperator::Not: return Precedence::Not; + case LogicalOperator::Eqv: + case LogicalOperator::Neqv: return Precedence::Equivalence; + } +} +template constexpr Precedence ToPrecedence(const Not &) { + return Precedence::Not; +} +template constexpr Precedence ToPrecedence(const Relational &) { + return Precedence::Relational; +} +template constexpr Precedence ToPrecedence(const Add &) { + return Precedence::Additive; +} +template constexpr Precedence ToPrecedence(const Subtract &) { + return Precedence::Additive; +} +template constexpr Precedence ToPrecedence(const Concat &) { + return Precedence::Additive; +} +template constexpr Precedence ToPrecedence(const Negate &) { + return Precedence::Negate; +} +template constexpr Precedence ToPrecedence(const Multiply &) { + return Precedence::Multiplicative; +} +template constexpr Precedence ToPrecedence(const Divide &) { + return Precedence::Multiplicative; +} +template constexpr Precedence ToPrecedence(const Power &) { + return Precedence::Power; +} +template +constexpr Precedence ToPrecedence(const RealToIntPower &) { + return Precedence::Power; +} +template static Precedence ToPrecedence(const Constant &x) { + static constexpr TypeCategory cat{T::category}; + if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) { + if (auto n{GetScalarConstantValue(x)}) { + if (n->IsNegative()) { + return Precedence::Negate; + } + } + } + return Precedence::Literal; +} +template constexpr Precedence ToPrecedence(const Parentheses &) { + return Precedence::Parenthesize; +} + +template static Precedence ToPrecedence(const Expr &expr) { + return std::visit([](const auto &x) { return ToPrecedence(x); }, expr.u); } template static bool IsNegatedScalarConstant(const Expr &expr) { @@ -243,29 +247,105 @@ static bool IsNegatedScalarConstant(const Expr> &expr) { [](const auto &x) { return IsNegatedScalarConstant(x); }, expr.u); } +struct OperatorSpelling { + const char *prefix{""}, *infix{","}, *suffix{""}; +}; + +template constexpr OperatorSpelling SpellOperator(const A &) { + return OperatorSpelling{}; +} +template +constexpr OperatorSpelling SpellOperator(const Negate &) { + return OperatorSpelling{"-", "", ""}; +} +template +static OperatorSpelling SpellOperator(const ComplexComponent &x) { + return OperatorSpelling{x.isImaginaryPart ? "AIMAG(" : "REAL(", "", ")"}; +} +template constexpr OperatorSpelling SpellOperator(const Not &) { + return OperatorSpelling{".NOT.", "", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const SetLength &) { + return OperatorSpelling{"%SET_LENGTH(", ",", ")"}; +} +template +constexpr OperatorSpelling SpellOperator(const ComplexConstructor &) { + return OperatorSpelling{"(", ",", ")"}; +} +template constexpr OperatorSpelling SpellOperator(const Add &) { + return OperatorSpelling{"", "+", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const Subtract &) { + return OperatorSpelling{"", "-", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const Multiply &) { + return OperatorSpelling{"", "*", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const Divide &) { + return OperatorSpelling{"", "/", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const Power &) { + return OperatorSpelling{"", "**", ""}; +} +template +constexpr OperatorSpelling SpellOperator(const RealToIntPower &) { + return OperatorSpelling{"", "**", ""}; +} +template +static OperatorSpelling SpellOperator(const Extremum &x) { + return OperatorSpelling{ + x.ordering == Ordering::Less ? "MIN(" : "MAX(", ",", ")"}; +} +template +constexpr OperatorSpelling SpellOperator(const Concat &) { + return OperatorSpelling{"", "//", ""}; +} +template +static OperatorSpelling SpellOperator(const LogicalOperation &x) { + return OperatorSpelling{"", AsFortran(x.logicalOperator), ""}; +} +template +static OperatorSpelling SpellOperator(const Relational &x) { + return OperatorSpelling{"", AsFortran(x.opr), ""}; +} + template std::ostream &Operation::AsFortran(std::ostream &o) const { - Precedence lhsPrec{GetPrecedence(left())}; - o << derived().Prefix(); - static constexpr Precedence thisPrec{ToPrecedence}; + Precedence lhsPrec{ToPrecedence(left())}; + OperatorSpelling spelling{SpellOperator(derived())}; + o << spelling.prefix; + Precedence thisPrec{ToPrecedence(derived())}; if constexpr (operands == 1) { - bool parens{lhsPrec < Precedence::Constant && - !(thisPrec == Precedence::Not && lhsPrec == Precedence::Relational)}; - o << (parens ? "(" : "") << left() << (parens ? ")" : ""); + if (lhsPrec <= thisPrec) { + o << '(' << left() << ')'; + } else { + o << left(); + } } else { - bool lhsParens{lhsPrec == Precedence::Parenthesize || lhsPrec < thisPrec || - (lhsPrec == thisPrec && lhsPrec == Precedence::Power) || - (thisPrec != Precedence::Additive && lhsPrec == Precedence::Constant && - IsNegatedScalarConstant(left()))}; - o << (lhsParens ? "(" : "") << left() << (lhsParens ? ")" : ""); - o << derived().Infix(); - Precedence rhsPrec{GetPrecedence(right())}; - bool rhsParens{rhsPrec == Precedence::Parenthesize || - rhsPrec == Precedence::Negate || rhsPrec < thisPrec || - (rhsPrec == Precedence::Constant && IsNegatedScalarConstant(right()))}; - o << (rhsParens ? "(" : "") << right() << (rhsParens ? ")" : ""); + if (lhsPrec == Precedence::Parenthesize) { + o << left(); + } else if (lhsPrec < thisPrec || + (lhsPrec == Precedence::Power && thisPrec == Precedence::Power)) { + o << '(' << left() << ')'; + } else { + o << left(); + } + o << spelling.infix; + Precedence rhsPrec{ToPrecedence(right())}; + if (rhsPrec == Precedence::Parenthesize) { + o << right(); + } else if (rhsPrec < thisPrec) { + o << '(' << right() << ')'; + } else { + o << right(); + } } - return o << derived().Suffix(); + return o << spelling.suffix; } template @@ -287,19 +367,11 @@ std::ostream &Convert::AsFortran(std::ostream &o) const { return o << ",kind=" << TO::kind << ')'; } -template const char *Relational::Infix() const { - return common::AsFortran(opr); -} - std::ostream &Relational::AsFortran(std::ostream &o) const { std::visit([&](const auto &rel) { rel.AsFortran(o); }, u); return o; } -template const char *LogicalOperation::Infix() const { - return AsFortran(logicalOperator); -} - template std::ostream &EmitArray(std::ostream &o, const Expr &expr) { return expr.AsFortran(o); diff --git a/flang/lib/parser/unparse.cc b/flang/lib/parser/unparse.cc index f68856edb7ea..e13118926993 100644 --- a/flang/lib/parser/unparse.cc +++ b/flang/lib/parser/unparse.cc @@ -847,7 +847,9 @@ public: } void Unparse(const AssignmentStmt &x) { // R1032 if (asFortran_ && x.typedAssignment.get()) { + Put(' '); asFortran_->assignment(out_, *x.typedAssignment); + Put('\n'); } else { Walk(x.t, " = "); } diff --git a/flang/lib/semantics/assignment.cc b/flang/lib/semantics/assignment.cc index 57e55a669c00..526fe47dde45 100644 --- a/flang/lib/semantics/assignment.cc +++ b/flang/lib/semantics/assignment.cc @@ -359,19 +359,21 @@ private: }; void AssignmentContext::Analyze(const parser::AssignmentStmt &stmt) { - const auto &lhs{std::get(stmt.t)}; - const auto &rhs{std::get(stmt.t)}; - auto lhsExpr{AnalyzeExpr(context_, lhs)}; - auto rhsExpr{AnalyzeExpr(context_, rhs)}; - CheckForImpureCall(lhsExpr); - CheckForImpureCall(rhsExpr); - // TODO: preserve analyzed typed expressions - if (forall_) { - // TODO: Warn if some name in forall_->activeNames or its outer - // contexts does not appear on LHS - } - if (lhsExpr && rhsExpr) { - CheckForPureContext(*lhsExpr, *rhsExpr, rhs.source, false /* not => */); + // Assignment statement analysis is in expression.cc where user-defined + // assignments can be recognized and replaced. + if (const evaluate::Assignment * + asst{AnalyzeAssignmentStmt(context_, stmt)}) { + if (const auto *intrinsicAsst{ + std::get_if(&asst->u)}) { + CheckForImpureCall(intrinsicAsst->lhs); + CheckForImpureCall(intrinsicAsst->rhs); + if (forall_) { + // TODO: Warn if some name in forall_->activeNames or its outer + // contexts does not appear on LHS + } + CheckForPureContext(intrinsicAsst->lhs, intrinsicAsst->rhs, + std::get(stmt.t).source, false /* not => */); + } } // TODO: Fortran 2003 ALLOCATABLE assignment semantics (automatic // (re)allocation of LHS array when unallocated or nonconformable) @@ -381,8 +383,9 @@ void AssignmentContext::Analyze(const parser::PointerAssignmentStmt &stmt) { CHECK(!where_); const auto &lhs{std::get(stmt.t)}; const auto &rhs{std::get(stmt.t)}; - auto lhsExpr{AnalyzeExpr(context_, lhs)}; - auto rhsExpr{AnalyzeExpr(context_, rhs)}; + auto &foldingContext{context_.foldingContext()}; + auto lhsExpr{evaluate::Fold(foldingContext, AnalyzeExpr(context_, lhs))}; + auto rhsExpr{evaluate::Fold(foldingContext, AnalyzeExpr(context_, rhs))}; CheckForImpureCall(lhsExpr); CheckForImpureCall(rhsExpr); // TODO: CheckForImpureCall() in the bounds / bounds remappings diff --git a/flang/lib/semantics/expression.cc b/flang/lib/semantics/expression.cc index cb60f4fa52f7..34194838c575 100644 --- a/flang/lib/semantics/expression.cc +++ b/flang/lib/semantics/expression.cc @@ -350,8 +350,7 @@ MaybeExpr ExpressionAnalyzer::Analyze(const parser::Designator &d) { // These checks have to be deferred to these "top level" data-refs where // we can be sure that there are no following subscripts (yet). if (MaybeExpr result{Analyze(d.u)}) { - if (std::optional dataRef{ - evaluate::ExtractDataRef(std::move(result))}) { + if (std::optional dataRef{ExtractDataRef(std::move(result))}) { return TopLevelChecks(std::move(*dataRef)); } return result; @@ -1853,16 +1852,20 @@ MaybeExpr ExpressionAnalyzer::AnalyzeCall( return std::nullopt; } -void ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { - ArgumentAnalyzer analyzer{*this}; - analyzer.Analyze(std::get(x.t)); - analyzer.Analyze(std::get(x.t)); - if (!analyzer.fatalErrors()) { - std::optional procRef{analyzer.TryDefinedAssignment()}; - x.typedAssignment.reset(new GenericAssignmentWrapper{procRef - ? Assignment{std::move(*procRef)} - : Assignment{analyzer.MoveExpr(0), analyzer.MoveExpr(1)}}); +const Assignment *ExpressionAnalyzer::Analyze(const parser::AssignmentStmt &x) { + if (!x.typedAssignment) { + ArgumentAnalyzer analyzer{*this}; + analyzer.Analyze(std::get(x.t)); + analyzer.Analyze(std::get(x.t)); + if (!analyzer.fatalErrors()) { + std::optional procRef{analyzer.TryDefinedAssignment()}; + x.typedAssignment.reset(new GenericAssignmentWrapper{procRef + ? Assignment{std::move(*procRef)} + : Assignment{Fold(foldingContext_, analyzer.MoveExpr(0)), + Fold(foldingContext_, analyzer.MoveExpr(1))}}); + } } + return x.typedAssignment ? &x.typedAssignment->v : nullptr; } static bool IsExternalCalledImplicitly( @@ -2824,9 +2827,9 @@ void AnalyzeCallStmt(SemanticsContext &context, const parser::CallStmt &call) { evaluate::ExpressionAnalyzer{context}.Analyze(call); } -void AnalyzeAssignmentStmt( +const evaluate::Assignment *AnalyzeAssignmentStmt( SemanticsContext &context, const parser::AssignmentStmt &stmt) { - evaluate::ExpressionAnalyzer{context}.Analyze(stmt); + return evaluate::ExpressionAnalyzer{context}.Analyze(stmt); } ExprChecker::ExprChecker(SemanticsContext &context) : context_{context} {} diff --git a/flang/lib/semantics/expression.h b/flang/lib/semantics/expression.h index f016db0a4df9..f1b16c0bb2cc 100644 --- a/flang/lib/semantics/expression.h +++ b/flang/lib/semantics/expression.h @@ -238,7 +238,7 @@ public: MaybeExpr Analyze(const parser::StructureComponent &); void Analyze(const parser::CallStmt &); - void Analyze(const parser::AssignmentStmt &); + const Assignment *Analyze(const parser::AssignmentStmt &); protected: int IntegerTypeSpecKind(const parser::IntegerTypeSpec &); @@ -386,7 +386,8 @@ evaluate::Expr AnalyzeKindSelector( const std::optional &); void AnalyzeCallStmt(SemanticsContext &, const parser::CallStmt &); -void AnalyzeAssignmentStmt(SemanticsContext &, const parser::AssignmentStmt &); +const evaluate::Assignment *AnalyzeAssignmentStmt( + SemanticsContext &, const parser::AssignmentStmt &); // Semantic analysis of all expressions in a parse tree, which becomes // decorated with typed representations for top-level expressions. diff --git a/flang/tools/f18/f18.cc b/flang/tools/f18/f18.cc index ba5176588253..32ffa7c4e26a 100644 --- a/flang/tools/f18/f18.cc +++ b/flang/tools/f18/f18.cc @@ -451,6 +451,9 @@ int main(int argc, char *const argv[]) { options.isFixedForm = false; } else if (arg == "-Mextend") { options.fixedFormColumns = 132; + } else if (arg == "-Munlimited") { + // For reparsing f18's -E output of fixed-form cooked character stream + options.fixedFormColumns = 1000000; } else if (arg == "-Mbackslash") { options.features.Enable( Fortran::common::LanguageFeature::BackslashEscapes, false);