// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "expression.h" #include "common.h" #include "int-power.h" #include "variable.h" #include "../common/idioms.h" #include "../parser/characters.h" #include "../parser/message.h" #include #include #include using namespace Fortran::parser::literals; namespace Fortran::evaluate { template std::ostream &Operation::Dump(std::ostream &o) const { operand<0>().Dump(o << derived().prefix_); if constexpr (operands() > 1) { operand<1>().Dump(o << infix_); } return o << derived().suffix_; } // TODO: dump Convert as INT(x,KIND=), &c. template auto Operation::Fold(FoldingContext &context) -> std::optional> { auto c0{operand<0>().Fold(context)}; if constexpr (operands() == 1) { if (c0.has_value()) { return derived().FoldScalar(context, *c0); } } else { auto c1{operand<1>().Fold(context)}; if (c0.has_value() && c1.has_value()) { return derived().FoldScalar(context, *c0, *c1); } } return std::nullopt; } template auto Convert::FoldScalar(FoldingContext &context, const Scalar &c) -> std::optional> { if constexpr (std::is_same_v) { return {c}; } else if constexpr (std::is_same_v) { using Generic = SomeKind; if constexpr (std::is_same_v) { return {Scalar{c}}; } else { return {Scalar{Generic{c}}}; } } else if constexpr (std::is_same_v) { return std::visit( [&](const auto &x) -> std::optional> { using Ty = std::decay_t; return Convert::FoldScalar(context, x); }, c.u); } else if constexpr (std::is_same_v>) { if constexpr (Result::category == Operand::category) { return {Scalar{c}}; } } else if constexpr (std::is_same_v>) { return std::visit( [&](const auto &x) -> std::optional> { using Ty = ScalarValueType>; return Convert::FoldScalar(context, x); }, c.u); } else if constexpr (Result::category == TypeCategory::Integer) { if constexpr (Operand::category == TypeCategory::Integer) { auto converted{Scalar::ConvertSigned(c)}; if (converted.overflow) { context.messages.Say("INTEGER to INTEGER conversion overflowed"_en_US); } else { return {std::move(converted.value)}; } } else if constexpr (Operand::category == TypeCategory::Real) { auto converted{c.template ToInteger>()}; if (converted.flags.test(RealFlag::InvalidArgument)) { context.messages.Say( "REAL to INTEGER conversion: invalid argument"_en_US); } else if (converted.flags.test(RealFlag::Overflow)) { context.messages.Say("REAL to INTEGER conversion overflowed"_en_US); } else { return {std::move(converted.value)}; } } } else if constexpr (Result::category == TypeCategory::Real) { if constexpr (Operand::category == TypeCategory::Integer) { auto converted{Scalar::FromInteger(c)}; RealFlagWarnings(context, converted.flags, "INTEGER to REAL conversion"); return {std::move(converted.value)}; } else if constexpr (Operand::category == TypeCategory::Real) { auto converted{Scalar::Convert(c)}; RealFlagWarnings(context, converted.flags, "REAL to REAL conversion"); return {std::move(converted.value)}; } } return std::nullopt; } template auto Negate::FoldScalar(FoldingContext &context, const Scalar &c) -> std::optional> { if constexpr (Result::category == TypeCategory::Integer) { auto negated{c.Negate()}; if (negated.overflow) { context.messages.Say("INTEGER negation overflowed"_en_US); } else { return {std::move(negated.value)}; } } else { return {c.Negate()}; // REAL & COMPLEX: no exceptions possible } return std::nullopt; } template auto ComplexComponent::FoldScalar(FoldingContext &context, const Scalar &z) -> std::optional> { return {isRealPart ? z.REAL() : z.AIMAG()}; } template auto Not::FoldScalar(FoldingContext &context, const Scalar &x) -> std::optional> { return {Scalar{!x.IsTrue()}}; } // Dumping template std::ostream &DumpExpr(std::ostream &o, const std::variant &u) { std::visit(common::visitors{[&](const BOZLiteralConstant &x) { o << "Z'" << x.Hexadecimal() << "'"; }, [&](const auto &x) { x.Dump(o); }}, u); return o; } template std::ostream &Expr>::Dump(std::ostream &o) const { return DumpExpr(o, u); } template std::ostream &CategoryComparison::Dump(std::ostream &o) const { return DumpExpr(o, u); } std::ostream &Expr::Dump(std::ostream &o) const { return DumpExpr(o, u); } template std::ostream &Binary::Dump( std::ostream &o, const char *opr, const char *before) const { return right().Dump(left().Dump(o << before) << opr) << ')'; } template std::ostream &Expr>::Dump( std::ostream &o) const { std::visit( common::visitors{[&](const Scalar &n) { o << n.SignedDecimal(); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const Add &a) { a.Dump(o, "+"); }, [&](const Subtract &s) { s.Dump(o, "-"); }, [&](const Multiply &m) { m.Dump(o, "*"); }, [&](const Divide &d) { d.Dump(o, "/"); }, [&](const Power &p) { p.Dump(o, "**"); }, [&](const Max &m) { m.Dump(o, ",", "MAX("); }, [&](const Min &m) { m.Dump(o, ",", "MIN("); }, [&](const auto &x) { x.Dump(o); }}, u_); return o; } template std::ostream &Expr>::Dump( std::ostream &o) const { std::visit(common::visitors{[&](const Scalar &n) { o << n.DumpHexadecimal(); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const Convert &c) { c.Dump(o); }, [&](const Convert &c) { c.Dump(o); }, [&](const ComplexComponent &z) { z.Dump(o); }, [&](const ComplexComponent &z) { z.Dump(o); }, [&](const Parentheses &p) { p.Dump(o); }, [&](const Negate &n) { n.Dump(o); }, [&](const Add &a) { a.Dump(o, "+"); }, [&](const Subtract &s) { s.Dump(o, "-"); }, [&](const Multiply &m) { m.Dump(o, "*"); }, [&](const Divide &d) { d.Dump(o, "/"); }, [&](const Power &p) { p.Dump(o, "**"); }, [&](const IntPower &p) { p.Dump(o, "**"); }, [&](const Max &m) { m.Dump(o, ",", "MAX("); }, [&](const Min &m) { m.Dump(o, ",", "MIN("); }}, u_); return o; } template std::ostream &Expr>::Dump( std::ostream &o) const { std::visit(common::visitors{[&](const Scalar &n) { o << n.DumpHexadecimal(); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const Add &a) { a.Dump(o, "+"); }, [&](const Subtract &s) { s.Dump(o, "-"); }, [&](const Multiply &m) { m.Dump(o, "*"); }, [&](const Divide &d) { d.Dump(o, "/"); }, [&](const Power &p) { p.Dump(o, "**"); }, [&](const IntPower &p) { p.Dump(o, "**"); }, [&](const CMPLX &c) { c.Dump(o, ","); }, [&](const auto &x) { x.Dump(o); }}, u_); return o; } template std::ostream &Expr>::Dump( std::ostream &o) const { std::visit(common::visitors{[&](const Scalar &s) { o << parser::QuoteCharacterLiteral(s); }, // [&](const Parentheses &p) { p.Dump(o); }, [&](const Concat &concat) { concat.Dump(o, "//"); }, [&](const Max &m) { m.Dump(o, ",", "MAX("); }, [&](const Min &m) { m.Dump(o, ",", "MIN("); }, [&](const auto &ind) { ind->Dump(o); }}, u_); return o; } template std::ostream &Comparison::Dump(std::ostream &o) const { o << '(' << A::Dump() << "::"; this->left().Dump(o); o << '.' << EnumToString(this->opr) << '.'; return this->right().Dump(o) << ')'; } template std::ostream &Expr>::Dump( std::ostream &o) const { std::visit(common::visitors{[&](const Scalar &tf) { o << (tf.IsTrue() ? ".TRUE." : ".FALSE."); }, [&](const CopyableIndirection &d) { d->Dump(o); }, [&](const CopyableIndirection &d) { d->Dump(o); }, // [&](const Parentheses &p) { p.Dump(o); }, [&](const Not &n) { n.Dump(o); }, [&](const And &a) { a.Dump(o, ".AND."); }, [&](const Or &a) { a.Dump(o, ".OR."); }, [&](const Eqv &a) { a.Dump(o, ".EQV."); }, [&](const Neqv &a) { a.Dump(o, ".NEQV."); }, [&](const auto &comparison) { comparison.Dump(o); }}, u_); return o; } // LEN() template Expr Expr>::LEN() const { return std::visit( common::visitors{[](const Scalar &c) { // std::string::size_type isn't convertible to uint64_t // on Darwin return Expr{ static_cast(c.size())}; }, [](const Concat &c) { return c.left().LEN() + c.right().LEN(); }, [](const Max &c) { return Expr{ Expr::Max{c.left().LEN(), c.right().LEN()}}; }, [](const Min &c) { return Expr{ Expr::Max{c.left().LEN(), c.right().LEN()}}; }, [](const CopyableIndirection &dr) { return dr->LEN(); }, [](const CopyableIndirection &ss) { return ss->LEN(); }, [](const CopyableIndirection &fr) { return fr->proc().LEN(); }}, u_); } // Rank template int Binary::Rank() const { int lrank{left_.Rank()}; if (lrank > 0) { return lrank; } return right_.Rank(); } // Folding template auto Binary::Fold(FoldingContext &context) -> std::optional> { std::optional> lc{left_->Fold(context)}; std::optional> rc{right_->Fold(context)}; if (lc.has_value() && rc.has_value()) { return static_cast(this)->FoldScalar(context, *lc, *rc); } return std::nullopt; } template auto Expr>::Add::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto sum{a.AddSigned(b)}; if (sum.overflow) { context.messages.Say("integer addition overflowed"_en_US); return std::nullopt; } return {std::move(sum.value)}; } template auto Expr>::Subtract::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto diff{a.SubtractSigned(b)}; if (diff.overflow) { context.messages.Say("integer subtraction overflowed"_en_US); return std::nullopt; } return {std::move(diff.value)}; } template auto Expr>::Multiply::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto product{a.MultiplySigned(b)}; if (product.SignedMultiplicationOverflowed()) { context.messages.Say("integer multiplication overflowed"_en_US); return std::nullopt; } return {std::move(product.lower)}; } template auto Expr>::Divide::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto qr{a.DivideSigned(b)}; if (qr.divisionByZero) { context.messages.Say("integer division by zero"_en_US); return std::nullopt; } if (qr.overflow) { context.messages.Say("integer division overflowed"_en_US); return std::nullopt; } return {std::move(qr.quotient)}; } template auto Expr>::Power::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { typename Scalar::PowerWithErrors power{a.Power(b)}; if (power.divisionByZero) { context.messages.Say("zero to negative power"_en_US); return std::nullopt; } if (power.overflow) { context.messages.Say("integer power overflowed"_en_US); return std::nullopt; } if (power.zeroToZero) { context.messages.Say("integer 0**0"_en_US); return std::nullopt; } return {std::move(power.power)}; } template auto Expr>::Max::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (a.CompareSigned(b) == Ordering::Greater) { return {a}; } return {b}; } template auto Expr>::Min::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (a.CompareSigned(b) == Ordering::Less) { return {a}; } return {b}; } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { if (auto c{ScalarValue()}) { return c; } return std::visit( [&](auto &x) -> std::optional> { using Ty = std::decay_t; if constexpr (evaluate::FoldableTrait) { if (auto c{x.Fold(context)}) { if constexpr (std::is_same_v>) { // Preserve parentheses around constants. u_ = Parentheses{Expr{*c}}; } else { u_ = *c; } return c; } } return std::nullopt; }, u_); } template auto Expr>::Add::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto sum{a.Add(b, context.rounding)}; RealFlagWarnings(context, sum.flags, "real addition"); return {std::move(sum.value)}; } template auto Expr>::Subtract::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto difference{a.Subtract(b, context.rounding)}; RealFlagWarnings(context, difference.flags, "real subtraction"); return {std::move(difference.value)}; } template auto Expr>::Multiply::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto product{a.Multiply(b, context.rounding)}; RealFlagWarnings(context, product.flags, "real multiplication"); return {std::move(product.value)}; } template auto Expr>::Divide::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto quotient{a.Divide(b, context.rounding)}; RealFlagWarnings(context, quotient.flags, "real division"); return {std::move(quotient.value)}; } template auto Expr>::Power::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return std::nullopt; // TODO } template auto Expr>::IntPower::FoldScalar( FoldingContext &context, const Scalar &a, const SomeKindScalar &b) -> std::optional> { return std::visit( [&](const auto &pow) -> std::optional> { auto power{evaluate::IntPower(a, pow)}; RealFlagWarnings(context, power.flags, "raising to integer power"); return {std::move(power.value)}; }, b.u); } template auto Expr>::Max::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (b.IsNotANumber() || a.Compare(b) == Relation::Less) { return {b}; } return {a}; } template auto Expr>::Min::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (b.IsNotANumber() || a.Compare(b) == Relation::Greater) { return {b}; } return {a}; } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { if (auto c{ScalarValue()}) { return c; } return std::visit( [&](auto &x) -> std::optional> { using Ty = std::decay_t; if constexpr (evaluate::FoldableTrait) { if (auto c{x.Fold(context)}) { if (context.flushDenormalsToZero) { *c = c->FlushDenormalToZero(); } if constexpr (std::is_same_v>) { // Preserve parentheses around constants. u_ = Parentheses{Expr{*c}}; } else { u_ = *c; } return c; } } return std::nullopt; }, u_); } template auto Expr>::Add::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto sum{a.Add(b, context.rounding)}; RealFlagWarnings(context, sum.flags, "complex addition"); return {std::move(sum.value)}; } template auto Expr>::Subtract::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto difference{a.Subtract(b, context.rounding)}; RealFlagWarnings(context, difference.flags, "complex subtraction"); return {std::move(difference.value)}; } template auto Expr>::Multiply::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto product{a.Multiply(b, context.rounding)}; RealFlagWarnings(context, product.flags, "complex multiplication"); return {std::move(product.value)}; } template auto Expr>::Divide::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { auto quotient{a.Divide(b, context.rounding)}; RealFlagWarnings(context, quotient.flags, "complex division"); return {std::move(quotient.value)}; } template auto Expr>::Power::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return std::nullopt; // TODO } template auto Expr>::IntPower::FoldScalar( FoldingContext &context, const Scalar &a, const SomeKindScalar &b) -> std::optional> { return std::visit( [&](const auto &pow) -> std::optional> { auto power{evaluate::IntPower(a, pow)}; RealFlagWarnings(context, power.flags, "raising to integer power"); return {std::move(power.value)}; }, b.u); } template auto Expr>::CMPLX::FoldScalar( FoldingContext &context, const Scalar> &a, const Scalar> &b) -> std::optional> { return {Scalar{a, b}}; } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { if (auto c{ScalarValue()}) { return c; } return std::visit( [&](auto &x) -> std::optional> { using Ty = std::decay_t; if constexpr (evaluate::FoldableTrait) { if (auto c{x.Fold(context)}) { if (context.flushDenormalsToZero) { *c = c->FlushDenormalToZero(); } if constexpr (std::is_same_v>) { // Preserve parentheses around constants. u_ = Parentheses{Expr{*c}}; } else { u_ = *c; } return c; } } return std::nullopt; }, u_); } template auto Expr>::Concat::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if constexpr (KIND == 1) { return {a + b}; } return std::nullopt; } template auto Expr>::Max::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (Compare(a, b) == Ordering::Less) { return {b}; } return {a}; } template auto Expr>::Min::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { if (Compare(a, b) == Ordering::Greater) { return {b}; } return {a}; } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { if (auto c{ScalarValue()}) { return c; } return std::visit( [&](auto &x) -> std::optional> { using Ty = std::decay_t; if constexpr (evaluate::FoldableTrait) { if (auto c{x.Fold(context)}) { u_ = *c; return c; } } return std::nullopt; }, u_); } template auto Comparison::FoldScalar(FoldingContext &c, const Scalar &a, const Scalar &b) -> std::optional> { if constexpr (A::category == TypeCategory::Integer) { switch (a.CompareSigned(b)) { case Ordering::Less: return {opr == RelationalOperator::LE || opr == RelationalOperator::LE || opr == RelationalOperator::NE}; case Ordering::Equal: return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ || opr == RelationalOperator::GE}; case Ordering::Greater: return {opr == RelationalOperator::NE || opr == RelationalOperator::GE || opr == RelationalOperator::GT}; } } if constexpr (A::category == TypeCategory::Real) { switch (a.Compare(b)) { case Relation::Less: return {opr == RelationalOperator::LE || opr == RelationalOperator::LE || opr == RelationalOperator::NE}; case Relation::Equal: return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ || opr == RelationalOperator::GE}; case Relation::Greater: return {opr == RelationalOperator::NE || opr == RelationalOperator::GE || opr == RelationalOperator::GT}; case Relation::Unordered: return std::nullopt; } } if constexpr (A::category == TypeCategory::Complex) { bool eqOk{opr == RelationalOperator::LE || opr == RelationalOperator::EQ || opr == RelationalOperator::GE}; return {eqOk == a.Equals(b)}; } if constexpr (A::category == TypeCategory::Character) { switch (Compare(a, b)) { case Ordering::Less: return {opr == RelationalOperator::LE || opr == RelationalOperator::LE || opr == RelationalOperator::NE}; case Ordering::Equal: return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ || opr == RelationalOperator::GE}; case Ordering::Greater: return {opr == RelationalOperator::NE || opr == RelationalOperator::GE || opr == RelationalOperator::GT}; } } return std::nullopt; } template auto Expr>::And::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return {Scalar{a.IsTrue() && b.IsTrue()}}; } template auto Expr>::Or::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return {Scalar{a.IsTrue() || b.IsTrue()}}; } template auto Expr>::Eqv::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return {Scalar{a.IsTrue() == b.IsTrue()}}; } template auto Expr>::Neqv::FoldScalar( FoldingContext &context, const Scalar &a, const Scalar &b) -> std::optional> { return {Scalar{a.IsTrue() != b.IsTrue()}}; } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { if (auto c{ScalarValue()}) { return c; } return std::visit( [&](auto &x) -> std::optional> { using Ty = std::decay_t; if constexpr (evaluate::FoldableTrait) { if (auto c{x.Fold(context)}) { u_ = *c; return c; } } return std::nullopt; }, u_); } auto Expr::ScalarValue() const -> std::optional> { return std::visit( common::visitors{ [](const BOZLiteralConstant &) -> std::optional> { return std::nullopt; }, [](const auto &x) -> std::optional> { if (auto c{x.ScalarValue()}) { return {Scalar{std::move(*c)}}; } return std::nullopt; }}, u); } template auto Expr>::ScalarValue() const -> std::optional> { return std::visit( [](const auto &x) -> std::optional> { if (auto c{x.ScalarValue()}) { return {Scalar{std::move(*c)}}; } return std::nullopt; }, u); } template auto Expr>::Fold(FoldingContext &context) -> std::optional> { return std::visit( [&](auto &x) -> std::optional> { if (auto c{x.Fold(context)}) { return {Scalar{std::move(*c)}}; } return std::nullopt; }, u); } template int Expr>::Rank() const { return std::visit([](const auto &x) { return x.Rank(); }, u); } auto Expr::Fold(FoldingContext &context) -> std::optional> { return std::visit( common::visitors{ [](BOZLiteralConstant &) -> std::optional> { return std::nullopt; }, [&](auto &x) -> std::optional> { if (auto c{x.Fold(context)}) { return {Scalar{std::move(*c)}}; } return std::nullopt; }}, u); } int Expr::Rank() const { // Written thus, instead of common::visitors, to dodge a bug in G++ 7.2. return std::visit( [](const auto &x) { if constexpr (std::is_same_v, BOZLiteralConstant>) { return 1; } else { return x.Rank(); } }, u); } template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr>; // TODO others template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; template struct Comparison>; // TODO others template class Expr>; template class Expr>; template class Expr>; template class Expr>; template class Expr; template class Expr; template class Expr; template class Expr; template class Expr; template class Expr; } // namespace Fortran::evaluate